int mk_http_send_file(struct client_session *cs, struct session_request *sr) { long int nbytes = 0; nbytes = mk_socket_send_file(cs->socket, sr->fd_file, &sr->bytes_offset, sr->bytes_to_send); if (nbytes > 0) { sr->bytes_to_send -= nbytes; if (sr->bytes_to_send == 0) { mk_server_cork_flag(cs->socket, TCP_CORK_OFF); } } sr->loop++; /* * In some circumstances when writing data, the connection can get broken. * So, we must be aware of that. * * Also, if for some reason the file that is being served changes it's size * we can get a zero bytes send as return value. We need to validate the * return values <= zero */ if (mk_unlikely(nbytes <= 0)) { MK_TRACE("sendfile() = -1;"); return EXIT_ABORT; } return sr->bytes_to_send; }
int mk_socket_ip_str(int socket_fd, char **buf, int size, unsigned long *len) { int ret; struct sockaddr_storage addr; socklen_t s_len = sizeof(addr); ret = getpeername(socket_fd, (struct sockaddr *) &addr, &s_len); if (mk_unlikely(ret == -1)) { MK_TRACE("[FD %i] Can't get addr for this socket", socket_fd); return -1; } errno = 0; if(addr.ss_family == AF_INET) { if((inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr, *buf, size)) == NULL) { mk_warn("mk_socket_ip_str: Can't get the IP text form (%i)", errno); return -1; } } else if(addr.ss_family == AF_INET6) { if((inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, *buf, size)) == NULL) { mk_warn("mk_socket_ip_str: Can't get the IP text form (%i)", errno); return -1; } } *len = strlen(*buf); return 0; }
ssize_t mk_iov_send(int fd, struct mk_iov *mk_io) { ssize_t n = writev(fd, mk_io->io, mk_io->iov_idx); if (mk_unlikely(n < 0)) { MK_TRACE( "[FD %i] writev() '%s'", fd, strerror(errno)); return -1; } return n; }
static inline struct mk_sched_conn *mk_server_listen_handler(struct mk_sched_worker *sched, void *data) { int ret; int client_fd = -1; struct mk_sched_conn *conn; struct mk_server_listen *listener = data; client_fd = mk_socket_accept(listener->server_fd); if (mk_unlikely(client_fd == -1)) { MK_TRACE("[server] Accept connection failed: %s", strerror(errno)); goto error; } conn = mk_sched_add_connection(client_fd, listener, sched); if (mk_unlikely(!conn)) { goto error; } ret = mk_event_add(sched->loop, client_fd, MK_EVENT_CONNECTION, MK_EVENT_READ, conn); if (mk_unlikely(ret != 0)) { mk_err("[server] Error registering file descriptor: %s", strerror(errno)); goto error; } sched->accepted_connections++; MK_TRACE("[server] New connection arrived: FD %i", client_fd); return conn; error: if (client_fd != -1) { listener->network->network->close(client_fd); } return NULL; }
int _mkp_network_io_send_file(int socket_fd, int file_fd, off_t *file_offset, size_t file_count) { ssize_t bytes_written = -1; bytes_written = sendfile(socket_fd, file_fd, file_offset, file_count); if (mk_unlikely(bytes_written == -1)) { PLUGIN_TRACE("[FD %i] error from sendfile() = -1", socket_fd); return -1; } return bytes_written; }
ssize_t mk_iov_send(int fd, struct mk_iov *mk_io) { #ifdef __rtems__ ssize_t n = rtems_writev(fd, mk_io->io, mk_io->iov_idx); #else ssize_t n = writev(fd, mk_io->io, mk_io->iov_idx); #endif if (mk_unlikely(n < 0)) { MK_TRACE( "[FD %i] writev() '%s'", fd, strerror(errno)); return -1; } return n; }
static int mk_utils_gmt_cache_get(char **data, time_t date) { unsigned int i; struct mk_gmt_cache *gcache = mk_cache_get(mk_cache_utils_gmt_text); if (mk_unlikely(!gcache)) { return MK_FALSE; } for (i = 0; i < MK_GMT_CACHES; i++) { if (date == gcache[i].time) { memcpy(*data, gcache[i].text, 32); gcache[i].hits++; return MK_TRUE; } } return MK_FALSE; }
int mk_http_method_get(char *body) { int int_method, pos = 0; int max_len_method = 8; mk_ptr_t method; /* Max method length is 7 (GET/POST/HEAD/PUT/DELETE/OPTIONS) */ pos = mk_string_char_search(body, ' ', max_len_method); if (mk_unlikely(pos <= 2 || pos >= max_len_method)) { return MK_HTTP_METHOD_UNKNOWN; } method.data = body; method.len = (unsigned long) pos; int_method = mk_http_method_check(method); return int_method; }
int mk_buffer_cat(mk_ptr_t *p, char *buf1, int len1, char *buf2, int len2) { /* Validate lengths */ if (mk_unlikely(len1 < 0 || len2 < 0)) { return -1; } /* alloc space */ p->data = (char *) mk_mem_malloc(len1 + len2 + 1); /* copy data */ memcpy(p->data, buf1, len1); memcpy(p->data + len1, buf2, len2); p->data[len1 + len2] = '\0'; /* assign len */ p->len = len1 + len2; return 0; }
void mk_server_loop(int server_fd) { int ret; int remote_fd; /* Activate TCP_DEFER_ACCEPT */ if (mk_socket_set_tcp_defer_accept(server_fd) != 0) { mk_warn("TCP_DEFER_ACCEPT failed"); } /* Rename worker */ mk_utils_worker_rename("monkey: server"); mk_info("HTTP Server started"); while (1) { remote_fd = mk_socket_accept(server_fd); if (mk_unlikely(remote_fd == -1)) { continue; } #ifdef TRACE MK_TRACE("New connection arrived: FD %i", remote_fd); int i; struct sched_list_node *node; node = sched_list; for (i=0; i < config->workers; i++) { MK_TRACE("Worker Status"); MK_TRACE(" WID %i / conx = %llu", node[i].idx, node[i].accepted_connections - node[i].closed_connections); } #endif /* Assign socket to worker thread */ ret = mk_sched_add_client(remote_fd); if (ret == -1) { close(remote_fd); } } }
int mk_http_request_end(int socket) { int ka; struct client_session *cs; struct sched_list_node *sched; sched = mk_sched_get_thread_conf(); cs = mk_session_get(socket); if (!cs) { MK_TRACE("[FD %i] Not found", socket); return -1; } if (mk_unlikely(!sched)) { MK_TRACE("Could not find sched list node :/"); return -1; } /* * We need to ask to http_keepalive if this * connection can continue working or we must * close it. */ ka = mk_http_keepalive_check(cs); mk_request_free_list(cs); if (ka < 0) { MK_TRACE("[FD %i] No KeepAlive mode, remove", socket); mk_session_remove(socket); } else { mk_request_ka_next(cs); mk_epoll_change_mode(sched->epoll_fd, socket, MK_EPOLL_READ, MK_EPOLL_LEVEL_TRIGGERED); return 0; } return -1; }
int create_event(int efd, int fd, int init_mode, unsigned int behavior) { int ret; struct epoll_event event = {0, {0}}; event.data.fd = fd; event.events = EPOLLERR | EPOLLHUP | EPOLLRDHUP; if (behavior == MK_EPOLL_EDGE_TRIGGERED) { event.events |= EPOLLET; } switch (init_mode) { case MK_EPOLL_READ: event.events |= EPOLLIN; break; case MK_EPOLL_WRITE: event.events |= EPOLLOUT; break; case MK_EPOLL_RW: event.events |= EPOLLIN | EPOLLOUT; break; case MK_EPOLL_SLEEP: event.events = 0; break; } /* Add to epoll queue */ ret = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); if (mk_unlikely(ret < 0 && errno != EEXIST)) { perror("epoll_ctl"); return ret; } return ret; }
int mk_http_init(struct client_session *cs, struct session_request *sr) { int ret; int bytes = 0; struct mimetype *mime; MK_TRACE("HTTP Protocol Init"); /* Request to root path of the virtualhost in question */ if (sr->uri_processed.len == 1 && sr->uri_processed.data[0] == '/') { sr->real_path.data = sr->host_conf->documentroot.data; sr->real_path.len = sr->host_conf->documentroot.len; } /* Compose real path */ if (sr->user_home == MK_FALSE) { int len; len = sr->host_conf->documentroot.len + sr->uri_processed.len; if (len < MK_PATH_BASE) { memcpy(sr->real_path_static, sr->host_conf->documentroot.data, sr->host_conf->documentroot.len); memcpy(sr->real_path_static + sr->host_conf->documentroot.len, sr->uri_processed.data, sr->uri_processed.len); sr->real_path_static[len] = '\0'; sr->real_path.data = sr->real_path_static; sr->real_path.len = len; } else { ret = mk_buffer_cat(&sr->real_path, sr->host_conf->documentroot.data, sr->host_conf->documentroot.len, sr->uri_processed.data, sr->uri_processed.len); if (ret < 0) { MK_TRACE("Error composing real path"); return EXIT_ERROR; } } } /* Check backward directory request */ if (memmem(sr->uri_processed.data, sr->uri_processed.len, MK_HTTP_DIRECTORY_BACKWARD, sizeof(MK_HTTP_DIRECTORY_BACKWARD) - 1)) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } if (mk_file_get_info(sr->real_path.data, &sr->file_info) != 0) { /* if the requested resource doesn't exist, * check if some plugin would like to handle it */ MK_TRACE("No file, look for handler plugin"); ret = mk_plugin_stage_run(MK_PLUGIN_STAGE_30, cs->socket, NULL, cs, sr); if (ret == MK_PLUGIN_RET_CLOSE_CONX) { if (sr->headers.status > 0) { return mk_request_error(sr->headers.status, cs, sr); } else { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } } else if (ret == MK_PLUGIN_RET_CONTINUE) { return MK_PLUGIN_RET_CONTINUE; } else if (ret == MK_PLUGIN_RET_END) { return EXIT_NORMAL; } if (sr->file_info.exists == MK_FALSE) { return mk_request_error(MK_CLIENT_NOT_FOUND, cs, sr); } else if (sr->stage30_blocked == MK_FALSE) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } } /* is it a valid directory ? */ if (sr->file_info.is_directory == MK_TRUE) { /* Send redirect header if end slash is not found */ if (mk_http_directory_redirect_check(cs, sr) == -1) { MK_TRACE("Directory Redirect"); /* Redirect has been sent */ return -1; } /* looking for an index file */ mk_ptr_t index_file; char tmppath[MK_MAX_PATH]; index_file = mk_request_index(sr->real_path.data, tmppath, MK_MAX_PATH); if (index_file.data) { if (sr->real_path.data != sr->real_path_static) { mk_ptr_t_free(&sr->real_path); sr->real_path = index_file; sr->real_path.data = mk_string_dup(index_file.data); } /* If it's static and it still fits */ else if (index_file.len < MK_PATH_BASE) { memcpy(sr->real_path_static, index_file.data, index_file.len); sr->real_path_static[index_file.len] = '\0'; sr->real_path.len = index_file.len; } /* It was static, but didn't fit */ else { sr->real_path = index_file; sr->real_path.data = mk_string_dup(index_file.data); } mk_file_get_info(sr->real_path.data, &sr->file_info); } } /* Check symbolic link file */ if (sr->file_info.is_link == MK_TRUE) { if (config->symlink == MK_FALSE) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } else { int n; char linked_file[MK_MAX_PATH]; n = readlink(sr->real_path.data, linked_file, MK_MAX_PATH); if (n < 0) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } } } /* Plugin Stage 30: look for handlers for this request */ if (sr->stage30_blocked == MK_FALSE) { ret = mk_plugin_stage_run(MK_PLUGIN_STAGE_30, cs->socket, NULL, cs, sr); MK_TRACE("[FD %i] STAGE_30 returned %i", cs->socket, ret); switch (ret) { case MK_PLUGIN_RET_CONTINUE: return MK_PLUGIN_RET_CONTINUE; case MK_PLUGIN_RET_CLOSE_CONX: if (sr->headers.status > 0) { return mk_request_error(sr->headers.status, cs, sr); } else { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } case MK_PLUGIN_RET_END: return EXIT_NORMAL; } } /* * Monkey listens for PUT and DELETE methods in addition to GET, POST and * HEAD, but it does not care about them, so if any plugin did not worked * on it, Monkey will return error 501 (501 Not Implemented). */ if (sr->method == MK_HTTP_METHOD_PUT || sr->method == MK_HTTP_METHOD_DELETE || sr->method == MK_HTTP_METHOD_UNKNOWN) { return mk_request_error(MK_SERVER_NOT_IMPLEMENTED, cs, sr); } /* counter connections */ sr->headers.pconnections_left = (int) (config->max_keep_alive_request - cs->counter_connections); /* Set default value */ mk_header_set_http_status(sr, MK_HTTP_OK); sr->headers.location = NULL; sr->headers.content_length = 0; /* * For OPTIONS method, we let the plugin handle it and * return without any content. */ if (sr->method == MK_HTTP_METHOD_OPTIONS) { sr->headers.allow_methods.data = MK_HTTP_METHOD_AVAILABLE; sr->headers.allow_methods.len = strlen(MK_HTTP_METHOD_AVAILABLE); mk_ptr_t_reset(&sr->headers.content_type); mk_header_send(cs->socket, cs, sr); return EXIT_NORMAL; } else { mk_ptr_t_reset(&sr->headers.allow_methods); } /* read permissions and check file */ if (sr->file_info.read_access == MK_FALSE) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } /* Matching MimeType */ mime = mk_mimetype_find(&sr->real_path); if (!mime) { mime = mimetype_default; } if (sr->file_info.is_directory == MK_TRUE) { return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } /* get file size */ if (sr->file_info.size < 0) { return mk_request_error(MK_CLIENT_NOT_FOUND, cs, sr); } sr->headers.last_modified = sr->file_info.last_modification; if (sr->if_modified_since.data && sr->method == MK_HTTP_METHOD_GET) { time_t date_client; /* Date sent by client */ time_t date_file_server; /* Date server file */ date_client = mk_utils_gmt2utime(sr->if_modified_since.data); date_file_server = sr->file_info.last_modification; if (date_file_server <= date_client && date_client > 0 && date_client <= log_current_utime) { mk_header_set_http_status(sr, MK_NOT_MODIFIED); mk_header_send(cs->socket, cs, sr); return EXIT_NORMAL; } } /* Object size for log and response headers */ sr->headers.content_length = sr->file_info.size; sr->headers.real_length = sr->file_info.size; /* Open file */ if (mk_likely(sr->file_info.size > 0)) { sr->fd_file = mk_vhost_open(sr); if (sr->fd_file == -1) { MK_TRACE("open() failed"); return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr); } sr->bytes_to_send = sr->file_info.size; } /* Process methods */ if (sr->method == MK_HTTP_METHOD_GET || sr->method == MK_HTTP_METHOD_HEAD) { sr->headers.content_type = mime->type; /* HTTP Ranges */ if (sr->range.data != NULL && config->resume == MK_TRUE) { if (mk_http_range_parse(sr) < 0) { sr->headers.ranges[0] = -1; sr->headers.ranges[1] = -1; return mk_request_error(MK_CLIENT_BAD_REQUEST, cs, sr); } if (sr->headers.ranges[0] >= 0 || sr->headers.ranges[1] >= 0) { mk_header_set_http_status(sr, MK_HTTP_PARTIAL); } /* Calc bytes to send & offset */ if (mk_http_range_set(sr, sr->file_info.size) != 0) { sr->headers.content_length = -1; sr->headers.ranges[0] = -1; sr->headers.ranges[1] = -1; return mk_request_error(MK_CLIENT_REQUESTED_RANGE_NOT_SATISF, cs, sr); } } } else { /* without content-type */ mk_ptr_t_reset(&sr->headers.content_type); } /* Send headers */ mk_header_send(cs->socket, cs, sr); if (mk_unlikely(sr->headers.content_length == 0)) { return 0; } /* Send file content */ if (sr->method == MK_HTTP_METHOD_GET || sr->method == MK_HTTP_METHOD_POST) { bytes = mk_http_send_file(cs, sr); } return bytes; }
/* Return a struct with method, URI , protocol version and all static headers defined here sent in request */ static int mk_request_header_process(struct session_request *sr) { int uri_init = 0, uri_end = 0; int query_init = 0; int prot_init = 0, prot_end = 0, pos_sep = 0; int fh_limit; char *headers; char *temp = 0; mk_ptr_t host; /* Method */ sr->method_p = mk_http_method_check_str(sr->method); /* Request URI */ temp = index(sr->body.data, ' '); if (mk_unlikely(!temp)) { MK_TRACE("Error, invalid first header"); return -1; } uri_init = (temp - sr->body.data) + 1; temp = index(sr->body.data, '\n'); if (mk_unlikely(!temp)) { MK_TRACE("Error, invalid header CRLF"); return -1; } fh_limit = (temp - sr->body.data); uri_end = mk_string_char_search_r(sr->body.data, ' ', fh_limit) - 1; if (mk_unlikely(uri_end <= 0)) { MK_TRACE("Error, first header bad formed"); return -1; } prot_init = uri_end + 2; if (mk_unlikely(uri_end < uri_init)) { return -1; } /* Query String */ query_init = mk_string_char_search(sr->body.data + uri_init, '?', prot_init); if (query_init > 0) { int init, end; init = query_init + uri_init; if (init <= uri_end) { end = uri_end; uri_end = init - 1; sr->query_string = mk_ptr_create(sr->body.data, init + 1, end + 1); } } /* Request URI Part 2 */ sr->uri = mk_ptr_create(sr->body.data, uri_init, uri_end + 1); if (mk_unlikely(sr->uri.len < 1)) { return -1; } /* HTTP Version */ prot_end = fh_limit - 1; if (mk_unlikely(prot_init == prot_end)) { return -1; } if (prot_end != prot_init && prot_end > 0) { sr->protocol = mk_http_protocol_check(sr->body.data + prot_init, prot_end - prot_init); sr->protocol_p = mk_http_protocol_check_str(sr->protocol); } headers = sr->body.data + prot_end + mk_crlf.len; /* * Process URI, if it contains ASCII encoded strings like '%20', * it will return a new memory buffer with the decoded string, otherwise * it returns NULL */ temp = mk_utils_url_decode(sr->uri); if (temp) { sr->uri_processed.data = temp; sr->uri_processed.len = strlen(temp); } else { sr->uri_processed.data = sr->uri.data; sr->uri_processed.len = sr->uri.len; } /* Creating Table of Content (index) for HTTP headers */ sr->headers_len = sr->body.len - (prot_end + mk_crlf.len); if (mk_request_header_toc_parse(&sr->headers_toc, headers, sr->headers_len) < 0) { MK_TRACE("Invalid headers"); return -1; } /* Host */ host = mk_request_header_get(&sr->headers_toc, mk_rh_host.data, mk_rh_host.len); if (host.data) { if ((pos_sep = mk_string_char_search_r(host.data, ':', host.len)) >= 0) { /* TCP port should not be higher than 65535 */ char *p; short int port_len, port_size = 6; char port[port_size]; /* just the host */ sr->host.data = host.data; sr->host.len = pos_sep; /* including the port */ sr->host_port = host; /* Port string length */ port_len = (host.len - pos_sep - 1); if (port_len >= port_size) { return -1; } /* Copy to buffer */ memcpy(port, host.data + pos_sep + 1, port_len); port[port_len] = '\0'; /* Validate that the input port is numeric */ p = port; while (*p) { if (!isdigit(*p)) return -1; p++; } /* Convert to base 10 */ errno = 0; sr->port = strtol(port, (char **) NULL, 10); if ((errno == ERANGE && (sr->port == LONG_MAX || sr->port == LONG_MIN)) || sr->port == 0) { return -1; } } else { sr->host = host; /* maybe null */ sr->port = config->standard_port; } } else { sr->host.data = NULL; } /* Looking for headers that ONLY Monkey uses */ sr->connection = mk_request_header_get(&sr->headers_toc, mk_rh_connection.data, mk_rh_connection.len); sr->range = mk_request_header_get(&sr->headers_toc, mk_rh_range.data, mk_rh_range.len); sr->if_modified_since = mk_request_header_get(&sr->headers_toc, mk_rh_if_modified_since.data, mk_rh_if_modified_since.len); /* Default Keepalive is off */ if (sr->protocol == MK_HTTP_PROTOCOL_10) { sr->keep_alive = MK_FALSE; sr->close_now = MK_TRUE; } else if(sr->protocol == MK_HTTP_PROTOCOL_11) { sr->keep_alive = MK_TRUE; sr->close_now = MK_FALSE; } if (sr->connection.data) { if (mk_string_search_n(sr->connection.data, "Keep-Alive", MK_STR_INSENSITIVE, sr->connection.len) >= 0) { sr->keep_alive = MK_TRUE; sr->close_now = MK_FALSE; } else if (mk_string_search_n(sr->connection.data, "Close", MK_STR_INSENSITIVE, sr->connection.len) >= 0) { sr->keep_alive = MK_FALSE; sr->close_now = MK_TRUE; } else { /* Set as a non-valid connection header value */ sr->connection.len = 0; } } return 0; }
int duda_body_buffer_flush(int sock, struct duda_body_buffer *bb) { int i; int count = 0; unsigned int bytes_sent, bytes_to; int reset_to = -1; struct mk_iov *buf = bb->buf; /* FIXME: Temporal Check */ if (mk_unlikely(buf->iov_idx > IOV_MAX)) { mk_err("Boddy buffer flush: enqueued data is larger than IOV_MAX (%i)\n", IOV_MAX); exit(EXIT_FAILURE); } bytes_sent = mk_api->socket_sendv(sock, buf); PLUGIN_TRACE("body_flush: %i/%i", bytes_sent, buf->total_len); /* * If the call sent less data than total, we must modify the mk_iov struct * to mark the buffers already processed and set them with with length = zero, * so on the next calls to this function Monkey will skip buffers with bytes * length = 0. */ if (bytes_sent < buf->total_len) { /* Go around each buffer entry and check where the offset took place */ for (i = 0; i < buf->iov_idx; i++) { if (count + buf->io[i].iov_len == bytes_sent) { reset_to = i; break; } else if (bytes_sent < (count + buf->io[i].iov_len)) { reset_to = i - 1; bytes_to = (bytes_sent - count); buf->io[i].iov_base += bytes_to; buf->io[i].iov_len = buf->io[i].iov_len - bytes_to; break; } count += buf->io[i].iov_len; } /* Reset entries */ for (i = 0; i <= reset_to; i++) { buf->io[i].iov_len = 0; } buf->total_len -= bytes_sent; #ifdef TRACE PLUGIN_TRACE("new total len: %i (iov_idx=%i)", buf->total_len, buf->iov_idx); int j; for (j = 0; j < buf->iov_idx; j++) { PLUGIN_TRACE("io[%i] = %i", j, buf->io[j].iov_len); } #endif } /* Successfully end ? */ if (bytes_sent == buf->total_len) { buf->total_len = 0; return 0; } return bytes_sent; }