int mk_user_init(struct client_session *cs, struct session_request *sr) { int limit; const int offset = 2; /* The user is defined after the '/~' string, so offset = 2 */ const int user_len = 255; char user[user_len], *user_uri; struct passwd *s_user; if (sr->uri_processed.len <= 2) { return -1; } limit = mk_string_char_search(sr->uri_processed.data + offset, '/', sr->uri_processed.len); if (limit == -1) { limit = (sr->uri_processed.len) - offset; } if (limit + offset >= (user_len)) { return -1; } memcpy(user, sr->uri_processed.data + offset, limit); user[limit] = '\0'; MK_TRACE("user: '******'", user); /* Check system user */ if ((s_user = getpwnam(user)) == NULL) { mk_request_error(MK_CLIENT_NOT_FOUND, cs, sr); return -1; } if (sr->uri_processed.len > (unsigned int) (offset+limit)) { user_uri = mk_mem_malloc(sr->uri_processed.len); if (!user_uri) { return -1; } memcpy(user_uri, sr->uri_processed.data + (offset + limit), sr->uri_processed.len - offset - limit); user_uri[sr->uri_processed.len - offset - limit] = '\0'; mk_string_build(&sr->real_path.data, &sr->real_path.len, "%s/%s%s", s_user->pw_dir, config->user_dir, user_uri); mk_mem_free(user_uri); } else { mk_string_build(&sr->real_path.data, &sr->real_path.len, "%s/%s", s_user->pw_dir, config->user_dir); } sr->user_home = MK_TRUE; return 0; }
int mk_socket_connect(char *host, int port, int async) { int ret; int socket_fd = -1; char *port_str = 0; unsigned long len; struct addrinfo hints; struct addrinfo *res, *rp; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; mk_string_build(&port_str, &len, "%d", port); ret = getaddrinfo(host, port_str, &hints, &res); mk_mem_free(port_str); if(ret != 0) { mk_err("Can't get addr info: %s", gai_strerror(ret)); return -1; } for (rp = res; rp != NULL; rp = rp->ai_next) { socket_fd = mk_socket_create(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (socket_fd == -1) { mk_warn("Error creating client socket, retrying"); continue; } if (async == MK_TRUE) { mk_socket_set_nonblocking(socket_fd); } ret = connect(socket_fd, (struct sockaddr *) rp->ai_addr, rp->ai_addrlen); if (ret == -1) { if (errno == EINPROGRESS) { break; } else { printf("%s", strerror(errno)); perror("connect"); exit(1); close(socket_fd); continue; } } break; } freeaddrinfo(res); if (rp == NULL) return -1; return socket_fd; }
/* Write Monkey's PID */ int mk_utils_register_pid() { int fd; char pidstr[MK_MAX_PID_LEN]; unsigned long len = 0; char *filepath = NULL; struct flock lock; struct stat sb; struct mk_config_listener *listen; if (config->pid_status == MK_TRUE) return -1; listen = mk_list_entry_first(&config->listeners, struct mk_config_listener, _head); mk_string_build(&filepath, &len, "%s.%s", config->pid_file_path, listen->port); if (!stat(filepath, &sb)) { /* file exists, perhaps previously kepts by SIGKILL */ unlink(filepath); } if ((fd = open(filepath, O_WRONLY | O_CREAT | O_CLOEXEC, 0444)) < 0) { mk_err("Error: I can't log pid of monkey"); exit(EXIT_FAILURE); } /* create a write exclusive lock for the entire file */ lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { close(fd); mk_err("Error: I cannot set the lock for the pid of monkey"); exit(EXIT_FAILURE); } sprintf(pidstr, "%i", getpid()); ssize_t write_len = strlen(pidstr); if (write(fd, pidstr, write_len) != write_len) { close(fd); mk_err("Error: I cannot write the lock for the pid of monkey"); exit(EXIT_FAILURE); } mk_mem_free(filepath); config->pid_status = MK_TRUE; return 0; }
/* Remove PID file */ int mk_utils_remove_pid() { unsigned long len = 0; char *filepath = NULL; mk_string_build(&filepath, &len, "%s.%d", config->pid_file_path, config->serverport); mk_user_undo_uidgid(); if (unlink(filepath)) { mk_warn("cannot delete pidfile\n"); } mk_mem_free(filepath); config->pid_status = MK_FALSE; return 0; }
/* This function is called when a thread is created */ void mk_cache_thread_init() { mk_pointer *cache_header_lm; mk_pointer *cache_header_cl; mk_pointer *cache_header_ka; mk_pointer *cache_header_ka_max; struct tm *cache_utils_gmtime; struct mk_iov *cache_iov_header; struct mk_gmt_cache *cache_utils_gmt_text; /* Cache header request -> last modified */ cache_header_lm = mk_mem_malloc_z(sizeof(mk_pointer)); cache_header_lm->data = mk_mem_malloc_z(32); cache_header_lm->len = -1; pthread_setspecific(mk_cache_header_lm, (void *) cache_header_lm); /* Cache header request -> content length */ cache_header_cl = mk_mem_malloc_z(sizeof(mk_pointer)); cache_header_cl->data = mk_mem_malloc_z(MK_UTILS_INT2MKP_BUFFER_LEN); cache_header_cl->len = -1; pthread_setspecific(mk_cache_header_cl, (void *) cache_header_cl); /* Cache header response -> keep-alive */ cache_header_ka = mk_mem_malloc_z(sizeof(mk_pointer)); mk_string_build(&cache_header_ka->data, &cache_header_ka->len, "Keep-Alive: timeout=%i, max=", config->keep_alive_timeout); pthread_setspecific(mk_cache_header_ka, (void *) cache_header_ka); /* Cache header response -> max=%i */ cache_header_ka_max = mk_mem_malloc_z(sizeof(mk_pointer)); cache_header_ka_max->data = mk_mem_malloc_z(64); cache_header_ka_max->len = 0; pthread_setspecific(mk_cache_header_ka_max, (void *) cache_header_ka_max); /* Cache iov header struct */ cache_iov_header = mk_iov_create(32, 0); pthread_setspecific(mk_cache_iov_header, (void *) cache_iov_header); /* Cache gmtime buffer */ cache_utils_gmtime = mk_mem_malloc(sizeof(struct tm)); pthread_setspecific(mk_cache_utils_gmtime, (void *) cache_utils_gmtime); /* Cache the most used text representations of utime2gmt */ cache_utils_gmt_text = mk_mem_malloc_z(sizeof(struct mk_gmt_cache) * MK_GMT_CACHES); pthread_setspecific(mk_cache_utils_gmt_text, (void *) cache_utils_gmt_text); }
int mk_socket_connect(char *host, int port) { int ret; int socket_fd = -1; char *port_str = 0; unsigned long len; struct addrinfo hints; struct addrinfo *res, *rp; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; mk_string_build(&port_str, &len, "%d", port); ret = getaddrinfo(host, port_str, &hints, &res); mk_mem_free(port_str); if(ret != 0) { mk_err("Can't get addr info: %s", gai_strerror(ret)); return -1; } for (rp = res; rp != NULL; rp = rp->ai_next) { socket_fd = mk_socket_create(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if( socket_fd == -1) { mk_warn("Error creating client socket, retrying"); continue; } if (connect(socket_fd, (struct sockaddr *) rp->ai_addr, rp->ai_addrlen) == -1) { close(socket_fd); continue; } break; } freeaddrinfo(res); if (rp == NULL) return -1; return socket_fd; }
/* Remove PID file */ int mk_utils_remove_pid() { unsigned long len = 0; char *filepath = NULL; struct mk_config_listener *listen; listen = mk_list_entry_first(&config->listeners, struct mk_config_listener, _head); mk_string_build(&filepath, &len, "%s.%s", config->pid_file_path, listen->port); mk_user_undo_uidgid(); if (unlink(filepath)) { mk_warn("cannot delete pidfile\n"); } mk_mem_free(filepath); config->pid_status = MK_FALSE; return 0; }
/* Send response headers */ int mk_header_prepare(struct mk_http_session *cs, struct mk_http_request *sr, struct mk_server *server) { int i = 0; unsigned long len = 0; char *buffer = 0; mk_ptr_t response; struct response_headers *sh; struct mk_iov *iov; sh = &sr->headers; iov = &sh->headers_iov; /* HTTP Status Code */ if (sh->status == MK_CUSTOM_STATUS) { response.data = sh->custom_status.data; response.len = sh->custom_status.len; } else { for (i = 0; i < status_response_len; i++) { if (status_response[i].status == sh->status) { response.data = status_response[i].response; response.len = status_response[i].length; break; } } } /* Invalid status set */ mk_bug(i == status_response_len); mk_iov_add(iov, response.data, response.len, MK_FALSE); /* * Preset headers (mk_clock.c): * * - Server * - Date */ mk_iov_add(iov, headers_preset.data, headers_preset.len, MK_FALSE); /* Last-Modified */ if (sh->last_modified > 0) { mk_ptr_t *lm = MK_TLS_GET(mk_tls_cache_header_lm); lm->len = mk_utils_utime2gmt(&lm->data, sh->last_modified); mk_iov_add(iov, mk_header_last_modified.data, mk_header_last_modified.len, MK_FALSE); mk_iov_add(iov, lm->data, lm->len, MK_FALSE); } /* Connection */ if (sh->connection == 0) { if (cs->close_now == MK_FALSE) { if (sr->connection.len > 0) { if (sr->protocol != MK_HTTP_PROTOCOL_11) { mk_iov_add(iov, mk_header_conn_ka.data, mk_header_conn_ka.len, MK_FALSE); } } } else { mk_iov_add(iov, mk_header_conn_close.data, mk_header_conn_close.len, MK_FALSE); } } else if (sh->connection == MK_HEADER_CONN_UPGRADED) { mk_iov_add(iov, mk_header_conn_upgrade.data, mk_header_conn_upgrade.len, MK_FALSE); } /* Location */ if (sh->location != NULL) { mk_iov_add(iov, mk_header_short_location.data, mk_header_short_location.len, MK_FALSE); mk_iov_add(iov, sh->location, strlen(sh->location), MK_TRUE); } /* allowed methods */ if (sh->allow_methods.len > 0) { mk_iov_add(iov, mk_header_allow.data, mk_header_allow.len, MK_FALSE); mk_iov_add(iov, sh->allow_methods.data, sh->allow_methods.len, MK_FALSE); } /* Content type */ if (sh->content_type.len > 0) { mk_iov_add(iov, sh->content_type.data, sh->content_type.len, MK_FALSE); } /* * Transfer Encoding: the transfer encoding header is just sent when * the response has some content defined by the HTTP status response */ switch (sh->transfer_encoding) { case MK_HEADER_TE_TYPE_CHUNKED: mk_iov_add(iov, mk_header_te_chunked.data, mk_header_te_chunked.len, MK_FALSE); break; } /* E-Tag */ if (sh->etag_len > 0) { mk_iov_add(iov, sh->etag_buf, sh->etag_len, MK_FALSE); } /* Content-Encoding */ if (sh->content_encoding.len > 0) { mk_iov_add(iov, mk_header_content_encoding.data, mk_header_content_encoding.len, MK_FALSE); mk_iov_add(iov, sh->content_encoding.data, sh->content_encoding.len, MK_FALSE); } /* Content-Length */ if (sh->content_length >= 0 && sh->transfer_encoding != 0) { /* Map content length to MK_POINTER */ mk_ptr_t *cl = MK_TLS_GET(mk_tls_cache_header_cl); mk_string_itop(sh->content_length, cl); /* Set headers */ mk_iov_add(iov, mk_header_content_length.data, mk_header_content_length.len, MK_FALSE); mk_iov_add(iov, cl->data, cl->len, MK_FALSE); } if ((sh->content_length != 0 && (sh->ranges[0] >= 0 || sh->ranges[1] >= 0)) && server->resume == MK_TRUE) { buffer = 0; /* yyy- */ if (sh->ranges[0] >= 0 && sh->ranges[1] == -1) { mk_string_build(&buffer, &len, "%s bytes %d-%ld/%ld\r\n", RH_CONTENT_RANGE, sh->ranges[0], (sh->real_length - 1), sh->real_length); mk_iov_add(iov, buffer, len, MK_TRUE); } /* yyy-xxx */ if (sh->ranges[0] >= 0 && sh->ranges[1] >= 0) { mk_string_build(&buffer, &len, "%s bytes %d-%d/%ld\r\n", RH_CONTENT_RANGE, sh->ranges[0], sh->ranges[1], sh->real_length); mk_iov_add(iov, buffer, len, MK_TRUE); } /* -xxx */ if (sh->ranges[0] == -1 && sh->ranges[1] > 0) { mk_string_build(&buffer, &len, "%s bytes %ld-%ld/%ld\r\n", RH_CONTENT_RANGE, (sh->real_length - sh->ranges[1]), (sh->real_length - 1), sh->real_length); mk_iov_add(iov, buffer, len, MK_TRUE); } } if (sh->upgrade == MK_HEADER_UPGRADED_H2C) { mk_iov_add(iov, mk_header_upgrade_h2c.data, mk_header_upgrade_h2c.len, MK_FALSE); } if (sh->cgi == SH_NOCGI || sh->breakline == MK_HEADER_BREAKLINE) { if (!sr->headers._extra_rows) { mk_iov_add(iov, mk_iov_crlf.data, mk_iov_crlf.len, MK_FALSE); } else { mk_iov_add(sr->headers._extra_rows, mk_iov_crlf.data, mk_iov_crlf.len, MK_FALSE); } } /* * Configure the Stream to dispatch the headers */ /* Set the IOV input stream */ sr->in_headers.buffer = iov; sr->in_headers.bytes_total = iov->total_len; sr->in_headers.cb_finished = mk_header_cb_finished; if (sr->headers._extra_rows) { /* Our main sr->stream contains the main headers (header_iov) * and 'may' have already some linked data. If we have some * extra headers rows we need to link this IOV right after * the main header_iov. */ struct mk_stream_input *in = &sr->in_headers_extra; in->type = MK_STREAM_IOV; in->dynamic = MK_FALSE; in->cb_consumed = NULL; in->cb_finished = cb_stream_iov_extended_free; in->stream = &sr->stream; in->buffer = sr->headers._extra_rows; in->bytes_total = sr->headers._extra_rows->total_len; mk_list_add_after(&sr->in_headers_extra._head, &sr->in_headers._head, &sr->stream.inputs); } sh->sent = MK_TRUE; return 0; }
static int mk_http_directory_redirect_check(struct client_session *cs, struct session_request *sr) { int port_redirect = 0; char *host; char *location = 0; char *real_location = 0; unsigned long len; /* * We have to check if there is a slash at the end of * this string. If it doesn't exist, we send a redirection header. */ if (sr->uri_processed.data[sr->uri_processed.len - 1] == '/') { return 0; } host = mk_ptr_t_to_buf(sr->host); /* * Add ending slash to the location string */ location = mk_mem_malloc(sr->uri_processed.len + 2); memcpy(location, sr->uri_processed.data, sr->uri_processed.len); location[sr->uri_processed.len] = '/'; location[sr->uri_processed.len + 1] = '\0'; /* FIXME: should we done something similar for SSL = 443 */ if (sr->host.data && sr->port > 0) { if (sr->port != config->standard_port) { port_redirect = sr->port; } } if (port_redirect > 0) { mk_string_build(&real_location, &len, "%s://%s:%i%s", config->transport, host, port_redirect, location); } else { mk_string_build(&real_location, &len, "%s://%s%s", config->transport, host, location); } MK_TRACE("Redirecting to '%s'", real_location); mk_mem_free(host); mk_header_set_http_status(sr, MK_REDIR_MOVED); sr->headers.content_length = 0; mk_ptr_t_reset(&sr->headers.content_type); sr->headers.location = real_location; sr->headers.cgi = SH_NOCGI; sr->headers.pconnections_left = (config->max_keep_alive_request - cs->counter_connections); mk_header_send(cs->socket, cs, sr); mk_server_cork_flag(cs->socket, TCP_CORK_OFF); /* * we do not free() real_location * as it's freed by iov */ mk_mem_free(location); sr->headers.location = NULL; return -1; }
/* Send response headers */ int mk_header_send(int fd, struct client_session *cs, struct session_request *sr) { int i=0; unsigned long len = 0; char *buffer = 0; mk_pointer response; struct response_headers *sh; struct mk_iov *iov; sh = &sr->headers; iov = mk_header_iov_get(); /* HTTP Status Code */ if (sh->status == MK_CUSTOM_STATUS) { response.data = sh->custom_status.data; response.len = sh->custom_status.len; } else { for (i=0; i < status_response_len; i++) { if (status_response[i].status == sh->status) { response.data = status_response[i].response; response.len = status_response[i].length; break; } } } /* Invalid status set */ mk_bug(i == status_response_len); mk_header_iov_add_entry(iov, response, mk_iov_none, MK_IOV_NOT_FREE_BUF); /* Server details */ mk_iov_add_entry(iov, sr->host_conf->header_host_signature.data, sr->host_conf->header_host_signature.len, mk_iov_crlf, MK_IOV_NOT_FREE_BUF); /* Date */ mk_iov_add_entry(iov, mk_header_short_date.data, mk_header_short_date.len, header_current_time, MK_IOV_NOT_FREE_BUF); /* Last-Modified */ if (sh->last_modified > 0) { mk_pointer *lm; lm = mk_cache_get(mk_cache_header_lm); lm->len = mk_utils_utime2gmt(&lm->data, sh->last_modified); mk_iov_add_entry(iov, mk_header_last_modified.data, mk_header_last_modified.len, *lm, MK_IOV_NOT_FREE_BUF); } /* Connection */ if (sh->connection == 0) { if (mk_http_keepalive_check(cs) == 0) { if (sr->connection.len > 0) { /* Get cached mk_pointers */ mk_pointer *ka_format = mk_cache_get(mk_cache_header_ka); mk_pointer *ka_header = mk_cache_get(mk_cache_header_ka_max); /* Compose header and add entries to iov */ mk_string_itop(config->max_keep_alive_request - cs->counter_connections, ka_header); mk_iov_add_entry(iov, ka_format->data, ka_format->len, mk_iov_none, MK_IOV_NOT_FREE_BUF); mk_iov_add_entry(iov, ka_header->data, ka_header->len, mk_header_conn_ka, MK_IOV_NOT_FREE_BUF); } } else { mk_iov_add_entry(iov, mk_header_conn_close.data, mk_header_conn_close.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); } } /* Location */ if (sh->location != NULL) { mk_iov_add_entry(iov, mk_header_short_location.data, mk_header_short_location.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); mk_iov_add_entry(iov, sh->location, strlen(sh->location), mk_iov_crlf, MK_IOV_FREE_BUF); } /* allowed methods */ if (sh->allow_methods.len > 0) { mk_iov_add_entry(iov, mk_header_allow.data, mk_header_allow.len, sh->allow_methods, MK_IOV_NOT_FREE_BUF) ; } /* Content type */ if (sh->content_type.len > 0) { mk_iov_add_entry(iov, mk_header_short_ct.data, mk_header_short_ct.len, sh->content_type, MK_IOV_NOT_FREE_BUF); } /* * Transfer Encoding: the transfer encoding header is just sent when * the response has some content defined by the HTTP status response */ if ((sh->status < MK_REDIR_MULTIPLE) || (sh->status > MK_REDIR_USE_PROXY)) { switch (sh->transfer_encoding) { case MK_HEADER_TE_TYPE_CHUNKED: mk_iov_add_entry(iov, mk_header_te_chunked.data, mk_header_te_chunked.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); break; } } /* Content-Encoding */ if (sh->content_encoding.len > 0) { mk_iov_add_entry(iov, mk_header_content_encoding.data, mk_header_content_encoding.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); mk_iov_add_entry(iov, sh->content_encoding.data, sh->content_encoding.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); } /* Content-Length */ if (sh->content_length >= 0) { /* Map content length to MK_POINTER */ mk_pointer *cl; cl = mk_cache_get(mk_cache_header_cl); mk_string_itop(sh->content_length, cl); /* Set headers */ mk_iov_add_entry(iov, mk_header_content_length.data, mk_header_content_length.len, *cl, MK_IOV_NOT_FREE_BUF); } if ((sh->content_length != 0 && (sh->ranges[0] >= 0 || sh->ranges[1] >= 0)) && config->resume == MK_TRUE) { buffer = 0; /* yyy- */ if (sh->ranges[0] >= 0 && sh->ranges[1] == -1) { mk_string_build(&buffer, &len, "%s bytes %d-%ld/%ld", RH_CONTENT_RANGE, sh->ranges[0], (sh->real_length - 1), sh->real_length); mk_iov_add_entry(iov, buffer, len, mk_iov_crlf, MK_IOV_FREE_BUF); } /* yyy-xxx */ if (sh->ranges[0] >= 0 && sh->ranges[1] >= 0) { mk_string_build(&buffer, &len, "%s bytes %d-%d/%ld", RH_CONTENT_RANGE, sh->ranges[0], sh->ranges[1], sh->real_length); mk_iov_add_entry(iov, buffer, len, mk_iov_crlf, MK_IOV_FREE_BUF); } /* -xxx */ if (sh->ranges[0] == -1 && sh->ranges[1] > 0) { mk_string_build(&buffer, &len, "%s bytes %ld-%ld/%ld", RH_CONTENT_RANGE, (sh->real_length - sh->ranges[1]), (sh->real_length - 1), sh->real_length); mk_iov_add_entry(iov, buffer, len, mk_iov_crlf, MK_IOV_FREE_BUF); } } mk_socket_set_cork_flag(fd, TCP_CORK_ON); if (sh->cgi == SH_NOCGI || sh->breakline == MK_HEADER_BREAKLINE) { if (!sr->headers._extra_rows) { mk_iov_add_entry(iov, mk_iov_crlf.data, mk_iov_crlf.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); } else { mk_iov_add_entry(sr->headers._extra_rows, mk_iov_crlf.data, mk_iov_crlf.len, mk_iov_none, MK_IOV_NOT_FREE_BUF); } } mk_socket_sendv(fd, iov); if (sr->headers._extra_rows) { mk_socket_sendv(fd, sr->headers._extra_rows); mk_iov_free(sr->headers._extra_rows); sr->headers._extra_rows = NULL; } mk_header_iov_free(iov); sh->sent = MK_TRUE; return 0; }