/*
* Copyright 2010 Nathan Phillip Brink
*
* This file is a part of DistRen.
*
* DistRen is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DistRen is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with DistRen. If not, see .
*/
#include "common/config.h"
#include "common/request.h"
#include "common/protocol.h"
#include
#include
#include
#include
int distren_request_free_with_data(struct distren_request *req, void *data)
{
free(data);
return distren_request_free(req);
}
int distren_request_version(struct distren_request **req, void **data, uint32_t servertype, const char *package_string)
{
struct distren_request_version *version;
distren_request_new(req, sizeof(struct distren_request_version), DISTREN_REQUEST_VERSION);
version = malloc(sizeof(struct distren_request_version));
if(!version || !*req)
{
free(version);
if(*req)
distren_request_free(*req);
return 1;
}
memset(version, 0, sizeof(struct distren_request_version));
version->servertype = servertype;
strncpy(version->package_string, package_string, DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN);
*data = version;
return 0;
}
int distren_request_parse_version(struct distren_request *req, void *data, struct distren_request_version *version)
{
if(req->len < sizeof(struct distren_request_version))
return 1;
memcpy(version, data, sizeof(struct distren_request_version));
/* there is space for another '\0' */
version->package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN] = '\0';
return 0;
}
int distren_request_pass(struct distren_request **req, void **data, const char *username, const char *pass)
{
struct distren_request_pass *request_pass;
distren_request_new(req, sizeof(struct distren_request_pass), DISTREN_REQUEST_PASS);
request_pass = malloc(sizeof(struct distren_request_pass));
if(!request_pass || !*req)
{
if(*req)
distren_request_free(*req);
free(request_pass);
return 1;
}
/*
* The packet itself doesn't need the string's NULL terminator
* _unless_ if the string is shorter than the maximum length of the
* username or password. Thus, strncpy()'s behavior (not strlcpy()'s
* behavior) is _exactly_ what we want for this case.
*/
strncpy(request_pass->username, username, DISTREN_REQUEST_PASS_USERNAME_LEN);
strncpy(request_pass->pass, pass, DISTREN_REQUEST_PASS_PASS_LEN);
*data = request_pass;
return 0;
}
int distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len)
{
enum distren_request_type type;
if(is_ping)
type = DISTREN_REQUEST_PING;
else
type = DISTREN_REQUEST_PONG;
distren_request_new(req, poing_data_len, type);
(*data) = malloc(poing_data_len);
memcpy(*data, poing_cookie, poing_data_len);
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;
}