static inline void handle_setup_packet() { bool all_ok = false; struct setup_packet s; USB_OUT_read_buffer(&s, 8); /* acknowledge setup *after* reading, because it clears the bank */ USB_ack_SETUP(); /* process all Standard Device Requests */ if (request_type(&s, TYPE | RECIPIENT, STANDARD | DEVICE)) { all_ok = process_standard_device_requests(&s); } else if (request_type(&s, TYPE | RECIPIENT, STANDARD | INTERFACE)) { all_ok = process_standard_interface_requests(&s); } else if (request_type(&s, TYPE | RECIPIENT, STANDARD | ENDPOINT)) { all_ok = process_standard_endpoint_requests(&s); } else if (request_type(&s, TYPE | RECIPIENT, CLASS | INTERFACE)) { all_ok = process_class_interface_requests(&s); } if (!all_ok) USB_stall_endpoint(); }
/* static inline just for size optimization */ static inline bool process_class_interface_requests(struct setup_packet *s) { bool found = false; for (uint8_t i = 0; i < NUM_INTERFACE_REQUEST_HANDLERS; ++i) { if (get_pgm_struct_field(&iface_req_handlers[i], iface_num) == (s->wIndex & 0xFF)) { interface_request_handler_fun handler = (void*)(uint16_t) get_pgm_struct_field(&iface_req_handlers[i], f); found = (*handler)(s); break; } } if (!found) return false; if (request_type(s, DIRECTION, DEVICE_TO_HOST)) USB_control_read_complete_status_stage(); else USB_control_write_complete_status_stage(); return true; }
int process_logline(request * req) { char *stop, *stop2; static char *SIMPLE_HTTP_VERSION = "HTTP/0.9"; req->logline = req->header_line; req->method = request_type(req); if (req->method == M_INVALID || req->method == M_SHORT) { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "malformed request: \"%s\"\n", req->logline); #endif syslog(LOG_ERR, "malformed request: \"%s\" from %s\n", req->logline, req->remote_ip_addr); send_r_bad_request(req); return 0; } /* Guaranteed to find ' ' since we matched a method above */ stop = req->logline + 3; if (*stop != ' ') ++stop; /* scan to start of non-whitespace */ while (*(++stop) == ' '); stop2 = stop; /* scan to end of non-whitespace */ while (*stop2 != '\0' && *stop2 != ' ') ++stop2; if (stop2 - stop > MAX_HEADER_LENGTH) { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH, req->logline); #endif syslog(LOG_ERR, "URI too long %d: \"%s\" from %s\n", MAX_HEADER_LENGTH, req->logline, req->remote_ip_addr); send_r_bad_request(req); return 0; } memcpy(req->request_uri, stop, stop2 - stop); req->request_uri[stop2 - stop] = '\0'; if (*stop2 == ' ') { /* if found, we should get an HTTP/x.x */ int p1, p2; if (sscanf(++stop2, "HTTP/%d.%d", &p1, &p2) == 2 && p1 >= 1) { req->http_version = stop2; req->simple = 0; } else { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2); #endif syslog(LOG_ERR, "bogus HTTP version: \"%s\" from %s\n", stop2, req->remote_ip_addr); send_r_bad_request(req); return 0; } } else { req->http_version = SIMPLE_HTTP_VERSION; req->simple = 1; } if (req->method == M_HEAD && req->simple) { syslog(LOG_ERR, "Simple HEAD request not allowed from %s\n", req->remote_ip_addr); send_r_bad_request(req); return 0; } create_env(req); /* create cgi env[], we don't know if url is cgi */ return 1; }
int read_header(request * req) { int bytes, buf_bytes_left; char *check, *buffer; if (req->pipeline_start){ buffer = req->client_stream; bytes = req->client_stream_pos = req->pipeline_start; req->pipeline_start = 0; } else { /* first from free_request */ buffer = req->client_stream + req->client_stream_pos; buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos; if (buf_bytes_left < 0) { #ifdef BOA_TIME_LOG log_error_time(); fputs("buffer overrun - read.c, read_header - closing\n", stderr); #endif return 0; } /*MN stuff to support ssl*/ #ifdef SERVER_SSL if(req->ssl == NULL){ #endif /*SERVER_SSL*/ bytes = read(req->fd, buffer, buf_bytes_left); #ifdef SERVER_SSL } else{ char tmp; bytes = SSL_read(req->ssl, buffer, buf_bytes_left); #if 0 printf("SSL_read 1\n"); if(bytes > 0){ tmp = buffer[bytes-1]; buffer[bytes-1] = '\0'; printf("buffer-\n %s\n----------\n", buffer); buffer[bytes-1] = tmp; } #endif /*0*/ } #endif /*SERVER_SSL*/ if (bytes == -1) { if (errno == EINTR) return 1; if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */ return -1; else if (errno == EBADF || errno == EPIPE) { SQUASH_KA(req); /* force close fd */ return 0; } else { #if 0 boa_perror(req, "header read"); #endif return 0; } } else if (bytes == 0) return 0; req->client_stream_pos += bytes; } check = buffer; while (check < (buffer + bytes)) { switch (req->status) { case READ_HEADER: if (*check == '\r') { req->status = ONE_CR; req->header_end = check; } else if (*check == '\n') { req->status = ONE_LF; req->header_end = check; } break; case ONE_CR: if (*check == '\n') req->status = ONE_LF; else req->status = READ_HEADER; break; case ONE_LF: /* if here, we've found the end (for sure) of a header */ if (*check == '\r') /* could be end o headers */ req->status = TWO_CR; else if (*check == '\n') req->status = BODY_READ; else req->status = READ_HEADER; break; case TWO_CR: if (*check == '\n') req->status = BODY_READ; else req->status = READ_HEADER; break; default: break; } ++check; if (req->status == ONE_LF) { *req->header_end = '\0'; /* terminate string that begins at req->header_line */ /* (or at req->data_mem, if we've never been here before */ /* the following logic still needs work, esp. after req->simple */ if (req->logline) process_option_line(req); else { if (process_logline(req) == 0) return 0; if (req->simple) return process_header_end(req); } req->header_line = check; /* start of unprocessed data */ } else if (req->status == BODY_READ) { int retval = process_header_end(req); /* process_header_end inits non-POST cgi's */ req->pipeline_start = (check - req->client_stream); if (retval && req->method == M_POST) { /* rest of non-header data is contained in the area following check check now points to data */ if (req->content_length) req->filesize = atoi(req->content_length); else { #ifdef BOA_TIME_LOG log_error_time(); fprintf(stderr, "Unknown Content-Length POST\n"); #endif } /* buffer + bytes is 1 past the end of the data */ req->filepos = (buffer + bytes) - check; /* copy the remainder into req->buffer, otherwise * we don't have a full BUFFER_SIZE to play with, * and buffer overruns occur */ memcpy(req->buffer, check, req->filepos); req->header_line = req->buffer; req->header_end = req->buffer + req->filepos; if (req->filepos >= req->filesize) req->cgi_status = CGI_CLOSE; /* close after write */ } return retval; /* 0 - close it done, 1 - keep on ready */ } } /* check for bogus requests */ if (req->status == READ_HEADER && request_type(req) == M_INVALID) { syslog(LOG_ERR, "malformed request: \"%s\"\n", req->logline); send_r_bad_request(req); return 0; } /* only reached if request is split across more than one packet */ return 1; }
void webhit(struct hitArgs *args) { int j; http_verb type; long i, body_size = 0, request_size = 0, body_start, headers_end; char tmp_buf[READ_BUF_LEN+1]; char *body; struct http_header content_length; args->buffer = new_string(READ_BUF_LEN); // we need to read the HTTP headers first... // so loop until we receive "\r\n\r\n" while (get_body_start(string_chars(args->buffer)) < 0 && args->buffer->used_bytes <= MAX_INCOMING_REQUEST) { memset(tmp_buf, 0, READ_BUF_LEN+1); request_size += read(args->socketfd, tmp_buf, READ_BUF_LEN); string_add(args->buffer, tmp_buf); if (tmp_buf[0]==0) break; } if (request_size == 0) { finish_hit(args, 3); return; } content_length = get_header("Content-Length", string_chars(args->buffer)); args->content_length = atoi(content_length.value); body_start = get_body_start(string_chars(args->buffer)); headers_end = body_start-4; if (headers_end > 0) { args->headers = mallocx((int)headers_end+1); strncpy(args->headers, string_chars(args->buffer), headers_end); args->headers[headers_end]=0; } else { args->headers = mallocx(1); args->headers[0] = 0; } if (body_start >= 0) { body_size = request_size - body_start; } // safari seems to send the headers, and then the body slightly later while (body_size < args->content_length && args->buffer->used_bytes <= MAX_INCOMING_REQUEST) { memset(tmp_buf, 0, READ_BUF_LEN+1); i = read(args->socketfd, tmp_buf, READ_BUF_LEN); if (i>0) { request_size += i; string_add(args->buffer, tmp_buf); body_size = request_size - body_start; } else { // stop looping if we cannot read any more bytes break; } } if (request_size <= 0) { // cannot read request, so we'll stop forbidden_403(args, "failed to read http request"); finish_hit(args, 3); return; } args->logger_function(LOG, "request", string_chars(args->buffer), args->hit); if (type = request_type(string_chars(args->buffer)), type == HTTP_NOT_SUPPORTED) { forbidden_403(args, "Only simple GET and POST operations are supported"); finish_hit(args, 3); return; } // get a pointer to the request body (or NULL if it's not there) body = (type==HTTP_GET) ? NULL : args->buffer->ptr+get_body_start(string_chars(args->buffer)); // the request will be "GET [URL] " or "POST [URL] " followed by other details // we will terminate after the second space, to ignore everything else for (i = (type==HTTP_GET) ? 4 : 5; i < args->buffer->used_bytes; i++) { if (string_chars(args->buffer)[i] == ' ') { string_chars(args->buffer)[i] = 0; // second space, terminate string here break; } } j = (type==HTTP_GET) ? 4 : 5; // check for an absolute directory if (string_chars(args->buffer)[j+1] == '/') { forbidden_403(args, "Sorry, absolute paths are not permitted"); finish_hit(args, 3); return; } for (; j<i-1; j++) { // check for any parent directory use if (string_chars(args->buffer)[j] == '.' && string_chars(args->buffer)[j+1] == '.') { forbidden_403(args, "Sorry, parent paths (..) are not permitted"); finish_hit(args, 3); return; } } struct http_header ctype = get_header("Content-Type", args->headers); j = (int)strlen(ctype.value); if (j > 0) { args->content_type = mallocx(j+1); strncpy(args->content_type, ctype.value, j); if (string_matches_value(args->content_type, "application/x-www-form-urlencoded")) { get_form_values(args, body); } } else { args->content_type = mallocx(1); args->content_type[0] = 0; } // call the "responder function" which has been provided to do the rest args->responder_function(args, string_chars(args->buffer) + ((type==HTTP_GET) ? 5 : 6), body, type); finish_hit(args, 1); }