diff --git a/src/common/request.c b/src/common/request.c --- a/src/common/request.c +++ b/src/common/request.c @@ -19,8 +19,12 @@ #include "common/config.h" +#include "common/request.h" + #include "common/protocol.h" +#include +#include #include #include @@ -79,3 +83,230 @@ int distren_request_poing(struct distren return 0; } + +/* + * file posting stuffs + */ +struct distren_request_file_post_context +{ + EVP_MD_CTX *digest_ctx; + /* stored in network-byte order */ + uint32_t post_id; +}; + +/** + * Helper for distren_request_file_post_start() and + * distren_request_parse_file_post_start(). + */ +static int _distren_request_file_post_context_new(distren_request_file_post_context_t *post_context, + uint32_t post_id) +{ + *post_context = malloc(sizeof(struct distren_request_file_post_context)); + if(!*post_context) + return 1; + + (*post_context)->post_id = htonl(post_id); + (*post_context)->digest_ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex((*post_context)->digest_ctx, EVP_sha1(), NULL); + + return 0; +} + +/** + * Frees a post_context and sets the pointer to NULL. + */ +static int _distren_request_file_post_context_free(distren_request_file_post_context_t *post_context) +{ + if(!*post_context) + return 0; + + EVP_MD_CTX_destroy((*post_context)->digest_ctx); + free(*post_context); + + *post_context = NULL; + + return 0; +} + +int distren_request_file_post_context_free(distren_request_file_post_context_t post_context) +{ + /* + * for the ``public'' function, we don't need to have post_context + * set to NULL. Thus, we have this wrapper around + * _distren_request_file_post_context_free(). + */ + return _distren_request_file_post_context_free(&post_context); +} + +int distren_request_file_post_start(struct distren_request **req, + void **data, + distren_request_file_post_context_t *post_context, + uint32_t post_id, + const char *filename) +{ + struct distren_request_file_post_start *file_post_start; + + distren_request_new(req, sizeof(struct distren_request_file_post_start), DISTREN_REQUEST_FILE_POST_START); + file_post_start = malloc(sizeof(struct distren_request_file_post_start)); + _distren_request_file_post_context_new(post_context, post_id); + if(!*req + || !file_post_start) + { + if(*req) + distren_request_free(*req); + free(file_post_start); + _distren_request_file_post_context_free(post_context); + return 1; + } + + file_post_start->post_id = (*post_context)->post_id; + strncpy(file_post_start->filename, filename, DISTREN_REQUEST_FILE_POST_NAME_LEN); + + *data = file_post_start; + return 0; +} + +int distren_request_parse_file_post_start(struct distren_request *req, + void *data, + distren_request_file_post_context_t *post_context, + uint32_t *post_id, + char **filename) +{ + int tmp; + struct distren_request_file_post_start *file_post_start; + + /* keep our promises ;-) */ + *post_context = NULL; + *filename = NULL; + + if(req->len < sizeof(struct distren_request_file_post_start)) + return 1; + + file_post_start = data; + + tmp = _distren_request_file_post_context_new(post_context, file_post_start->post_id); + *filename = malloc(DISTREN_REQUEST_FILE_POST_NAME_LEN + 1); + if(tmp + || !*filename) + { + _distren_request_file_post_context_free(post_context); + free(*filename); + } + + memcpy(*filename, file_post_start->filename, DISTREN_REQUEST_FILE_POST_NAME_LEN); + (*filename)[DISTREN_REQUEST_FILE_POST_NAME_LEN] = '\0'; + + *post_id = ntohl(file_post_start->post_id); + + return 0; +} + +int distren_request_file_post(struct distren_request **req, + struct distren_request_file_post *data_header, + distren_request_file_post_context_t post_context, + const void *data, + size_t len) +{ + distren_request_new(req, sizeof(struct distren_request_file_post) + len, DISTREN_REQUEST_FILE_POST); + if(!*req) + return 1; + + data_header->post_id = post_context->post_id; + + EVP_DigestUpdate(post_context->digest_ctx, data, len); + + return 0; +} + +int distren_request_parse_file_post(struct distren_request *req, + void *data, + uint32_t *post_id, + distren_request_parse_file_post_find_context_func_t find_context_func, + void *find_post_context_data, + void **file_data, + size_t *file_data_len) +{ + struct distren_request_file_post *file_post; + distren_request_file_post_context_t post_context; + + if(req->type != DISTREN_REQUEST_FILE_POST + || req->len < sizeof(struct distren_request_file_post)) + return 1; + + file_post = data; + *post_id = ntohl(file_post->post_id); + post_context = (*find_context_func)(*post_id, find_post_context_data); + if(!post_context + || *post_id != post_context->post_id) + return 1; + + *file_data = data + sizeof(struct distren_request_file_post); + *file_data_len = req->len - sizeof(struct distren_request_file_post); + EVP_DigestUpdate(post_context->digest_ctx, *file_data, *file_data_len); + + return 0; +} + +int distren_request_file_post_finish(struct distren_request **req, + void **data, + distren_request_file_post_context_t post_context, + uint32_t cancel) +{ + struct distren_request_file_post_finish *file_post_finish; + + distren_request_new(req, sizeof(struct distren_request_file_post_finish), DISTREN_REQUEST_FILE_POST_FINISH); + file_post_finish = malloc(sizeof(struct distren_request_file_post_finish)); + if(!*req + || !file_post_finish) + { + if(*req) + distren_request_free(*req); + free(file_post_finish); + + _distren_request_file_post_context_free(&post_context); + return 1; + } + file_post_finish->post_id = post_context->post_id; + file_post_finish->cancel = htonl(cancel); + + EVP_DigestFinal(post_context->digest_ctx, file_post_finish->sha, NULL); + _distren_request_file_post_context_free(&post_context); + + *data = file_post_finish; + return 0; +} + +int distren_request_parse_file_post_finish(struct distren_request *req, + void *data, + uint32_t *post_id, + distren_request_parse_file_post_find_context_func_t find_context_func, + void *find_post_context_data) +{ + struct distren_request_file_post_finish *file_post_finish; + distren_request_file_post_context_t post_context; + + unsigned int checksum_len; + unsigned char checksum[EVP_MAX_MD_SIZE]; + + if(req->type != DISTREN_REQUEST_FILE_POST_FINISH + || req->len < sizeof(struct distren_request_file_post_finish)) + return 1; + + file_post_finish = data; + *post_id = ntohl(file_post_finish->post_id); + if(ntohl(file_post_finish->cancel)) + return 2; + + post_context = find_context_func(*post_id, find_post_context_data); + if(!post_context) + return 1; + + /* finish up checksumming */ + EVP_DigestFinal_ex(post_context->digest_ctx, checksum, &checksum_len); + if(checksum_len > EVP_MAX_MD_SIZE) + checksum_len = EVP_MAX_MD_SIZE; + if(memcmp(checksum, file_post_finish->sha, checksum_len)) + return 3; + + return 0; +}