#include "core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int tip_client_method_not_found(struct tip_client *cli) { /* To meet RFC 7231, this function MUST generate an Allow header field * containing the correct methods. For example: "Allow: POST\r\n" */ char buf[] = "HTTP/1.1 405 Method Not Allowed\r\n" "Content-Length: 0\r\n\r\n"; send(tip_client_socket(cli), buf, strlen(buf), 0); return -1; } static int tip_client_file_not_found(struct tip_client *cli) { char buf[] = "HTTP/1.1 404 Not Found\r\n" "Content-Length: 0\r\n\r\n"; send(tip_client_socket(cli), buf, strlen(buf), 0); return -1; } /* TODO: sanitize uri, don't escape directory serving files. */ static bool sanitize(const char *uri) { return true; } #define BLOCK 1024000 int tip_client_state_process_payload(struct tip_client *cli) { const char *trailer, *x_redirect; enum tip_http_method method; bool allow_redirect = true; char _uri[32], *uri = _uri; char path[PATH_MAX + 1]; char redirect[5]; struct stat st; int err; /* syslog(LOG_DEBUG, "%s:rhu %.32s ...\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), cli->buf); */ if (!strncmp(cli->buf, "GET", strlen("GET"))) { method = TIP_METHOD_GET; if (sscanf(cli->buf, "GET %31s HTTP/1.1", uri) != 1) return tip_client_method_not_found(cli); } else { return tip_client_method_not_found(cli); } x_redirect = strstr(cli->buf, "X-Accept-Redirect: "); if (x_redirect && sscanf(x_redirect, "X-Accept-Redirect: %4s", redirect) == 1 && !strncmp(redirect, "off", strlen("off"))) allow_redirect = false; trailer = strstr(cli->buf, "\r\n\r\n"); if (!sanitize(uri)) return tip_client_method_not_found(cli); snprintf(path, PATH_MAX, "%s/%s", root, uri); err = stat(path, &st); if (err < 0) return tip_client_file_not_found(cli); /* skip initial / */ uri++; cli->uri = strdup(uri); cli->path = strdup(path); cli->size = st.st_size; cli->allow_redirect = allow_redirect; num_clients++; if (cli->size > FILE_SIZE_THRESHOLD && num_clients > max_clients) { if (!tip_client_redirect(cli)) { tip_client_pending(cli); return 1; } } cli->state = TIP_CLIENT_PROCESSING_REQUEST_2; return 0; } int tip_client_state_process_payload_reply(struct tip_client *cli) { char buf[1024]; int fd; if (cli->redirect) { snprintf(buf, sizeof(buf), "HTTP/1.1 301 Moves Permanently\r\nLocation: http://%s:%hu/%s\r\n\r\n", inet_ntoa(cli->redirect_addr.sin_addr), htons(cli->redirect_addr.sin_port), cli->uri); send(tip_client_socket(cli), buf, strlen(buf), 0); return 1; } fd = open(cli->path, O_RDONLY); if (fd < 0) return tip_client_file_not_found(cli); snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n", cli->size); send(tip_client_socket(cli), buf, strlen(buf), 0); cli->fd = fd; cli->state = TIP_CLIENT_PROCESSING_REQUEST_3; return 0; } int tip_client_state_process_payload_bulk(struct tip_client *cli) { sendfile(tip_client_socket(cli), cli->fd, &cli->offset, BLOCK); if (cli->offset >= cli->size) return 1; return 0; }