diff options
-rw-r--r-- | src/main.c | 104 |
1 files changed, 89 insertions, 15 deletions
@@ -25,6 +25,7 @@ #include <limits.h> #include <syslog.h> #include <netinet/tcp.h> +#include <fcntl.h> #define TIP_TORRENT_PORT 9999 @@ -43,6 +44,7 @@ enum { TIP_CLIENT_GET_HEADER, TIP_CLIENT_GET_PAYLOAD, TIP_CLIENT_POST_REDIRECT, + TIP_CLIENT_HEAD_HEADER, TIP_CLIENT_DONE, }; @@ -53,6 +55,7 @@ struct tip_client { uint32_t buf_len; uint64_t data_len; uint64_t content_len; + uint64_t chunk_offset; int state; int num_retries; int fd; @@ -188,13 +191,7 @@ static int tip_client_get_hdr(struct tip_client *cli) else tip_client_stats.direct_from_server++; - cli->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (cli->fd < 0) { - syslog(LOG_ERR, "failed to open file %s: %s", - filename, strerror(errno)); - return -1; - } - + lseek(cli->fd, cli->chunk_offset, SEEK_SET); header_len = trailer - cli->buf; payload = cli->buf + header_len; payload_len = cli->buf_len - header_len; @@ -247,6 +244,50 @@ static int tip_client_get_payload(struct tip_client *cli) return 1; } +static int tip_client_head_hdr(struct tip_client *cli) +{ + char *ptr; + + ptr = strstr(cli->buf, "\r\n\r\n"); + if (!ptr) + return 0; + + if (!strncmp(cli->buf, "HTTP/1.1 404 Not Found", strlen("HTTP/1.1 404 Not Found"))) { + syslog(LOG_ERR, "server says file `%s' not found\n", filename); + return -1; + } + + ptr = strstr(cli->buf, "Content-Length: "); + if (!ptr) + return -1; + + if (sscanf(ptr, "Content-Length: %lu[^\r\n]", &cli->content_len) != 1) + return -1; + if (cli->content_len < 0) + return -1; + if (cli->content_len == 0) { + syslog(LOG_ERR, "server reports zero size file %s", filename); + return -1; + } + + cli->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (cli->fd < 0) { + syslog(LOG_ERR, "failed to open file %s: %s", + filename, strerror(errno)); + return -1; + } + + if (posix_fallocate(cli->fd, 0, cli->content_len) < 0) { + syslog(LOG_ERR, "failed to allocate room for file %s: %s", + filename, strerror(errno)); + return -1; + } + + cli->state = TIP_CLIENT_DONE; + + return 1; +} + static int tip_client_post_redirect(struct tip_client *cli) { char *ptr; @@ -310,6 +351,13 @@ static void tip_client_read_cb(struct ev_loop *loop, struct ev_io *io, int event if (ret == 0) goto close; break; + case TIP_CLIENT_HEAD_HEADER: + ret = tip_client_head_hdr(cli); + if (ret < 0) + goto error; + if (!ret) + return; + goto close; case TIP_CLIENT_POST_REDIRECT: ret = tip_client_post_redirect(cli); if (ret < 0) @@ -348,12 +396,23 @@ static void tip_client_connect_cb(struct ev_loop *loop, struct ev_io *io, int ev return; } - syslog(LOG_INFO, "connected to %s to fetch file %s\n", addr, filename); - - if (cli->state == TIP_CLIENT_POST_REDIRECT) - snprintf(buf, sizeof(buf), "POST /%s HTTP/1.1\r\n\r\n", filename); - else + switch (cli->state) { + case TIP_CLIENT_GET_HEADER: + syslog(LOG_INFO, "connected to %s to fetch file %s\n", + inet_ntoa(cli->addr.sin_addr), filename); snprintf(buf, sizeof(buf), "GET /%s HTTP/1.1\r\n\r\n", filename); + break; + case TIP_CLIENT_HEAD_HEADER: + syslog(LOG_INFO, "connected to %s to get file size of %s\n", + inet_ntoa(cli->addr.sin_addr), filename); + snprintf(buf, sizeof(buf), "HEAD /%s HTTP/1.1\r\n\r\n", filename); + break; + case TIP_CLIENT_POST_REDIRECT: + syslog(LOG_INFO, "connected to %s to report redirection for %s\n", + inet_ntoa(cli->addr.sin_addr), filename); + snprintf(buf, sizeof(buf), "POST /%s HTTP/1.1\r\n\r\n", filename); + break; + } ret = send(cli->io.fd, buf, strlen(buf), 0); if (ret < 0) { @@ -472,9 +531,10 @@ static char _filename[PATH_MAX + 1]; int main(int argc, char *argv[]) { struct timeval tv_start, tv_stop, tv; + uint64_t data_len = 0, file_size = 0; bool file_chunk[MAX_CHUNKS] = {}; - uint64_t data_len = 0; - int i, k; + uint64_t chunk_size; + int i, k, fd; if (argc != 3) { printf("%s [ip] [file]\n", argv[0]); @@ -490,12 +550,26 @@ int main(int argc, char *argv[]) gettimeofday(&tv_start, NULL); + do { + filename = argv[2]; + _cli.state = TIP_CLIENT_HEAD_HEADER; + } while (tip_client_request_file(&_cli, addr, filename) > 0); + + if (_cli.state != TIP_CLIENT_DONE) + goto err; + + fd = _cli.fd; + file_size = _cli.content_len; + for (i = 0; i < MAX_CHUNKS; i++) { memset(&_cli, 0, sizeof(_cli)); k = select_file_chunk(file_chunk); snprintf(_filename, sizeof(_filename), "%s.%u", argv[2], k); filename = _filename; + chunk_size = file_size / MAX_CHUNKS; + _cli.chunk_offset = chunk_size * k; + _cli.fd = fd; do { syslog(LOG_INFO, "Requesting file %s to server\n", filename); @@ -507,7 +581,7 @@ int main(int argc, char *argv[]) file_chunk[k] = true; data_len += _cli.data_len; } - +err: gettimeofday(&tv_stop, NULL); timersub(&tv_stop, &tv_start, &tv); |