uftp-3.5/ 0000755 0003316 0000550 00000000000 11577014250 011346 5 ustar bush alumni uftp-3.5/client_announce.c 0000644 0003316 0000550 00000167232 11577014247 014677 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
#include "client_announce.h"
#include "client_transfer.h"
/**
* Finds next open slot in the global group list.
* Returns the index of the open slot, or -1 if none found.
*/
int find_open_slot()
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id == 0) {
memset(&group_list[i], 0, sizeof(group_list[i]));
return i;
}
}
return -1;
}
/**
* Returns the verify_data string used in certain messages. This value
* is then run through the PRF with the result going into the message.
*/
uint8_t *build_verify_data(int listidx, int *verifylen)
{
uint8_t *verifydata;
uint32_t exponent;
uint16_t modlen;
uint8_t modulus[PUBKEY_LEN];
uint32_t group_id;
*verifylen = 0;
if (group_list[listidx].phase == PHASE_REGISTERED) {
verifydata = calloc(sizeof(group_list[listidx].group_id) +
sizeof(group_list[listidx].multi.s_addr) +
sizeof(group_list[listidx].rand1) +
sizeof(group_list[listidx].rand2) +
sizeof(group_list[listidx].premaster), 1);
} else {
verifydata = calloc(sizeof(group_list[listidx].group_id) +
sizeof(group_list[listidx].multi.s_addr) +
sizeof(group_list[listidx].rand1) +
sizeof(group_list[listidx].rand2) +
sizeof(group_list[listidx].premaster) +
PUBKEY_LEN + 4 + sizeof(group_list[listidx].groupmaster), 1);
}
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
group_id = htonl(group_list[listidx].group_id);
memcpy(verifydata, &group_id, sizeof(group_id));
*verifylen += sizeof(group_id);
memcpy(verifydata + *verifylen,
&group_list[listidx].multi.s_addr,
sizeof(group_list[listidx].multi.s_addr));
*verifylen += sizeof(group_list[listidx].multi.s_addr);
memcpy(verifydata + *verifylen, group_list[listidx].rand1,
sizeof(group_list[listidx].rand1));
*verifylen += sizeof(group_list[listidx].rand1);
memcpy(verifydata + *verifylen, group_list[listidx].rand2,
sizeof(group_list[listidx].rand2));
*verifylen += sizeof(group_list[listidx].rand2);
memcpy(verifydata + *verifylen, group_list[listidx].premaster,
sizeof(group_list[listidx].premaster));
*verifylen += sizeof(group_list[listidx].premaster);
if (group_list[listidx].phase != PHASE_REGISTERED) {
if (group_list[listidx].client_auth) {
if (!export_RSA_key(group_list[listidx].clientkey, &exponent,
modulus, &modlen)) {
free(verifydata);
return NULL;
}
exponent = htonl(exponent);
memcpy(verifydata + *verifylen, &exponent, sizeof(exponent));
*verifylen += sizeof(exponent);
memcpy(verifydata + *verifylen, modulus, modlen);
*verifylen += modlen;
}
memcpy(verifydata + *verifylen, group_list[listidx].groupmaster,
sizeof(group_list[listidx].groupmaster));
*verifylen += sizeof(group_list[listidx].groupmaster);
}
return verifydata;
}
/**
* Sends a CLIENT_KEY message if the server requested it.
* Always sent right after a REGISTER.
*/
void send_client_key(int listidx)
{
struct uftp_h *header;
struct client_key_h *client_key;
unsigned char *buf, *keymod, *verify;
uint32_t exponent;
uint16_t modlen;
uint8_t modulus[PUBKEY_LEN];
uint8_t *verifydata;
unsigned int siglen, meslen;
int verifylen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h));
keymod = (unsigned char *)client_key + sizeof(struct client_key_h);
verify = keymod + group_list[listidx].client_keylen;
set_uftp_header(header, CLIENT_KEY, listidx);
client_key->func = CLIENT_KEY;
if (!export_RSA_key(group_list[listidx].clientkey, &exponent,
modulus, &modlen)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error exporting public key");
send_abort(listidx, "Error exporting public key");
free(buf);
return;
}
client_key->keyexp = htonl(exponent);
memcpy(keymod, modulus, group_list[listidx].client_keylen);
client_key->keylen = htons(modlen);
verifydata = build_verify_data(listidx, &verifylen);
if (!verifydata) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error getting verify data");
send_abort(listidx, "Error getting verify data");
free(verifydata);
free(buf);
return;
}
if (!create_RSA_sig(group_list[listidx].clientkey,
group_list[listidx].hashtype, verifydata,
verifylen, verify, &siglen) ||
siglen > group_list[listidx].client_keylen) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error signing verify data");
send_abort(listidx, "Error signing verify data");
free(verifydata);
free(buf);
return;
}
free(verifydata);
client_key->verifylen = htons(siglen);
header->blsize = htons(sizeof(struct client_key_h) + modlen + siglen);
meslen = sizeof(struct uftp_h) + ntohs(header->blsize);
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group_list[listidx].replyaddr),
sizeof(group_list[listidx].replyaddr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending CLIENT_KEY");
} else {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"CLIENT_KEY sent");
}
free(buf);
}
/**
* Sends a REGISTER message in response to an ANNOUNCE or on timeout when
* waiting for a KEYINFO or REG_CONF. If the register timeout expired, abort.
*/
void send_register(int listidx)
{
struct uftp_h *header;
struct register_h *reg;
struct uftp2_h *v2_header;
unsigned char *buf, *premaster;
struct timeval tv;
int len, meslen;
RSA_key_t key;
gettimeofday(&tv, NULL);
if (diff_usec(group_list[listidx].expire_time, tv) <= 0) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Registration unconfirmed by server");
send_abort(listidx, "Registration unconfirmed");
return;
}
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)buf;
v2_header->uftp_id = htonl(V2_UFTP_ID);
v2_header->func = htonl(V2_REGISTER);
v2_header->tx_id = htonl(group_list[listidx].group_id);
v2_header->blsize = 0;
meslen = sizeof(struct uftp2_h);
} else {
header = (struct uftp_h *)buf;
reg = (struct register_h *)(buf + sizeof(struct uftp_h));
premaster = (unsigned char *)reg + sizeof(struct register_h);
set_uftp_header(header, REGISTER, listidx);
reg->func = REGISTER;
reg->destcount = 0;
if (group_list[listidx].keytype != KEY_NONE) {
memcpy(reg->rand2, group_list[listidx].rand2, RAND_LEN);
if (has_proxy) {
key = proxy_key;
} else {
key = group_list[listidx].serverkey;
}
if (!RSA_encrypt(key, group_list[listidx].premaster, MASTER_LEN,
premaster, &len)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error encrypting premaster secret");
send_abort(listidx, "Error encrypting premaster secret");
free(buf);
return;
}
reg->premaster_len = htons(len);
} else {
len = 0;
}
header->blsize = htons(sizeof(struct register_h) + len);
meslen = sizeof(struct uftp_h) + ntohs(header->blsize);
}
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group_list[listidx].replyaddr),
sizeof(group_list[listidx].replyaddr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending REGISTER");
} else {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"REGISTER sent");
}
set_timeout(listidx);
if (group_list[listidx].client_auth) {
send_client_key(listidx);
}
free(buf);
}
/**
* Sends an INFO_ACK in response to a FILEINFO or KEYINFO
*/
void send_info_ack(int listidx, int restart)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct infoack_h *info_ack;
unsigned char *verifydata, *verify_hash, *verify_val;
unsigned int payloadlen, hashlen;
int verifylen, len;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
info_ack = (struct infoack_h *)(buf + sizeof(struct uftp_h));
payloadlen = sizeof(struct infoack_h);
set_uftp_header(header, INFO_ACK, listidx);
if (restart) {
info_ack->flags |= FLAG_PARTIAL;
}
info_ack->func = INFO_ACK;
info_ack->destcount = 0;
info_ack->file_id = htons(group_list[listidx].file_id);
if (group_list[listidx].keytype != KEY_NONE) {
// If we're responding to a KEYINFO, populate verify_data
if (group_list[listidx].file_id == 0) {
verifydata = build_verify_data(listidx, &verifylen);
if (!verifydata) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error getting verify data");
send_abort(listidx, "Error getting verify data");
free(buf);
return;
}
verify_hash = calloc(group_list[listidx].hmaclen, 1);
verify_val = calloc(VERIFY_LEN + group_list[listidx].hmaclen, 1);
if ((verify_hash == NULL) || (verify_val == NULL)){
syserror(0, 0, "calloc failed!");
exit(1);
}
hash(group_list[listidx].hashtype, verifydata, verifylen,
verify_hash, &hashlen);
PRF(group_list[listidx].hashtype, VERIFY_LEN,
group_list[listidx].groupmaster,
sizeof(group_list[listidx].groupmaster),
"client finished", verify_hash, hashlen, verify_val, &len);
memcpy(info_ack->verify_data, verify_val, VERIFY_LEN);
free(verifydata);
free(verify_hash);
free(verify_val);
}
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].clientkey,
group_list[listidx].client_keylen)) {
log0(0, 0, "Error encrypting INFO_ACK");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&(group_list[listidx].replyaddr),
sizeof(group_list[listidx].replyaddr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending INFO_ACK");
} else {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"INFO_ACK sent");
}
free(encrypted);
free(buf);
}
/**
* Verifies a server's public key fingerprint
*/
int verify_server_fingerprint(const unsigned char *keymod, uint32_t keyexp,
int keylen, int listidx)
{
unsigned char *verifydata, fingerprint[HMAC_LEN];
unsigned int verifylen, fplen;
int found, keyidx;
if (server_count == 0) {
return 1;
}
for (keyidx = 0, found = 0; (keyidx < server_count) && !found; keyidx++) {
if (server_keys[keyidx].addr.s_addr ==
group_list[listidx].replyaddr.sin_addr.s_addr) {
keyidx--;
found = 1;
}
}
if (!found) {
return 0;
}
if (!server_keys[keyidx].has_fingerprint) {
return 1;
}
verifylen = 0;
verifydata = calloc(sizeof(keyexp) + keylen, 1);
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(verifydata, &keyexp, sizeof(keyexp));
verifylen += sizeof(keyexp);
memcpy(verifydata + verifylen, keymod, keylen);
verifylen += keylen;
hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen);
if (memcmp(server_keys[keyidx].fingerprint, fingerprint, fplen)) {
free(verifydata);
return 0;
} else {
free(verifydata);
return 1;
}
}
/**
* Calculate the master key and do key expansion to determine the symmetric
* cypher key and IV salt, and hash key for the server
*/
int calculate_server_keys(int listidx, const struct announce_h *announce)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
time_t t;
uint32_t t2;
memcpy(group_list[listidx].rand1, announce->rand1, sizeof(announce->rand1));
if (!get_random_bytes(group_list[listidx].rand2,
sizeof(group_list[listidx].rand2))) {
log0(group_list[listidx].group_id, 0,
"Failed to get random bytes for rand2");
send_abort(listidx, "Failed to get random bytes for rand2");
return 0;
}
// Sets the first 4 bytes of rand2 to the current time
t = time(NULL);
t2 = (uint32_t)(t & 0xFFFFFFFF);
*(uint32_t *)(group_list[listidx].rand2) = t2;
if (!get_random_bytes(group_list[listidx].premaster,
sizeof(group_list[listidx].premaster))) {
log0(group_list[listidx].group_id, 0,
"Failed to get random bytes for premaster");
send_abort(listidx, "Failed to get random bytes for premaster");
return 0;
}
get_key_info(group_list[listidx].keytype,
&group_list[listidx].keylen, &group_list[listidx].ivlen);
group_list[listidx].hmaclen = get_hash_len(group_list[listidx].hashtype);
explen = group_list[listidx].keylen + group_list[listidx].ivlen +
group_list[listidx].hmaclen;
seedlen = RAND_LEN * 2;
seed = calloc(seedlen, 1);
prf_buf = calloc(MASTER_LEN + explen + group_list[listidx].hmaclen, 1);
if ((seed == NULL) || (prf_buf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(seed, group_list[listidx].rand1, sizeof(group_list[listidx].rand1));
memcpy(seed + sizeof(group_list[listidx].rand1), group_list[listidx].rand2,
sizeof(group_list[listidx].rand2));
PRF(group_list[listidx].hashtype, MASTER_LEN, group_list[listidx].premaster,
sizeof(group_list[listidx].premaster),
"master secret", seed, seedlen, prf_buf, &len);
memcpy(group_list[listidx].master,prf_buf,
sizeof(group_list[listidx].master));
PRF(group_list[listidx].hashtype, explen, group_list[listidx].master,
sizeof(group_list[listidx].master), "key expansion",
seed, seedlen, prf_buf, &len);
memcpy(group_list[listidx].hmackey, prf_buf, group_list[listidx].hmaclen);
memcpy(group_list[listidx].key, prf_buf + group_list[listidx].hmaclen,
group_list[listidx].keylen);
memcpy(group_list[listidx].salt, prf_buf + group_list[listidx].hmaclen +
group_list[listidx].keylen, group_list[listidx].ivlen);
free(seed);
free(prf_buf);
return 1;
}
/**
* Read encryption related fields from an ANNOUNCE
*/
int handle_announce_encryption(int listidx, const struct announce_h *announce,
const unsigned char *keymod)
{
int i;
// Sanity check the selected encryption parameters
if ((group_list[listidx].keytype != KEY_DES) &&
(group_list[listidx].keytype != KEY_DES_EDE3) &&
(group_list[listidx].keytype != KEY_AES128) &&
(group_list[listidx].keytype != KEY_AES256)) {
log0(group_list[listidx].group_id, 0, "Invalid keytype specified");
send_abort(listidx, "Invalid keytype specified");
return 0;
}
if (!cipher_supported(group_list[listidx].keytype)) {
log0(group_list[listidx].group_id, 0, "Keytype not supported here");
send_abort(listidx, "Keytype not supported here");
return 0;
}
if ((group_list[listidx].hashtype != HASH_SHA1) &&
(group_list[listidx].hashtype != HASH_SHA256)) {
log0(group_list[listidx].group_id, 0, "Invalid hashtype specified");
send_abort(listidx, "Invalid hashtype specified");
return 0;
}
if (!hash_supported(group_list[listidx].hashtype)) {
log0(group_list[listidx].group_id, 0, "Hashtype not supported here");
send_abort(listidx, "Hashtype not supported here");
return 0;
}
if ((group_list[listidx].sigtype != SIG_HMAC) &&
(group_list[listidx].sigtype != SIG_RSA)) {
log0(group_list[listidx].group_id, 0, "Invalid sigtype specified");
send_abort(listidx, "Invalid sigtype specified");
return 0;
}
// Load server key and select a matching client key
if (!import_RSA_key(&group_list[listidx].serverkey, ntohl(announce->keyexp),
keymod, ntohs(announce->keylen))) {
log0(group_list[listidx].group_id, 0,
"Failed to load server public key");
send_abort(listidx, "Failed to load server public key");
return 0;
}
if (!verify_server_fingerprint(keymod, announce->keyexp,
ntohs(announce->keylen), listidx)) {
log0(group_list[listidx].group_id, 0,
"Failed to verify server key fingerprint");
send_abort(listidx, "Failed to verify server key fingerprint");
return 0;
}
group_list[listidx].server_keylen=RSA_keylen(group_list[listidx].serverkey);
for (i = 0; i < key_count; i++) {
if (RSA_keylen(privkey[i]) == group_list[listidx].server_keylen) {
group_list[listidx].clientkey = privkey[i];
group_list[listidx].client_keylen = RSA_keylen(privkey[i]);
break;
}
}
if (!group_list[listidx].clientkey) {
log0(group_list[listidx].group_id, 0,
"No client key with keysize == server keysize of %d",
group_list[listidx].server_keylen * 8);
send_abort(listidx, "No client key with keysize == server keysize");
return 0;
}
if (has_proxy) {
if (proxy_key == (RSA_key_t)NULL) {
log0(group_list[listidx].group_id, 0,
"Response proxy set but haven't gotten key yet");
send_abort(listidx,"Response proxy set but haven't gotten key yet");
return 0;
}
if (RSA_keylen(proxy_key) != group_list[listidx].server_keylen) {
log0(group_list[listidx].group_id, 0,
"Response proxy key size doesn't match server key size");
send_abort(listidx, "Response proxy key size != server key size");
return 0;
}
}
// Calculate keys
if (!calculate_server_keys(listidx, announce)) {
return 0;
}
// Reset payload and block sizes
group_list[listidx].encpayloadsize = group_list[listidx].payloadsize -
sizeof(struct encrypted_h) - KEYBLSIZE -
((group_list[listidx].sigtype == SIG_RSA) ?
group_list[listidx].server_keylen :
group_list[listidx].hmaclen );
group_list[listidx].blocksize = group_list[listidx].encpayloadsize -
sizeof(struct fileseg_h);
return 1;
}
/**
* Reads the file info portion of a 2.X ANNOUNCE.
*/
void read_v2_fileinfo(int listidx, const unsigned char *message)
{
struct uftp2_h *v2_header;
struct fileinfo2 *v2_fileinfo;
v2_header = (struct uftp2_h *)message;
v2_fileinfo = (struct fileinfo2 *)(message + sizeof(struct uftp2_h));
// Load fileinfo params into list
group_list[listidx].fileinfo.ftype = FTYPE_REG;
strncpy(group_list[listidx].fileinfo.name, v2_fileinfo->name,
sizeof(group_list[listidx].fileinfo.name)-1);
group_list[listidx].fileinfo.blocks = ntohl(v2_fileinfo->block_total);
group_list[listidx].fileinfo.sections = ntohl(v2_fileinfo->section_total);
if (ntohl(v2_fileinfo->fsize) == 0 ) {
group_list[listidx].fileinfo.size =
(f_offset_t)ntohl(v2_fileinfo->hilargefsize) << 32;
group_list[listidx].fileinfo.size |= ntohl(v2_fileinfo->lolargefsize);
} else {
group_list[listidx].fileinfo.size = ntohl(v2_fileinfo->fsize);
}
group_list[listidx].fileinfo.tstamp = 0;
}
/**
* Read in the contents of a 2.X ANNOUNCE.
*/
void read_v2_announce(int listidx, int addridx, const unsigned char *message,
struct sockaddr_in src)
{
struct uftp2_h *v2_header;
struct fileinfo2 *v2_fileinfo;
v2_header = (struct uftp2_h *)message;
v2_fileinfo = (struct fileinfo2 *)(message + sizeof(struct uftp2_h));
group_list[listidx].group_id = ntohl(v2_header->tx_id);
group_list[listidx].file_id = 0;
group_list[listidx].srcaddr = src.sin_addr;
if (addridx != -1) {
group_list[listidx].destaddr = m_interface[addridx].addr;
} else {
// unicast mode with no match -- accept anyway
group_list[listidx].destaddr.s_addr = v2_fileinfo->addr_list[0];
}
group_list[listidx].replyaddr = src;
group_list[listidx].register_int = ntohs(v2_fileinfo->rxlatency1);
group_list[listidx].announce_time =
group_list[listidx].register_int * 5 / 1000;
group_list[listidx].done_int = ntohs(v2_fileinfo->rxlatency2);
group_list[listidx].status_time =
group_list[listidx].done_int * 5 / 1000;
group_list[listidx].mtu = V2_PACKETSIZE;
group_list[listidx].payloadsize = V2_PACKETSIZE;
group_list[listidx].encpayloadsize = V2_PACKETSIZE;
group_list[listidx].blocksize = V2_BLOCKSIZE;
group_list[listidx].client_auth = 0;
group_list[listidx].hashtype = HASH_NONE;
group_list[listidx].keytype = KEY_NONE;
group_list[listidx].multi.s_addr = v2_fileinfo->mcast;
group_list[listidx].version = UFTP_V2_VER;
group_list[listidx].phase = PHASE_REGISTERED;
gettimeofday(&group_list[listidx].expire_time, NULL);
group_list[listidx].expire_time.tv_sec += group_list[listidx].announce_time;
group_list[listidx].fileinfo.fd = -1;
}
/**
* Read in the contents of an ANNOUNCE.
*/
void read_announce(int listidx, int addridx, const unsigned char *buf,
struct sockaddr_in src)
{
struct uftp_h *header;
struct announce_h *announce;
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
group_list[listidx].group_id = ntohl(header->group_id);
if (header->srcaddr != 0) {
group_list[listidx].srcaddr.s_addr = header->srcaddr;
} else {
group_list[listidx].srcaddr = src.sin_addr;
}
if ((announce->destcount == 0) && (!uid)) {
// open group, set destaddr to 0 to make server use IP src addr
group_list[listidx].destaddr.s_addr = 0;
} else if (addridx != -1) {
group_list[listidx].destaddr = m_interface[addridx].addr;
} else {
// unicast mode with no match -- accept anyway
unsigned char *keymod;
uint32_t *addrlist;
keymod = (unsigned char *)announce + sizeof(struct announce_h);
addrlist = (uint32_t *)(keymod + ntohs(announce->keylen));
group_list[listidx].destaddr.s_addr = addrlist[0];
}
if (has_proxy) {
group_list[listidx].replyaddr.sin_family = AF_INET;
group_list[listidx].replyaddr.sin_addr = proxy_info.addr;
group_list[listidx].replyaddr.sin_port = htons(port);
} else {
group_list[listidx].replyaddr = src;
}
group_list[listidx].register_int = ntohs(announce->register_int);
group_list[listidx].done_int = ntohs(announce->done_int);
group_list[listidx].announce_time = announce->announce_time;
group_list[listidx].status_time = announce->status_time;
group_list[listidx].mtu = ntohs(announce->mtu);
group_list[listidx].payloadsize =
group_list[listidx].mtu - 28 - sizeof(struct uftp_h);
group_list[listidx].encpayloadsize = group_list[listidx].payloadsize;
group_list[listidx].blocksize = group_list[listidx].payloadsize -
sizeof(struct fileseg_h);
group_list[listidx].client_auth = announce->client_auth;
group_list[listidx].restart = (((announce->flags & FLAG_RESTART) != 0) &&
(strcmp(tempdir, "")));
group_list[listidx].sync_preview =
((announce->flags & FLAG_SYNC_PREVIEW) != 0);
group_list[listidx].sync_mode = group_list[listidx].sync_preview ||
((announce->flags & FLAG_SYNC_MODE) != 0);
group_list[listidx].hashtype = announce->hashtype;
group_list[listidx].keytype = announce->keytype;
group_list[listidx].sigtype = announce->sigtype;
group_list[listidx].multi.s_addr = announce->privatemcast;
group_list[listidx].version = header->uftp_id;
group_list[listidx].phase = PHASE_REGISTERED;
gettimeofday(&group_list[listidx].expire_time, NULL);
group_list[listidx].expire_time.tv_sec += group_list[listidx].announce_time;
group_list[listidx].fileinfo.fd = -1;
}
/**
* Processes a new incoming ANNOUNCE
*/
void handle_announce(struct sockaddr_in src, uint8_t version,
const unsigned char *buf)
{
struct uftp_h *header;
struct announce_h *announce;
unsigned char *keymod;
uint32_t *addrlist;
struct uftp2_h *v2_header;
struct fileinfo2 *v2_fileinfo;
uint32_t group_id;
int open, addrlen, addridx, listidx, unicast, i;
struct hostent *hp;
time_t t;
struct tm *start_time;
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
v2_header = (struct uftp2_h *)buf;
v2_fileinfo = (struct fileinfo2 *)(buf + sizeof(struct uftp2_h));
// A little setup to make checking the address list common between versions
if (version == UFTP_V2_VER) {
addrlen = V2_MAXDEST;
open = (ntohl(v2_fileinfo->open) != 0);
addrlist = v2_fileinfo->addr_list;
group_id = ntohl(v2_header->tx_id);
unicast = (v2_fileinfo->mcast == 0);
} else {
addrlen = ntohs(announce->destcount);
open = (addrlen == 0);
keymod = (unsigned char *)announce + sizeof(struct announce_h);
addrlist = (uint32_t *)(keymod + ntohs(announce->keylen));
group_id = ntohl(header->group_id);
unicast = (announce->privatemcast == 0);
}
if (!open) {
if (((addridx = interface_in_list(addrlist, addrlen)) == -1) &&
(!unicast)) {
log1(group_id, 0, "Name not in host list");
return;
}
} else {
addridx = 0;
// Look for the first non-loopback interface or the UID
for (i = 0; i < interface_count; i++) {
if (uid) {
if (m_interface[i].addr.s_addr == uid) {
addridx = i;
break;
}
} else if (!m_interface[i].isloopback) {
addridx = i;
break;
}
}
}
if ((listidx = find_open_slot()) == -1 ) {
log0(group_id, 0,
"Error: maximum number of incoming files exceeded: %d\n", MAXLIST);
return;
}
t = time(NULL);
start_time = localtime(&t);
sprintf(group_list[listidx].start_date, "%04d%02d%02d",
start_time->tm_year + 1900, start_time->tm_mon + 1,
start_time->tm_mday);
sprintf(group_list[listidx].start_time, "%02d%02d%02d",
start_time->tm_hour, start_time->tm_min, start_time->tm_sec);
if (version == UFTP_V2_VER) {
read_v2_announce(listidx, addridx, buf, src);
} else {
read_announce(listidx, addridx, buf, src);
}
if (!noname && (hp =
gethostbyaddr((char *)&(group_list[listidx].srcaddr),
sizeof(struct in_addr), AF_INET))) {
log0(group_list[listidx].group_id, 0,
"Received request from %s (%s)",
hp->h_name, inet_ntoa(group_list[listidx].srcaddr));
} else {
log0(group_list[listidx].group_id, 0,
"Received request from %s",
inet_ntoa(group_list[listidx].srcaddr));
}
log1(group_list[listidx].group_id, 0,
"Using private multicast address %s",
inet_ntoa(group_list[listidx].multi));
if (group_list[listidx].keytype != KEY_NONE) {
if (!handle_announce_encryption(listidx, announce, keymod)) {
return;
}
} else if (encrypted_only) {
log0(group_list[listidx].group_id, 0,
"No unencrypted transfers allowed");
send_abort(listidx, "No unencrypted transfers allowed");
return;
}
if (group_list[listidx].restart) {
if (group_list[listidx].sync_mode) {
log0(group_list[listidx].group_id, 0,
"Sync mode and restart mode incompatable");
send_abort(listidx, "Sync mode and restart mode incompatable");
return;
}
read_restart_file(listidx);
}
if (group_list[listidx].multi.s_addr != 0) {
if (server_count) {
if (!is_multicast(group_list[listidx].multi, 1)) {
log0(group_list[listidx].group_id, 0,
"Invalid source specific multicast address: %s",
inet_ntoa(group_list[listidx].multi));
send_abort(listidx, "Invalid source specific "
"multicast address");
return;
}
} else {
if (!is_multicast(group_list[listidx].multi, 0)) {
log0(group_list[listidx].group_id, 0,
"Invalid multicast address: %s",
inet_ntoa(group_list[listidx].multi));
send_abort(listidx, "Invalid multicast address");
return;
}
}
if (server_count > 0) {
if (!multicast_join(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface,
interface_count, server_keys, server_count)) {
send_abort(listidx, "Error joining multicast group");
return;
}
if (has_proxy) {
if (!multicast_join(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface,
interface_count, &proxy_info, 1)) {
send_abort(listidx, "Error joining multicast group");
return;
}
}
} else {
if (!multicast_join(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface, interface_count,
NULL, 0)) {
send_abort(listidx, "Error joining multicast group");
return;
}
}
group_list[listidx].multi_join = 1;
}
// A 2.X ANNOUNCE also contains file info, so process that as well
if ((version == UFTP_V2_VER) && (!handle_fileinfo(listidx, buf, 0))) {
return;
}
send_register(listidx);
}
/**
* Processes an incoming REG_CONF message.
* Expected in response to a REGISTER when encryption is disabled.
* For 2.X, always expected in response to a REGISTER.
*/
void handle_regconf(int listidx, const unsigned char *message, int meslen)
{
struct regconf_h *regconf;
uint32_t *addrlist;
struct uftp2_h *v2_header;
struct statusinfo2 *v2_statusinfo;
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)message;
v2_statusinfo = (struct statusinfo2 *)(message + sizeof(struct uftp2_h));
if (ntohl(v2_header->func) != V2_REG_CONF) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Expected REG_CONF, got %s",
v2_func_name(ntohl(v2_header->func)));
return;
}
if (addr_in_list(listidx, v2_statusinfo->addr_list, V2_MAXINFODEST)) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Registration confirmed");
group_list[listidx].phase = PHASE_RECEIVING;
set_timeout(listidx);
}
} else {
regconf = (struct regconf_h *)message;
addrlist = (uint32_t *)((char *)regconf + sizeof(struct regconf_h));
if (meslen != sizeof(struct regconf_h) +
(ntohs(regconf->destcount) * sizeof(uint32_t))) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting REG_CONF from server: invalid message size");
return;
}
if (addr_in_list(listidx, addrlist, ntohs(regconf->destcount))) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Registration confirmed");
group_list[listidx].phase = PHASE_MIDGROUP;
set_timeout(listidx);
}
}
}
/**
* Process an incoming KEYINFO message.
* Expected in response to a REGISTER when encryption is enabled.
*/
void handle_keyinfo(int listidx, const unsigned char *buf)
{
struct uftp_h *header;
struct keyinfo_h *keyinfo;
struct destkey *keylist;
int i, j, keyidx, len;
unsigned explen, declen;
uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv;
header = (struct uftp_h *)buf;
keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));
if (ntohs(header->blsize) != sizeof(struct keyinfo_h) +
(keyinfo->destcount * sizeof(struct destkey))) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting KEYINFO from server: invalid message size");
return;
}
// This duplicates addr_in_list, but here it's addressed in a struct array
for (i = 0, keyidx = -1; (i < keyinfo->destcount) &&
(keyidx == -1); i++) {
if (group_list[listidx].destaddr.s_addr == 0) {
for (j = 0; j < interface_count; j++) {
if (keylist[i].destaddr == m_interface[j].addr.s_addr) {
group_list[listidx].destaddr = m_interface[j].addr;
keyidx = i;
break;
}
}
} else {
if (group_list[listidx].destaddr.s_addr == keylist[i].destaddr) {
keyidx = i;
break;
}
}
}
if (keyidx != -1) {
log(group_list[listidx].group_id, 0, "Received KEYINFO");
if (group_list[listidx].phase == PHASE_MIDGROUP) {
// We already got the KEYINFO, so no need to reprocess.
// Just resend the INFO_ACK and reset the timeout
send_info_ack(listidx, 0);
set_timeout(listidx);
return;
}
iv = calloc(group_list[listidx].ivlen, 1);
if (iv == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
build_iv(iv, group_list[listidx].salt, group_list[listidx].ivlen,
htonl(group_list[listidx].group_id), header->srcaddr,
keyinfo->tstamp_sec, keyinfo->tstamp_usec);
if (!decrypt_block(group_list[listidx].keytype, iv,
group_list[listidx].key, keylist[keyidx].groupmaster,
keyinfo->groupmaster_len, decgroupmaster, &declen) ||
(declen != MASTER_LEN - 1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Decrypt failed for group master");
send_abort(listidx, "Decrypt failed for group master");
free(iv);
return;
}
free(iv);
group_list[listidx].groupmaster[0] = group_list[listidx].version;
memcpy(&group_list[listidx].groupmaster[1], decgroupmaster, declen);
explen = group_list[listidx].keylen + group_list[listidx].ivlen +
group_list[listidx].hmaclen;
prf_buf = calloc(explen + group_list[listidx].hmaclen, 1);
if (prf_buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
PRF(group_list[listidx].hashtype, explen,
group_list[listidx].groupmaster,
sizeof(group_list[listidx].groupmaster), "key expansion",
group_list[listidx].rand1, sizeof(group_list[listidx].rand1),
prf_buf, &len);
memcpy(group_list[listidx].grouphmackey, prf_buf,
group_list[listidx].hmaclen);
memcpy(group_list[listidx].groupkey,
prf_buf + group_list[listidx].hmaclen,
group_list[listidx].keylen);
memcpy(group_list[listidx].groupsalt,
prf_buf + group_list[listidx].hmaclen +
group_list[listidx].keylen, group_list[listidx].ivlen);
free(prf_buf);
group_list[listidx].phase = PHASE_MIDGROUP;
send_info_ack(listidx, 0);
set_timeout(listidx);
}
}
/**
* Send a COMPLETE with the given status in reponse to a FILEINFO,
* set the phase to MIDGROUP, and reset the timeout
*/
void early_complete(int listidx, int status)
{
group_list[listidx].phase = PHASE_MIDGROUP;
group_list[listidx].fileinfo.comp_status = status;
send_complete(listidx);
set_timeout(listidx);
}
/**
* Read in the contents of a FILEINFO message
* Returns 1 on success, 0 on error or ignore
*/
int read_fileinfo(int listidx, const unsigned char *message, int meslen)
{
struct fileinfo_h *fileinfo;
struct fileinfo_30_h *fileinfo30;
uint32_t *addrlist;
int structlen, listlen;
char *name, *p;
fileinfo = (struct fileinfo_h *)message;
fileinfo30 = (struct fileinfo_30_h *)message;
if (group_list[listidx].version == UFTP_3_0_VER) {
addrlist = (uint32_t *)((char *)fileinfo30 +
sizeof(struct fileinfo_30_h));
structlen = sizeof(struct fileinfo_30_h);
name = fileinfo30->name;
listlen = ntohs(fileinfo30->destcount);
} else {
addrlist = (uint32_t *)((char *)fileinfo + sizeof(struct fileinfo_h));
structlen = sizeof(struct fileinfo_h);
name = fileinfo->name;
listlen = ntohs(fileinfo->destcount);
}
if (meslen != (structlen + (listlen * sizeof(uint32_t)))) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILEINFO from server: invalid message size");
send_abort(listidx, "Rejecting FILEINFO: invalid message size");
return 0;
}
if (group_list[listidx].phase == PHASE_RECEIVING) {
// We already got the FILEINFO, so no need to reprocess.
// Just resend the INFO_ACK if requested and reset the timeout
if (addr_in_list(listidx, addrlist, listlen)) {
send_info_ack(listidx, group_list[listidx].fileinfo.restart);
}
set_timeout(listidx);
return 0;
}
if ((group_list[listidx].phase == PHASE_MIDGROUP) &&
(group_list[listidx].file_id == ntohs(fileinfo->file_id))) {
// We already got the FILEINFO, and it's for a completed file.
// So resend the COMPLETE if requested and reset the timeout
if (addr_in_list(listidx, addrlist, listlen)) {
send_complete(listidx);
}
set_timeout(listidx);
return 0;
}
// Load fileinfo params into list
memset(&group_list[listidx].fileinfo, 0, sizeof(struct file_t));
group_list[listidx].fileinfo.ftype = fileinfo->ftype;
group_list[listidx].file_id = ntohs(fileinfo->file_id);
strncpy(group_list[listidx].fileinfo.name, name,
sizeof(group_list[listidx].fileinfo.name)-1);
group_list[listidx].fileinfo.blocks = ntohl(fileinfo->block_total);
group_list[listidx].fileinfo.sections = ntohs(fileinfo->section_total);
group_list[listidx].fileinfo.size =
(f_offset_t)ntohl(fileinfo->hifsize) << 32;
group_list[listidx].fileinfo.size |= ntohl(fileinfo->lofsize);
if (group_list[listidx].version == UFTP_3_0_VER) {
group_list[listidx].fileinfo.tstamp = 0;
} else {
group_list[listidx].fileinfo.tstamp = ntohl(fileinfo->ftstamp);
}
group_list[listidx].fileinfo.fd = -1;
// Run some checks on the filename
if (strlen(name) == 0) {
log1(group_list[listidx].group_id, ntohs(fileinfo->file_id),
"Rejecting FILEINFO from server: blank file name");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
p = strstr(name, "..");
if ((p != NULL) && ((p[2] == '\x0') || (p[2] == '/') || (p[2] == '\\')) &&
((p == name) || (p[-1] == '/') || (p[-1] == '\\'))) {
log1(group_list[listidx].group_id, ntohs(fileinfo->file_id),
"Rejecting FILEINFO from server: filename contains ..");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
if (fileinfo->ftype == FTYPE_LINK) {
p = name + strlen(name) + 1;
if (p - name >= sizeof(name)) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILEINFO from server: bad link name");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
if (strlen(p) == 0) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILEINFO from server: blank link name");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
strncpy(group_list[listidx].fileinfo.linkname, p,
sizeof(group_list[listidx].fileinfo.linkname)-1);
}
return 1;
}
/**
* Validate and establish the destination name of an incoming file.
* Returns 0 if the file was rejected for some reason, 1 otherwise.
*/
int setup_dest_file(int listidx)
{
int found_dest_dir, i;
int (*cmp)(const char *, const char *);
int (*ncmp)(const char *, const char *, size_t);
#if PATH_SEP != '/'
// First translate any '/' in the sent file name to PATH_SEP
{
char *p;
while ((p = strchr(group_list[listidx].fileinfo.name, '/')) != NULL) {
*p = PATH_SEP;
}
}
#endif
#ifdef WINDOWS
cmp = stricmp;
ncmp = strnicmp;
#else
cmp = strcmp;
ncmp = strncmp;
#endif
if (isfullpath(group_list[listidx].fileinfo.name)) {
if (strcmp(tempdir, "")) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting file with absolute pathname: "
"temp directory is in use");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
for (found_dest_dir = 0, i = 0; i < destdircnt; i++) {
if (!ncmp(group_list[listidx].fileinfo.name,
destdir[i], strlen(destdir[i]))) {
if (!cmp(group_list[listidx].fileinfo.name, destdir[i])) {
log0(group_list[listidx].group_id,
group_list[listidx].file_id,
"Rejecting file with absolute pathname: "
"can't have the same name as a dest directory");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
} else {
found_dest_dir = 1;
break;
}
}
}
if (!found_dest_dir) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting file with absolute pathname: "
"doesn't match any dest directory");
early_complete(listidx, COMP_STAT_REJECTED);
return 0;
}
group_list[listidx].fileinfo.destdiridx = i;
snprintf(group_list[listidx].fileinfo.filepath,
sizeof(group_list[listidx].fileinfo.filepath), "%s",
group_list[listidx].fileinfo.name);
} else {
if (!strcmp(tempdir, "")) {
snprintf(group_list[listidx].fileinfo.filepath,
sizeof(group_list[listidx].fileinfo.filepath), "%s%c%s",
destdir[0], PATH_SEP, group_list[listidx].fileinfo.name);
} else {
snprintf(group_list[listidx].fileinfo.filepath,
sizeof(group_list[listidx].fileinfo.filepath),
"%s%c_group_%08X%c%s", tempdir,
PATH_SEP, group_list[listidx].group_id, PATH_SEP,
group_list[listidx].fileinfo.name);
}
}
snprintf(group_list[listidx].fileinfo.temppath,
sizeof(group_list[listidx].fileinfo.temppath),
"%s.~uftp-%08X-%04X", group_list[listidx].fileinfo.filepath,
group_list[listidx].group_id, group_list[listidx].file_id);
return 1;
}
/**
* Perform FILEINFO processing specific to a regular file in restart mode
* Returns 1 if a COMPLETE was sent in response, 0 otherwise
*/
int handle_fileinfo_restart(int listidx)
{
stat_struct statbuf;
if ((!strcmp(group_list[listidx].fileinfo.name,
group_list[listidx].restartinfo->name)) &&
(group_list[listidx].fileinfo.size ==
group_list[listidx].restartinfo->size) &&
(group_list[listidx].fileinfo.blocks ==
group_list[listidx].restartinfo->blocks) &&
(group_list[listidx].fileinfo.sections ==
group_list[listidx].restartinfo->sections)) {
// Flag this file to restart a failed transfer
group_list[listidx].fileinfo.restart = 1;
return 0;
} else if ((lstat_func(group_list[listidx].fileinfo.filepath,
&statbuf) != -1) && S_ISREG(statbuf.st_mode) &&
(statbuf.st_size == group_list[listidx].fileinfo.size)) {
// This file was finished on the last attempt,
// so respond with a COMPLETE right away
early_complete(listidx, COMP_STAT_NORMAL);
return 1;
}
return 0;
}
/**
* Perform FILEINFO processing specific to a regular file in sync mode
* Returns 1 if a COMPLETE was sent in response, 0 otherwise
*/
int handle_fileinfo_sync(int listidx)
{
stat_struct statbuf;
if (lstat_func(group_list[listidx].fileinfo.filepath,
&statbuf) != -1) {
// If source is newer, skip
// If source is older, overwrite
// If timstamps same, skip if sizes are also same
int skip;
if (group_list[listidx].fileinfo.tstamp < statbuf.st_mtime) {
skip = 1;
} else if (group_list[listidx].fileinfo.tstamp >
statbuf.st_mtime) {
skip = 0;
} else if (S_ISREG(statbuf.st_mode) &&
(statbuf.st_size == group_list[listidx].fileinfo.size)) {
skip = 1;
} else {
skip = 0;
}
if (skip) {
if (log_level >= 1) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"skipping file, in sync");
} else {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Skip %s", group_list[listidx].fileinfo.name);
}
early_complete(listidx, COMP_STAT_SKIPPED);
return 1;
} else {
if (log_level >= 1) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"overwriting out of sync file");
} else {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Overwrite %s", group_list[listidx].fileinfo.name);
}
group_list[listidx].fileinfo.comp_status = COMP_STAT_OVERWRITE;
if (group_list[listidx].sync_preview) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Sync preview mode, skipping receive");
early_complete(listidx, COMP_STAT_OVERWRITE);
return 1;
}
if (!tempfile) {
move_to_backup(listidx);
}
}
} else {
if (log_level >= 1) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"copying new file");
} else {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Copy %s", group_list[listidx].fileinfo.name);
}
if (group_list[listidx].sync_preview) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Sync preview mode, skipping receive");
early_complete(listidx, COMP_STAT_NORMAL);
return 1;
}
if (!tempfile) {
move_to_backup(listidx);
}
}
return 0;
}
/**
* Perform FILEINFO processing specific to a regular file
* Returns 1 on success, 0 on error
*/
int handle_fileinfo_regular(int listidx)
{
// First handle restart or sync mode,
// then create/open the file.
if (group_list[listidx].restartinfo) {
if (handle_fileinfo_restart(listidx)) {
return 1;
}
} else if (group_list[listidx].sync_mode) {
if (handle_fileinfo_sync(listidx)) {
return 1;
}
}
if (group_list[listidx].fileinfo.restart) {
group_list[listidx].fileinfo.fd =
open(group_list[listidx].fileinfo.filepath, OPENWRITE);
} else {
const char *filename;
if (tempfile) {
filename = group_list[listidx].fileinfo.temppath;
} else {
filename = group_list[listidx].fileinfo.filepath;
}
#ifdef WINDOWS
SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
#else
chmod(filename, 0644);
#endif
group_list[listidx].fileinfo.fd =
open(filename, OPENWRITE | O_CREAT | O_TRUNC, 0644);
}
if (group_list[listidx].fileinfo.fd == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error opening data file");
send_abort(listidx, "Error opening data file");
return 0;
}
// Final preparations for receiving a file.
if (group_list[listidx].fileinfo.restart) {
group_list[listidx].fileinfo.naklist =
group_list[listidx].restartinfo->naklist;
group_list[listidx].fileinfo.section_done =
group_list[listidx].restartinfo->section_done;
group_list[listidx].restartinfo->naklist = NULL;
group_list[listidx].restartinfo->section_done = NULL;
free(group_list[listidx].restartinfo);
group_list[listidx].restartinfo = NULL;
} else {
group_list[listidx].fileinfo.naklist =
calloc(group_list[listidx].fileinfo.blocks, 1);
if (group_list[listidx].fileinfo.naklist == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
group_list[listidx].fileinfo.section_done =
calloc(group_list[listidx].fileinfo.sections, 1);
if (group_list[listidx].fileinfo.section_done == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memset(group_list[listidx].fileinfo.naklist, 1,
group_list[listidx].fileinfo.blocks);
}
group_list[listidx].fileinfo.last_block = -1;
group_list[listidx].fileinfo.curr_offset = 0;
group_list[listidx].fileinfo.wait = 0;
if (group_list[listidx].version != UFTP_V2_VER) {
group_list[listidx].phase = PHASE_RECEIVING;
send_info_ack(listidx, group_list[listidx].fileinfo.restart);
set_timeout(listidx);
}
return 1;
}
/**
* Perform FILEINFO processing specific to an empty directory
* Returns 1 on success, 0 on error
*/
int handle_fileinfo_dir(int listidx, int found_dir)
{
if (!found_dir && !group_list[listidx].sync_preview) {
log2(group_list[listidx].group_id, group_list[listidx].file_id,
"Creating directory");
if (mkdir(group_list[listidx].fileinfo.filepath, 0755) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to create directory %s",
group_list[listidx].fileinfo.filepath);
send_abort(listidx, "Failed to create directory");
return 0;
}
}
early_complete(listidx, found_dir ? COMP_STAT_SKIPPED : COMP_STAT_NORMAL);
return 1;
}
/**
* Perform FILEINFO processing specific to a symbolic link
* Returns 1 on success, 0 on error
*/
int handle_fileinfo_link(int listidx)
{
#ifndef WINDOWS
if (!group_list[listidx].sync_preview)
if (symlink(group_list[listidx].fileinfo.linkname,
group_list[listidx].fileinfo.filepath) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to create symlink %s",
group_list[listidx].fileinfo.filepath);
send_abort(listidx, "Failed to create symlink");
return 0;
}
#endif
early_complete(listidx, COMP_STAT_NORMAL);
return 1;
}
/**
* Process an incoming FILEINFO message.
* Expected in the middle of a group with no current file.
* For version 2, process the file info portion of an ANNOUNCE message.
* Returns 1 on success, 0 on error or ignore
*/
int handle_fileinfo(int listidx, const unsigned char *message, int meslen)
{
stat_struct statbuf;
int found_dir;
if (group_list[listidx].version == UFTP_V2_VER) {
read_v2_fileinfo(listidx, message);
} else if (!read_fileinfo(listidx, message, meslen)) {
return 0;
}
if (!group_list[listidx].sync_mode || (log_level >= 1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Name of file to receive: %s", group_list[listidx].fileinfo.name);
}
switch (group_list[listidx].fileinfo.ftype) {
case FTYPE_REG:
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Bytes: %s, Blocks: %d, Sections: %d",
printll(group_list[listidx].fileinfo.size),
group_list[listidx].fileinfo.blocks,
group_list[listidx].fileinfo.sections);
break;
case FTYPE_DIR:
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Empty directory");
break;
case FTYPE_LINK:
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Symbolic link to %s", group_list[listidx].fileinfo.linkname);
break;
default:
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Invalid file type: %d", group_list[listidx].fileinfo.ftype);
send_abort(listidx, "Invalid file type");
return 0;
}
if (!setup_dest_file(listidx)) {
// A rejected file is still a success because we responded with a
// COMPLETE with status=rejected instead of with an ABORT
return 1;
}
// Make sure the path to the destination file exists and
// remove or back up any existing file
if (!create_path_to_file(listidx, group_list[listidx].fileinfo.filepath)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error creating path to data file");
send_abort(listidx, "Error creating path to data file");
return 0;
}
found_dir = 0;
if (tempfile && !group_list[listidx].sync_preview) {
clear_path(group_list[listidx].fileinfo.temppath, listidx);
}
if (lstat_func(group_list[listidx].fileinfo.filepath, &statbuf) != -1) {
log3(group_list[listidx].group_id, group_list[listidx].file_id,
"checking existing file");
if ((group_list[listidx].fileinfo.ftype != FTYPE_DIR) ||
!S_ISDIR(statbuf.st_mode)) {
if ((group_list[listidx].fileinfo.ftype != FTYPE_REG) ||
!S_ISREG(statbuf.st_mode) ||
((!group_list[listidx].restart) &&
(!group_list[listidx].sync_mode))) {
// Don't clear/backup if we're receiving a regular file
// and we're in either restart mode or sync mode
log3(group_list[listidx].group_id, group_list[listidx].file_id,
"calling move_to_backup");
if (!tempfile) {
move_to_backup(listidx);
}
}
} else {
log3(group_list[listidx].group_id, group_list[listidx].file_id,
"found dir");
found_dir = 1;
}
} else if (errno != ENOENT) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error checking file %s",group_list[listidx].fileinfo.filepath);
}
switch (group_list[listidx].fileinfo.ftype) {
case FTYPE_REG:
return handle_fileinfo_regular(listidx);
case FTYPE_DIR:
return handle_fileinfo_dir(listidx, found_dir);
case FTYPE_LINK:
return handle_fileinfo_link(listidx);
}
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error handling FILEINFO: shouldn't get here!");
return 0;
}
uftp-3.5/client_common.c 0000644 0003316 0000550 00000070654 11577014247 014362 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
/**
* Look for a given file in the global group list
* Returns the file's index in the list, or -1 if not found
*/
int find_file(uint32_t group_id)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id == group_id) {
return i;
}
}
return -1;
}
/**
* Looks for one of the local listening addresses in a list of addresses.
* Returns the index in m_interface of the match, or -1 if no match
*/
int interface_in_list(uint32_t *addrlist, int size)
{
int i, j;
for (i = 0; i < size; i++) {
if (addrlist[i] == 0) {
return -1;
}
for (j = 0; j < interface_count; j++) {
if (addrlist[i] == m_interface[j].addr.s_addr) {
return j;
}
}
}
return -1;
}
/**
* Looks for the group's matched destination address in a list of addresses.
* Returns 1 if found, 0 if not found
*/
int addr_in_list(int listidx, uint32_t *addrlist, int size)
{
int i, rval;
if (group_list[listidx].destaddr.s_addr == 0) {
rval = interface_in_list(addrlist, size);
if (rval == -1) {
return 0;
} else {
group_list[listidx].destaddr = m_interface[rval].addr;
return 1;
}
}
for (i = 0; i < size; i++) {
if (addrlist[i] == 0) {
return 0;
}
if (group_list[listidx].destaddr.s_addr == addrlist[i]) {
return 1;
}
}
return 0;
}
/**
* Reads in the contents of the restart file.
*/
void read_restart_file(int listidx)
{
struct client_restart_t *restart;
char restart_name[MAXPATHNAME];
int fd;
// Don't bother if we're not using a temp directory.
if (!strcmp(tempdir, "")) {
return;
}
log1(group_list[listidx].group_id, 0, "Reading restart file");
snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
tempdir, PATH_SEP, group_list[listidx].group_id);
if ((fd = open(restart_name, OPENREAD, 0644)) == -1) {
syserror(group_list[listidx].group_id, 0,
"Failed to read restart file");
return;
}
// Read header
restart = calloc(sizeof(struct client_restart_t), 1);
if (restart == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (file_read(fd, restart, sizeof(struct client_restart_t), 0) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to read header for restart file");
goto err1;
}
// Read NAK list
if (restart->blocks) {
restart->naklist = calloc(restart->blocks, 1);
if (restart->naklist == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (file_read(fd, restart->naklist, restart->blocks, 0) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to read NAK list for restart file");
goto err2;
}
}
// Read section_done list
if (restart->sections) {
restart->section_done = calloc(restart->sections, 1);
if (restart->section_done == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (file_read(fd, restart->section_done, restart->sections, 0) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to read section_done list for restart file");
goto err3;
}
}
close(fd);
unlink(restart_name);
group_list[listidx].restartinfo = restart;
return;
err3:
free(restart->section_done);
err2:
free(restart->naklist);
err1:
free(restart);
close(fd);
}
/**
* Save the state of a failed transfer so it can restarted later.
*/
void write_restart_file(int listidx)
{
struct file_t *fileinfo;
struct client_restart_t restart;
char restart_name[MAXPATHNAME];
int fd;
// Don't bother if we're not using a temp directory.
if (!strcmp(tempdir, "")) {
return;
}
log1(group_list[listidx].group_id, 0, "Writing restart file");
memset(&restart, 0, sizeof(restart));
fileinfo = &group_list[listidx].fileinfo;
if (group_list[listidx].phase != PHASE_MIDGROUP) {
restart.blocks = fileinfo->blocks;
restart.sections = fileinfo->sections;
restart.size = fileinfo->size;
strcpy(restart.name, fileinfo->name);
}
snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
tempdir, PATH_SEP, group_list[listidx].group_id);
if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC, 0644)) == -1) {
syserror(group_list[listidx].group_id, 0,
"Failed to create restart file");
return;
}
if (file_write(fd, &restart, sizeof(restart)) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to write header for restart file");
goto errexit;
}
if (fileinfo->blocks && fileinfo->naklist) {
if (file_write(fd, fileinfo->naklist, fileinfo->blocks) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to write NAK list for restart file");
goto errexit;
}
}
if (fileinfo->sections && fileinfo->section_done) {
if (file_write(fd, fileinfo->section_done, fileinfo->sections) == -1) {
log0(group_list[listidx].group_id, 0,
"Failed to write section_done list for restart file");
goto errexit;
}
}
close(fd);
return;
errexit:
close(fd);
unlink(restart_name);
}
/**
* Checks to see if the multicast address used for the given group list member
* is also being used by either another member or the public address list
*/
int other_mcast_users(int listidx)
{
int i;
for (i = 0; i < pub_multi_count; i++) {
if (group_list[listidx].multi.s_addr == pub_multi[i].s_addr) {
return 1;
}
}
for (i = 0; i < MAXLIST; i++) {
if ((i != listidx) && (group_list[i].group_id != 0) &&
(group_list[listidx].multi.s_addr ==
group_list[i].multi.s_addr)) {
return 1;
}
}
return 0;
}
/**
* Clean up a group list entry. Close the file if open,
* free malloc'ed structures, drop the multicast group
* (if no one else is using it) and free the slot.
*/
void file_cleanup(int listidx, int abort)
{
if (group_list[listidx].fileinfo.fd >= 0) {
close(group_list[listidx].fileinfo.fd);
group_list[listidx].fileinfo.fd = -1;
if (tempfile) {
move_to_backup(listidx);
if (rename(group_list[listidx].fileinfo.temppath,
group_list[listidx].fileinfo.filepath) == -1) {
syserror(group_list[listidx].group_id,
group_list[listidx].file_id,
"Couldn't rename from %s to %s",
group_list[listidx].fileinfo.temppath,
group_list[listidx].fileinfo.filepath);
}
}
if (group_list[listidx].fileinfo.tstamp) {
utim_buf utbuf;
utbuf.actime = group_list[listidx].fileinfo.tstamp;
utbuf.modtime = group_list[listidx].fileinfo.tstamp;
if (utime(group_list[listidx].fileinfo.filepath, &utbuf) == -1) {
syserror(group_list[listidx].group_id,
group_list[listidx].file_id, "utime failed");
}
}
}
if ((group_list[listidx].version == UFTP_V2_VER) ||
abort || (group_list[listidx].file_id == 0)) {
if ((group_list[listidx].multi.s_addr != 0) &&
!other_mcast_users(listidx) && group_list[listidx].multi_join) {
if (server_count > 0) {
multicast_leave(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface,
interface_count, server_keys, server_count);
if (has_proxy) {
multicast_leave(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface,
interface_count, &proxy_info, 1);
}
} else {
multicast_leave(listener, group_list[listidx].group_id,
&group_list[listidx].multi,
m_interface, interface_count, NULL, 0);
}
}
if (group_list[listidx].serverkey) {
free_RSA_key(group_list[listidx].serverkey);
}
if (group_list[listidx].restartinfo &&
(strcmp(group_list[listidx].restartinfo->name, ""))) {
// We have unused restart info from the last run.
// Chalk this up as a loss and delete the data file
char filepath[MAXPATHNAME];
snprintf(filepath, sizeof(filepath), "%s%c_group_%08X%c%s", tempdir,
PATH_SEP, group_list[listidx].group_id, PATH_SEP,
group_list[listidx].restartinfo->name);
unlink(filepath);
}
if (abort) {
write_restart_file(listidx);
}
free(group_list[listidx].fileinfo.naklist);
free(group_list[listidx].fileinfo.section_done);
if (group_list[listidx].restartinfo) {
free(group_list[listidx].restartinfo->naklist);
free(group_list[listidx].restartinfo->section_done);
free(group_list[listidx].restartinfo);
}
memset(&group_list[listidx], 0, sizeof(group_list[listidx]));
} else {
// Don't clear the file_id in case we need to respond to late DONEs
group_list[listidx].phase = PHASE_MIDGROUP;
set_timeout(listidx);
free(group_list[listidx].fileinfo.naklist);
free(group_list[listidx].fileinfo.section_done);
group_list[listidx].fileinfo.naklist = NULL;
group_list[listidx].fileinfo.section_done = NULL;
}
}
/**
* Initializes the uftp header of an outgoing packet
* blsize is initialized to 0, but must be set later before sending
*/
void set_uftp_header(struct uftp_h *header, int func, int listidx)
{
header->uftp_id = group_list[listidx].version;
header->func = func;
header->blsize = 0;
header->group_id = htonl(group_list[listidx].group_id);
header->srcaddr = group_list[listidx].destaddr.s_addr;
header->destaddr = group_list[listidx].srcaddr.s_addr;
}
/**
* Sets the timeout time for a given group list member
*/
void set_timeout(int listidx)
{
gettimeofday(&group_list[listidx].timeout_time, NULL);
switch (group_list[listidx].phase) {
case PHASE_REGISTERED:
group_list[listidx].timeout_time.tv_usec +=
group_list[listidx].register_int * 1000;
break;
case PHASE_RECEIVING:
case PHASE_MIDGROUP:
group_list[listidx].timeout_time.tv_sec +=
group_list[listidx].status_time;
break;
case PHASE_COMPLETE:
group_list[listidx].timeout_time.tv_usec +=
group_list[listidx].done_int * 1000;
break;
}
while (group_list[listidx].timeout_time.tv_usec >= 1000000) {
group_list[listidx].timeout_time.tv_usec -= 1000000;
group_list[listidx].timeout_time.tv_sec++;
}
}
/**
* Sends an ABORT message to a server
*/
void send_abort(int listidx, const char *message)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct abort_h *abort;
struct uftp2_h *v2_header;
char *v2_message;
int payloadlen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)buf;
v2_message = (char *)v2_header + sizeof(struct uftp2_h);
v2_header->uftp_id = htonl(V2_UFTP_ID);
v2_header->func = htonl(V2_ABORT);
v2_header->tx_id = htonl(group_list[listidx].group_id);
v2_header->blsize = htonl(strlen(message));
payloadlen = sizeof(struct uftp2_h) + strlen(message);
strncpy(v2_message, message, V2_BLOCKSIZE - 1);
encrypted = NULL;
outpacket = buf;
} else {
header = (struct uftp_h *)buf;
abort = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, listidx);
abort->func = ABORT;
abort->host = 0;
strncpy(abort->message, message, sizeof(abort->message) - 1);
payloadlen = sizeof(struct abort_h);
if ((group_list[listidx].phase != PHASE_REGISTERED) &&
(group_list[listidx].keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].clientkey,
group_list[listidx].client_keylen)) {
log0(0, 0, "Error encrypting ABORT");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
}
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].replyaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending ABORT");
}
file_cleanup(listidx, 1);
free(buf);
free(encrypted);
}
/**
* Handles an ABORT message from a server
*/
void handle_abort(int listidx, const unsigned char *message, int meslen)
{
struct abort_h *abort;
struct uftp2_h *v2_header;
const char *v2_message;
int found, i;
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)message;
v2_message = (const char *)message + sizeof(struct uftp2_h);
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Transfer aborted by server: %s", v2_message);
file_cleanup(listidx, 1);
return;
}
abort = (struct abort_h *)message;
if (meslen != sizeof(struct abort_h)) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting ABORT from server: invalid message size");
}
for (i = 0, found = 0; (i < interface_count) && !found; i++) {
if (abort->host == m_interface[i].addr.s_addr) {
found = 1;
}
}
if ((abort->host == 0) || found) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Transfer aborted by server: %s", abort->message);
file_cleanup(listidx, 1);
}
}
/**
* Sends a KEY_REQ message to the proxy specified as the reply proxy
*/
void send_key_req()
{
unsigned char *packet;
struct uftp_h *header;
struct key_req_h *keyreq;
struct sockaddr_in proxyaddr;
int meslen;
packet = calloc(sizeof(struct uftp_h) + sizeof(struct key_req_h), 1);
if (packet == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
keyreq = (struct key_req_h *)(packet + sizeof(struct uftp_h));
header->uftp_id = UFTP_VER_NUM;
header->func = KEY_REQ;
keyreq->func = KEY_REQ;
meslen = sizeof(struct key_req_h);
header->blsize = htons(meslen);
meslen += sizeof(struct uftp_h);
proxyaddr.sin_family = AF_INET;
proxyaddr.sin_addr = proxy_info.addr;
proxyaddr.sin_port = htons(port);
if (nb_sendto(listener, packet, meslen, 0,
(struct sockaddr *)&proxyaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending KEY_REQ");
} else {
log(0, 0, "Sent KEY_REQ to %s:%d", inet_ntoa(proxy_info.addr), port);
}
free(packet);
gettimeofday(&next_keyreq_time, NULL);
next_keyreq_time.tv_sec += KEY_REQ_INT;
}
/**
* Process a PROXY_KEY message
*/
void handle_proxy_key(const struct sockaddr_in *src,
const unsigned char *message)
{
struct uftp_h *header;
struct proxy_key_h *proxykey;
unsigned char *keymod, *sig;
struct hostent *hp;
RSA_key_t key;
unsigned char *verifydata, fingerprint[HMAC_LEN];
unsigned int verifylen, fplen, keylen, siglen;
header = (struct uftp_h *)message;
proxykey = (struct proxy_key_h *)(message + sizeof(struct uftp_h));
if (!noname && (hp = gethostbyaddr((char *)&src->sin_addr,
sizeof(struct in_addr), AF_INET))) {
log(0, 0, "Received PROXY_KEY from %s (%s)",
hp->h_name, inet_ntoa(src->sin_addr));
} else {
log(0, 0, "Received PROXY_KEY from %s", inet_ntoa(src->sin_addr));
}
if (!has_proxy) {
log(0, 0, "No reply proxy specified");
return;
}
if ((src->sin_addr.s_addr != proxy_info.addr.s_addr) ||
(src->sin_port != htons(port))) {
log(0, 0, "PROXY_KEY not from specified reply proxy");
return;
}
keymod = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
keylen = ntohs(proxykey->keylen);
sig = keymod + keylen;
siglen = ntohs(proxykey->siglen);
// First check key fingerprint
if (proxy_info.has_fingerprint) {
verifylen = 0;
verifydata = calloc(sizeof(proxykey->keyexp) + keylen, 1);
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(verifydata, &proxykey->keyexp, sizeof(proxykey->keyexp));
verifylen += sizeof(proxykey->keyexp);
memcpy(verifydata + verifylen, keymod, keylen);
verifylen += keylen;
hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen);
if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) {
log0(0, 0, "Failed to verify PROXY_KEY fingerprint");
free(verifydata);
return;
}
free(verifydata);
}
// Then check signature
if (!import_RSA_key(&key, ntohl(proxykey->keyexp), keymod, keylen)) {
log0(0, 0, "Failed to import public key from PROXY_KEY");
return;
}
if (!verify_RSA_sig(key, HASH_SHA1, (unsigned char *)&proxykey->nonce,
sizeof(proxykey->nonce), sig, siglen)) {
log0(0, 0, "Failed to verify PROXY_KEY signature");
return;
}
// Load key
if (!import_RSA_key(&proxy_key, ntohl(proxykey->keyexp), keymod, keylen)) {
log0(0, 0, "Failed to import public key from PROXY_KEY");
return;
}
}
/**
* Removes a full path from disk
*/
void clear_path(const char *path, int listidx)
{
stat_struct statbuf;
char filename[MAXPATHNAME];
int len;
if (lstat_func(path, &statbuf) == -1) {
if (errno != ENOENT) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error getting file status for %s", path);
}
return;
}
if (!S_ISDIR(statbuf.st_mode)) {
unlink(path);
} else {
#ifdef WINDOWS
intptr_t ffhandle;
struct _finddatai64_t finfo;
char dirglob[MAXPATHNAME];
snprintf(dirglob, sizeof(dirglob), "%s%c*", path,
PATH_SEP, group_list[listidx].group_id, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to open directory %s", path);
return;
}
do {
len = snprintf(filename, sizeof(filename), "%s%c%s", path,
PATH_SEP, finfo.name);
if ((len >= sizeof(filename)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c%s",
filename, PATH_SEP, finfo.name);
continue;
}
if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
clear_path(filename, listidx);
}
} while (_findnexti64(ffhandle, &finfo) == 0);
_findclose(ffhandle);
#else
DIR *dir;
struct dirent *de;
if ((dir = opendir(path)) == NULL) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to open directory %s", path);
return;
}
// errno needs to be set to 0 before calling readdir, otherwise
// we'll report a false error when we exhaust the directory
while ((errno = 0, de = readdir(dir)) != NULL) {
len = snprintf(filename, sizeof(filename), "%s%c%s", path, PATH_SEP,
de->d_name);
if ((len >= sizeof(filename)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c%s", path,
PATH_SEP, de->d_name);
continue;
}
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
clear_path(filename, listidx);
}
}
if (errno && (errno != ENOENT)) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to read directory %s", path);
}
closedir(dir);
#endif
if (rmdir(path) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed remove directory %s", path);
}
}
}
/**
* For the current file in a group, move the existing file to
* the appropriate backup directory, if it exists.
* In the event of a failure, delete the original file
*/
void move_to_backup(int listidx)
{
stat_struct statbuf;
char backup_file[MAXBACKUPPATHNAME], *trim_name;
int len;
if (lstat_func(group_list[listidx].fileinfo.filepath, &statbuf) == -1) {
return;
}
if (backupcnt == 0) {
clear_path(group_list[listidx].fileinfo.filepath, listidx);
return;
}
#ifdef WINDOWS
if ((group_list[listidx].fileinfo.filepath[1] == ':') &&
(group_list[listidx].fileinfo.filepath[2] == '\\')) {
trim_name = &group_list[listidx].fileinfo.filepath[3];
} else {
trim_name = group_list[listidx].fileinfo.filepath;
}
#else
trim_name = group_list[listidx].fileinfo.filepath;
#endif
len = snprintf(backup_file, sizeof(backup_file), "%s%c%s%c%s%c%s",
backupdir[group_list[listidx].fileinfo.destdiridx], PATH_SEP,
group_list[listidx].start_date, PATH_SEP,
group_list[listidx].start_time, PATH_SEP, trim_name);
if (len >= sizeof(backup_file)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded for backup file, deleting",
group_list[listidx].fileinfo.filepath);
clear_path(group_list[listidx].fileinfo.filepath, listidx);
return;
}
clear_path(backup_file, listidx);
if (!create_path_to_file(listidx, backup_file)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error creating path to backup file");
clear_path(group_list[listidx].fileinfo.filepath, listidx);
}
#ifdef WINDOWS
if (!MoveFile(group_list[listidx].fileinfo.filepath, backup_file)) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Couldn't rename from %s to %s, deleting: (%d): %s",
group_list[listidx].fileinfo.filepath, backup_file,
GetLastError(), errbuf);
clear_path(group_list[listidx].fileinfo.filepath, listidx);
} else {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Backed up existing file to %s", backup_file);
}
#else
if (rename(group_list[listidx].fileinfo.filepath, backup_file) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Couldn't rename from %s to %s, deleting",
group_list[listidx].fileinfo.filepath, backup_file);
clear_path(group_list[listidx].fileinfo.filepath, listidx);
} else {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Backed up existing file to %s", backup_file);
}
#endif
}
/**
* Creates all directories in the given file's path, removing existing files.
* Returns 1 on success, 0 on failure
*/
int create_path_to_file(int listidx, const char *filename)
{
char *dir, *base;
stat_struct statbuf;
int rval;
split_path(filename, &dir, &base);
if (!dir) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Invalid path element %s", filename);
rval = 0;
goto end;
}
#ifdef WINDOWS
if ((base == NULL) || ((strlen(dir) == 2) && (dir[1] == ':'))) {
#else
if ((!strcmp(dir, ".")) || (!strcmp(dir, "/"))) {
#endif
// At top level directory, so stop recursion
rval = 1;
goto end;
}
if (lstat_func(dir, &statbuf) != -1) {
if (!S_ISDIR(statbuf.st_mode)) {
if (unlink(dir) == -1) {
syserror(group_list[listidx].group_id,
group_list[listidx].file_id,
"Failed to delete path element %s", dir);
rval = 0;
goto end;
}
if (mkdir(dir, 0755) == -1) {
syserror(group_list[listidx].group_id,
group_list[listidx].file_id,
"Failed to create path element %s", dir);
rval = 0;
goto end;
}
}
} else {
// If the file's directory does not exist, recurse first to make sure
// all parent directories exist
if (!create_path_to_file(listidx, dir)) {
rval = 0;
goto end;
}
if (mkdir(dir, 0755) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to create path element %s", dir);
rval = 0;
goto end;
}
}
rval = 1;
end:
free(dir);
free(base);
return rval;
}
uftp-3.5/client_config.c 0000644 0003316 0000550 00000035413 11577014250 014323 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#endif
#include "client.h"
#include "client_config.h"
/**
* Global command line values and sockets
*/
SOCKET listener;
char tempdir[MAXDIRNAME], destdir[MAXDIR][MAXDIRNAME];
char logfile[MAXPATHNAME], pidfile[MAXPATHNAME];
char keyfile[MAXLIST][MAXPATHNAME], backupdir[MAXDIR][MAXDIRNAME];
int noname, debug, newkeylen, encrypted_only, dscp, destdircnt, tempfile;
int interface_count, pub_multi_count, keyfile_count, buffer, backupcnt;
uint16_t port;
uint32_t uid;
struct sockaddr_in hb_hosts[MAXLIST];
struct iflist m_interface[MAX_INTERFACES];
struct in_addr pub_multi[MAX_INTERFACES];
struct group_list_t group_list[MAXLIST];
struct fp_list_t server_keys[MAXLIST];
struct iflist ifl[MAX_INTERFACES];
struct timeval next_keyreq_time, next_hb_time;
int ifl_len, server_count, key_count, has_proxy, sys_keys;
int hbhost_count, hb_interval;
RSA_key_t privkey[MAXLIST];
struct fp_list_t proxy_info;
RSA_key_t proxy_key;
extern char *optarg;
extern int optind;
/**
* Adds a server and its fingerprint to the list of approved servers
*/
void add_server_by_name(const char *server, const char *fingerprint)
{
struct hostent *hp;
struct in_addr *addr;
if (inet_addr(server) == INADDR_NONE) {
if ((hp = gethostbyname(server)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", server);
exit(1);
} else {
addr = (struct in_addr *)hp->h_addr_list[0];
server_keys[server_count].addr = *addr;
server_keys[server_count].has_fingerprint =
parse_fingerprint(server_keys[server_count].fingerprint,
fingerprint);
server_count++;
}
} else {
server_keys[server_count].addr.s_addr = inet_addr(server);
server_keys[server_count].has_fingerprint =
parse_fingerprint(server_keys[server_count].fingerprint,
fingerprint);
server_count++;
}
}
/**
* Set defaults for all command line arguments
*/
void set_defaults()
{
debug = 0;
log_level = DEF_LOG_LEVEL;
noname = 0;
encrypted_only = 0;
uid = 0;
dscp = DEF_DSCP;
strncpy(logfile, DEF_LOGFILE, sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
memset(pidfile, 0, sizeof(pidfile));
interface_count = 0;
port = DEF_PORT;
tempfile = 0;
strncpy(tempdir, DEF_TEMPDIR, sizeof(tempdir)-1);
tempdir[sizeof(tempdir)-1] = '\x0';
destdircnt = 0;
backupcnt = 0;
pub_multi_count = 0;
key_count = 0;
keyfile_count = 0;
memset(keyfile[0], 0, sizeof(keyfile[0]));
buffer = 0;
server_count = 0;
newkeylen = 0;
has_proxy = 0;
sys_keys = 0;
memset(hb_hosts, 0, sizeof(hb_hosts));
hbhost_count = 0;
hb_interval = DEF_HB_INT;
}
/**
* Set argument defaults, read and validate command line options
*/
void process_args(int argc, char *argv[])
{
int c, i, listidx, hbport;
long tmpval;
struct hostent *hp;
struct in_addr addr, *paddr;
char line[1000], *servername, *fingerprint, *p, *p2, *hoststr, *portstr;
FILE *serverfile;
const char opts[] = "dx:nL:P:I:p:tT:D:A:M:B:Q:EU:S:R:k:K:mh:H:";
set_defaults();
// read lettered arguments
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 'd':
debug = 1;
break;
case 'n':
noname = 1;
break;
case 'x':
log_level = atoi(optarg);
if (log_level < 0) {
fprintf(stderr, "Invalid log level\n");
exit(1);
}
break;
case 'L':
strncpy(logfile, optarg, sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
break;
case 'P':
strncpy(pidfile, optarg, sizeof(pidfile)-1);
pidfile[sizeof(pidfile)-1] = '\x0';
break;
case 'I':
p = strtok(optarg, ",");
while (p != NULL) {
if ((listidx = getifbyname(p, ifl, ifl_len)) != -1) {
m_interface[interface_count++] = ifl[listidx];
p = strtok(NULL, ",");
continue;
}
if (inet_addr(p) == INADDR_NONE) {
if ((hp = gethostbyname(p)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", p);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
}
} else {
addr.s_addr = inet_addr(p);
paddr = &addr;
}
if ((listidx = getifbyaddr(*paddr, ifl, ifl_len)) != -1) {
m_interface[interface_count++] = ifl[listidx];
} else {
fprintf(stderr, "Interface %s not found\n", p);
exit(1);
}
p = strtok(NULL, ",");
}
break;
case 'p':
port = atoi(optarg);
if (port == 0) {
fprintf(stderr, "Invalid port\n");
exit(1);
}
break;
case 't':
tempfile = 1;
break;
case 'T':
strncpy(tempdir, optarg, sizeof(tempdir)-1);
tempdir[sizeof(tempdir)-1] = '\x0';
break;
case 'D':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(destdir[destdircnt], p, sizeof(destdir[destdircnt])-1);
destdir[destdircnt][sizeof(destdir[destdircnt])-1] = '\x0';
destdircnt++;
p = strtok(NULL, ",");
}
break;
case 'A':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(backupdir[backupcnt],p,sizeof(backupdir[backupcnt])-1);
backupdir[backupcnt][sizeof(backupdir[backupcnt])-1] = '\x0';
backupcnt++;
p = strtok(NULL, ",");
}
break;
case 'M':
p = strtok(optarg, ",");
while (p != NULL) {
pub_multi[pub_multi_count].s_addr = inet_addr(p);
if ((pub_multi[pub_multi_count].s_addr == INADDR_NONE) ||
(!is_multicast(pub_multi[pub_multi_count], 0))) {
fprintf(stderr, "Invalid multicast address: %s\n", p);
exit(1);
}
pub_multi_count++;
p = strtok(NULL, ",");
}
break;
case 'B':
buffer = atoi(optarg);
if ((buffer < 65536) || (buffer > 104857600)) {
fprintf(stderr, "Invalid buffer size\n");
exit(1);
}
break;
case 'Q':
tmpval = strtol(optarg, NULL, 0);
if ((tmpval < 0) || (tmpval > 63)) {
fprintf(stderr, "Invalid dscp\n");
exit(1);
}
dscp = (tmpval & 0xFF) << 2;
break;
case 'E':
encrypted_only = 1;
break;
case 'U':
if ((uid = inet_addr(optarg)) != 0) {
if (ntohl(uid) > 0xffffff) {
fprintf(stderr, "Invalid UID\n");
exit(1);
}
} else {
uid = strtol(optarg, NULL, 16);
if ((uid > 0xffffff) || (uid <= 0)) {
fprintf(stderr, "Invalid UID\n");
exit(1);
}
uid = htonl(uid);
}
break;
case 'S':
if ((serverfile = fopen(optarg, "r")) == NULL) {
fprintf(stderr, "Couldn't open server list %s: %s\n",
optarg, strerror(errno));
exit(1);
}
while (fgets(line, sizeof(line), serverfile)) {
while ((strlen(line) != 0) && ((line[strlen(line)-1] == '\r') ||
(line[strlen(line)-1] == '\n'))) {
line[strlen(line)-1] = '\x0';
}
servername = strtok(line, " \t");
if (!servername) continue;
if (servername[0] == '#') continue;
if (strlen(servername) > DESTNAME_LEN) {
fprintf(stderr, "Server list: name too long\n");
exit(1);
}
fingerprint = strtok(NULL, " \t");
add_server_by_name(servername, fingerprint);
}
if (!feof(serverfile) && ferror(serverfile)) {
perror("Failed to read from server list file");
exit(1);
}
fclose(serverfile);
break;
case 'R':
strncpy(line, optarg, sizeof(line));
line[sizeof(line)-1] = '\x0';
servername = strtok(line, "/");
if (!servername) {
fprintf(stderr, "Invalid host name\n");
exit(1);
}
fingerprint = strtok(NULL, "/");
if (inet_addr(servername) == INADDR_NONE) {
if ((hp = gethostbyname(servername)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", servername);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
proxy_info.addr = *paddr;
proxy_info.has_fingerprint =
parse_fingerprint(proxy_info.fingerprint,
fingerprint);
has_proxy = 1;
}
} else {
proxy_info.addr.s_addr = inet_addr(servername);
proxy_info.has_fingerprint =
parse_fingerprint(proxy_info.fingerprint, fingerprint);
has_proxy = 1;
}
break;
case 'k':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(keyfile[keyfile_count], p, sizeof(keyfile[0])-1);
keyfile[keyfile_count][sizeof(keyfile[0])-1] = '\x0';
keyfile_count++;
p = strtok(NULL, ",");
}
break;
case 'K':
newkeylen = atoi(optarg);
if ((newkeylen < 512) || (newkeylen > 2048)) {
fprintf(stderr, "Invalid new key length\n");
exit(1);
}
break;
case 'm':
sys_keys = 1;
break;
case 'H':
p = strtok(optarg, ",");
while (p != NULL) {
p2 = strchr(p, ':');
if (p2) {
hoststr = strdup(p);
hoststr[p2 - p] = '\x0';
portstr = p2 + 1;
} else {
hoststr = p;
portstr = NULL;
}
hb_hosts[hbhost_count].sin_family = AF_INET;
if (inet_addr(hoststr) == INADDR_NONE) {
if ((hp = gethostbyname(hoststr)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", hoststr);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
hb_hosts[hbhost_count].sin_addr.s_addr = paddr->s_addr;
}
} else {
hb_hosts[hbhost_count].sin_addr.s_addr = inet_addr(hoststr);
}
if (portstr) {
free(hoststr);
hbport = atoi(portstr);
if ((hbport <= 0) || (hbport > 65535)) {
hbport = DEF_PORT;
}
} else {
hbport = DEF_PORT;
}
hb_hosts[hbhost_count++].sin_port = htons(hbport);
p = strtok(NULL, ",");
}
break;
case 'h':
hb_interval = atoi(optarg);
if ((hb_interval <= 0) || (hb_interval > 3600)) {
fprintf(stderr, "Invalid hearbeat interval\n");
exit(1);
}
break;
case '?':
fprintf(stderr, USAGE);
exit(1);
}
}
if (server_count) {
for (i = 0; i < pub_multi_count; i++) {
if (!is_multicast(pub_multi[i], 1)) {
fprintf(stderr, "Invalid source specific "
"multicast address: %s\n", inet_ntoa(pub_multi[i]));
exit(1);
}
}
if (pub_multi_count == 0) {
fprintf(stderr, "Default multicast address %s invalid "
"for source specific multicast\n", DEF_PUB_MULTI);
exit(1);
}
}
if (destdircnt == 0) {
strncpy(destdir[0], DEF_DESTDIR, sizeof(destdir[0])-1);
destdir[0][sizeof(destdir[0])-1] = '\x0';
destdircnt++;
}
if ((backupcnt > 0) && (backupcnt != destdircnt)) {
fprintf(stderr, "Must specify same number of backup directories "
"as destination directories\n");
exit(1);
}
if (tempfile && (strcmp(tempdir, ""))) {
fprintf(stderr, "Cannot specify both -t and -T\n");
exit(1);
}
}
uftp-3.5/client_init.c 0000644 0003316 0000550 00000033663 11577014250 014026 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#else // WINDOWS
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_init.h"
#include "client_config.h"
#include "client_common.h"
static int parent; // Is the the parent process that exits after a fork?
/**
* Cleanup routine set up by atexit
*/
void cleanup(void)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id != 0) {
file_cleanup(i, 1);
}
}
if (!parent) {
for (i = 0; i < pub_multi_count; i++) {
if (server_count > 0) {
multicast_leave(listener, 0, &pub_multi[i], m_interface,
interface_count, server_keys, server_count);
if (has_proxy) {
multicast_leave(listener, 0, &pub_multi[i], m_interface,
interface_count, &proxy_info, 1);
}
} else {
multicast_leave(listener, 0, &pub_multi[i], m_interface,
interface_count, NULL, 0);
}
}
}
closesocket(listener);
for (i = 0; i < key_count; i++) {
free_RSA_key(privkey[i]);
}
crypto_cleanup();
#ifdef WINDOWS
WSACleanup();
#endif
fclose(stderr);
}
/**
* Generic signal handler, exits on signal
*/
void gotsig(int sig)
{
log0(0, 0, "Exiting on signal %d", sig);
exit(0);
}
/**
* Signal handler for SIGPIPE
*/
void gotpipe(int sig)
{
log0(0, 0, "Got SIGPIPE");
}
/**
* Do initial setup before parsing arguments, including getting interface list
*/
void pre_initialize()
{
#ifdef WINDOWS
struct WSAData data;
if (WSAStartup(2, &data)) {
fprintf(stderr, "Error in WSAStartup: %d\n", WSAGetLastError());
exit(1);
}
#endif
applog = stderr;
ifl_len = sizeof(ifl) / sizeof(struct iflist);
getiflist(ifl, &ifl_len);
}
/**
* Set up log file and run in the backgroud
*/
void daemonize()
{
#ifdef WINDOWS
if (!debug) {
int fd;
FILE *pidfh;
if ((fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) {
perror("Can't open log file");
exit(1);
}
if (strcmp(pidfile, "")) {
// Write out the pid file, before we redirect STDERR to the log.
if ((pidfh = fopen(pidfile, "w")) == NULL) {
perror("Can't open pid file for writing");
exit(1);
}
fprintf(pidfh, "%d\n", GetCurrentProcessId());
fclose(pidfh);
}
dup2(fd, 2);
close(fd);
applog = stderr;
}
#else // WINDOWS
if (!debug) {
int pid, fd;
FILE *pidfh;
if ((fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) {
perror("Can't open log file");
exit(1);
}
if ((pid = fork()) == -1) {
perror("Couldn't fork");
exit(1);
} else if (pid > 0) {
parent = 1;
exit(0);
}
if (strcmp(pidfile, "")) {
// Write out the pid file, before we redirect STDERR to the log.
if ((pidfh = fopen(pidfile, "w")) == NULL) {
perror("Can't open pid file for writing");
exit(1);
}
fprintf(pidfh, "%d\n", getpid());
fclose(pidfh);
}
setsid();
dup2(fd, 2);
close(fd);
for (fd = 0; fd < 30; fd++) {
if ((fd != 2) && (fd != listener)) {
close(fd);
}
}
#ifdef VMS
chdir("SYS$LOGIN");
#else
chdir("/");
#endif
umask(0);
applog = stderr;
}
nice(-20);
{
struct sigaction act;
sigfillset(&act.sa_mask);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
act.sa_handler = gotsig;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
act.sa_handler = gotpipe;
sigaction(SIGPIPE, &act, NULL);
act.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &act, NULL);
}
#endif // WINDOWS
}
/**
* Initialize crypto library, generate keys
*/
void key_init()
{
#ifndef NO_ENCRYPTION
int i;
crypto_init(sys_keys);
if ((keyfile_count == 0) || (newkeylen != 0)) {
privkey[0] = gen_RSA_key(newkeylen, RSA_EXP, keyfile[0]);
if (!privkey[0]) {
exit(1);
}
key_count = 1;
} else {
for (i = 0; i < keyfile_count; i++) {
privkey[key_count] = read_RSA_key(keyfile[i]);
if (!privkey[key_count]) {
exit(1);
}
key_count++;
}
}
#endif
}
/**
* Do all socket creation and initialization
*/
void create_sockets()
{
struct sockaddr_in sin;
int fdflag, i;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);
if ((listener = socket(AF_INET,SOCK_DGRAM,0)) == INVALID_SOCKET) {
sockerror(0, 0, "Error creating socket for listener");
exit(1);
}
if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
sockerror(0, 0, "Error binding socket for listener");
exit(1);
}
#ifndef BLOCKING
#ifdef WINDOWS
fdflag = 1;
if (ioctlsocket(listener, FIONBIO, &fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(listener);
exit(1);
}
#else
if ((fdflag = fcntl(listener, F_GETFL)) == SOCKET_ERROR) {
sockerror(0, 0, "Error getting socket descriptor flags");
closesocket(listener);
exit(1);
}
fdflag |= O_NONBLOCK;
if (fcntl(listener, F_SETFL, fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(listener);
exit(1);
}
#endif
#endif // BLOCKING
if (setsockopt(listener, IPPROTO_IP, IP_TOS, (char *)&dscp,
sizeof(dscp)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting dscp");
closesocket(listener);
exit(1);
}
if (buffer) {
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
} else {
buffer = DEF_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
buffer = DEF_BSD_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
}
}
for (i = 0; i < pub_multi_count; i++) {
if (server_count > 0) {
log(0, 0, "joining ssm for server IPs");
if (!multicast_join(listener, 0, &pub_multi[i], m_interface,
interface_count, server_keys, server_count)) {
exit(1);
}
if (has_proxy) {
log(0, 0, "joining ssm for proxy IPs");
if (!multicast_join(listener, 0, &pub_multi[i], m_interface,
interface_count, &proxy_info, 1)) {
exit(1);
}
}
} else {
if (!multicast_join(listener, 0, &pub_multi[i], m_interface,
interface_count, NULL, 0)) {
exit(1);
}
}
}
}
/**
* Tests to see if files can be moved/renamed between two directories.
* Returns 1 on success, 0 on failure
*/
int dirs_movable(const char *dir1, const char *dir2)
{
char tempf1[MAXPATHNAME], tempf2[MAXPATHNAME];
int fd;
snprintf(tempf1, sizeof(tempf1)-1, "%s%c_uftptmp1", dir1, PATH_SEP);
tempf1[sizeof(tempf1)-1] = '\x0';
snprintf(tempf2, sizeof(tempf1)-1, "%s%c_uftptmp2", dir2, PATH_SEP);
tempf2[sizeof(tempf2)-1] = '\x0';
if ((fd = open(tempf1, O_WRONLY | O_CREAT, 0644)) < 0) {
fprintf(stderr, "couldn't write to directory %s: %s\n",
dir1, strerror(errno));
return 0;
}
close(fd);
if ((fd = open(tempf2, O_WRONLY | O_CREAT, 0644)) < 0) {
fprintf(stderr, "couldn't write to directory %s: %s\n",
dir2, strerror(errno));
return 0;
}
close(fd);
unlink(tempf2);
if (rename(tempf1, tempf2) == -1) {
fprintf(stderr, "couldn't move between directories %s and %s: %s\n",
dir1, dir2, strerror(errno));
unlink(tempf1);
return 0;
}
unlink(tempf1);
unlink(tempf2);
return 1;
}
/**
* Initialization based on command line args
*/
void initialize()
{
char tempf1[MAXPATHNAME], hostname[256];
struct hostent *hp;
struct in_addr *addr;
int fd, i;
parent = 0;
// Load list of multicast interfaces
if (interface_count == 0) {
for (i = 0; i < ifl_len; i++) {
if (!ifl[i].isloopback) {
m_interface[interface_count++] = ifl[i];
}
}
}
// No non-loopback interfaces, so just use the hostname's interface
if (interface_count == 0) {
gethostname(hostname, sizeof(hostname));
if ((hp = gethostbyname(hostname)) == NULL) {
fprintf(stderr, "Can't get host name\n");
exit(1);
} else {
addr = (struct in_addr *)hp->h_addr_list[0];
m_interface[interface_count].addr = *addr;
m_interface[interface_count].ismulti = 1;
m_interface[interface_count++].isloopback = 0;
}
}
if (uid) {
m_interface[interface_count].addr.s_addr = uid;
m_interface[interface_count].ismulti = 0;
m_interface[interface_count++].isloopback = 0;
}
// Check validity of dest, backup, and temp directories
for (i = 0; i < destdircnt; i++) {
if (!isfullpath(destdir[i])) {
fprintf(stderr, "ERROR: must specify absolute pathname "
"for dest directory\n");
exit(1);
}
snprintf(tempf1, sizeof(tempf1)-1, "%s%c_uftptmp1",destdir[i],PATH_SEP);
tempf1[sizeof(tempf1)-1] = '\x0';
if ((fd = open(tempf1, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("couldn't write to dest directory");
exit(1);
}
close(fd);
unlink(tempf1);
if (backupcnt > 0) {
// backupcnt and destdircnt are always equal
if (!strcmp(backupdir[i], destdir[i])) {
fprintf(stderr, "ERROR: corresponding backup dir and dest dir "
"must be different\n");
exit(1);
}
if (!isfullpath(backupdir[i])) {
fprintf(stderr, "ERROR: must specify absolute pathname "
"for backup directory\n");
exit(1);
}
if (!dirs_movable(destdir[i], backupdir[i])) {
exit(1);
}
}
}
if (strcmp(tempdir, "")) {
if (destdircnt > 1) {
fprintf(stderr, "ERROR: Cannot use a temp directory "
"with multiple dest directories\n");
exit(1);
}
if (backupcnt > 0) {
fprintf(stderr, "ERROR: Cannot use a temp directory "
"with a backup directory\n");
exit(1);
}
if (!strcmp(tempdir, destdir[0])) {
fprintf(stderr, "ERROR: temp dir and dest dir must be different\n");
exit(1);
}
if (!isfullpath(tempdir)) {
fprintf(stderr, "ERROR: must specify absolute pathname "
"for temp directory\n");
exit(1);
}
if (!dirs_movable(tempdir, destdir[0])) {
exit(1);
}
}
if (!pub_multi_count) {
pub_multi[0].s_addr = inet_addr(DEF_PUB_MULTI);
pub_multi_count = 1;
}
for (i = 0; i < MAXLIST; i++) {
group_list[i].group_id = 0;
}
next_hb_time.tv_sec = 0;
next_hb_time.tv_usec = 0;
next_keyreq_time.tv_sec = 0;
next_keyreq_time.tv_usec = 0;
atexit(cleanup);
key_init();
create_sockets();
daemonize();
showtime = 1;
}
uftp-3.5/client_loop.c 0000644 0003316 0000550 00000033737 11577014247 014044 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else // WINDOWS
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
#include "client_loop.h"
#include "client_announce.h"
#include "client_transfer.h"
#include "heartbeat_send.h"
/**
* Gets the current timeout value to use for the main loop
*
* First check to see if any active groups have an expired timeout, and
* handle that timeout. Once all expired timeouts have been handled, find
* the active group with the earliest timeout and return the time until that
* timeout. If there are no active groups, return NULL.
*/
struct timeval *getrecenttimeout()
{
static struct timeval tv = {0,0};
struct timeval current_timestamp, min_timestamp;
int i, found_timeout, done;
int32_t usecs;
gettimeofday(¤t_timestamp, NULL);
done = 0;
while (!done) {
found_timeout = 0;
done = 1;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id != 0) {
if (cmptimestamp(current_timestamp,
group_list[i].timeout_time) >= 0) {
switch (group_list[i].phase) {
case PHASE_REGISTERED:
send_register(i);
break;
case PHASE_RECEIVING:
case PHASE_MIDGROUP:
log1(group_list[i].group_id, group_list[i].file_id,
"Transfer timed out");
send_abort(i, "Transfer timed out");
break;
case PHASE_COMPLETE:
send_complete(i);
break;
}
done = 0;
} else if (!found_timeout) {
min_timestamp = group_list[i].timeout_time;
found_timeout = 1;
} else if (cmptimestamp(group_list[i].timeout_time,
min_timestamp) < 0) {
min_timestamp = group_list[i].timeout_time;
}
}
}
// Check timeout for proxy key request
if (has_proxy && (proxy_key == (RSA_key_t)NULL)) {
if (cmptimestamp(current_timestamp, next_keyreq_time) >= 0) {
send_key_req();
done = 0;
} else if ((!found_timeout) ||
(cmptimestamp(next_keyreq_time, min_timestamp) < 0)) {
min_timestamp = next_keyreq_time;
found_timeout = 1;
}
}
// Check timeout for sending heartbeat
if (hbhost_count) {
if (cmptimestamp(current_timestamp, next_hb_time) >= 0) {
send_hb_request(listener, hb_hosts, hbhost_count,
&next_hb_time, hb_interval);
done = 0;
} else if ((!found_timeout) ||
(cmptimestamp(next_hb_time, min_timestamp) < 0)) {
min_timestamp = next_hb_time;
found_timeout = 1;
}
}
}
if (found_timeout) {
usecs = (int32_t)diff_usec(min_timestamp, current_timestamp);
tv.tv_sec = usecs / 1000000;
tv.tv_usec = usecs % 1000000;
return &tv;
} else {
return NULL;
}
}
/**
* This is the main message reading loop. Messages are read, validated,
* decrypted if necessary, then passed to the appropriate routine for handling.
*/
void mainloop()
{
struct uftp_h *header;
struct uftp2_h *v2_header;
unsigned char *buf, *decrypted, *message;
unsigned int decryptlen, meslen, expectedlen;
int packetlen, listidx, i;
uint8_t version, *func;
uint32_t v2func;
struct sockaddr_in src;
struct timeval *tv;
const int bsize = 9000; // Roughly size of ethernet jumbo frame
log0(0, 0, "%s", VERSIONSTR);
for (i = 0; i < key_count; i++) {
log1(0, 0, "Loaded key with fingerprint %s",
print_key_fingerprint(privkey[i]));
}
buf = calloc(bsize, 1);
decrypted = calloc(bsize, 1);
if ((buf == NULL) || (decrypted == NULL)){
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
v2_header = (struct uftp2_h *)buf;
while (1) {
tv = getrecenttimeout();
if (read_packet(listener, &src, buf, &packetlen,
bsize, tv) <= 0) {
continue;
}
if ((header->uftp_id == UFTP_VER_NUM) ||
(header->uftp_id == UFTP_3_0_VER)) {
version = header->uftp_id;
expectedlen = sizeof(struct uftp_h) + ntohs(header->blsize);
listidx = find_file(ntohl(header->group_id));
} else if (ntohl(v2_header->uftp_id) == V2_UFTP_ID) {
version = UFTP_V2_VER;
expectedlen = V2_PACKETSIZE;
listidx = find_file(ntohl(v2_header->tx_id));
v2func = ntohl(v2_header->func);
} else {
log(0, 0, "Invalid message from %s: not uftp packet "
"or invalid version", inet_ntoa(src.sin_addr));
continue;
}
if (packetlen != expectedlen) {
log(0, 0, "Invalid packet size from %s: got %d, expected %d",
inet_ntoa(src.sin_addr), packetlen, expectedlen);
continue;
}
if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(header->func == ENCRYPTED) && (listidx != -1) &&
(group_list[listidx].keytype != KEY_NONE)) {
if (group_list[listidx].phase == PHASE_REGISTERED) {
log(ntohl(header->group_id), 0, "Got encrypted packet from %s "
"but keys not established", inet_ntoa(src.sin_addr));
}
if (!validate_and_decrypt(buf, &decrypted, &decryptlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].serverkey,
group_list[listidx].server_keylen)) {
log(ntohl(header->group_id), 0, "Rejecting message from %s: "
"decrypt/validate failed", inet_ntoa(src.sin_addr));
continue;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else if ((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) {
if ((listidx != -1) && (group_list[listidx].keytype != KEY_NONE) &&
((header->func == FILEINFO) || (header->func == FILESEG) ||
(header->func == DONE) || (header->func == DONE_CONF) ||
((header->func == ABORT) &&
(group_list[listidx].phase != PHASE_REGISTERED)))) {
log(0, 0, "Rejecting %s message from %s: not encrypted",
func_name(header->func), inet_ntoa(src.sin_addr));
continue;
}
func = (uint8_t *)&header->func;
message = buf + sizeof(struct uftp_h);
meslen = ntohs(header->blsize);
} else {
// Version 2
message = buf;
meslen = V2_PACKETSIZE;
}
if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(header->func == PROXY_KEY)) {
handle_proxy_key(&src, buf);
continue;
}
if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(header->func == HB_RESP)) {
handle_hb_response(listener, &src, buf, hb_hosts, hbhost_count,
noname, privkey[0]);
continue;
}
if ((((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(header->func == ANNOUNCE)) ||
((version == UFTP_V2_VER) && (v2func == V2_ANNOUNCE))) {
// Ignore any ANNOUNCE for a group we're already handling
if (listidx == -1) {
handle_announce(src, version, buf);
} else if (group_list[listidx].phase == PHASE_MIDGROUP) {
// Make sure we don't time out while waiting for other
// clients to register with the server.
set_timeout(listidx);
}
} else {
if (listidx == -1) {
// group / file ID not in list
continue;
}
if (group_list[listidx].version != version) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Version mismatch");
continue;
}
if ((((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(*func == ABORT)) ||
((version == UFTP_V2_VER) && (v2func == V2_ABORT))) {
handle_abort(listidx, message, meslen);
continue;
}
switch (group_list[listidx].phase) {
case PHASE_REGISTERED:
if (version == UFTP_V2_VER) {
// This is repeated in one of the cases below, but it's
// separated out anyway to keep the V2 stuff separate.
handle_regconf(listidx, message, meslen);
} else if (group_list[listidx].keytype != KEY_NONE) {
if (*func == KEYINFO) {
handle_keyinfo(listidx, buf);
} else {
log(group_list[listidx].group_id,
group_list[listidx].file_id,
"Expected KEYINFO, got %s", func_name(*func));
}
} else if (group_list[listidx].keytype == KEY_NONE) {
if (*func == REG_CONF) {
handle_regconf(listidx, message, meslen);
} else if (*func == FILEINFO) {
handle_fileinfo(listidx, message, meslen);
} else {
log(group_list[listidx].group_id,
group_list[listidx].file_id,
"Expected REG_CONF, got %s", func_name(*func));
}
}
break;
case PHASE_MIDGROUP:
if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(*func == FILEINFO)) {
handle_fileinfo(listidx, message, meslen);
} else if (((version == UFTP_VER_NUM) ||
(version == UFTP_3_0_VER)) && (*func == KEYINFO)) {
handle_keyinfo(listidx, buf);
} else if (((version == UFTP_V2_VER) && (v2func == V2_DONE)) ||
(((version == UFTP_VER_NUM) ||
(version == UFTP_3_0_VER)) && (*func == DONE))) {
handle_done(listidx, message, meslen);
} else {
// Other clients may be still getting earlier files or
// setting up, so silently ignore anything unexpected
// and reset the timeout.
set_timeout(listidx);
}
break;
case PHASE_RECEIVING:
if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
(*func == FILEINFO)) {
handle_fileinfo(listidx, message, meslen);
} else if (((version == UFTP_V2_VER) &&
(v2func == V2_FILESEG)) ||
(((version == UFTP_VER_NUM) ||
(version == UFTP_3_0_VER)) && (*func == FILESEG))) {
handle_fileseg(listidx, message, meslen);
} else if (((version == UFTP_V2_VER) && (v2func == V2_DONE)) ||
(((version == UFTP_VER_NUM) ||
(version == UFTP_3_0_VER)) && (*func == DONE))) {
handle_done(listidx, message, meslen);
}
break;
case PHASE_COMPLETE:
if (((version == UFTP_V2_VER) && (v2func == V2_DONE_CONF)) ||
(((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) && (*func == DONE_CONF))) {
handle_done_conf(listidx, message, meslen);
}
break;
}
}
}
}
uftp-3.5/client_main.c 0000644 0003316 0000550 00000003041 11346327251 013774 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2010 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "client_config.h"
#include "client_init.h"
#include "client_loop.h"
int main(int argc, char *argv[])
{
pre_initialize();
process_args(argc, argv);
initialize();
mainloop();
return 0;
}
uftp-3.5/client_transfer.c 0000644 0003316 0000550 00000061743 11577014247 014715 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
#include "client_transfer.h"
/**
* Move all files from temp to destination directory if at end of group
* TODO: add option to move files one at a time?
*/
void move_files(int listidx)
{
char temppath[MAXPATHNAME], destpath[MAXPATHNAME];
int len;
if (!strcmp(tempdir, "") || (group_list[listidx].file_id != 0)) {
return;
}
{
#ifdef WINDOWS
intptr_t ffhandle;
struct _finddatai64_t finfo;
char dirglob[MAXPATHNAME];
snprintf(dirglob, sizeof(dirglob), "%s%c_group_%08X%c*", tempdir,
PATH_SEP, group_list[listidx].group_id, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to open directory %s", dirglob);
return;
}
do {
len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X%c%s",
tempdir, PATH_SEP, group_list[listidx].group_id,
PATH_SEP, finfo.name);
if ((len >= sizeof(temppath)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c_group_%08X%c%s",
tempdir, PATH_SEP, group_list[listidx].group_id,
PATH_SEP, finfo.name);
continue;
}
len = snprintf(destpath, sizeof(destpath), "%s%c%s",
destdir[0], PATH_SEP, finfo.name);
if ((len >= sizeof(destpath)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c%s",
destdir[0], PATH_SEP, finfo.name);
continue;
}
// do the move
if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
clear_path(destpath, listidx);
if (!MoveFile(temppath, destpath)) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
log0(group_list[listidx].group_id,
group_list[listidx].file_id,
"error (%d): %s", GetLastError(), errbuf);
}
}
} while (_findnexti64(ffhandle, &finfo) == 0);
_findclose(ffhandle);
#else
DIR *dir;
struct dirent *de;
char dirname[MAXPATHNAME];
snprintf(dirname, sizeof(dirname), "%s%c_group_%08X", tempdir,
PATH_SEP, group_list[listidx].group_id);
if ((dir = opendir(dirname)) == NULL) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to open directory %s", dirname);
return;
}
// errno needs to be set to 0 before calling readdir, otherwise
// we'll report a false error when we exhaust the directory
while ((errno = 0, de = readdir(dir)) != NULL) {
len = snprintf(temppath, sizeof(temppath), "%s%c%s", dirname,
PATH_SEP, de->d_name);
if ((len >= sizeof(temppath)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c%s", dirname,
PATH_SEP, de->d_name);
continue;
}
len = snprintf(destpath, sizeof(destpath), "%s%c%s", destdir[0],
PATH_SEP, de->d_name);
if ((len >= sizeof(destpath)) || (len == -1)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Max pathname length exceeded: %s%c%s", destdir[0],
PATH_SEP, de->d_name);
continue;
}
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
clear_path(destpath, listidx);
if (rename(temppath, destpath) == -1) {
syserror(group_list[listidx].group_id,
group_list[listidx].file_id, "Couldn't move file");
}
}
}
if (errno && (errno != ENOENT)) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed to read directory %s", dirname);
}
closedir(dir);
#endif
}
snprintf(temppath, sizeof(temppath), "%s%c_group_%08X", tempdir,
PATH_SEP, group_list[listidx].group_id);
if (rmdir(temppath) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Failed remove temp directory %s", temppath);
}
}
/**
* Sends back a STATUS message with the given NAK list
*/
void send_status(int listidx, unsigned int pass, unsigned int section,
const unsigned char *naks, unsigned int nak_count)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct status_h *status;
struct uftp2_h *v2_header;
unsigned char *sent_naks;
int payloadlen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)buf;
v2_header->uftp_id = htonl(V2_UFTP_ID);
v2_header->func = htonl(V2_STATUS);
v2_header->tx_id = htonl(group_list[listidx].group_id);
v2_header->nak_count = htonl(nak_count);
v2_header->section_num = htonl(section);
v2_header->pass = htonl(pass);
v2_header->blsize = (nak_count ? htonl(V2_BLOCKSIZE) : 0);
payloadlen = sizeof(struct uftp2_h) + ntohl(v2_header->blsize);
if (nak_count) {
sent_naks = buf + sizeof(struct uftp2_h);
memcpy(sent_naks, naks, V2_BLOCKSIZE);
}
encrypted = NULL;
outpacket = buf;
} else {
header = (struct uftp_h *)buf;
status = (struct status_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, STATUS, listidx);
status->func = STATUS;
status->file_id = htons(group_list[listidx].file_id);
status->pass = pass;
status->seq = 0;
status->section = htons(section);
status->nak_count = htonl(nak_count);
if (nak_count) {
payloadlen = group_list[listidx].blocksize;
sent_naks = (unsigned char *)status + sizeof(struct status_h);
memcpy(sent_naks, naks, payloadlen);
} else {
payloadlen = 0;
}
payloadlen += sizeof(struct status_h);
if ((group_list[listidx].phase != PHASE_REGISTERED) &&
(group_list[listidx].keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].clientkey,
group_list[listidx].client_keylen)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Error encrypting STATUS");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
}
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].replyaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending STATUS");
} else {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Sent %d NAKs for pass %d section %d", nak_count,pass,section);
}
free(buf);
free(encrypted);
}
/**
* Sends back a COMPLETE message in response to a DONE or FILEINFO
*/
void send_complete(int listidx)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct complete_h *complete;
struct uftp2_h *v2_header;
int payloadlen;
struct timeval tv;
gettimeofday(&tv, NULL);
if ((group_list[listidx].phase == PHASE_COMPLETE) &&
(diff_usec(group_list[listidx].expire_time, tv) <= 0)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Completion unconfirmed by server");
move_files(listidx);
file_cleanup(listidx, 0);
return;
}
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)buf;
v2_header->uftp_id = htonl(V2_UFTP_ID);
v2_header->func = htonl(V2_STATUS);
v2_header->tx_id = htonl(group_list[listidx].group_id);
v2_header->nak_count = htonl(-1);
v2_header->blsize = 0;
payloadlen = sizeof(struct uftp2_h);
encrypted = NULL;
outpacket = buf;
} else {
header = (struct uftp_h *)buf;
complete = (struct complete_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, COMPLETE, listidx);
complete->func = COMPLETE;
complete->status = group_list[listidx].fileinfo.comp_status;
complete->file_id = htons(group_list[listidx].file_id);
complete->destcount = 0;
payloadlen = sizeof(struct complete_h);
if ((group_list[listidx].phase != PHASE_REGISTERED) &&
(group_list[listidx].keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].clientkey,
group_list[listidx].client_keylen)) {
log(0, 0, "Error encrypting COMPLETE");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
}
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].replyaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, group_list[listidx].file_id,
"Error sending COMPLETE");
} else {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"COMPLETE sent");
}
set_timeout(listidx);
free(buf);
free(encrypted);
}
/**
* Reads an expected FILESEG and writes it to the proper place in the file
*/
void handle_fileseg(int listidx, const unsigned char *message, int meslen)
{
struct uftp2_h *v2_header;
struct fileseg_h *fileseg;
const unsigned char *data;
int datalen, pass, section, seq, wrote_len;
f_offset_t offset, seek_rval;
if (group_list[listidx].fileinfo.ftype != FTYPE_REG) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILESEG: not a regular file");
return;
}
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)message;
data = message + sizeof(struct uftp2_h);
datalen = ntohl(v2_header->blsize);
pass = ntohl(v2_header->pass);
section = ntohl(v2_header->section_num);
seq = ntohl(v2_header->seq_num);
} else {
fileseg = (struct fileseg_h *)message;
data = message + sizeof(struct fileseg_h);
datalen = meslen - sizeof(struct fileseg_h);
pass = fileseg->pass;
section = ntohs(fileseg->section);
seq = ntohl(fileseg->seq_num);
if (ntohs(fileseg->file_id) != group_list[listidx].file_id) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILESEG: got incorrect file_id %04X",
ntohs(fileseg->file_id));
return;
}
}
if ((datalen != group_list[listidx].blocksize) &&
(seq != group_list[listidx].fileinfo.blocks - 1)) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting FILESEG: invalid message size %d", datalen);
return;
}
if (log_level >= 4) {
if (pass != 1) {
log4(group_list[listidx].group_id, group_list[listidx].file_id,
"Got packet %d", seq);
}
} else if (log_level == 3) {
if (seq != group_list[listidx].fileinfo.last_block + 1) {
log3(group_list[listidx].group_id, group_list[listidx].file_id,
"Got packet %d, last was %d",
seq, group_list[listidx].fileinfo.last_block);
}
group_list[listidx].fileinfo.last_block = seq;
}
offset = (f_offset_t) seq * group_list[listidx].blocksize;
if ((seek_rval = lseek_func(group_list[listidx].fileinfo.fd,
offset - group_list[listidx].fileinfo.curr_offset,
SEEK_CUR)) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"lseek failed for file");
}
if (seek_rval != offset) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"offset is %s", printll(seek_rval));
log(group_list[listidx].group_id, group_list[listidx].file_id,
" should be %s", printll(offset));
if ((seek_rval = lseek_func(group_list[listidx].fileinfo.fd,
seek_rval - group_list[listidx].fileinfo.curr_offset,
SEEK_CUR)) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"lseek failed for file");
return;
}
}
if ((wrote_len = write(group_list[listidx].fileinfo.fd,
data, datalen)) == -1) {
syserror(group_list[listidx].group_id, group_list[listidx].file_id,
"Write failed for segment %d", seq);
}
group_list[listidx].fileinfo.curr_offset = offset + wrote_len;
if (wrote_len != datalen) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Write failed for segment %d, only wrote %d bytes",
seq, wrote_len);
}
group_list[listidx].fileinfo.naklist[seq] = 0;
set_timeout(listidx);
}
/**
* Returns 1 if a file has been completely received, 0 otherwise
*/
int file_done(int listidx, int section)
{
unsigned int i;
if ((group_list[listidx].version == UFTP_V2_VER) &&
(section != group_list[listidx].fileinfo.sections)) {
// For version 2, don't check until the DONE for the last section
return 0;
}
if ((group_list[listidx].version != UFTP_V2_VER) &&
((group_list[listidx].phase == PHASE_MIDGROUP) ||
(group_list[listidx].file_id == 0))) {
return 1;
}
for (i = 0; i < group_list[listidx].fileinfo.sections; i++) {
if (!group_list[listidx].fileinfo.section_done[i]) {
return 0;
}
}
return 1;
}
/**
* Build the NAK list for a given pass and section. Returns the NAK count.
*/
int get_naks(int listidx, unsigned int pass, unsigned int section,
unsigned char **naks)
{
unsigned int section_offset, blocks_this_sec, i;
int nakidx, naklistidx, numnaks;
if ((group_list[listidx].version != UFTP_V2_VER) &&
((group_list[listidx].phase == PHASE_MIDGROUP) ||
(group_list[listidx].file_id == 0))) {
*naks = NULL;
return 0;
}
section_offset = (group_list[listidx].blocksize * 8) * (section - 1);
blocks_this_sec = ((section < group_list[listidx].fileinfo.sections) ?
(group_list[listidx].blocksize * 8) :
(group_list[listidx].fileinfo.blocks %
(group_list[listidx].blocksize * 8)));
if (group_list[listidx].fileinfo.sections && !blocks_this_sec) {
blocks_this_sec = group_list[listidx].blocksize * 8;
}
*naks = calloc(group_list[listidx].blocksize, 1);
if (*naks == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
// Build NAK list
numnaks = 0;
for (i = 0; i < blocks_this_sec; i++) {
nakidx = i + section_offset;
naklistidx = i;
if (group_list[listidx].fileinfo.naklist[nakidx]) {
log4(group_list[listidx].group_id, group_list[listidx].file_id,
"NAK for %d", nakidx);
// Each bit represents a NAK; set each one we have a NAK for
// Simplified: *naks[naklistidx / 8] |= (1 << (naklistidx % 8))
(*naks)[naklistidx >> 3] |= (1 << (naklistidx & 7));
numnaks++;
}
}
// Highly verbose debugging -- print NAK list to send
if (log_level >= 5) {
for (i = 0; i < group_list[listidx].blocksize; i++) {
sclog5("%02X ", (*naks)[i]);
if (i % 25 == 24) slog5("");
}
slog5("");
}
if (numnaks == 0) {
group_list[listidx].fileinfo.section_done[section - 1] = 1;
}
return numnaks;
}
/**
* Processes an expected DONE message
*/
void handle_done(int listidx, const unsigned char *message, int meslen)
{
struct uftp2_h *v2_header;
struct statusinfo2 *v2_statusinfo;
struct done_h *done;
uint32_t *addrlist;
unsigned char *naks;
unsigned int pass, section, listlen, nak_count, n_index;
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)message;
v2_statusinfo = (struct statusinfo2 *)(message + sizeof(struct uftp2_h));
pass = ntohl(v2_header->pass);
section = ntohl(v2_header->section_num);
addrlist = v2_statusinfo->addr_list;
listlen = V2_MAXINFODEST;
} else {
done = (struct done_h *)message;
addrlist = (uint32_t *)((char *)done + sizeof(struct done_h));
pass = done->pass;
section = ntohs(done->section);
listlen = ntohs(done->destcount);
if ((ntohs(done->file_id) != group_list[listidx].file_id) &&
(ntohs(done->file_id) != 0)) {
// Silently reject if not for this file and not end of group
return;
}
if (meslen != (sizeof(struct done_h) +
(listlen * sizeof(uint32_t)))) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting DONE: invalid message size");
return;
}
if (ntohs(done->file_id) == 0) {
// We're at end of group, so set local file_id=0 to flag this
group_list[listidx].file_id = 0;
}
}
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"Got DONE message for pass %d section %d", pass, section);
if (addr_in_list(listidx, addrlist, listlen)) {
nak_count = get_naks(listidx, pass, section, &naks);
if (file_done(listidx, section)) {
if (group_list[listidx].file_id != 0) {
log1(group_list[listidx].group_id, group_list[listidx].file_id,
"File transfer complete");
file_cleanup(listidx, 0);
} else {
log1(group_list[listidx].group_id, 0, "Group complete");
group_list[listidx].phase = PHASE_COMPLETE;
group_list[listidx].fileinfo.comp_status = COMP_STAT_NORMAL;
gettimeofday(&group_list[listidx].expire_time, NULL);
group_list[listidx].expire_time.tv_sec +=
group_list[listidx].status_time;
}
send_complete(listidx);
} else {
if (!group_list[listidx].fileinfo.wait && (nak_count == 1)) {
// Check to see if we're missing only the last packet in the
// section. If so, wait for the next DONE message in case the
// DONE and last data packet were swapped on the way in. If
// we're still missing the last packet, then send the STATUS.
if (section == group_list[listidx].fileinfo.sections) {
n_index = group_list[listidx].fileinfo.blocks - 1;
} else {
n_index = (group_list[listidx].blocksize * 8 * section) - 1;
}
if (group_list[listidx].fileinfo.naklist[n_index]) {
log(group_list[listidx].group_id,
group_list[listidx].file_id,
"Waiting for last packet in section...");
group_list[listidx].fileinfo.wait = 1;
}
} else {
group_list[listidx].fileinfo.wait = 0;
}
if (!group_list[listidx].fileinfo.wait) {
send_status(listidx, pass, section, naks, nak_count);
}
}
free(naks);
}
set_timeout(listidx);
}
/**
* Processes an expected DONE_CONF message
*/
void handle_done_conf(int listidx, const unsigned char *message, int meslen)
{
struct uftp2_h *v2_header;
struct statusinfo2 *v2_statusinfo;
struct doneconf_h *doneconf;
uint32_t *addrlist;
int listlen;
if (group_list[listidx].version == UFTP_V2_VER) {
v2_header = (struct uftp2_h *)message;
v2_statusinfo = (struct statusinfo2 *)(message + sizeof(struct uftp2_h));
addrlist = v2_statusinfo->addr_list;
listlen = V2_MAXINFODEST;
} else {
doneconf = (struct doneconf_h *)message;
addrlist = (uint32_t *)((char *)doneconf + sizeof(struct done_h));
listlen = ntohs(doneconf->destcount);
if (ntohs(doneconf->file_id) != group_list[listidx].file_id) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting DONE_CONF: got incorrect file_id %04X",
ntohs(doneconf->file_id));
return;
}
if (meslen != (sizeof(struct doneconf_h) +
(listlen * sizeof(uint32_t)))) {
log(group_list[listidx].group_id, group_list[listidx].file_id,
"Rejecting DONE_CONF: invalid message size");
return;
}
}
if (addr_in_list(listidx, addrlist, listlen)) {
log0(group_list[listidx].group_id, group_list[listidx].file_id,
"Group file transfer confirmed");
move_files(listidx);
file_cleanup(listidx, 0);
}
}
uftp-3.5/encrypt_cryptoapi.c 0000644 0003316 0000550 00000065534 11554720336 015311 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
//#include
#include "uftp_common.h"
#include "encryption.h"
#define MAXLIST 100
#define BLOBLEN 1000
const char *DEF_KEY_CONTAINER = "UFTP_default_key";
struct private_key_list_t {
HCRYPTPROV provider;
HCRYPTKEY key;
};
struct key_list_t {
char keystr[MAXKEY];
int keylen;
HCRYPTKEY mskey;
};
static const struct keyinfo_t {
ALG_ID alg;
int keysize;
int blocksize;
} keyinfo[] = {
{ CALG_DES, 8, 8 },
{ CALG_3DES, 24, 8 },
#ifdef CALG_AES_128
{ CALG_AES_128, 16, 16 },
#endif
#ifdef CALG_AES_256
{ CALG_AES_256, 32, 16}
#endif
};
static const struct hashinfo_t {
ALG_ID alg;
int hashsize;
} hashinfo[] = {
{ CALG_MD5, 16 },
{ CALG_SHA1, 20 },
#ifdef CALG_SHA_256
{ CALG_SHA_256, 32 }
#endif
};
// The CSP used for all non-signing operations
static HCRYPTPROV base_prov;
// The provider type of the best available CSP
static DWORD prov_type;
// Since using an RSA key for signing must be done via the provider and
// not directly with the key, each is in its own key container with its
// own separate provider. The user passes in the key to sign with, and this
// list matches the key with the provider that has access to it for signing.
static struct private_key_list_t private_key_list[MAXLIST];
// Since symmetric keys and hmac keys get an HCRYPTKEY associated with them,
// the first time the user wants to use a key a HCRYPTKEY is created. Then
// on subsequent uses the associated HCRYPTKEY is looked up so it doesn't
// have to be created and destroyed every time it's used.
static struct key_list_t symmetric_keys[MAXLIST], hmac_keys[MAXLIST];
static int machine_keyset = 0;
/**
* Prints Microsoft specific error messages to log
*/
static void mserror(const char *str)
{
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
0, errbuf, sizeof(errbuf), NULL);
clog(0, 0, "%s: (0x%08X) %s", str, GetLastError(), errbuf);
}
static int init_done = 0;
/**
* Performs all necessary steps to initialize the crypto library
*/
void crypto_init(int set_sys_key)
{
DWORD tmp_prov_type, name_len;
int cnt, found_aes, i;
// First see if we have an AES capable provider.
// If so, use that, otherwise use one without.
#ifdef PROV_RSA_AES
found_aes = 0;
cnt = 0;
name_len = 0;
while (CryptEnumProviderTypes(cnt, 0, 0, &tmp_prov_type, NULL, &name_len) &&
(!found_aes)) {
if (tmp_prov_type == PROV_RSA_AES) {
found_aes = 1;
}
cnt++;
name_len = 0;
}
if ((!found_aes) && (GetLastError() != ERROR_NO_MORE_ITEMS)) {
mserror("CryptEnumProviderTypes failed");
exit(1);
}
prov_type = (found_aes ? PROV_RSA_AES : PROV_RSA_FULL);
#else
prov_type = PROV_RSA_FULL;
#endif
if (set_sys_key) {
machine_keyset = CRYPT_MACHINE_KEYSET;
} else {
machine_keyset = 0;
}
if (!CryptAcquireContext(&base_prov, NULL, NULL, prov_type,
CRYPT_VERIFYCONTEXT | machine_keyset)) {
mserror("CryptAcquireContext failed");
exit(1);
}
for (i = 0; i < MAXLIST; i++) {
memset(&private_key_list[i], 0, sizeof(private_key_list[i]));
memset(&symmetric_keys[i], 0, sizeof(symmetric_keys[i]));
memset(&hmac_keys[i], 0, sizeof(hmac_keys[i]));
}
init_done = 1;
}
/**
* Performs all necessary steps to clean up the crypto library
*/
void crypto_cleanup()
{
int i;
if (!init_done) {
return;
}
for (i = 0; i < MAXLIST; i++) {
if (private_key_list[i].provider != 0) {
// Cleanup of private (and public) keys should be done by caller,
// since they have a handle to the key under CryptoAPI and OpenSSL
if (!CryptReleaseContext(private_key_list[i].provider, 0)) {
mserror("CryptReleaseContext failed");
}
}
if (symmetric_keys[i].keylen != 0) {
if (!CryptDestroyKey(symmetric_keys[i].mskey)) {
mserror("CryptDestroyKey failed");
}
}
if (hmac_keys[i].keylen != 0) {
if (!CryptDestroyKey(hmac_keys[i].mskey)) {
mserror("CryptDestroyKey failed");
}
}
}
if (!CryptReleaseContext(base_prov, 0)) {
mserror("CryptReleaseContext failed");
}
}
/**
* Returns the next key container for the current user
*/
const char *get_next_container()
{
static int flag = CRYPT_FIRST;
static char *item = NULL;
static int mlen = 0;
int rval, len;
if (flag == CRYPT_FIRST) {
rval = CryptGetProvParam(base_prov, PP_ENUMCONTAINERS, NULL,
&mlen, CRYPT_FIRST);
if (!rval) {
return NULL;
}
item = malloc(mlen);
if (item == NULL) {
syserror(0, 0, "malloc failed!");
exit(1);
}
}
len = mlen;
rval = CryptGetProvParam(base_prov, PP_ENUMCONTAINERS, item, &len, flag);
if (!rval) {
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
mserror("CryptGetProvParam failed");
}
flag = CRYPT_FIRST;
free(item);
item = NULL;
return NULL;
}
flag = CRYPT_NEXT;
return item;
}
/**
* Deletes the key container with the given name
*/
void delete_container(const char *name)
{
HCRYPTPROV prov;
if (!CryptAcquireContext(&prov, name, NULL, prov_type,
CRYPT_DELETEKEYSET | machine_keyset)) {
mserror("CryptAcquireContext for delete failed");
}
}
/**
* Returns the ALG_ID associated with a given keytype
*/
static ALG_ID get_cipher(int keytype)
{
switch (keytype) {
case KEY_DES:
return CALG_DES;
break;
case KEY_DES_EDE3:
return CALG_3DES;
break;
case KEY_AES128:
#ifdef CALG_AES_128
return CALG_AES_128;
#else
return 0;
#endif
break;
case KEY_AES256:
#ifdef CALG_AES_256
return CALG_AES_256;
#else
return 0;
#endif
break;
default:
log(0, 0, "Unknown keytype: %d", keytype);
exit(1);
}
}
/**
* Returns the ALG_ID associated with a given hashtype
*/
static ALG_ID get_hash(int hashtype)
{
switch (hashtype) {
case HASH_SHA256:
#ifdef CALG_SHA_256
return CALG_SHA_256;
#else
return 0;
#endif
break;
case HASH_SHA1:
return CALG_SHA1;
break;
case HASH_MD5:
return CALG_MD5;
break;
default:
log(0, 0, "Unknown hashtype: %d", hashtype);
exit(1);
}
}
/**
* Returns whether a particular ALG_ID is available in the current CSP
*/
static int alg_found(ALG_ID alg)
{
PROV_ENUMALGS alg_info;
int alg_info_len, flag, found_alg;
found_alg = 0;
alg_info_len = sizeof(alg_info);
flag = CRYPT_FIRST;
while (CryptGetProvParam(base_prov, PP_ENUMALGS, (BYTE *)&alg_info,
&alg_info_len, flag) && (!found_alg)) {
if (alg_info.aiAlgid == alg) {
found_alg = 1;
}
alg_info_len = sizeof(alg_info);
flag = CRYPT_NEXT;
}
if (!found_alg) {
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
mserror("CryptGetProvParam failed");
}
return 0;
} else {
return 1;
}
}
/**
* Returns whether a particular cipher is supported
*/
int cipher_supported(int keytype)
{
ALG_ID alg;
if ((alg = get_cipher(keytype)) == 0) {
return 0;
}
return alg_found(alg);
}
/**
* Returns whether a particular hash is supported
*/
int hash_supported(int hashtype)
{
ALG_ID alg;
if ((alg = get_hash(hashtype)) == 0) {
return 0;
}
return alg_found(alg);
}
/**
* Gets the key length and IV/block length of a given key
*/
void get_key_info(int keytype, int *keylen, int *ivlen)
{
ALG_ID alg;
int numkeys, i;
alg = get_cipher(keytype);
numkeys = sizeof(keyinfo) / sizeof(struct keyinfo_t);
for (i = 0; i < numkeys; i++) {
if (alg == keyinfo[i].alg) {
*keylen = keyinfo[i].keysize;
*ivlen = keyinfo[i].blocksize;
return;
}
}
log(0, 0, "Key %d not found\n", keytype);
exit(1);
}
/**
* Gets the length of the given hash
*/
int get_hash_len(int hashtype)
{
ALG_ID alg;
int numhash, i;
alg = get_hash(hashtype);
numhash = sizeof(hashinfo) / sizeof(struct hashinfo_t);
for (i = 0; i < numhash; i++) {
if (alg == hashinfo[i].alg) {
return hashinfo[i].hashsize;
}
}
log(0, 0, "Hash %d not found\n", hashtype);
exit(1);
}
/**
* Gets num cryptographically random bytes
*/
int get_random_bytes(unsigned char *buf, int num)
{
int rval;
if (!(rval = CryptGenRandom(base_prov, num, buf))) {
mserror("Error getting random bytes");
}
return rval;
}
/**
* Takes a block of data and encrypts it with a symmetric cypher.
* The output buffer must be at least the size of source data + block size.
*/
int encrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
// TODO: right now we reimport the key each time. Test to see if this
// is quick enough or if we need to cache an imported key.
HCRYPTKEY hckey;
char keyblob[BLOBLEN];
BLOBHEADER *bheader;
DWORD *keysize;
BYTE *keydata;
int bloblen, keylen, ivlen, rval;
ALG_ID alg;
DWORD mode, _destlen;
get_key_info(keytype, &keylen, &ivlen);
alg = get_cipher(keytype);
bheader = (BLOBHEADER *)keyblob;
keysize = (DWORD *)(keyblob + sizeof(BLOBHEADER));
keydata = (BYTE *)((char *)keysize + sizeof(DWORD));
memset(keyblob, 0, sizeof(keyblob));
bheader->bType = PLAINTEXTKEYBLOB;
bheader->bVersion = CUR_BLOB_VERSION;
bheader->aiKeyAlg = alg;
*keysize = keylen;
memcpy(keydata, key, keylen);
bloblen = sizeof(BLOBHEADER) + sizeof(DWORD) + keylen;
if (!CryptImportKey(base_prov, keyblob, bloblen, 0, 0, &hckey)) {
mserror("CryptImportKey failed");
return 0;
}
mode = CRYPT_MODE_CBC;
if (!CryptSetKeyParam(hckey, KP_MODE, (BYTE *)&mode, 0)) {
mserror("CryptSetKeyParam failed on KP_MODE");
rval = 0;
goto end;
}
if (!CryptSetKeyParam(hckey, KP_IV, IV, 0)) {
mserror("CryptSetKeyParam failed on KP_IV");
rval = 0;
goto end;
}
memcpy(dest, src, srclen);
_destlen = srclen;
if (!CryptEncrypt(hckey, 0, 1, 0, dest, &_destlen, srclen + ivlen)) {
mserror("CryptEncrypt failed");
rval = 0;
goto end;
}
*destlen = _destlen;
rval = 1;
end:
if (!CryptDestroyKey(hckey)) {
mserror("CryptDestroyKey failed");
}
return rval;
}
/**
* Takes a block of data encrypted with a symmetric cypher and decrypts it.
* The output buffer must be at least the size of source data.
*/
int decrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
// TODO: right now we reimport the key each time. Test to see if this
// is quick enough or if we need to cache an imported key.
HCRYPTKEY hckey;
char keyblob[BLOBLEN];
BLOBHEADER *bheader;
DWORD *keysize;
BYTE *keydata;
int bloblen, keylen, ivlen, rval;
ALG_ID alg;
DWORD mode, _destlen;
get_key_info(keytype, &keylen, &ivlen);
alg = get_cipher(keytype);
bheader = (BLOBHEADER *)keyblob;
keysize = (DWORD *)(keyblob + sizeof(BLOBHEADER));
keydata = (BYTE *)((char *)keysize + sizeof(DWORD));
memset(keyblob, 0, sizeof(keyblob));
bheader->bType = PLAINTEXTKEYBLOB;
bheader->bVersion = CUR_BLOB_VERSION;
bheader->aiKeyAlg = alg;
*keysize = keylen;
memcpy(keydata, key, keylen);
bloblen = sizeof(BLOBHEADER) + sizeof(DWORD) + keylen;
if (!CryptImportKey(base_prov, keyblob, bloblen, 0, 0, &hckey)) {
mserror("CryptImportKey failed");
return 0;
}
mode = CRYPT_MODE_CBC;
if (!CryptSetKeyParam(hckey, KP_MODE, (BYTE *)&mode, 0)) {
mserror("CryptSetKeyParam failed on KP_MODE");
rval = 0;
goto end;
}
if (!CryptSetKeyParam(hckey, KP_IV, IV, 0)) {
mserror("CryptSetKeyParam failed on KP_IV");
rval = 0;
goto end;
}
memcpy(dest, src, srclen);
_destlen = srclen;
if (!CryptDecrypt(hckey, 0, 1, 0, dest, &_destlen)) {
mserror("CryptDecrypt failed");
rval = 0;
goto end;
}
*destlen = _destlen;
rval = 1;
end:
if (!CryptDestroyKey(hckey)) {
mserror("CryptDestroyKey failed");
}
return rval;
}
/**
* Calculates the HMAC of the given message, hashtype, and hashkey.
* dest must be at least the hash length.
*/
int create_hmac(int hashtype, const unsigned char *key, unsigned int keylen,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
// TODO: right now we reimport the hmac key each time. Test to see if this
// is quick enough or if we need to cache an imported hmac key.
HCRYPTKEY hmackey;
HCRYPTHASH hash;
char keyblob[BLOBLEN];
BLOBHEADER *bheader;
DWORD *keysize;
BYTE *keydata;
HMAC_INFO info;
ALG_ID alg;
int bloblen, hashlen, rval;
DWORD _destlen;
hashlen = get_hash_len(hashtype);
alg = get_hash(hashtype);
bheader = (BLOBHEADER *)keyblob;
keysize = (DWORD *)(keyblob + sizeof(BLOBHEADER));
keydata = (BYTE *)((char *)keysize + sizeof(DWORD));
memset(keyblob, 0, sizeof(keyblob));
bheader->bType = PLAINTEXTKEYBLOB;
bheader->bVersion = CUR_BLOB_VERSION;
bheader->aiKeyAlg = CALG_RC2;
*keysize = keylen;
memcpy(keydata, key, keylen);
bloblen = sizeof(BLOBHEADER) + sizeof(DWORD) + hashlen;
if (!CryptImportKey(base_prov, keyblob, bloblen, 0,
CRYPT_IPSEC_HMAC_KEY, &hmackey)) {
mserror("CryptImportKey failed");
return 0;
}
if (!CryptCreateHash(base_prov, CALG_HMAC, hmackey, 0, &hash)) {
mserror("CryptCreateHash failed");
rval = 0;
goto end1;
}
memset(&info, 0, sizeof(info));
info.HashAlgid = alg;
if (!CryptSetHashParam(hash, HP_HMAC_INFO, (BYTE *)&info, 0)) {
mserror("CryptSetHashParam failed");
rval = 0;
goto end2;
}
if (!CryptHashData(hash, src, srclen, 0)) {
mserror("CryptHashData failed");
rval = 0;
goto end2;
}
_destlen = hashlen;
if (!CryptGetHashParam(hash, HP_HASHVAL, dest, &_destlen, 0)) {
mserror("CryptGetHashParam failed");
rval = 0;
goto end2;
}
*destlen = _destlen;
rval = 1;
end2:
if (!CryptDestroyHash(hash)) {
mserror("CryptDestroyHash failed");
}
end1:
if (!CryptDestroyKey(hmackey)) {
mserror("CryptDestroyKey failed");
}
return rval;
}
/**
* Calculates the hash of the given message and hashtype
*/
int hash(int hashtype, const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
HCRYPTHASH hash;
ALG_ID alg;
int hashlen, rval;
DWORD _destlen;
hashlen = get_hash_len(hashtype);
alg = get_hash(hashtype);
if (!CryptCreateHash(base_prov, alg, 0, 0, &hash)) {
mserror("CryptCreateHash failed");
return 0;
}
if (!CryptHashData(hash, src, srclen, 0)) {
mserror("CryptHashData failed");
rval = 0;
goto end;
}
_destlen = hashlen;
if (!CryptGetHashParam(hash, HP_HASHVAL, dest, &_destlen, 0)) {
mserror("CryptGetHashParam failed");
rval = 0;
goto end;
}
*destlen = _destlen;
rval = 1;
end:
if (!CryptDestroyHash(hash)) {
mserror("CryptDestroyHash failed");
}
return rval;
}
/**
* Returns the length in bytes of the modulus for the given RSA key
*/
int RSA_keylen(const RSA_key_t rsa)
{
DWORD keylen, bsize;
bsize = sizeof(keylen);
if (!CryptGetKeyParam(rsa, KP_KEYLEN, (BYTE *)&keylen, &bsize, 0)) {
mserror("CryptGetKeyParam failed");
return 0;
}
return keylen / 8;
}
/**
* Encrypts a small block of data with an RSA public key.
* Output buffer must be at least the key size.
*/
int RSA_encrypt(RSA_key_t rsa, const unsigned char *from, int fromlen,
unsigned char *to, int *tolen)
{
DWORD _tolen;
int flags;
unsigned int i;
unsigned char *outbuf;
if (RSA_keylen(rsa) * 8 < 768) {
flags = 0;
} else {
flags = CRYPT_OAEP;
}
outbuf = calloc(RSA_keylen(rsa), 1);
if (outbuf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(outbuf, from, fromlen);
_tolen = fromlen;
if (!CryptEncrypt(rsa, 0, 1, flags, outbuf, &_tolen, RSA_keylen(rsa))) {
mserror("CryptEncrypt failed");
free(outbuf);
return 0;
}
*tolen = _tolen;
// CryptoAPI returns ciphertext in little endian, so reverse the bytes
for (i = 0; i < _tolen; i++) {
to[i] = outbuf[_tolen - i - 1];
}
free(outbuf);
return 1;
}
/**
* Decrypts a small block of data with an RSA private key.
*/
int RSA_decrypt(RSA_key_t rsa, const unsigned char *from, int fromlen,
unsigned char *to, int *tolen)
{
DWORD _tolen;
int flags;
int i;
if (RSA_keylen(rsa) * 8 < 768) {
flags = 0;
} else {
flags = CRYPT_OAEP;
}
// CryptoAPI expects ciphertext in little endian, so reverse the bytes
for (i = 0; i < fromlen; i++) {
to[i] = from[fromlen - i - 1];
}
_tolen = fromlen;
if (!CryptDecrypt(rsa, 0, 1, flags, to, &_tolen)) {
mserror("CryptDecrypt failed");
return 0;
}
*tolen = _tolen;
return 1;
}
/**
* Hashes a block of data and signs it with an RSA private key.
* Output buffer must be at least the key size.
*/
int create_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
HCRYPTHASH hash;
DWORD _siglen;
int idx, found;
ALG_ID alg;
int hashlen, rval;
unsigned int i;
unsigned char *outsig;
for (idx = 0, found = 0; (idx < MAXLIST) && (!found); idx++) {
if (private_key_list[idx].key == rsa) {
found = 1;
}
}
if (!found) {
log(0, 0, "Couldn't find provider for RSA key");
return 0;
}
idx--;
hashlen = get_hash_len(hashtype);
alg = get_hash(hashtype);
if (!CryptCreateHash(private_key_list[idx].provider, alg, 0, 0, &hash)) {
mserror("CryptCreateHash failed");
return 0;
}
if (!CryptHashData(hash, mes, meslen, 0)) {
mserror("CryptHashData failed");
rval = 0;
goto end;
}
_siglen = RSA_keylen(rsa);
outsig = calloc(_siglen, 1);
if (outsig == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
if (!CryptSignHash(hash, AT_KEYEXCHANGE, NULL, 0, outsig, &_siglen)) {
mserror("CryptSignHash failed");
free(outsig);
rval = 0;
goto end;
}
*siglen = _siglen;
// CryptoAPI returns signatures in little endian, so reverse the bytes
for (i = 0; i < _siglen; i++) {
sig[i] = outsig[_siglen - i - 1];
}
free(outsig);
rval = 1;
end:
if (!CryptDestroyHash(hash)) {
mserror("CryptDestroyHash failed");
}
return rval;
}
/**
* Hashes a block of data and verifies it against an RSA signature.
*/
int verify_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int siglen)
{
HCRYPTHASH hash;
ALG_ID alg;
unsigned hashlen, i;
int rval;
unsigned char *insig;
hashlen = get_hash_len(hashtype);
alg = get_hash(hashtype);
if (!CryptCreateHash(base_prov, alg, 0, 0, &hash)) {
mserror("CryptCreateHash failed");
return 0;
}
if (!CryptHashData(hash, mes, meslen, 0)) {
mserror("CryptHashData failed");
rval = 0;
goto end;
}
insig = calloc(siglen, 1);
if (insig == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
// CryptoAPI expects signatures in little endian, so reverse the bytes
for (i = 0; i < siglen; i++) {
insig[i] = sig[siglen - i - 1];
}
if (!CryptVerifySignature(hash, insig, siglen, rsa, NULL, 0)) {
mserror("CryptVerifySignature failed");
free(insig);
rval = 0;
goto end;
}
free(insig);
rval = 1;
end:
if (!CryptDestroyHash(hash)) {
mserror("CryptDestroyHash failed");
}
return rval;
}
/**
* Creates an RSA public key with the given modulus and public exponent
*/
int import_RSA_key(RSA_key_t *rsa, uint32_t exponent,
const unsigned char *modulus, uint16_t modlen)
{
char keyblob[BLOBLEN];
BLOBHEADER *bheader;
RSAPUBKEY *pubkeyheader;
BYTE *blob_modulus;
int bloblen, i;
bheader = (BLOBHEADER *)keyblob;
pubkeyheader = (RSAPUBKEY *)(keyblob + sizeof(BLOBHEADER));
blob_modulus = (BYTE *)pubkeyheader + sizeof(RSAPUBKEY);
memset(keyblob, 0, sizeof(keyblob));
bheader->bType = PUBLICKEYBLOB;
bheader->bVersion = CUR_BLOB_VERSION;
bheader->aiKeyAlg = CALG_RSA_KEYX;
pubkeyheader->magic = 0x31415352;
pubkeyheader->bitlen = modlen * 8;
pubkeyheader->pubexp = exponent;
// CrypoAPI expects the modulus in little endian, so reverse the bytes
for (i = 0; i < modlen; i++) {
blob_modulus[i] = modulus[modlen - i - 1];
}
bloblen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + modlen;
if (!CryptImportKey(base_prov, keyblob, bloblen, 0, 0, rsa)) {
mserror("CryptImportKey failed");
return 0;
}
return 1;
}
/**
* Extracts the modulus and public exponent from an RSA public key
*/
int export_RSA_key(const RSA_key_t rsa, uint32_t *exponent,
unsigned char *modulus, uint16_t *modlen)
{
char keyblob[BLOBLEN];
BLOBHEADER *bheader;
RSAPUBKEY *pubkeyheader;
BYTE *blob_modulus;
int bloblen, i;
bloblen = sizeof(keyblob);
if (!CryptExportKey(rsa, 0, PUBLICKEYBLOB, 0, keyblob, &bloblen)) {
mserror("CryptExportKey failed");
return 0;
}
bheader = (BLOBHEADER *)keyblob;
pubkeyheader = (RSAPUBKEY *)(keyblob + sizeof(BLOBHEADER));
blob_modulus = (BYTE *)pubkeyheader + sizeof(RSAPUBKEY);
*exponent = pubkeyheader->pubexp;
*modlen = (pubkeyheader->bitlen / 8) & 0xFFFF;
// CrypoAPI exports the modulus in little endian, so reverse the bytes
for (i = 0; i < *modlen; i++) {
modulus[i] = blob_modulus[*modlen - i - 1];
}
return 1;
}
/**
* Generates an RSA private key with the given exponent and number of bits
* and writes it into the specified key container
*/
RSA_key_t gen_RSA_key(int bits, int exponent, const char *container)
{
int idx, found;
const char *_container;
// First find available private key slot
for (idx = 0, found = 0; (idx < MAXLIST) && (!found); idx++) {
if (private_key_list[idx].provider == 0) {
found = 1;
}
}
if (!found) {
log(0, 0, "Couldn't find empty key slot for private key");
return (RSA_key_t)NULL;
}
idx--;
if (!strcmp(container, "")) {
_container = DEF_KEY_CONTAINER;
} else {
_container = container;
}
if (!CryptAcquireContext(&private_key_list[idx].provider, _container,
NULL, prov_type, machine_keyset)) {
if (!CryptAcquireContext(&private_key_list[idx].provider, _container,
NULL, prov_type, CRYPT_NEWKEYSET | machine_keyset)) {
mserror("CryptAcquireContext failed");
return (RSA_key_t)NULL;
}
}
if (!CryptGenKey(private_key_list[idx].provider, AT_KEYEXCHANGE,
((bits ? bits : DEF_RSA_LEN) << 16) | CRYPT_EXPORTABLE,
&private_key_list[idx].key)) {
mserror("CryptGenKey failed");
return (RSA_key_t)NULL;
}
return private_key_list[idx].key;
}
/**
* Loads an RSA private key from the specified key container
*/
RSA_key_t read_RSA_key(const char *container)
{
int idx, found;
const char *_container;
// First find available private key slot
for (idx = 0, found = 0; (idx < MAXLIST) && (!found); idx++) {
if (private_key_list[idx].provider == 0) {
found = 1;
}
}
if (!found) {
log(0, 0, "Couldn't find empty key slot for private key");
return (RSA_key_t)NULL;
}
idx--;
if (!strcmp(container, "")) {
_container = DEF_KEY_CONTAINER;
} else {
_container = container;
}
if (!CryptAcquireContext(&private_key_list[idx].provider, _container,
NULL, prov_type, machine_keyset)) {
mserror("CryptAcquireContext failed");
return (RSA_key_t)NULL;
}
if (!CryptGetUserKey(private_key_list[idx].provider, AT_KEYEXCHANGE,
&private_key_list[idx].key)) {
mserror("CryptGetUserKey failed");
return (RSA_key_t)NULL;
}
return (RSA_key_t)private_key_list[idx].key;
}
void free_RSA_key(RSA_key_t rsa)
{
if (!CryptDestroyKey(rsa)) {
mserror("CryptDestroyKey failed");
}
}
void set_sys_keys(int set_sys_key)
{
if (set_sys_key) {
machine_keyset = CRYPT_MACHINE_KEYSET;
} else {
machine_keyset = 0;
}
}
uftp-3.5/encrypt_openssl.c 0000644 0003316 0000550 00000031766 11554720336 014762 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifdef WINDOWS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "uftp_common.h"
#include "encryption.h"
/**
* Prints OpenSSL errors to log
*/
static void log_ssl_err(const char *mes)
{
unsigned long err;
char errstr[1000];
while ((err = ERR_get_error())) {
ERR_error_string(err, errstr);
log(0, 0, "%s: %s", mes, errstr);
}
}
static int init_done;
/**
* Performs all necessary steps to initialize the crypto library
*/
void crypto_init(int set_sys_key)
{
// TODO: include calls to RAND_add and the like?
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
init_done = 1;
}
/**
* Performs all necessary steps to clean up the crypto library
*/
void crypto_cleanup()
{
if (init_done) {
ERR_free_strings();
EVP_cleanup();
}
}
/**
* Gets the EVP_CIPHER associated with a given keytype
*/
static const EVP_CIPHER *get_cipher(int keytype)
{
switch (keytype) {
case KEY_DES:
return EVP_get_cipherbyname("DES-CBC");
case KEY_DES_EDE3:
return EVP_get_cipherbyname("DES-EDE3-CBC");
case KEY_AES128:
return EVP_get_cipherbyname("AES-128-CBC");
case KEY_AES256:
return EVP_get_cipherbyname("AES-256-CBC");
default:
log(0, 0, "Unknown keytype: %d", keytype);
exit(1);
}
}
/**
* Gets the EVP_MD associated with a given hashtype
*/
static const EVP_MD *get_hash(int hashtype)
{
switch (hashtype) {
case HASH_SHA256:
return EVP_get_digestbyname("SHA256");
case HASH_SHA1:
return EVP_get_digestbyname("SHA1");
case HASH_MD5:
return EVP_get_digestbyname("MD5");
default:
log(0, 0, "Unknown hashtype: %d", hashtype);
exit(1);
}
}
/**
* Returns whether a particular cipher is supported
*/
int cipher_supported(int keytype)
{
return (get_cipher(keytype) != NULL);
}
/**
* Returns whether a particular hash is supported
*/
int hash_supported(int hashtype)
{
return (get_hash(hashtype) != NULL);
}
/**
* Gets the key length and IV/block length of a given key
*/
void get_key_info(int keytype, int *keylen, int *ivlen)
{
const EVP_CIPHER *cipher = get_cipher(keytype);
*keylen = EVP_CIPHER_key_length(cipher);
*ivlen = EVP_CIPHER_iv_length(cipher);
}
/**
* Gets the length of the given hash
*/
int get_hash_len(int hashtype)
{
const EVP_MD *hashptr = get_hash(hashtype);
return EVP_MD_size(hashptr);
}
/**
* Gets num cryptographically random bytes
*/
int get_random_bytes(unsigned char *buf, int num)
{
int rval;
if (!(rval = RAND_bytes(buf, num))) {
log_ssl_err("Error getting random bytes");
}
return rval;
}
/**
* Takes a block of data and encrypts it with a symmetric cypher.
* The output buffer must be at least the size of source data + block size.
*/
int encrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher = get_cipher(keytype);
int len;
EVP_CIPHER_CTX_init(&ctx);
if (!EVP_EncryptInit_ex(&ctx, cipher, NULL, key, IV)) {
log_ssl_err("EncryptInit failed");
return 0;
}
if (!EVP_EncryptUpdate(&ctx, dest, &len, src, srclen)) {
log_ssl_err("EncryptUpdate failed");
return 0;
}
*destlen = len;
if (!EVP_EncryptFinal_ex(&ctx, dest + *destlen, &len)) {
log_ssl_err("EncryptFinal failed");
return 0;
}
*destlen += len;
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
/**
* Takes a block of data encrypted with a symmetric cypher and decrypts it.
* The output buffer must be at least the size of source data.
*/
int decrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher = get_cipher(keytype);
int len;
EVP_CIPHER_CTX_init(&ctx);
if (!EVP_DecryptInit_ex(&ctx, cipher, NULL, key, IV)) {
log_ssl_err("DecryptInit failed");
return 0;
}
if (!EVP_DecryptUpdate(&ctx, dest, &len, src, srclen)) {
log_ssl_err("DecryptUpdate failed");
return 0;
}
*destlen = len;
if (!EVP_DecryptFinal_ex(&ctx, dest + *destlen, &len)) {
log_ssl_err("DecryptFinal failed");
return 0;
}
*destlen += len;
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
/**
* Calculates the HMAC of the given message, hashtype, and hashkey.
* dest must be at least the hash length.
*/
int create_hmac(int hashtype, const unsigned char *key, unsigned int keylen,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
const EVP_MD *hashptr = get_hash(hashtype);
HMAC(hashptr, key, keylen, src, srclen, dest, destlen);
return 1;
}
/**
* Calculates the hash of the given message and hashtype
*/
int hash(int hashtype, const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
EVP_MD_CTX hashctx;
const EVP_MD *hashptr = get_hash(hashtype);
EVP_MD_CTX_init(&hashctx);
if (!EVP_DigestInit_ex(&hashctx, hashptr, NULL)) {
log_ssl_err("DigestInit failed");
return 0;
}
if (!EVP_DigestUpdate(&hashctx, src, srclen)) {
log_ssl_err("DigestUpdate failed");
return 0;
}
if (!EVP_DigestFinal_ex(&hashctx, dest, (unsigned int *)destlen)) {
log_ssl_err("DigestUpdate failed");
return 0;
}
EVP_MD_CTX_cleanup(&hashctx);
return 1;
}
/**
* Returns the length in bytes of the modulus for the given RSA key
*/
int RSA_keylen(const RSA_key_t rsa)
{
return RSA_size(rsa);
}
/**
* Encrypts a small block of data with an RSA public key.
* Output buffer must be at least the key size.
*/
int RSA_encrypt(RSA_key_t rsa, const unsigned char *from, int fromlen,
unsigned char *to, int *tolen)
{
int padding;
if (RSA_size(rsa) * 8 < 768) {
padding = RSA_PKCS1_PADDING;
} else {
padding = RSA_PKCS1_OAEP_PADDING;
}
if ((*tolen = RSA_public_encrypt(fromlen, from, to, rsa, padding)) == -1) {
log_ssl_err("RSA_public_encrypt failed");
return 0;
}
return 1;
}
/**
* Decrypts a small block of data with an RSA private key.
*/
int RSA_decrypt(RSA_key_t rsa, const unsigned char *from, int fromlen,
unsigned char *to, int *tolen)
{
int padding;
if (RSA_size(rsa) * 8 < 768) {
padding = RSA_PKCS1_PADDING;
} else {
padding = RSA_PKCS1_OAEP_PADDING;
}
if ((*tolen = RSA_private_decrypt(fromlen, from, to, rsa, padding)) == -1) {
log_ssl_err("RSA_private_decrypt failed");
return 0;
}
return 1;
}
/**
* Hashes a block of data and signs it with an RSA private key.
* Output buffer must be at least the key size.
*/
int create_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
unsigned char meshash[HMAC_LEN];
unsigned int meshashlen;
const EVP_MD *hashptr;
if (!hash(hashtype, mes, meslen, meshash, &meshashlen)) {
return 0;
}
hashptr = get_hash(hashtype);
if (!RSA_sign(EVP_MD_type(hashptr), meshash, meshashlen,
sig, siglen, rsa)) {
log_ssl_err("RSA_sign failed");
return 0;
} else {
return 1;
}
}
/**
* Hashes a block of data and verifies it against an RSA signature.
*/
int verify_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int siglen)
{
unsigned char meshash[HMAC_LEN];
unsigned int meshashlen;
const EVP_MD *hashptr;
if (!hash(hashtype, mes, meslen, meshash, &meshashlen)) {
return 0;
}
hashptr = get_hash(hashtype);
if (!RSA_verify(EVP_MD_type(hashptr), meshash, meshashlen,
sig, siglen, rsa)) {
log_ssl_err("RSA_verify failed");
return 0;
} else {
return 1;
}
}
/**
* Creates an RSA public key with the given modulus and public exponent
*/
int import_RSA_key(RSA_key_t *rsa, uint32_t exponent,
const unsigned char *modulus, uint16_t modlen)
{
unsigned char bin_exponent[4];
*rsa = RSA_new();
bin_exponent[0] = (exponent & 0xFF000000) >> 24;
bin_exponent[1] = (exponent & 0x00FF0000) >> 16;
bin_exponent[2] = (exponent & 0x0000FF00) >> 8;
bin_exponent[3] = (exponent & 0x000000FF);
if (((*rsa)->e = BN_bin2bn(bin_exponent, 4, NULL)) == NULL) {
log_ssl_err("BN_bin2bn failed for e");
return 0;
}
if (((*rsa)->n = BN_bin2bn(modulus, modlen, NULL)) == NULL) {
log_ssl_err("BN_bin2bn failed for n");
return 0;
}
return 1;
}
/**
* Extracts the modulus and public exponent from an RSA public key
*/
int export_RSA_key(const RSA_key_t rsa, uint32_t *exponent,
unsigned char *modulus, uint16_t *modlen)
{
unsigned char bin_exponent[4];
int explen, l_modlen, i;
if (BN_num_bytes(rsa->e) > sizeof(bin_exponent)) {
log(0, 0, "exponent too big for export");
return 0;
}
if ((explen = BN_bn2bin(rsa->e, bin_exponent)) <= 0) {
log_ssl_err("BN_bn2bin failed for e");
return 0;
}
if (explen > 4) {
log(0, 0, "exponent too big, size %d", explen);
return 0;
}
*exponent = 0;
for (i = 0; i < explen; i++) {
*exponent |= bin_exponent[i] << (8 * (explen - i - 1));
}
if ((l_modlen = BN_bn2bin(rsa->n, modulus)) <= 0) {
log_ssl_err("BN_bn2bin failed for n");
return 0;
}
*modlen = l_modlen;
return 1;
}
/**
* Generates an RSA private key with the given exponent and number of bits
* and writes it to the given file (if specified).
*/
RSA_key_t gen_RSA_key(int bits, int exponent, const char *filename)
{
RSA_key_t rsa;
FILE *f;
if ((rsa = RSA_generate_key(bits ? bits : DEF_RSA_LEN,
exponent, NULL, NULL)) == NULL) {
log_ssl_err("couldn't generate rsa key");
return NULL;
}
if (strcmp(filename, "")) {
if ((f = fopen(filename, "rb")) != NULL) {
log(0, 0, "Private key file already exists, won't overwrite");
fclose(f);
return NULL;
}
if ((f = fopen(filename, "wb")) == NULL) {
syserror(0, 0, "failed to open key file");
return NULL;
}
if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL, NULL)) {
log_ssl_err("couldn't write rsa private key");
fclose(f);
return NULL;
}
fclose(f);
}
return rsa;
}
/**
* Reads an RSA private key from the specified file
*/
RSA_key_t read_RSA_key(const char *filename)
{
RSA_key_t rsa;
FILE *f;
if ((f = fopen(filename, "rb")) == NULL) {
syserror(0, 0, "failed to open key file");
return NULL;
}
if ((rsa = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL)) == NULL) {
log_ssl_err("couldn't read rsa private key");
return NULL;
}
fclose(f);
return rsa;
}
void free_RSA_key(RSA_key_t rsa)
{
RSA_free(rsa);
}
const char *get_next_container()
{
return NULL;
}
void delete_container(const char *name)
{
}
void set_sys_keys(int set)
{
}
uftp-3.5/proxy_common.c 0000644 0003316 0000550 00000103116 11577014247 014253 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_common.h"
#include "proxy_upstream.h"
/**
* Look for a given group in the global group list
* Returns the file's index in the list, or -1 if not found
*/
int find_group(uint32_t group_id)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id == group_id) {
return i;
}
}
return -1;
}
/**
* Look for a given client in the group's client list
* Returns the client's index in the list, or -1 if not found
*/
int find_client(int listidx, uint32_t addr)
{
int i;
for (i = 0; i < group_list[listidx].destcount; i++) {
if (group_list[listidx].destinfo[i].addr.s_addr == addr) {
return i;
}
}
return -1;
}
/**
* Looks for one of the local listening addresses in a list of addresses.
* Returns the index in m_interface of the match, or -1 if no match
*/
int addr_in_list(uint32_t *addrlist, int size)
{
int i, j;
for (i = 0; i < size; i++) {
if (addrlist[i] == 0) {
return -1;
}
for (j = 0; j < interface_count; j++) {
if (addrlist[i] == m_interface[j].addr.s_addr) {
return j;
}
}
}
return -1;
}
/**
* Checks to see if the multicast address used for the given group list member
* is also being used by either another member or the public address list
*/
int other_mcast_users(int listidx)
{
int i;
for (i = 0; i < pub_multi_count; i++) {
if (group_list[listidx].multi.s_addr == pub_multi[i].s_addr) {
return 1;
}
}
for (i = 0; i < MAXLIST; i++) {
if ((i != listidx) && (group_list[i].group_id != 0) &&
(group_list[listidx].multi.s_addr ==
group_list[i].multi.s_addr)) {
return 1;
}
}
return 0;
}
/**
* Clean up a group list entry. Free malloc'ed structures, drop the
* multicast group (if no one else is using it) and free the slot.
*/
void group_cleanup(int listidx)
{
int i;
for (i = 0; i < MAX_PEND; i++) {
free(group_list[listidx].pending[i].naklist);
}
if ((group_list[listidx].multi.s_addr != 0) &&
(proxy_type != CLIENT_PROXY) && !other_mcast_users(listidx) &&
group_list[listidx].multi_join) {
multicast_leave(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface, interface_count,
server_fp, server_fp_count);
}
if (group_list[listidx].serverkey) {
free_RSA_key(group_list[listidx].serverkey);
}
for (i = 0; i < group_list[listidx].destcount; i++) {
if (group_list[listidx].destinfo[i].pubkey) {
free_RSA_key(group_list[listidx].destinfo[i].pubkey);
}
}
memset(&group_list[listidx], 0, sizeof(group_list[listidx]));
}
/**
* Initializes the uftp header of an outgoing packet
* blsize is initialized to 0, but must be set later before sending
*/
void set_uftp_header(struct uftp_h *header, int func, int listidx)
{
header->uftp_id = group_list[listidx].version;
header->func = func;
header->blsize = 0;
header->group_id = htonl(group_list[listidx].group_id);
header->srcaddr = group_list[listidx].destaddr.s_addr;
switch (func) {
case REGISTER:
case INFO_ACK:
case STATUS:
case COMPLETE:
header->destaddr = group_list[listidx].srcaddr.s_addr;
break;
case KEYINFO:
header->destaddr = group_list[listidx].multi.s_addr;
break;
}
// Let ABORTs set destaddr themselves, since it depends on the direction
}
/**
* Sets the timeout time for a given group list member
*/
void set_timeout(int listidx, int pending_reset)
{
int pending, i;
if (group_list[listidx].phase == PR_PHASE_READY) {
gettimeofday(&group_list[listidx].phase_timeout_time, NULL);
group_list[listidx].phase_timeout_time.tv_usec +=
group_list[listidx].announce_int * 1000;
while (group_list[listidx].phase_timeout_time.tv_usec >= 1000000) {
group_list[listidx].phase_timeout_time.tv_usec -= 1000000;
group_list[listidx].phase_timeout_time.tv_sec++;
}
}
for (pending = 0, i = 0; (i < MAX_PEND) && !pending; i++) {
if (group_list[listidx].pending[i].msg != 0) {
pending = group_list[listidx].pending[i].msg;
}
}
if (pending) {
if (pending_reset) {
gettimeofday(&group_list[listidx].timeout_time, NULL);
switch (pending) {
case REGISTER:
case INFO_ACK:
group_list[listidx].timeout_time.tv_usec +=
// It's really: announce_int * 1000 * (proxy_int / 100)
group_list[listidx].announce_int * 10 * proxy_int;
break;
case STATUS:
case COMPLETE:
// TODO: a COMPLETE could be send in response to a FILEINFO.
// In this case using announce_int would be better.
// Need to figure out how we would know this.
group_list[listidx].timeout_time.tv_usec +=
// It's really: status_int * 1000 * (proxy_int / 100)
group_list[listidx].status_int * 10 * proxy_int;
break;
}
}
} else {
gettimeofday(&group_list[listidx].timeout_time, NULL);
group_list[listidx].timeout_time.tv_sec +=
group_list[listidx].status_time;
}
while (group_list[listidx].timeout_time.tv_usec >= 1000000) {
group_list[listidx].timeout_time.tv_usec -= 1000000;
group_list[listidx].timeout_time.tv_sec++;
}
}
/**
* Returns the maximum number of clients that can be listed in a given message
*/
int max_msg_dest(int listidx, int func)
{
switch (func) {
case REGISTER:
return (group_list[listidx].payloadsize -
sizeof(struct register_h)) / sizeof(uint32_t);
case KEYINFO:
return (group_list[listidx].payloadsize -
sizeof(struct keyinfo_h)) / sizeof(struct destkey);
case INFO_ACK:
return (group_list[listidx].encpayloadsize -
sizeof(struct infoack_h)) / sizeof(uint32_t);
case STATUS:
return (group_list[listidx].encpayloadsize -
sizeof(struct prstatus_h)) / sizeof(uint32_t);
case COMPLETE:
return (group_list[listidx].encpayloadsize -
sizeof(struct complete_h)) / sizeof(uint32_t);
default:
return 0;
}
}
/**
* Sends a pending aggregate message for a given group and message
*/
void send_pending(int listidx, int pendidx)
{
switch (group_list[listidx].pending[pendidx].msg) {
case REGISTER:
send_register(listidx, pendidx);
break;
case INFO_ACK:
send_info_ack(listidx, pendidx, FILEINFO);
break;
case STATUS:
send_status(listidx, pendidx);
break;
case COMPLETE:
send_complete(listidx, pendidx);
break;
default:
log(group_list[listidx].group_id, 0, "Tried to send pending on "
"invalid type %s",
func_name(group_list[listidx].pending[pendidx].msg));
return;
}
if (group_list[listidx].pending[pendidx].count <= 0) {
// Finish the cleanup we started in load_pending
free(group_list[listidx].pending[pendidx].naklist);
memset(&group_list[listidx].pending[pendidx], 0,
sizeof(struct pr_pending_info_t));
}
}
/**
* Sends all pending aggregate message for a given group
*/
void send_all_pending(int listidx)
{
int i;
for (i = 0; i < MAX_PEND; i++) {
if (group_list[listidx].pending[i].msg != 0) {
send_pending(listidx, i);
}
}
}
/**
* Add the NAKs in the given STATUS message to the list of pending NAKs
*/
void add_naks_to_pending(int listidx, int pendidx,
const unsigned char *message)
{
const unsigned char *naks;
unsigned i;
naks = message + sizeof(struct status_h);
for (i = 0; i < group_list[listidx].blocksize; i++) {
group_list[listidx].pending[pendidx].naklist[i] |= naks[i];
}
}
/**
* Puts the given message on the pending message list. If it doesn't match
* any pending message and there are no open slots, first send what's pending.
* If the pending list is full after adding the given message, then send.
*/
void check_pending(int listidx, int hostidx, const unsigned char *message)
{
struct infoack_h *infoack;
struct status_h *status;
struct complete_h *complete;
const uint8_t *func;
struct pr_pending_info_t *pending;
int match, pendidx;
func = message;
infoack = (struct infoack_h *)message;
status = (struct status_h *)message;
complete = (struct complete_h *)message;
for (pendidx = 0; pendidx < MAX_PEND; pendidx++) {
pending = &group_list[listidx].pending[pendidx];
if (group_list[listidx].pending[pendidx].msg == 0) {
match = 1;
break;
}
match = (*func == pending->msg);
switch (*func) {
case REGISTER:
// REGISTER always matches itself
break;
case INFO_ACK:
// Only in response to FILEINFO.
// Responses to KEYINFO are not forwarded.
match = match && (ntohs(infoack->file_id) == pending->file_id);
break;
case STATUS:
match = match && ((ntohs(status->file_id) == pending->file_id) &&
(status->pass == pending->pass) &&
(ntohs(status->section) == pending->section));
break;
case COMPLETE:
match = match && (ntohs(complete->file_id) == pending->file_id);
break;
default:
log(group_list[listidx].group_id, 0, "Tried to check pending "
"on invalid type %s", func_name(*func));
return;
}
if (match) {
break;
}
}
if (!match) {
send_all_pending(listidx);
pendidx = 0;
pending = &group_list[listidx].pending[pendidx];
}
pending->msg = *func;
if (group_list[listidx].destinfo[hostidx].pending != pendidx) {
group_list[listidx].destinfo[hostidx].pending = pendidx;
pending->count++;
}
switch (*func) {
case INFO_ACK:
if (pending->count == 1) {
pending->partial = 1;
}
pending->file_id = ntohs(infoack->file_id);
pending->partial = pending->partial &&
((infoack->flags & FLAG_PARTIAL) != 0);
break;
case STATUS:
pending->file_id = ntohs(status->file_id);
pending->pass = status->pass;
pending->section = ntohs(status->section);
pending->seq = group_list[listidx].last_seq;
if (!pending->naklist) {
pending->naklist = calloc(group_list[listidx].blocksize, 1);
if (pending->naklist == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
}
if (ntohl(status->nak_count) != 0) {
add_naks_to_pending(listidx, pendidx, message);
}
break;
case COMPLETE:
pending->file_id = ntohs(complete->file_id);
break;
}
if (pending->count == max_msg_dest(listidx, *func)) {
send_pending(listidx, pendidx);
} else {
int total_pending, i;
for (total_pending = 0, i = 0; i < MAX_PEND; i++) {
total_pending += group_list[listidx].pending[i].count;
}
if (total_pending == 1) {
set_timeout(listidx, 1);
}
}
}
/**
* Check for any client that hasn't fully registered.
* If the abort parameter is set, send an ABORT to the server and client.
* Returns 1 if any aren't fully registered, 0 if all are registered.
*/
int check_unfinished_clients(int listidx, int abort)
{
int hostidx, found;
struct pr_destinfo_t *dest;
if (group_list[listidx].keytype == KEY_NONE) {
return 0;
}
found = 0;
for (hostidx = 0; hostidx < group_list[listidx].destcount; hostidx++) {
dest = &group_list[listidx].destinfo[hostidx];
if ((group_list[listidx].group_id != 0) &&
(dest->state != PR_CLIENT_READY)) {
if (abort) {
send_downstream_abort(listidx, dest->addr.s_addr,
"Client not fully registered at proxy");
send_upstream_abort(listidx, dest->addr.s_addr,
"Client not fully registered at proxy");
}
found = 1;
}
}
return found;
}
/**
* Load a message body with the list of pending clients
*/
int load_pending(int listidx, int pendidx, int func,
uint32_t *addrlist, int listlen)
{
int hostidx, cnt;
struct pr_destinfo_t *dest;
for (cnt = 0, hostidx = 0;
(hostidx < group_list[listidx].destcount) && (cnt < listlen);
hostidx++) {
dest = &group_list[listidx].destinfo[hostidx];
if (dest->pending == pendidx) {
addrlist[cnt++] = dest->addr.s_addr;
dest->pending = -1;
group_list[listidx].pending[pendidx].count--;
}
}
if (group_list[listidx].pending[pendidx].count <= 0) {
// Don't zero out the whole pending struct.
// We need to clear the message now to set timeouts properly but
// we still use the other fields just before sending the message.
// The full cleanup is done in send_pending
group_list[listidx].pending[pendidx].count = 0;
group_list[listidx].pending[pendidx].msg = 0;
}
return cnt;
}
/**
* Forward a message unmodified to the next hop, resigning if necessary.
*/
void forward_message(int listidx, const struct sockaddr_in *src,
const unsigned char *packet)
{
struct uftp_h *header;
struct encrypted_h *encrypted;
struct sockaddr_in dest;
unsigned int meslen, siglen;
int hostidx;
uint8_t *sig, *sigcopy;
RSA_key_t key;
header = (struct uftp_h *)packet;
meslen = sizeof(struct uftp_h) + ntohs(header->blsize);
memset(&dest, 0, sizeof(dest));
if (!memcmp(src, &group_list[listidx].up_addr, sizeof(*src))) {
if (proxy_type == RESPONSE_PROXY) {
// Response proxy, no downstream fowarding
set_timeout(listidx, 0);
return;
} else if (proxy_type == SERVER_PROXY) {
dest = down_addr;
} else {
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = header->destaddr;
dest.sin_port = htons(out_port);
key = group_list[listidx].serverkey;
}
} else {
dest = group_list[listidx].up_addr;
if (proxy_type != SERVER_PROXY) {
hostidx = find_client(listidx, header->srcaddr);
if (hostidx == -1) {
log(group_list[listidx].group_id, 0,
"Couldn't find receiver in list");
return;
}
key = group_list[listidx].destinfo[hostidx].pubkey;
}
}
// If we're using RSA signatures, verify the signature and resign
if ((proxy_type != SERVER_PROXY) && (header->func == ENCRYPTED) &&
(group_list[listidx].sigtype == SIG_RSA)) {
encrypted = (struct encrypted_h *)(packet + sizeof(struct uftp_h));
sig = (uint8_t *)encrypted + sizeof(struct encrypted_h);
siglen = ntohs(encrypted->sig_len);
sigcopy = calloc(siglen, 1);
if (sigcopy == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(sigcopy, sig, siglen);
memset(sig, 0, siglen);
if (!verify_RSA_sig(key, group_list[listidx].hashtype, packet,
meslen, sigcopy, siglen)) {
log(group_list[listidx].group_id, 0,
"Signature verification failed");
free(sigcopy);
return;
}
if (!create_RSA_sig(group_list[listidx].proxykey,
group_list[listidx].hashtype, packet, meslen,
sigcopy, &siglen)) {
log(group_list[listidx].group_id, 0,
"Signature creation failed");
free(sigcopy);
return;
}
memcpy(sig, sigcopy, siglen);
free(sigcopy);
}
if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)&dest,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0, "Error forwarding message");
log(group_list[listidx].group_id, 0, "Dest: %s:%d",
inet_ntoa(dest.sin_addr), ntohs(dest.sin_port));
}
set_timeout(listidx, 0);
}
/**
* Process an HB_REQ message
*/
void handle_hb_request(const struct sockaddr_in *src,
const unsigned char *packet)
{
struct uftp_h *header;
struct hb_req_h *hbreq;
unsigned char *keymod, *sig;
struct hostent *hp;
RSA_key_t key;
unsigned char *verifydata, fingerprint[HMAC_LEN];
unsigned int verifylen, fplen, keylen, siglen;
int resp;
header = (struct uftp_h *)packet;
hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h));
if (!noname && (hp = gethostbyaddr((char *)&src->sin_addr,
sizeof(struct in_addr), AF_INET))) {
log(0, 0, "Received HB_REQ from %s (%s)",
hp->h_name, inet_ntoa(src->sin_addr));
} else {
log(0, 0, "Received HB_REQ from %s", inet_ntoa(src->sin_addr));
}
if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) {
if ((down_addr.sin_addr.s_addr == src->sin_addr.s_addr) &&
down_addr.sin_port == src->sin_port) {
resp = HB_AUTH_OK;
} else if (down_nonce != ntohl(hbreq->nonce)) {
resp = HB_AUTH_CHALLENGE;
} else {
keymod = (unsigned char *)hbreq + sizeof(struct hb_req_h);
keylen = ntohs(hbreq->keylen);
sig = keymod + keylen;
siglen = ntohs(hbreq->siglen);
// First check key fingerprint
verifylen = 0;
verifydata = calloc(sizeof(hbreq->keyexp) + keylen, 1);
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(verifydata, &hbreq->keyexp, sizeof(hbreq->keyexp));
verifylen += sizeof(hbreq->keyexp);
memcpy(verifydata + verifylen, keymod, keylen);
verifylen += keylen;
hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen);
if (memcmp(down_fingerprint, fingerprint, fplen)) {
log(0, 0, "Failed to verify HB_REQ fingerprint");
free(verifydata);
resp = HB_AUTH_FAILED;
goto end;
}
free(verifydata);
// Then check signature
if (!import_RSA_key(&key, ntohl(hbreq->keyexp), keymod, keylen)) {
log(0, 0, "Failed to import public key from HB_REQ");
resp = HB_AUTH_FAILED;
goto end;
}
if (!verify_RSA_sig(key, HASH_SHA1, (unsigned char *)&hbreq->nonce,
sizeof(hbreq->nonce), sig, siglen)) {
log(0, 0, "Failed to verify HB_REQ signature");
resp = HB_AUTH_FAILED;
goto end;
}
down_addr = *src;
log(0, 0, "Using %s:%d as downstream address:port",
inet_ntoa(src->sin_addr), ntohs(src->sin_port));
down_nonce = rand();
resp = HB_AUTH_OK;
}
} else {
resp = HB_AUTH_OK;
}
end:
send_hb_response(src, resp);
}
/**
* Process an KEY_REQ message
*/
void handle_key_req(const struct sockaddr_in *src,
const unsigned char *packet)
{
struct hostent *hp;
struct timeval current_timestamp;
if (!noname && (hp = gethostbyaddr((char *)&src->sin_addr,
sizeof(struct in_addr), AF_INET))) {
log(0, 0, "Received KEY_REQ from %s (%s)",
hp->h_name, inet_ntoa(src->sin_addr));
} else {
log(0, 0, "Received KEY_REQ from %s", inet_ntoa(src->sin_addr));
}
gettimeofday(¤t_timestamp, NULL);
if (diff_sec(current_timestamp, last_key_req) > KEY_REQ_LIMIT) {
send_proxy_key();
}
}
/**
* Sends an HB_RESP in response to an HB_REQ
*/
void send_hb_response(const struct sockaddr_in *src, int response)
{
unsigned char *packet;
struct uftp_h *header;
struct hb_resp_h *hbresp;
int meslen;
packet = calloc(sizeof(struct uftp_h) + sizeof(struct hb_resp_h), 1);
if (packet == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
hbresp = (struct hb_resp_h *)(packet + sizeof(struct uftp_h));
header->uftp_id = UFTP_VER_NUM;
header->func = HB_RESP;
header->blsize = ntohs(sizeof(struct hb_resp_h));
hbresp->func = HB_RESP;
hbresp->authenticated = response;
if (response == HB_AUTH_CHALLENGE) {
hbresp->nonce = htonl(down_nonce);
}
meslen = sizeof(struct uftp_h) + sizeof(struct hb_resp_h);
if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)src,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending HB_RESP");
} else {
log(0, 0, "Sent HB_RESP to %s:%d", inet_ntoa(src->sin_addr),
ntohs(src->sin_port));
}
free(packet);
}
/**
* Sends a PROXY_KEY message to the first listed public multicast address.
*/
void send_proxy_key()
{
unsigned char *packet, *keymod, *sig;
struct uftp_h *header;
struct proxy_key_h *proxykey;
uint8_t modulus[PUBKEY_LEN];
uint32_t exponent, nonce;
uint16_t modlen;
unsigned int meslen, siglen;
struct sockaddr_in pubaddr;
packet = calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h) +
(PUBKEY_LEN * 2) , 1);
if (packet == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
proxykey = (struct proxy_key_h *)(packet + sizeof(struct uftp_h));
keymod = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
header->uftp_id = UFTP_VER_NUM;
header->func = PROXY_KEY;
proxykey->func = PROXY_KEY;
if (!export_RSA_key(privkey[0], &exponent,
modulus, &modlen)) {
log(0, 0, "Error exporting public key");
free(packet);
return;
}
nonce = htonl(rand());
proxykey->nonce = nonce;
proxykey->keyexp = htonl(exponent);
memcpy(keymod, modulus, modlen);
proxykey->keylen = htons(modlen);
sig = keymod + modlen;
if (!create_RSA_sig(privkey[0], HASH_SHA1, (unsigned char *)&nonce,
sizeof(nonce), sig, &siglen) ||
siglen > modlen) {
log(0, 0, "Error signing nonce");
free(packet);
return;
}
proxykey->siglen = htons(siglen);
meslen = sizeof(struct proxy_key_h) + modlen + siglen;
header->blsize = htons(meslen);
meslen += sizeof(struct uftp_h);
memset(&pubaddr, 0, sizeof(pubaddr));
pubaddr.sin_family = AF_INET;
pubaddr.sin_addr = pub_multi[0];
pubaddr.sin_port = htons(port);
if (nb_sendto(listener, packet, meslen, 0,
(struct sockaddr *)&pubaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending PROXY_KEY");
} else {
log(0, 0, "Sent PROXY_KEY to %s",
inet_ntoa(pubaddr.sin_addr));
}
free(packet);
}
/**
* Sends an ABORT message upstream to a server
*/
void send_upstream_abort(int listidx, uint32_t addr, const char *message)
{
unsigned char *buf;
struct uftp_h *header;
struct abort_h *abort;
int payloadlen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
abort = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, listidx);
if (!group_list[listidx].foundaddr) {
header->srcaddr = m_interface[0].addr.s_addr;
}
header->destaddr = group_list[listidx].srcaddr.s_addr;
abort->func = ABORT;
abort->host = addr;
strncpy(abort->message, message, sizeof(abort->message) - 1);
header->blsize = htons(sizeof(struct abort_h));
payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h);
// Proxies should never need to send an encrypted ABORT
if (nb_sendto(listener, buf, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].up_addr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0, "Error sending ABORT");
}
if (addr == 0) {
group_cleanup(listidx);
}
free(buf);
}
/**
* Sends an ABORT message downstream to clients
*/
void send_downstream_abort(int listidx, uint32_t addr, const char *message)
{
unsigned char *buf;
struct uftp_h *header;
struct abort_h *abort;
struct sockaddr_in sin;
int payloadlen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
abort = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, listidx);
header->destaddr = group_list[listidx].multi.s_addr;
abort->func = ABORT;
abort->host = addr;
strncpy(abort->message, message, sizeof(abort->message) - 1);
header->blsize = htons(sizeof(struct abort_h));
payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h);
// Proxies should never need to send an encrypted ABORT
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = group_list[listidx].multi;
sin.sin_port = htons(out_port);
if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&sin,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0, "Error sending ABORT");
}
free(buf);
}
/**
* Handles an ABORT message from a client or server
* and forwards if necessary.
*/
void handle_abort(int listidx, const struct sockaddr_in *src,
const unsigned char *packet)
{
struct uftp_h *header;
struct abort_h *abort;
int upstream, hostidx, found, i;
header = (struct uftp_h *)packet;
abort = (struct abort_h *)(packet + sizeof(struct uftp_h));
upstream = (!memcmp(src, &group_list[listidx].up_addr, sizeof(*src)));
if (ntohs(header->blsize) != sizeof(struct abort_h)) {
log(group_list[listidx].group_id, 0,
"Rejecting ABORT from %s: invalid message size",
upstream ? "server" : "client");
}
if (upstream) {
for (i = 0, found = 0; (i < interface_count) && !found; i++) {
if (abort->host == m_interface[i].addr.s_addr) {
found = 1;
}
}
if ((abort->host == 0) || found) {
log(group_list[listidx].group_id, 0,
"Transfer aborted by server: %s", abort->message);
if (proxy_type != RESPONSE_PROXY) {
send_downstream_abort(listidx, 0, abort->message);
}
group_cleanup(listidx);
} else {
if (proxy_type != RESPONSE_PROXY) {
send_downstream_abort(listidx, header->srcaddr, abort->message);
}
}
} else {
if ((hostidx = find_client(listidx, header->srcaddr)) != -1) {
log(group_list[listidx].group_id, 0, "Transfer aborted by %s: %s",
group_list[listidx].destinfo[hostidx].name, abort->message);
} else {
log(group_list[listidx].group_id, 0, "Transfer aborted by %s: %s",
inet_ntoa(to_addr(header->srcaddr)), abort->message);
}
send_upstream_abort(listidx, header->srcaddr, abort->message);
}
}
/**
* Verifies a server's or client's public key fingerprint
*/
int verify_fingerprint(const struct fp_list_t *fplist, int listlen,
const unsigned char *keymod, uint32_t keyexp,
int keylen, uint32_t addr)
{
unsigned char *verifydata, fingerprint[HMAC_LEN];
unsigned int verifylen, fplen;
int found, keyidx;
if (listlen == 0) {
return 1;
}
for (keyidx = 0, found = 0; (keyidx < listlen) && !found; keyidx++) {
if (fplist[keyidx].addr.s_addr == addr) {
keyidx--;
found = 1;
}
}
if (!found) {
return 0;
}
if (!fplist[keyidx].has_fingerprint) {
return 1;
}
verifylen = 0;
verifydata = calloc(sizeof(keyexp) + keylen, 1);
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(verifydata, &keyexp, sizeof(keyexp));
verifylen += sizeof(keyexp);
memcpy(verifydata + verifylen, keymod, keylen);
verifylen += keylen;
hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen);
if (memcmp(fplist[keyidx].fingerprint, fingerprint, fplen)) {
free(verifydata);
return 0;
} else {
free(verifydata);
return 1;
}
}
/**
* Returns the verify_data string used in certain messages. This value
* is then run through the PRF with the result going into the message.
*/
uint8_t *build_verify_data(int listidx, int hostidx, int *verifylen, int full)
{
uint8_t *verifydata;
uint32_t exponent;
uint16_t modlen;
uint8_t modulus[PUBKEY_LEN];
uint32_t group_id;
struct pr_group_list_t *group;
struct pr_destinfo_t *dest;
RSA_key_t key;
group = &group_list[listidx];
if (hostidx != -1) {
dest = &group->destinfo[hostidx];
}
*verifylen = 0;
if (!full) {
verifydata = calloc(sizeof(group->group_id) +
sizeof(group->multi.s_addr) + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster), 1);
} else {
verifydata = calloc(sizeof(group->group_id) +
sizeof(group->multi.s_addr) + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster) +
PUBKEY_LEN + 4 + sizeof(group->groupmaster), 1);
}
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
group_id = htonl(group->group_id);
memcpy(verifydata, &group_id, sizeof(group_id));
*verifylen += sizeof(group_id);
memcpy(verifydata + *verifylen, &group->multi.s_addr,
sizeof(group->multi.s_addr));
*verifylen += sizeof(group->multi.s_addr);
memcpy(verifydata + *verifylen, group->rand1, sizeof(group->rand1));
*verifylen += sizeof(group->rand1);
if (hostidx == -1) {
memcpy(verifydata + *verifylen, group->rand2, sizeof(group->rand2));
*verifylen += sizeof(group->rand2);
memcpy(verifydata + *verifylen, group->premaster,
sizeof(group->premaster));
*verifylen += sizeof(group->premaster);
} else {
memcpy(verifydata + *verifylen, dest->rand2, sizeof(dest->rand2));
*verifylen += sizeof(dest->rand2);
memcpy(verifydata + *verifylen, dest->premaster,
sizeof(dest->premaster));
*verifylen += sizeof(dest->premaster);
}
if (full) {
if (group->client_auth) {
if (hostidx == -1) {
key = group->proxykey;
} else {
key = dest->pubkey;
}
if (!export_RSA_key(key, &exponent, modulus, &modlen)) {
free(verifydata);
return NULL;
}
exponent = htonl(exponent);
memcpy(verifydata + *verifylen, &exponent, sizeof(exponent));
*verifylen += sizeof(exponent);
memcpy(verifydata + *verifylen, modulus, modlen);
*verifylen += modlen;
}
memcpy(verifydata + *verifylen, group->groupmaster,
sizeof(group->groupmaster));
*verifylen += sizeof(group->groupmaster);
}
return verifydata;
}
uftp-3.5/proxy_config.c 0000644 0003316 0000550 00000037517 11577014247 014243 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_config.h"
/**
* Global command line values and sockets
*/
SOCKET listener;
char logfile[MAXPATHNAME], pidfile[MAXPATHNAME];
char keyfile[MAXLIST][MAXPATHNAME];
int proxy_type, noname, debug, buffer, newkeylen, dscp;
int hb_interval, proxy_int;
unsigned char ttl;
uint16_t port, out_port;
struct sockaddr_in down_addr;
int have_down_fingerprint;
uint8_t down_fingerprint[HMAC_LEN];
uint32_t down_nonce, uid;
struct sockaddr_in hb_hosts[MAXLIST];
struct in_addr pub_multi[MAX_INTERFACES];
struct fp_list_t server_fp[MAXLIST], client_fp[MAXPROXYDEST];
struct iflist ifl[MAX_INTERFACES], m_interface[MAX_INTERFACES];
struct timeval next_hb_time, last_key_req;
int ifl_len, hbhost_count, server_fp_count, client_fp_count;
int keyfile_count, key_count, pub_multi_count, interface_count, sys_keys;
struct in_addr out_addr;
RSA_key_t privkey[MAXLIST];
struct pr_group_list_t group_list[MAXLIST];
extern char *optarg;
extern int optind;
/**
* Adds a host and its fingerprint to the given list
*/
void add_hosts_by_name(struct fp_list_t *list, int *list_count,
const char *filename)
{
char line[1000], *hostname, *fingerprint;
FILE *hostfile;
struct hostent *hp;
struct in_addr *addr;
if ((hostfile = fopen(filename, "r")) == NULL) {
fprintf(stderr,"Couldn't open server/client list %s: %s\n",
filename, strerror(errno));
exit(1);
}
while (fgets(line, sizeof(line), hostfile)) {
while (line[strlen(line)-1] == '\r' ||
line[strlen(line)-1] == '\n') {
line[strlen(line)-1] = '\x0';
}
hostname = strtok(line, " \t");
if (!hostname) continue;
if (hostname[0] == '#') continue;
if (strlen(hostname) > DESTNAME_LEN) {
fprintf(stderr, "Server/Client list %s: name too long\n", filename);
exit(1);
}
fingerprint = strtok(NULL, " \t");
if (inet_addr(hostname) == INADDR_NONE) {
if ((hp = gethostbyname(hostname)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", hostname);
exit(1);
} else {
addr = (struct in_addr *)hp->h_addr_list[0];
list[*list_count].addr = *addr;
list[*list_count].has_fingerprint =
parse_fingerprint(list[*list_count].fingerprint,
fingerprint);
(*list_count)++;
}
} else {
list[*list_count].addr.s_addr = inet_addr(hostname);
list[*list_count].has_fingerprint =
parse_fingerprint(list[*list_count].fingerprint, fingerprint);
(*list_count)++;
}
}
if (!feof(hostfile) && ferror(hostfile)) {
perror("Failed to read from server/client list file");
exit(1);
}
fclose(hostfile);
}
/**
* Set defaults for all command line arguments
*/
void set_defaults()
{
proxy_type = UNDEF_PROXY;
proxy_int = DEF_PROXY_INT;
hbhost_count = 0;
memset(hb_hosts, 0, sizeof(hb_hosts));
hb_interval = DEF_HB_INT;
debug = 0;
noname = 0;
log_level = DEF_LOG_LEVEL;
port = DEF_PORT;
out_addr.s_addr = INADDR_NONE;
ttl = DEF_TTL;
dscp = DEF_DSCP;
out_port = DEF_PORT;
uid = 0;
buffer = 0;
strncpy(logfile, DEF_LOGFILE, sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
memset(pidfile, 0, sizeof(pidfile));
server_fp_count = 0;
client_fp_count = 0;
key_count = 0;
keyfile_count = 0;
newkeylen = 0;
interface_count = 0;
pub_multi_count = 0;
sys_keys = 0;
}
/**
* Set argument defaults, read and validate command line options
*/
void process_args(int argc, char *argv[])
{
int c, i, listidx, hbport;
long tmpval;
struct hostent *hp;
struct in_addr *paddr, addr;
char *p, *p2, *hoststr, *portstr;
const char opts[] = "s:crdx:np:t:Q:O:U:q:T:mh:H:B:L:P:C:S:k:K:I:M:";
set_defaults();
// read lettered arguments
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 's':
if (proxy_type != UNDEF_PROXY) {
fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
exit(1);
}
proxy_type = SERVER_PROXY;
memset(&down_addr, 0, sizeof(down_addr));
if (!strncmp(optarg, "fp=", 3)) {
have_down_fingerprint =
parse_fingerprint(down_fingerprint, optarg + 3);
if (!have_down_fingerprint) {
fprintf(stderr, "Failed to parse downstream fingerprint\n");
exit(1);
}
srand((unsigned int)time(NULL) ^ getpid());
down_nonce = rand();
} else {
have_down_fingerprint = 0;
down_addr.sin_family = AF_INET;
if (inet_addr(optarg) == INADDR_NONE) {
if ((hp = gethostbyname(optarg)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", optarg);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
down_addr.sin_addr.s_addr = paddr->s_addr;
}
} else {
down_addr.sin_addr.s_addr = inet_addr(optarg);
}
}
break;
case 'c':
if (proxy_type != UNDEF_PROXY) {
fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
exit(1);
}
proxy_type = CLIENT_PROXY;
break;
case 'r':
if (proxy_type != UNDEF_PROXY) {
fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
exit(1);
}
proxy_type = RESPONSE_PROXY;
break;
case 'd':
debug = 1;
break;
case 'n':
noname = 1;
break;
case 'x':
log_level = atoi(optarg);
if (log_level < 0) {
fprintf(stderr, "Invalid log level\n");
exit(1);
}
break;
case 'p':
port = atoi(optarg);
if (port == 0) {
fprintf(stderr, "Invalid port\n");
exit(1);
}
break;
case 't':
tmpval = atoi(optarg);
if ((tmpval <= 0) || (tmpval > 255)) {
fprintf(stderr, "Invalid ttl\n");
exit(1);
}
ttl = (char)tmpval;
break;
case 'Q':
tmpval = strtol(optarg, NULL, 0);
if ((tmpval < 0) || (tmpval > 63)) {
fprintf(stderr, "Invalid dscp\n");
exit(1);
}
dscp = (tmpval & 0xFF) << 2;
break;
case 'O':
if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) {
out_addr = ifl[listidx].addr;
break;
}
if (inet_addr(optarg) == INADDR_NONE) {
if ((hp = gethostbyname(optarg)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", optarg);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
}
} else {
addr.s_addr = inet_addr(optarg);
paddr = &addr;
}
if ((listidx = getifbyaddr(*paddr, ifl, ifl_len)) != -1) {
out_addr = ifl[listidx].addr;
} else {
fprintf(stderr, "Interface %s not found\n", optarg);
exit(1);
}
break;
case 'U':
if ((uid = inet_addr(optarg)) != 0) {
if (ntohl(uid) > 0xffffff) {
fprintf(stderr, "Invalid UID\n");
exit(1);
}
} else {
uid = strtol(optarg, NULL, 16);
if ((uid > 0xffffff) || (uid <= 0)) {
fprintf(stderr, "Invalid UID\n");
exit(1);
}
uid = htonl(uid);
}
break;
case 'q':
out_port = atoi(optarg);
if (out_port == 0) {
fprintf(stderr, "Invalid outgoing port\n");
exit(1);
}
break;
case 'T':
proxy_int = atoi(optarg);
if ((proxy_int < 10) || (proxy_int >= 100)) {
fprintf(stderr, "Invalid interval percentage\n");
exit(1);
}
break;
case 'm':
sys_keys = 1;
break;
case 'H':
p = strtok(optarg, ",");
while (p != NULL) {
p2 = strchr(p, ':');
if (p2) {
hoststr = strdup(p);
hoststr[p2 - p] = '\x0';
portstr = p2 + 1;
} else {
hoststr = p;
portstr = NULL;
}
hb_hosts[hbhost_count].sin_family = AF_INET;
if (inet_addr(hoststr) == INADDR_NONE) {
if ((hp = gethostbyname(hoststr)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", hoststr);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
hb_hosts[hbhost_count].sin_addr.s_addr = paddr->s_addr;
}
} else {
hb_hosts[hbhost_count].sin_addr.s_addr = inet_addr(hoststr);
}
if (portstr) {
free(hoststr);
hbport = atoi(portstr);
if ((hbport <= 0) || (hbport > 65535)) {
hbport = DEF_PORT;
}
} else {
hbport = DEF_PORT;
}
hb_hosts[hbhost_count++].sin_port = htons(hbport);
p = strtok(NULL, ",");
}
break;
case 'h':
hb_interval = atoi(optarg);
if ((hb_interval <= 0) || (hb_interval > 3600)) {
fprintf(stderr, "Invalid hearbeat interval\n");
exit(1);
}
break;
case 'B':
buffer = atoi(optarg);
if ((buffer < 65536) || (buffer > 104857600)) {
fprintf(stderr, "Invalid buffer size\n");
exit(1);
}
break;
case 'L':
strncpy(logfile, optarg, sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
break;
case 'P':
strncpy(pidfile, optarg, sizeof(pidfile)-1);
pidfile[sizeof(pidfile)-1] = '\x0';
break;
case 'C':
add_hosts_by_name(client_fp, &client_fp_count, optarg);
break;
case 'S':
add_hosts_by_name(server_fp, &server_fp_count, optarg);
break;
case 'k':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(keyfile[keyfile_count], p, sizeof(keyfile[0])-1);
keyfile[keyfile_count][sizeof(keyfile[0])-1] = '\x0';
keyfile_count++;
p = strtok(NULL, ",");
}
break;
case 'K':
newkeylen = atoi(optarg);
if ((newkeylen < 512) || (newkeylen > 2048)) {
fprintf(stderr, "Invalid new key length\n");
exit(1);
}
break;
case 'I':
p = strtok(optarg, ",");
while (p != NULL) {
if ((listidx = getifbyname(p, ifl, ifl_len)) != -1) {
m_interface[interface_count++] = ifl[listidx];
p = strtok(NULL, ",");
continue;
}
if (inet_addr(p) == INADDR_NONE) {
if ((hp = gethostbyname(p)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", p);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
}
} else {
addr.s_addr = inet_addr(p);
paddr = &addr;
}
if ((listidx = getifbyaddr(*paddr, ifl, ifl_len)) != -1) {
m_interface[interface_count++] = ifl[listidx];
} else {
fprintf(stderr, "Interface %s not found\n", p);
exit(1);
}
p = strtok(NULL, ",");
}
break;
case 'M':
p = strtok(optarg, ",");
while (p != NULL) {
pub_multi[pub_multi_count].s_addr = inet_addr(p);
if ((pub_multi[pub_multi_count].s_addr == INADDR_NONE) ||
(!is_multicast(pub_multi[pub_multi_count], 0))) {
fprintf(stderr, "Invalid multicast address: %s\n", p);
exit(1);
}
pub_multi_count++;
p = strtok(NULL, ",");
}
break;
case '?':
fprintf(stderr, USAGE);
exit(1);
}
}
if (proxy_type == UNDEF_PROXY) {
fprintf(stderr, "Either -s, -c, or -r must be specified\n");
fprintf(stderr, USAGE);
exit(1);
}
if (proxy_type != CLIENT_PROXY) {
down_addr.sin_port = htons(out_port);
if (server_fp_count) {
for (i = 0; i < pub_multi_count; i++) {
if (!is_multicast(pub_multi[i], 1)) {
fprintf(stderr, "Invalid source specific "
"multicast address: %s\n", inet_ntoa(pub_multi[i]));
exit(1);
}
}
if (pub_multi_count == 0) {
fprintf(stderr, "Default multicast address %s invalid "
"for source specific multicast\n", DEF_PUB_MULTI);
exit(1);
}
}
}
if (proxy_type == RESPONSE_PROXY) {
out_port = port;
}
}
uftp-3.5/proxy_downstream.c 0000644 0003316 0000550 00000047244 11415505725 015154 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2010 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else
#include
#include
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_common.h"
#include "proxy_downstream.h"
/**
* Adds a client to the given group
*/
int add_client(uint32_t addr, int listidx)
{
struct hostent *hp;
struct in_addr inaddr;
struct pr_destinfo_t *dest;
dest = &group_list[listidx].destinfo[group_list[listidx].destcount];
inaddr.s_addr = addr;
if (ntohl(addr) <= 0xffffff) {
sprintf(dest->name, "0x%06X", ntohl(addr));
} else {
if (!noname && (hp = gethostbyaddr((char *)&inaddr,
sizeof(struct in_addr), AF_INET))) {
strncpy(dest->name, hp->h_name, sizeof(dest->name)-1);
} else {
strncpy(dest->name, inet_ntoa(inaddr), sizeof(dest->name)-1);
}
dest->name[sizeof(dest->name)-1] = '\x0';
}
dest->addr = inaddr;
dest->pending = -1;
return group_list[listidx].destcount++;
}
/**
* For a given client, calculate the master key and do key expansion
* to determine the symmetric cypher key and IV salt, and hash key
*/
void calculate_client_keys(int listidx, int hostidx)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
struct pr_group_list_t *group;
struct pr_destinfo_t *dest;
group = &group_list[listidx];
dest = &group->destinfo[hostidx];
explen = group->keylen + group->ivlen +
group->hmaclen;
seedlen = sizeof(group->rand1) * 2;
seed = calloc(seedlen, 1);
prf_buf = calloc(MASTER_LEN + explen + group->hmaclen, 1);
if ((seed == NULL) || (prf_buf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(seed, group->rand1, sizeof(group->rand1));
memcpy(seed + sizeof(group_list[listidx].rand1), dest->rand2,
sizeof(dest->rand2));
PRF(group->hashtype, MASTER_LEN, dest->premaster, sizeof(dest->premaster),
"master secret", seed, seedlen, prf_buf, &len);
memcpy(dest->master,prf_buf, sizeof(dest->master));
PRF(group->hashtype, explen, dest->master, sizeof(dest->master),
"key expansion", seed, seedlen, prf_buf, &len);
memcpy(dest->hmackey, prf_buf, group->hmaclen);
memcpy(dest->key, prf_buf + group->hmaclen, group->keylen);
memcpy(dest->salt, prf_buf + group->hmaclen + group->keylen, group->ivlen);
free(seed);
free(prf_buf);
}
/**
* Verifies the data in a CLIENT_KEY message signed by the client's public key
*/
int verify_client_key(int listidx, int hostidx)
{
uint8_t *verifydata;
int verifylen;
struct pr_destinfo_t *dest;
dest = &group_list[listidx].destinfo[hostidx];
// build_verify_data should never fail in this case
verifydata = build_verify_data(listidx, hostidx, &verifylen, 0);
if (!verify_RSA_sig(dest->pubkey, group_list[listidx].hashtype, verifydata,
verifylen, dest->verifydata, dest->verifylen)) {
log(group_list[listidx].group_id, 0, "Rejecting CLIENT_KEY from %s: "
"verify data mismatch", dest->name);
free(verifydata);
return 0;
}
free(verifydata);
return 1;
}
/**
* Processes encryption key information received in a REGISTER message
*/
int handle_register_keys(const struct register_h *reg,
const unsigned char *enckey, int listidx, int hostidx,
uint32_t src)
{
unsigned char *decrypted;
int len;
struct pr_destinfo_t *dest;
dest = &group_list[listidx].destinfo[hostidx];
decrypted = calloc(PUBKEY_LEN, 1);
if (decrypted == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(dest->rand2, reg->rand2, sizeof(dest->rand2));
if (!RSA_decrypt(group_list[listidx].proxykey, enckey,
ntohs(reg->premaster_len), decrypted, &len)) {
log(group_list[listidx].group_id, 0, "Rejecting REGISTER from %s: "
"failed to decrypt premaster secret", dest->name);
free(decrypted);
return 0;
}
if (len > ntohs(reg->premaster_len)) {
log(group_list[listidx].group_id, 0, "Rejecting REGISTER from %s: "
"decrypted premaster secret too long", dest->name);
free(decrypted);
return 0;
}
memcpy(dest->premaster, decrypted, len);
calculate_client_keys(listidx, hostidx);
free(decrypted);
if (dest->pubkey) {
if (!verify_client_key(listidx, hostidx)) {
return 0;
}
}
return 1;
}
/**
* Handles an incoming REGSITER message from a client.
*/
void handle_register(int listidx, int hostidx, const unsigned char *message,
int meslen, uint32_t src)
{
struct register_h *reg;
unsigned char *enckey;
struct pr_destinfo_t *dest;
int dupmsg;
reg = (struct register_h *)message;
enckey = (unsigned char *)reg + sizeof(struct register_h);
if (group_list[listidx].destcount == MAXDEST) {
log(group_list[listidx].group_id, 0, "Rejecting REGISTER from %s: "
"max destinations exceeded", inet_ntoa(to_addr(src)));
send_downstream_abort(listidx, src, "Max destinations exceeded");
return;
}
if (meslen != sizeof(struct register_h) + ntohs(reg->premaster_len) +
(ntohs(reg->destcount) * sizeof(uint32_t))) {
log(group_list[listidx].group_id, 0, "Rejecting REGISTER from %s: "
"invalid message size", inet_ntoa(to_addr(src)));
send_downstream_abort(listidx, src, "Invalid message size");
return;
}
if (hostidx == -1) {
hostidx = add_client(src, listidx);
}
dest = &group_list[listidx].destinfo[hostidx];
dupmsg = (dest->registered == 1);
dest->registered = 1;
if (dest->state != PR_CLIENT_REGISTERED) {
if (group_list[listidx].keytype != KEY_NONE) {
if (!handle_register_keys(reg, enckey, listidx, hostidx, src)) {
return;
}
}
if (!group_list[listidx].client_auth || dest->pubkey) {
dest->state = PR_CLIENT_REGISTERED;
}
}
log(group_list[listidx].group_id, 0, "Received REGISTER%s from %s",
dupmsg ? "+" : "", dest->name);
if (dest->state == PR_CLIENT_REGISTERED) {
check_pending(listidx, hostidx, message);
}
}
/**
* Handles an incoming CLIENT_KEY message from a client.
*/
void handle_clientkey(int listidx, int hostidx, const unsigned char *message,
int meslen, uint32_t src)
{
struct client_key_h *clientkey;
unsigned char *keymod, *verify;
struct pr_destinfo_t *dest;
int dupmsg;
clientkey = (struct client_key_h *)message;
keymod = (unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keymod + ntohs(clientkey->keylen);
if (group_list[listidx].destcount == MAXDEST) {
log(group_list[listidx].group_id, 0, "Rejecting CLIENT_KEY from %s: "
"max destinations exceeded", inet_ntoa(to_addr(src)));
send_downstream_abort(listidx, src, "Max destinations exceeded");
return;
}
if (meslen != sizeof(struct client_key_h) + ntohs(clientkey->keylen) +
ntohs(clientkey->verifylen)) {
log(group_list[listidx].group_id, 0, "Rejecting REGISTER from %s: "
"invalid message size", inet_ntoa(to_addr(src)));
send_downstream_abort(listidx, src, "Invalid message size");
return;
}
if (hostidx == -1) {
hostidx = add_client(src, listidx);
}
dest = &group_list[listidx].destinfo[hostidx];
dupmsg = (dest->pubkey != (RSA_key_t)NULL);
if (!dest->verified) {
if (!import_RSA_key(&dest->pubkey, ntohl(clientkey->keyexp),
keymod, ntohs(clientkey->keylen))) {
log(group_list[listidx].group_id, 0,
"Failed to load client public key");
send_downstream_abort(listidx, src,
"Failed to load client public key");
return;
}
dest->pubkeylen = RSA_keylen(dest->pubkey);
if (!verify_fingerprint(client_fp, client_fp_count,
keymod, clientkey->keyexp,
ntohs(clientkey->keylen), src)) {
log(group_list[listidx].group_id, 0,
"Failed to verify client key fingerprint");
send_downstream_abort(listidx, src,
"Failed to verify client key fingerprint");
return;
}
dest->verified = 1;
}
memcpy(dest->verifydata, verify, ntohs(clientkey->verifylen));
dest->verifylen = ntohs(clientkey->verifylen);
if (dest->registered) {
if (!verify_client_key(listidx, hostidx)) {
return;
}
dest->state = PR_CLIENT_REGISTERED;
}
log(group_list[listidx].group_id, 0, "Received CLIENT_KEY%s from %s",
dupmsg ? "+" : "", dest->name);
if (dest->state == PR_CLIENT_REGISTERED) {
// Pass in a dummy REGISTER message to check_pending, since
// CLIENT_KEY is basically an extention of REGISTER.
// Since only the leading byte is looked at for a REGISTER,
// the dummy message is just a single byte.
unsigned char reg = REGISTER;
check_pending(listidx, hostidx, ®);
}
}
/**
* Handles an incoming INFO_ACK from a client for either KEYINFO or FILEINFO
*/
void handle_info_ack(int listidx, int hostidx,
const unsigned char *message, int meslen)
{
struct infoack_h *infoack;
uint32_t *addr;
unsigned char *verifydata, *verify_hash, *verify_test;
int verifylen, len, clientcnt, dupmsg;
unsigned int hashlen;
struct pr_group_list_t *group;
struct pr_destinfo_t *dest;
infoack = (struct infoack_h *)message;
addr = (uint32_t *)((char *)infoack + sizeof(struct infoack_h));
clientcnt = ntohs(infoack->destcount);
group = &group_list[listidx];
dest = &group->destinfo[hostidx];
if (meslen != sizeof(struct infoack_h) +
(clientcnt * sizeof(uint32_t))) {
log(group->group_id, ntohs(infoack->file_id),
"Rejecting INFO_ACK from %s: invalid message size", dest->name);
send_downstream_abort(listidx, dest->addr.s_addr,
"Invalid message size");
return;
}
// If in response to a KEYINFO, check the verify data
if ((group->keytype != KEY_NONE) && ntohs(infoack->file_id) == 0) {
if (!(verifydata = build_verify_data(listidx, hostidx, &verifylen,1))) {
log(group->group_id, ntohs(infoack->file_id),
"Rejecting INFO_ACK from %s: error exporting "
"client public key", dest->name);
return;
}
verify_hash = calloc(group->hmaclen, 1);
verify_test = calloc(VERIFY_LEN + group->hmaclen, 1);
if ((verify_hash == NULL) || (verify_test == NULL)){
syserror(0, 0, "calloc failed!");
exit(1);
}
hash(group->hashtype, verifydata, verifylen, verify_hash, &hashlen);
PRF(group->hashtype, VERIFY_LEN, group->groupmaster,
sizeof(group->groupmaster), "client finished",
verify_hash, hashlen, verify_test, &len);
if (memcmp(infoack->verify_data, verify_test, VERIFY_LEN)) {
log(group->group_id, ntohs(infoack->file_id),
"Rejecting INFO_ACK from %s: verify data mismatch",
dest->name);
free(verifydata);
free(verify_hash);
free(verify_test);
return;
}
free(verifydata);
free(verify_hash);
free(verify_test);
dupmsg = (dest->state == PR_CLIENT_READY);
log(group->group_id, ntohs(infoack->file_id),
"Received INFO_ACK%s from %s", dupmsg ? "+" : "", dest->name);
dest->state = PR_CLIENT_READY;
if (!check_unfinished_clients(listidx, 0)) {
group->phase = PR_PHASE_RECEIVING;
}
} else {
log(group->group_id, ntohs(infoack->file_id),
"Received INFO_ACK from %s", dest->name);
check_pending(listidx, hostidx, message);
}
}
/**
* Sends a KEYINFO to each client that the server sent a REG_CONF for.
*/
void send_keyinfo(int listidx, uint32_t *addrlist, int addrlen)
{
unsigned char *buf, *iv;
struct uftp_h *header;
struct keyinfo_h *keyinfo;
struct destkey *keylist;
struct timeval tv;
unsigned int payloadlen, len;
int maxdest, packetcnt, dests, foundaddr, i, j;
struct pr_destinfo_t *dest;
struct sockaddr_in sin;
buf = calloc(group_list[listidx].mtu, 1);
iv = calloc(group_list[listidx].ivlen, 1);
if ((buf == NULL) || (iv == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));
set_uftp_header(header, KEYINFO, listidx);
keyinfo->func = KEYINFO;
maxdest = max_msg_dest(listidx, KEYINFO);
packetcnt = 1;
gettimeofday(&tv, NULL);
keyinfo->tstamp_sec = htonl(tv.tv_sec);
keyinfo->tstamp_usec = htonl(tv.tv_usec);
for (i = 0, dests = 0; i < group_list[listidx].destcount; i++) {
dest = &group_list[listidx].destinfo[i];
if (dest->state == PR_CLIENT_CONF) {
if (addrlist) {
// We just got a REG_CONF, so only send to listed hosts
for (j = 0, foundaddr = 0; (j < addrlen) && (!foundaddr); j++) {
if (dest->addr.s_addr == addrlist[j]) {
foundaddr = 1;
}
}
} else {
foundaddr = 1;
}
if (foundaddr) {
keylist[dests].destaddr = dest->addr.s_addr;
build_iv(iv, dest->salt, group_list[listidx].ivlen,
htonl(group_list[listidx].group_id),
group_list[listidx].destaddr.s_addr,
htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(group_list[listidx].keytype, iv, dest->key,
&group_list[listidx].groupmaster[1],
sizeof(group_list[listidx].groupmaster) - 1,
keylist[dests].groupmaster, &len)) {
log(group_list[listidx].group_id, 0,
"Error encrypting KEYINFO for %s", dest->name);
free(buf);
free(iv);
return;
}
if (!keyinfo->groupmaster_len) keyinfo->groupmaster_len = len;
dests++;
}
}
if ((dests >= maxdest) ||
((i == group_list[listidx].destcount - 1) && (dests > 0))) {
payloadlen = sizeof(struct keyinfo_h) +
(dests * sizeof(struct destkey));
header->blsize = htons(payloadlen);
keyinfo->destcount = dests;
log(group_list[listidx].group_id, 0, "Sending KEYINFO %d.%d",
group_list[listidx].keyinfo_cnt, packetcnt);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = group_list[listidx].multi;
sin.sin_port = htons(out_port);
if (nb_sendto(listener, buf, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0,
"Error sending KEYINFO");
free(buf);
free(iv);
return;
}
// TODO: This value is good for around 100Mbps. This is under the
// assumtion that the client proxy is local to the clients
// it serves. This should probably be a parameter.
usleep(120);
memset(keylist, 0, maxdest * sizeof(struct destkey));
dests = 0;
packetcnt++;
}
}
group_list[listidx].keyinfo_cnt++;
set_timeout(listidx, 0);
free(buf);
free(iv);
}
/**
* Handles an incoming STATUS message from a client
*/
void handle_status(int listidx, int hostidx,
const unsigned char *message, int meslen)
{
struct status_h *status;
int mes_pass, mes_section, nak_count;
struct pr_destinfo_t *dest;
status = (struct status_h *)message;
mes_pass = status->pass;
mes_section = ntohs(status->section);
nak_count = ntohl(status->nak_count);
dest = &group_list[listidx].destinfo[hostidx];
if (meslen != sizeof(struct status_h) +
(nak_count ? group_list[listidx].blocksize : 0)) {
log(group_list[listidx].group_id, 0,
"Rejecting STATUS from %s: invalid message size", dest->name);
return;
}
log(group_list[listidx].group_id, ntohs(status->file_id),
"Got %d NAKs for pass %d section %d from %s",
nak_count, mes_pass, mes_section, dest->name);
check_pending(listidx, hostidx, message);
}
/**
* Handles an incoming COMPLETE message from a client
*/
void handle_complete(int listidx, int hostidx,
const unsigned char *message, int meslen)
{
struct complete_h *complete;
struct pr_destinfo_t *dest;
int alldone, i;
complete = (struct complete_h *)message;
dest = &group_list[listidx].destinfo[hostidx];
if (meslen != sizeof(struct complete_h) +
(ntohs(complete->destcount) * sizeof(uint32_t))) {
log(group_list[listidx].group_id, 0,
"Rejecting COMPLETE from %s: invalid message size", dest->name);
return;
}
log(group_list[listidx].group_id, ntohs(complete->file_id),
"Received COMPLETE from %s", dest->name);
if (ntohs(complete->file_id) == 0) {
dest->state = PR_CLIENT_DONE;
for (alldone = 1, i = 0;
(i < group_list[listidx].destcount) && alldone; i++) {
alldone = alldone &&
(group_list[listidx].destinfo[i].state == PR_CLIENT_DONE);
}
if (alldone) {
group_list[listidx].phase = PR_PHASE_DONE;
}
}
check_pending(listidx, hostidx, message);
}
uftp-3.5/proxy_init.c 0000644 0003316 0000550 00000026644 11577014247 013740 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#else // WINDOWS
#include
#include
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_init.h"
#include "proxy_config.h"
#include "proxy_common.h"
static int parent; // is this the parent process that exits after a fork?
/**
* Cleanup routine set up by atexit
*/
void cleanup(void)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id != 0) {
group_cleanup(i);
}
}
if (!parent) {
for (i = 0; i < pub_multi_count; i++) {
multicast_leave(listener, 0, &pub_multi[i], m_interface,
interface_count, server_fp, server_fp_count);
}
}
closesocket(listener);
for (i = 0; i < key_count; i++) {
free_RSA_key(privkey[i]);
}
crypto_cleanup();
#ifdef WINDOWS
WSACleanup();
#endif
fclose(stderr);
}
/**
* Generic signal handler, exits on signal
*/
void gotsig(int sig)
{
log(0, 0, "Exiting on signal %d", sig);
exit(0);
}
/**
* Signal handler for SIGPIPE
*/
void gotpipe(int sig)
{
log(0, 0, "Got SIGPIPE");
}
/**
* Do initial setup before parsing arguments, including getting interface list
*/
void pre_initialize()
{
#ifdef WINDOWS
struct WSAData data;
if (WSAStartup(2, &data)) {
fprintf(stderr, "Error in WSAStartup: %d\n", WSAGetLastError());
exit(1);
}
#endif
applog = stderr;
ifl_len = sizeof(ifl) / sizeof(struct iflist);
getiflist(ifl, &ifl_len);
}
/**
* Set up log file and run in the backgroud
*/
void daemonize()
{
#ifdef WINDOWS
if (!debug) {
int fd;
FILE *pidfh;
if ((fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) {
perror("Can't open log file");
exit(1);
}
if (strcmp(pidfile, "")) {
// Write out the pid file, before we redirect STDERR to the log.
if ((pidfh = fopen(pidfile, "w")) == NULL) {
perror("Can't open pid file for writing");
exit(1);
}
fprintf(pidfh, "%d\n", GetCurrentProcessId());
fclose(pidfh);
}
dup2(fd, 2);
close(fd);
applog = stderr;
}
#else // WINDOWS
if (!debug) {
int pid, fd;
FILE *pidfh;
if ((fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) {
perror("Can't open log file");
exit(1);
}
if ((pid = fork()) == -1) {
perror("Couldn't fork");
exit(1);
} else if (pid > 0) {
parent = 1;
exit(0);
}
if (strcmp(pidfile, "")) {
// Write out the pid file, before we redirect STDERR to the log.
if ((pidfh = fopen(pidfile, "w")) == NULL) {
perror("Can't open pid file for writing");
exit(1);
}
fprintf(pidfh, "%d\n", getpid());
fclose(pidfh);
}
setsid();
dup2(fd, 2);
close(fd);
for (fd = 0; fd < 30; fd++) {
if ((fd != 2) && (fd != listener)) {
close(fd);
}
}
#ifdef VMS
chdir("SYS$LOGIN");
#else
chdir("/");
#endif
umask(0);
applog = stderr;
}
nice(-20);
{
struct sigaction act;
sigfillset(&act.sa_mask);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
act.sa_handler = gotsig;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
act.sa_handler = gotpipe;
sigaction(SIGPIPE, &act, NULL);
act.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &act, NULL);
}
#endif // WINDOWS
}
/**
* Initialize crypto library, generate keys
*/
void key_init()
{
#ifndef NO_ENCRYPTION
int i;
crypto_init(sys_keys);
if ((keyfile_count == 0) || (newkeylen != 0)) {
privkey[0] = gen_RSA_key(newkeylen, RSA_EXP, keyfile[0]);
if (!privkey[0]) {
exit(1);
}
key_count = 1;
} else {
for (i = 0; i < keyfile_count; i++) {
privkey[key_count] = read_RSA_key(keyfile[i]);
if (!privkey[key_count]) {
exit(1);
}
key_count++;
}
}
#endif
}
/**
* Do all socket creation and initialization
*/
void create_sockets()
{
struct sockaddr_in sin;
int fdflag, found_if, i;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(port);
if ((listener = socket(AF_INET,SOCK_DGRAM,0)) == INVALID_SOCKET) {
sockerror(0, 0, "Error creating socket for listener");
exit(1);
}
if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
sockerror(0, 0, "Error binding socket for listener");
exit(1);
}
#ifndef BLOCKING
#ifdef WINDOWS
fdflag = 1;
if (ioctlsocket(listener, FIONBIO, &fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(listener);
exit(1);
}
#else
if ((fdflag = fcntl(listener, F_GETFL)) == SOCKET_ERROR) {
sockerror(0, 0, "Error getting socket descriptor flags");
closesocket(listener);
exit(1);
}
fdflag |= O_NONBLOCK;
if (fcntl(listener, F_SETFL, fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(listener);
exit(1);
}
#endif
#endif // BLOCKING
if (setsockopt(listener, IPPROTO_IP, IP_TOS, (char *)&dscp,
sizeof(dscp)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting dscp");
closesocket(listener);
exit(1);
}
if (buffer) {
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
if (setsockopt(listener, SOL_SOCKET, SO_SNDBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting send buffer size");
exit(1);
}
} else {
buffer = DEF_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
buffer = DEF_BSD_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_RCVBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
}
buffer = DEF_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_SNDBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
buffer = DEF_BSD_RCVBUF;
if (setsockopt(listener, SOL_SOCKET, SO_SNDBUF,
(char *)&buffer, sizeof(buffer)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting send buffer size");
exit(1);
}
}
}
if (setsockopt(listener, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof(ttl)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting ttl");
closesocket(listener);
exit(1);
}
if (out_addr.s_addr == INADDR_NONE) {
for (i = 0, found_if = 0; (i < ifl_len) && !found_if; i++) {
if (!ifl[i].isloopback) {
found_if = 1;
out_addr.s_addr = ifl[i].addr.s_addr;
}
}
if (!found_if) {
if (ifl_len > 0) {
out_addr.s_addr = ifl[0].addr.s_addr;
} else {
fprintf(stderr, "ERROR: no network interfaces found!\n");
exit(1);
}
}
}
if (setsockopt(listener, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,
sizeof(out_addr)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting outgoing interface");
closesocket(listener);
exit(1);
}
for (i = 0; i < pub_multi_count; i++) {
if (!multicast_join(listener, 0, &pub_multi[i], m_interface,
interface_count, server_fp, server_fp_count)) {
exit(1);
}
}
}
/**
* Initialization based on command line args
*/
void initialize()
{
char hostname[256];
struct hostent *hp;
struct in_addr *addr;
int i;
parent = 0;
// Load list of multicast interfaces
if (interface_count == 0) {
for (i = 0; i < ifl_len; i++) {
if (!ifl[i].isloopback) {
m_interface[interface_count++] = ifl[i];
}
}
}
// No non-loopback interfaces, so just use the hostname's interface
if (interface_count == 0) {
gethostname(hostname, sizeof(hostname));
if ((hp = gethostbyname(hostname)) == NULL) {
fprintf(stderr, "Can't get host name\n");
exit(1);
} else {
addr = (struct in_addr *)hp->h_addr_list[0];
m_interface[interface_count].addr = *addr;
m_interface[interface_count].ismulti = 1;
m_interface[interface_count++].isloopback = 0;
}
}
if (uid) {
m_interface[interface_count].addr.s_addr = uid;
m_interface[interface_count].ismulti = 0;
m_interface[interface_count++].isloopback = 0;
}
if (proxy_type == CLIENT_PROXY) {
pub_multi_count = 0;
} else if (!pub_multi_count) {
pub_multi[0].s_addr = inet_addr(DEF_PUB_MULTI);
pub_multi_count = 1;
}
for (i = 0; i < MAXLIST; i++) {
group_list[i].group_id = 0;
}
next_hb_time.tv_sec = 0;
next_hb_time.tv_usec = 0;
last_key_req.tv_sec = 0;
last_key_req.tv_usec = 0;
atexit(cleanup);
key_init();
create_sockets();
daemonize();
showtime = 1;
}
uftp-3.5/proxy_loop.c 0000644 0003316 0000550 00000027546 11577014247 013750 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else // WINDOWS
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_common.h"
#include "proxy_loop.h"
#include "proxy_upstream.h"
#include "proxy_downstream.h"
#include "heartbeat_send.h"
/**
* Gets the current timeout value to use for the main loop
*
* First check to see if any active groups have an expired timeout, and
* handle that timeout. Once all expired timeouts have been handled, find
* the active group with the earliest timeout and return the time until that
* timeout. If there are no active groups, return NULL.
*/
struct timeval *getrecenttimeout()
{
static struct timeval tv = {0,0};
struct timeval current_timestamp, min_timestamp;
int i, j, found_timeout, recheck, pending;
int32_t usecs;
gettimeofday(¤t_timestamp, NULL);
recheck = 1;
while (recheck) {
found_timeout = 0;
recheck = 0;
// First check group timeouts
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id != 0) {
if ((group_list[i].phase == PR_PHASE_REGISTERED) ||
(group_list[i].phase == PR_PHASE_READY)) {
if ((group_list[i].phase == PR_PHASE_READY) &&
(cmptimestamp(current_timestamp,
group_list[i].phase_timeout_time) >= 0)) {
send_keyinfo(i, NULL, 0);
recheck = 1;
}
if (cmptimestamp(current_timestamp,
group_list[i].phase_expire_time) >= 0) {
group_list[i].phase = PR_PHASE_RECEIVING;
check_unfinished_clients(i, 1);
}
}
if (cmptimestamp(current_timestamp,
group_list[i].timeout_time) >= 0) {
// If at least one message is pending, timeout_time is
// time to next send of the specified message.
// Otherwise it's the overall timeout.
for (pending = 0, j = 0; (j < MAX_PEND) && !pending; j++) {
if (group_list[i].pending[j].msg != 0) {
pending = 1;
}
}
if (pending) {
send_all_pending(i);
} else {
log(group_list[i].group_id, 0, "Group timed out");
group_cleanup(i);
}
recheck = 1;
}
if (!recheck && ((!found_timeout) ||
(cmptimestamp(group_list[i].timeout_time,
min_timestamp) < 0))) {
min_timestamp = group_list[i].timeout_time;
found_timeout = 1;
}
}
}
// Then check timeout for sending heartbeat
if (hbhost_count) {
if (cmptimestamp(current_timestamp, next_hb_time) >= 0) {
send_hb_request(listener, hb_hosts, hbhost_count,
&next_hb_time, hb_interval);
recheck = 1;
} else if ((!found_timeout) ||
(cmptimestamp(next_hb_time, min_timestamp) < 0)) {
min_timestamp = next_hb_time;
found_timeout = 1;
}
}
}
if (found_timeout) {
usecs = (int32_t)diff_usec(min_timestamp, current_timestamp);
tv.tv_sec = usecs / 1000000;
tv.tv_usec = usecs % 1000000;
return &tv;
} else {
return NULL;
}
}
/**
* This is the main message reading loop. Messages are read, validated,
* decrypted if necessary, then passed to the appropriate routine for handling.
*/
void mainloop()
{
struct uftp_h *header;
unsigned char *buf, *decrypted, *message;
int packetlen, listidx, hostidx, i;
unsigned int decryptlen, meslen;
uint8_t *func;
struct sockaddr_in src;
struct timeval *tv;
const int bsize = 9000; // Roughly size of ethernet jumbo frame
log0(0, 0, "%s", VERSIONSTR);
for (i = 0; i < key_count; i++) {
log(0, 0, "Loaded key with fingerprint %s",
print_key_fingerprint(privkey[i]));
}
buf = calloc(bsize, 1);
decrypted = calloc(bsize, 1);
if ((buf == NULL) || (decrypted == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
while (1) {
tv = getrecenttimeout();
if (read_packet(listener, &src, buf, &packetlen,
bsize, tv) <= 0) {
continue;
}
if ((header->uftp_id != UFTP_VER_NUM) &&
(header->uftp_id != UFTP_3_0_VER)) {
log(0, 0, "Invalid message from %s: not uftp packet "
"or invalid version", inet_ntoa(src.sin_addr));
continue;
}
if (packetlen != sizeof(struct uftp_h) + ntohs(header->blsize)) {
log(0, 0, "Invalid packet size from %s: got %d, expected %d",
inet_ntoa(src.sin_addr), packetlen,
sizeof(struct uftp_h) + ntohs(header->blsize));
continue;
}
if ((src.sin_addr.s_addr == out_addr.s_addr) &&
(src.sin_port == htons(port))) {
// Packet from self -- drop
continue;
}
if (header->func == HB_REQ) {
handle_hb_request(&src, buf);
continue;
}
if (header->func == HB_RESP) {
handle_hb_response(listener, &src, buf, hb_hosts, hbhost_count,
noname, privkey[0]);
continue;
}
if (header->func == KEY_REQ) {
handle_key_req(&src, buf);
continue;
}
if ((proxy_type == SERVER_PROXY) &&
(down_addr.sin_addr.s_addr == INADDR_ANY)) {
log(0, 0, "Rejecting message from %s: downstream address "
"not established", inet_ntoa(src.sin_addr));
continue;
}
listidx = find_group(ntohl(header->group_id));
if (header->func == ANNOUNCE) {
handle_announce(listidx, &src, buf);
} else {
if (listidx == -1) {
continue;
}
if (proxy_type == SERVER_PROXY) {
// Server proxies don't do anything outside of an ANNOUNCE.
// Just send it on through.
forward_message(listidx, &src, buf);
continue;
}
if (header->func == ABORT) {
handle_abort(listidx, &src, buf);
continue;
}
if (!memcmp(&src, &group_list[listidx].up_addr, sizeof(src))) {
// Downstream message
if (header->func == KEYINFO) {
handle_keyinfo(listidx, buf);
} else if ((header->func == REG_CONF) &&
(group_list[listidx].keytype != KEY_NONE)) {
handle_regconf(listidx, buf);
} else {
// If we don't need to process the message, don't bother
// decrypting anything. Just forward it on.
forward_message(listidx, &src, buf);
}
} else {
// Upstream message
// Decrypt first if necessary
hostidx = find_client(listidx, header->srcaddr);
if ((hostidx != -1) && (header->func == ENCRYPTED) &&
(group_list[listidx].keytype != KEY_NONE)) {
if (!validate_and_decrypt(buf, &decrypted, &decryptlen,
group_list[listidx].mtu,group_list[listidx].keytype,
group_list[listidx].groupkey,
group_list[listidx].groupsalt,
group_list[listidx].ivlen,
group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen,
group_list[listidx].sigtype,
group_list[listidx].destinfo[hostidx].pubkey,
group_list[listidx].destinfo[hostidx].pubkeylen)) {
log(ntohl(header->group_id), 0, "Rejecting message "
"from %s: decrypt/validate failed",
inet_ntoa(src.sin_addr));
continue;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((hostidx != -1) &&
(group_list[listidx].keytype != KEY_NONE) &&
((header->func == INFO_ACK) ||
(header->func == STATUS) ||
(header->func == COMPLETE))) {
log(ntohl(header->group_id), 0, "Rejecting %s message "
"from %s: not encrypted",
func_name(header->func),
inet_ntoa(src.sin_addr));
continue;
}
func = (uint8_t *)&header->func;
message = buf + sizeof(struct uftp_h);
meslen = ntohs(header->blsize);
}
switch (*func) {
case REGISTER:
handle_register(listidx, hostidx, message, meslen,
header->srcaddr);
break;
case CLIENT_KEY:
handle_clientkey(listidx, hostidx, message, meslen,
header->srcaddr);
break;
case INFO_ACK:
handle_info_ack(listidx, hostidx, message, meslen);
break;
case STATUS:
handle_status(listidx, hostidx, message, meslen);
break;
case COMPLETE:
handle_complete(listidx, hostidx, message, meslen);
break;
default:
forward_message(listidx, &src, buf);
break;
}
}
}
}
}
uftp-3.5/proxy_main.c 0000644 0003316 0000550 00000003036 11346327251 013703 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2010 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "proxy_config.h"
#include "proxy_init.h"
#include "proxy_loop.h"
int main(int argc, char *argv[])
{
pre_initialize();
process_args(argc, argv);
initialize();
mainloop();
return 0;
}
uftp-3.5/proxy_upstream.c 0000644 0003316 0000550 00000116644 11577014250 014627 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include "win_func.h"
#else
#include
#include
#include
#include
#include
#endif
#include "proxy.h"
#include "proxy_common.h"
#include "proxy_upstream.h"
#include "proxy_downstream.h"
/**
* Finds next open slot in the global group list.
* Returns the index of the open slot, or -1 if none found.
*/
int find_open_slot()
{
int i;
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id == 0) {
memset(&group_list[i], 0, sizeof(group_list[i]));
return i;
}
}
return -1;
}
/**
* Read in the contents of an ANNOUNCE.
*/
void read_announce(int listidx, const unsigned char *buf,
const struct sockaddr_in *src)
{
struct uftp_h *header;
struct announce_h *announce;
//int i;
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
group_list[listidx].group_id = ntohl(header->group_id);
group_list[listidx].up_addr = *src;
group_list[listidx].srcaddr.s_addr = header->srcaddr;
group_list[listidx].announce_int = ntohs(announce->announce_int);
group_list[listidx].status_int = ntohs(announce->status_int);
group_list[listidx].register_int = ntohs(announce->register_int);
group_list[listidx].done_int = ntohs(announce->done_int);
group_list[listidx].announce_time = announce->announce_time;
group_list[listidx].status_time = announce->status_time;
group_list[listidx].mtu = ntohs(announce->mtu);
group_list[listidx].payloadsize =
group_list[listidx].mtu - 28 - sizeof(struct uftp_h);
group_list[listidx].encpayloadsize = group_list[listidx].payloadsize;
group_list[listidx].blocksize = group_list[listidx].payloadsize -
sizeof(struct fileseg_h);
/*
for (i = 0; i < MAX_PEND; i++) {
group_list[listidx].pending[i].naklist =
calloc(group_list[listidx].blocksize, 1);
if (group_list[listidx].pending[i].naklist == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
}
*/
group_list[listidx].client_auth = announce->client_auth;
group_list[listidx].hashtype = announce->hashtype;
group_list[listidx].keytype = announce->keytype;
group_list[listidx].sigtype = announce->sigtype;
group_list[listidx].multi.s_addr = announce->privatemcast;
gettimeofday(&group_list[listidx].timeout_time, NULL);
group_list[listidx].timeout_time.tv_sec += announce->status_time;
gettimeofday(&group_list[listidx].phase_expire_time, NULL);
group_list[listidx].phase_expire_time.tv_sec +=
group_list[listidx].announce_time;
group_list[listidx].version = header->uftp_id;
}
/**
* Calculate the master key and do key expansion to determine the symmetric
* cypher key and IV salt, and hash key for the server
*/
int calculate_server_keys(int listidx, const struct announce_h *announce)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
time_t t;
uint32_t t2;
memcpy(group_list[listidx].rand1, announce->rand1, sizeof(announce->rand1));
if (!get_random_bytes(group_list[listidx].rand2,
sizeof(group_list[listidx].rand2))) {
log(group_list[listidx].group_id, 0,
"Failed to get random bytes for rand2");
send_upstream_abort(listidx, 0, "Failed to get random bytes for rand2");
return 0;
}
// Sets the first 4 bytes of rand2 to the current time
t = time(NULL);
t2 = (uint32_t)(t & 0xFFFFFFFF);
*(uint32_t *)(group_list[listidx].rand2) = t2;
if (!get_random_bytes(group_list[listidx].premaster,
sizeof(group_list[listidx].premaster))) {
log(group_list[listidx].group_id, 0,
"Failed to get random bytes for premaster");
send_upstream_abort(listidx, 0,
"Failed to get random bytes for premaster");
return 0;
}
get_key_info(group_list[listidx].keytype,
&group_list[listidx].keylen, &group_list[listidx].ivlen);
group_list[listidx].hmaclen = get_hash_len(group_list[listidx].hashtype);
explen = group_list[listidx].keylen + group_list[listidx].ivlen +
group_list[listidx].hmaclen;
seedlen = RAND_LEN * 2;
seed = calloc(seedlen, 1);
prf_buf = calloc(MASTER_LEN + explen + group_list[listidx].hmaclen, 1);
if ((seed == NULL) || (prf_buf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(seed, group_list[listidx].rand1, sizeof(group_list[listidx].rand1));
memcpy(seed + sizeof(group_list[listidx].rand1), group_list[listidx].rand2,
sizeof(group_list[listidx].rand2));
PRF(group_list[listidx].hashtype, MASTER_LEN, group_list[listidx].premaster,
sizeof(group_list[listidx].premaster),
"master secret", seed, seedlen, prf_buf, &len);
memcpy(group_list[listidx].master,prf_buf,
sizeof(group_list[listidx].master));
PRF(group_list[listidx].hashtype, explen, group_list[listidx].master,
sizeof(group_list[listidx].master), "key expansion",
seed, seedlen, prf_buf, &len);
memcpy(group_list[listidx].hmackey, prf_buf, group_list[listidx].hmaclen);
memcpy(group_list[listidx].key, prf_buf + group_list[listidx].hmaclen,
group_list[listidx].keylen);
memcpy(group_list[listidx].salt, prf_buf + group_list[listidx].hmaclen +
group_list[listidx].keylen, group_list[listidx].ivlen);
free(seed);
free(prf_buf);
return 1;
}
/**
* Read encryption related fields from an ANNOUNCE
*/
int handle_announce_encryption(int listidx, struct announce_h *announce,
unsigned char *keymod)
{
int i;
// Sanity check the selected encryption parameters
if ((group_list[listidx].keytype != KEY_DES) &&
(group_list[listidx].keytype != KEY_DES_EDE3) &&
(group_list[listidx].keytype != KEY_AES128) &&
(group_list[listidx].keytype != KEY_AES256)) {
log(group_list[listidx].group_id, 0, "Invalid keytype specified");
send_upstream_abort(listidx, 0, "Invalid keytype specified");
return 0;
}
if (!cipher_supported(group_list[listidx].keytype)) {
log(group_list[listidx].group_id, 0, "Keytype not supported here");
send_upstream_abort(listidx, 0, "Keytype not supported here");
return 0;
}
if ((group_list[listidx].hashtype != HASH_SHA1) &&
(group_list[listidx].hashtype != HASH_SHA256)) {
log(group_list[listidx].group_id, 0, "Invalid hashtype specified");
send_upstream_abort(listidx, 0, "Invalid hashtype specified");
return 0;
}
if (!hash_supported(group_list[listidx].hashtype)) {
log(group_list[listidx].group_id, 0, "Hashtype not supported here");
send_upstream_abort(listidx, 0, "Hashtype not supported here");
return 0;
}
if ((group_list[listidx].sigtype != SIG_HMAC) &&
(group_list[listidx].sigtype != SIG_RSA)) {
log(group_list[listidx].group_id, 0, "Invalid sigtype specified");
send_upstream_abort(listidx, 0, "Invalid sigtype specified");
return 0;
}
// Load server key and select a matching client key
if (!import_RSA_key(&group_list[listidx].serverkey, ntohl(announce->keyexp),
keymod, ntohs(announce->keylen))) {
log(group_list[listidx].group_id, 0,
"Failed to load server public key");
send_upstream_abort(listidx, 0, "Failed to load server public key");
return 0;
}
if (!verify_fingerprint(server_fp, server_fp_count,
keymod, announce->keyexp, ntohs(announce->keylen),
group_list[listidx].srcaddr.s_addr)) {
log(group_list[listidx].group_id, 0,
"Failed to verify server key fingerprint");
send_upstream_abort(listidx, 0, "Failed to verify server key fingerprint");
return 0;
}
group_list[listidx].server_keylen=RSA_keylen(group_list[listidx].serverkey);
for (i = 0; i < key_count; i++) {
if (RSA_keylen(privkey[i]) == group_list[listidx].server_keylen) {
group_list[listidx].proxykey = privkey[i];
group_list[listidx].proxy_keylen = RSA_keylen(privkey[i]);
break;
}
}
if (!group_list[listidx].proxykey) {
log(group_list[listidx].group_id, 0,
"No proxy key with keysize == server keysize of %d",
group_list[listidx].server_keylen * 8);
send_upstream_abort(listidx, 0,
"No proxy key with keysize == server keysize");
return 0;
}
// Calculate keys
if (!calculate_server_keys(listidx, announce)) {
return 0;
}
// Reset payload and block sizes
group_list[listidx].encpayloadsize = group_list[listidx].payloadsize -
sizeof(struct encrypted_h) - KEYBLSIZE -
((group_list[listidx].sigtype == SIG_RSA) ?
group_list[listidx].server_keylen :
group_list[listidx].hmaclen );
group_list[listidx].blocksize = group_list[listidx].encpayloadsize -
sizeof(struct fileseg_h);
return 1;
}
/**
* Inserts the proxy's public key into an ANNOUNCE
* Returns 1 on success, 0 on fail
*/
int insert_pubkey_in_announce(int listidx, unsigned char *packet)
{
struct announce_h *announce;
unsigned char *keymod;
uint32_t pr_keyexp;
uint16_t pr_keylen;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
keymod = (unsigned char *)announce + sizeof(struct announce_h);
if ((group_list[listidx].keytype != KEY_NONE) &&
(proxy_type == CLIENT_PROXY)) {
// Plug in proxy's public key for server's
if (!export_RSA_key(group_list[listidx].proxykey,
&pr_keyexp, keymod, &pr_keylen)) {
log(group_list[listidx].group_id, 0,
"Error exporting proxy public key");
return 0;
}
if (pr_keylen != group_list[listidx].server_keylen) {
log(group_list[listidx].group_id, 0,
"Incorrect exported proxy key size");
return 0;
}
announce->keylen = htons(pr_keylen);
announce->keyexp = htonl(pr_keyexp);
}
return 1;
}
/**
* Handles an incoming ANNOUNCE message from a server.
* Sets up encryption if specified and forwards message.
*/
void handle_announce(int listidx, const struct sockaddr_in *src,
unsigned char *packet)
{
struct uftp_h *header;
struct announce_h *announce;
unsigned char *keymod;
uint32_t *addrlist;
struct hostent *hp;
int addrlen, open, addridx, i;
header = (struct uftp_h *)packet;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
keymod = (unsigned char *)announce + sizeof(struct announce_h);
addrlist = (uint32_t *)(keymod + ntohs(announce->keylen));
addrlen = ntohs(announce->destcount);
open = (addrlen == 0);
if ((listidx != -1) && (open || group_list[listidx].foundaddr)) {
if (insert_pubkey_in_announce(listidx, packet)) {
forward_message(listidx, src, packet);
}
return;
}
if (listidx == -1) {
if ((listidx = find_open_slot()) == -1 ) {
log(ntohl(header->group_id), 0, "Error: maximum number of "
"incoming files exceeded: %d\n", MAXLIST);
return;
}
read_announce(listidx, packet, src);
if (!noname && (hp =
gethostbyaddr((char *)&(group_list[listidx].srcaddr),
sizeof(struct in_addr), AF_INET))) {
log(group_list[listidx].group_id, 0,
"Received request from %s (%s)",
hp->h_name, inet_ntoa(group_list[listidx].srcaddr));
} else {
log(group_list[listidx].group_id, 0,
"Received request from %s",
inet_ntoa(group_list[listidx].srcaddr));
}
log(group_list[listidx].group_id, 0,
"Using private multicast address %s",
inet_ntoa(group_list[listidx].multi));
if ((group_list[listidx].keytype != KEY_NONE) &&
(proxy_type != SERVER_PROXY)) {
if (!handle_announce_encryption(listidx, announce, keymod)) {
return;
}
}
if ((group_list[listidx].multi.s_addr != 0) &&
(proxy_type != CLIENT_PROXY)) {
if (server_fp_count) {
if (!is_multicast(group_list[listidx].multi, 1)) {
log(group_list[listidx].group_id, 0,
"Invalid source specific multicast address: %s",
inet_ntoa(group_list[listidx].multi));
send_upstream_abort(listidx, 0,
"Invalid source specific multicast address");
return;
}
} else {
if (!is_multicast(group_list[listidx].multi, 0)) {
log(group_list[listidx].group_id, 0,
"Invalid multicast address: %s",
inet_ntoa(group_list[listidx].multi));
send_upstream_abort(listidx,0,"Invalid multicast address");
return;
}
}
if (!multicast_join(listener, group_list[listidx].group_id,
&group_list[listidx].multi, m_interface, interface_count,
server_fp, server_fp_count)) {
send_upstream_abort(listidx,0,"Error joining multicast group");
return;
}
group_list[listidx].multi_join = 1;
}
}
if (!open) {
addridx = addr_in_list(addrlist, addrlen);
} else {
// Look for the first non-loopback interface or the UID
addridx = 0;
for (i = 0; i < interface_count; i++) {
if (uid) {
if (m_interface[i].addr.s_addr == uid) {
addridx = i;
break;
}
} else if (!m_interface[i].isloopback) {
addridx = i;
break;
}
}
}
if (addridx == -1) {
// The ANNOUNCE that contains our address may not have been received
// yet, so just grab the first non-loopback interface or the UID
// and hope it matches up.
addridx = 0;
for (i = 0; i < interface_count; i++) {
if (uid) {
if (m_interface[i].addr.s_addr == uid) {
addridx = i;
break;
}
} else if (!m_interface[i].isloopback) {
addridx = i;
break;
}
}
group_list[listidx].destaddr = m_interface[addridx].addr;
group_list[listidx].foundaddr = 0;
} else {
group_list[listidx].destaddr = m_interface[addridx].addr;
group_list[listidx].foundaddr = 1;
}
group_list[listidx].phase = PR_PHASE_REGISTERED;
if (insert_pubkey_in_announce(listidx, packet)) {
forward_message(listidx, src, packet);
}
}
/**
* Handles in incoming REG_CONF from a server when encryption is enabled.
* Upon receiving this message, mark all clients listed as having received.
* If we got a KEYINFO from the server, send a KEYINFO to all marked clients.
*/
void handle_regconf(int listidx, const unsigned char *packet)
{
struct uftp_h *header;
struct regconf_h *regconf;
uint32_t *addrlist;
int hostidx, idx;
struct pr_destinfo_t *dest;
header = (struct uftp_h *)packet;
regconf = (struct regconf_h *)(packet + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)regconf + sizeof(struct regconf_h));
if (ntohs(header->blsize) != sizeof(struct regconf_h) +
(ntohs(regconf->destcount) * sizeof(uint32_t))) {
log(group_list[listidx].group_id, 0,
"Rejecting REG_CONF from server: invalid message size");
return;
}
log(group_list[listidx].group_id, 0, "Received REG_CONF");
for (idx = 0; idx < ntohs(regconf->destcount); idx++) {
hostidx = find_client(listidx, addrlist[idx]);
if (hostidx != -1) {
dest = &group_list[listidx].destinfo[hostidx];
log(group_list[listidx].group_id, 0, " for %s", dest->name);
if (dest->state != PR_CLIENT_READY) {
dest->state = PR_CLIENT_CONF;
}
}
}
if (group_list[listidx].phase == PR_PHASE_READY) {
send_keyinfo(listidx, addrlist, ntohs(regconf->destcount));
}
set_timeout(listidx, 0);
}
/**
* Handles an incoming KEYINFO message from a server.
* Expected in response to a REGISTER when encryption is enabled. The proxy
* itself should be specified, not any clients behind it.
*/
void handle_keyinfo(int listidx, const unsigned char *packet)
{
struct uftp_h *header;
struct keyinfo_h *keyinfo;
struct destkey *keylist;
unsigned explen, declen;
int i, j, keyidx, len;
uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv;
header = (struct uftp_h *)packet;
keyinfo = (struct keyinfo_h *)(packet + sizeof(struct uftp_h));
keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));
if (ntohs(header->blsize) != sizeof(struct keyinfo_h) +
(keyinfo->destcount * sizeof(struct destkey))) {
log(group_list[listidx].group_id, 0,
"Rejecting KEYINFO from server: invalid message size");
return;
}
// This duplicates addr_in_list, but here it's addressed in a struct array
for (i = 0, keyidx = -1; (i < keyinfo->destcount) &&
(keyidx == -1); i++) {
for (j = 0; j < interface_count; j++) {
if (keylist[i].destaddr == m_interface[j].addr.s_addr) {
keyidx = i;
break;
}
}
}
if (keyidx != -1) {
log(group_list[listidx].group_id, 0, "Received KEYINFO");
if (group_list[listidx].phase != PR_PHASE_REGISTERED) {
// We already got the KEYINFO, so no need to reprocess.
// Just resend the INFO_ACK and reset the timeout
send_info_ack(listidx, -1, KEYINFO);
return;
}
iv = calloc(group_list[listidx].ivlen, 1);
if (iv == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
build_iv(iv, group_list[listidx].salt, group_list[listidx].ivlen,
htonl(group_list[listidx].group_id), header->srcaddr,
keyinfo->tstamp_sec, keyinfo->tstamp_usec);
if (!decrypt_block(group_list[listidx].keytype, iv,
group_list[listidx].key, keylist[keyidx].groupmaster,
keyinfo->groupmaster_len, decgroupmaster, &declen) ||
(declen != MASTER_LEN - 1)) {
log(group_list[listidx].group_id, 0,
"Decrypt failed for group master");
send_upstream_abort(listidx, 0, "Decrypt failed for group master");
free(iv);
return;
}
free(iv);
group_list[listidx].groupmaster[0] = header->uftp_id;
memcpy(&group_list[listidx].groupmaster[1], decgroupmaster, declen);
explen = group_list[listidx].keylen + group_list[listidx].ivlen +
group_list[listidx].hmaclen;
prf_buf = calloc(explen + group_list[listidx].hmaclen, 1);
if (prf_buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
PRF(group_list[listidx].hashtype, explen,
group_list[listidx].groupmaster,
sizeof(group_list[listidx].groupmaster), "key expansion",
group_list[listidx].rand1, sizeof(group_list[listidx].rand1),
prf_buf, &len);
memcpy(group_list[listidx].grouphmackey, prf_buf,
group_list[listidx].hmaclen);
memcpy(group_list[listidx].groupkey,
prf_buf + group_list[listidx].hmaclen,
group_list[listidx].keylen);
memcpy(group_list[listidx].groupsalt,
prf_buf + group_list[listidx].hmaclen +
group_list[listidx].keylen, group_list[listidx].ivlen);
free(prf_buf);
group_list[listidx].phase = PR_PHASE_READY;
// Respond to server, then send any pending REG_CONFs as KEYINFO
send_info_ack(listidx, -1, KEYINFO);
send_keyinfo(listidx, NULL, 0);
}
}
/**
* Sends a REGISTER to the server for all pending clients.
*/
void send_register(int listidx, int pendidx)
{
struct uftp_h *header;
struct register_h *reg;
unsigned char *buf, *premaster;
uint32_t *addrlist;
int len, meslen, destcount;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
reg = (struct register_h *)(buf + sizeof(struct uftp_h));
premaster = (unsigned char *)reg + sizeof(struct register_h);
set_uftp_header(header, REGISTER, listidx);
reg->func = REGISTER;
if (group_list[listidx].keytype != KEY_NONE) {
memcpy(reg->rand2, group_list[listidx].rand2, RAND_LEN);
if (!RSA_encrypt(group_list[listidx].serverkey,
group_list[listidx].premaster, MASTER_LEN,
premaster, &len)) {
log(group_list[listidx].group_id, 0,
"Error encrypting premaster secret");
send_upstream_abort(listidx, 0,"Error encrypting premaster secret");
free(buf);
return;
}
reg->premaster_len = htons(len);
} else {
len = 0;
}
addrlist = (uint32_t *)(premaster + len);
destcount = load_pending(listidx, pendidx, REGISTER, addrlist,
max_msg_dest(listidx, REGISTER));
reg->destcount = htons(destcount);
header->blsize = htons(sizeof(struct register_h) + len +
(destcount * sizeof(uint32_t)));
meslen = sizeof(struct uftp_h) + ntohs(header->blsize);
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group_list[listidx].up_addr),
sizeof(group_list[listidx].up_addr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0, "Error sending REGISTER");
} else {
log(group_list[listidx].group_id, 0, "REGISTER sent");
}
if (group_list[listidx].client_auth) {
send_clientkey(listidx);
}
set_timeout(listidx, 1);
free(buf);
}
/**
* Sends a CLIENT_KEY message to the server if requested.
*/
void send_clientkey(int listidx)
{
struct uftp_h *header;
struct client_key_h *client_key;
unsigned char *buf, *keymod, *verify;
uint32_t exponent;
uint16_t modlen;
uint8_t modulus[PUBKEY_LEN];
uint8_t *verifydata;
unsigned int siglen, meslen;
int verifylen;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h));
keymod = (unsigned char *)client_key + sizeof(struct client_key_h);
verify = keymod + group_list[listidx].proxy_keylen;
set_uftp_header(header, CLIENT_KEY, listidx);
client_key->func = CLIENT_KEY;
if (!export_RSA_key(group_list[listidx].proxykey, &exponent,
modulus, &modlen)) {
log(group_list[listidx].group_id, 0, "Error exporting public key");
send_upstream_abort(listidx, 0, "Error exporting public key");
free(buf);
return;
}
client_key->keyexp = htonl(exponent);
memcpy(keymod, modulus, group_list[listidx].proxy_keylen);
client_key->keylen = htons(modlen);
verifydata = build_verify_data(listidx, -1, &verifylen, 0);
if (!verifydata) {
log(group_list[listidx].group_id, 0, "Error getting verify data");
send_upstream_abort(listidx, 0, "Error getting verify data");
free(verifydata);
free(buf);
return;
}
if (!create_RSA_sig(group_list[listidx].proxykey,
group_list[listidx].hashtype, verifydata,
verifylen, verify, &siglen) ||
siglen > group_list[listidx].proxy_keylen) {
log(group_list[listidx].group_id, 0, "Error signing verify data");
send_upstream_abort(listidx, 0, "Error signing verify data");
free(verifydata);
free(buf);
return;
}
free(verifydata);
client_key->verifylen = htons(siglen);
header->blsize = htons(sizeof(struct client_key_h) + modlen + siglen);
meslen = sizeof(struct uftp_h) + ntohs(header->blsize);
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group_list[listidx].up_addr),
sizeof(group_list[listidx].up_addr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, 0, "Error sending CLIENT_KEY");
} else {
log(group_list[listidx].group_id, 0, "CLIENT_KEY sent");
}
free(buf);
}
/**
* Sends an INFO_ACK to the server in response to a KEYINFO or FILEINFO
*/
void send_info_ack(int listidx, int pendidx, int func)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct infoack_h *info_ack;
struct pr_pending_info_t *pending;
unsigned char *verifydata, *verify_hash, *verify_val;
unsigned int payloadlen, hashlen;
int verifylen, len, destcount;
uint32_t *addrlist;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
info_ack = (struct infoack_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)info_ack + sizeof(struct infoack_h));
payloadlen = sizeof(struct infoack_h);
set_uftp_header(header, INFO_ACK, listidx);
info_ack->func = INFO_ACK;
// If we're responding to a FILEINFO, populate addrlist
if (func == FILEINFO) {
pending = &group_list[listidx].pending[pendidx];
info_ack->file_id = htons(pending->file_id);
if (pending->partial) {
info_ack->flags |= FLAG_PARTIAL;
}
destcount = load_pending(listidx, pendidx, INFO_ACK, addrlist,
max_msg_dest(listidx, INFO_ACK));
payloadlen += destcount * sizeof(uint32_t);
info_ack->destcount = htons(destcount);
}
if (group_list[listidx].keytype != KEY_NONE) {
// If we're responding to a KEYINFO, populate verify_data
if (func == KEYINFO) {
info_ack->file_id = 0;
info_ack->destcount = 0;
verifydata = build_verify_data(listidx, -1, &verifylen, 1);
if (!verifydata) {
log(group_list[listidx].group_id, 0,
"Error getting verify data");
send_upstream_abort(listidx, 0, "Error getting verify data");
free(buf);
return;
}
verify_hash = calloc(group_list[listidx].hmaclen, 1);
verify_val = calloc(VERIFY_LEN + group_list[listidx].hmaclen, 1);
if ((verify_hash == NULL) || (verify_val == NULL)){
syserror(0, 0, "calloc failed!");
exit(1);
}
hash(group_list[listidx].hashtype, verifydata, verifylen,
verify_hash, &hashlen);
PRF(group_list[listidx].hashtype, VERIFY_LEN,
group_list[listidx].groupmaster,
sizeof(group_list[listidx].groupmaster),
"client finished", verify_hash, hashlen, verify_val, &len);
memcpy(info_ack->verify_data, verify_val, VERIFY_LEN);
free(verifydata);
free(verify_hash);
free(verify_val);
}
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].proxykey,
group_list[listidx].proxy_keylen)) {
log(group_list[listidx].group_id,
(func == FILEINFO) ? pending->file_id : 0,
"Error encrypting INFO_ACK");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&(group_list[listidx].up_addr),
sizeof(group_list[listidx].up_addr)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id,
(func == FILEINFO) ? pending->file_id : 0,
"Error sending INFO_ACK");
} else {
log(group_list[listidx].group_id,
(func == FILEINFO) ? pending->file_id : 0,
"INFO_ACK sent");
}
set_timeout(listidx, (func == FILEINFO));
free(encrypted);
free(buf);
}
/**
* Sends a PRSTATUS to the server for all pending clients.
*/
void send_prstatus(int listidx, int pendidx)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct prstatus_h *prstatus;
uint32_t *addrlist;
struct pr_pending_info_t *pending;
int payloadlen, destcount;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
pending = &group_list[listidx].pending[pendidx];
header = (struct uftp_h *)buf;
prstatus = (struct prstatus_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)prstatus + sizeof(struct prstatus_h));
set_uftp_header(header, PRSTATUS, listidx);
prstatus->func = PRSTATUS;
prstatus->file_id = htons(pending->file_id);
prstatus->pass = pending->pass;
prstatus->seq = pending->seq;
prstatus->section = htons(pending->section);
destcount = load_pending(listidx, pendidx, STATUS, addrlist,
max_msg_dest(listidx, STATUS));
payloadlen = sizeof(struct prstatus_h) + (destcount * sizeof(uint32_t));
prstatus->destcount = htons(destcount);
if (group_list[listidx].keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].proxykey,
group_list[listidx].proxy_keylen)) {
log(group_list[listidx].group_id, pending->file_id,
"Error encrypting PRSTATUS");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].up_addr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, pending->file_id,
"Error sending PRSTATUS");
} else {
log(group_list[listidx].group_id, pending->file_id,
"Sent PRSTATUS for pass %d section %d seq %d" ,
pending->pass, pending->section, pending->seq);
}
free(buf);
free(encrypted);
}
/**
* Counts the pending naks for the given group
*/
int count_naks(int listidx, int pendidx)
{
unsigned nak_count, i;
for (nak_count = 0, i = 0; i < group_list[listidx].blocksize * 8; i++) {
if ((group_list[listidx].pending[pendidx].naklist[i >> 3] &
(1 << (i & 7))) != 0) {
nak_count++;
}
}
// Highly verbose debugging -- print aggregate NAKs before sending
if (log_level >= 5) {
for (nak_count = 0, i = 0; i < group_list[listidx].blocksize; i++) {
sclog5("%02X ", group_list[listidx].pending[pendidx].naklist[i]);
if (i % 25 == 24) slog5("");
}
slog5("");
}
return nak_count;
}
/**
* Sends a STATUS to the server for all pending clients.
*/
void send_status(int listidx, int pendidx)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct status_h *status;
unsigned char *sent_naks;
struct pr_pending_info_t *pending;
int payloadlen, nak_count;
send_prstatus(listidx, pendidx);
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
pending = &group_list[listidx].pending[pendidx];
header = (struct uftp_h *)buf;
status = (struct status_h *)(buf + sizeof(struct uftp_h));
nak_count = count_naks(listidx, pendidx);
set_uftp_header(header, STATUS, listidx);
status->func = STATUS;
status->file_id = htons(pending->file_id);
status->pass = pending->pass;
status->seq = pending->seq;
status->section = htons(pending->section);
status->nak_count = htonl(nak_count);
if (nak_count) {
payloadlen = group_list[listidx].blocksize;
sent_naks = (unsigned char *)status + sizeof(struct status_h);
memcpy(sent_naks, pending->naklist, payloadlen);
memset(pending->naklist, 0, payloadlen);
} else {
payloadlen = 0;
}
payloadlen += sizeof(struct status_h);
if (group_list[listidx].keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].proxykey,
group_list[listidx].proxy_keylen)) {
log(group_list[listidx].group_id, pending->file_id,
"Error encrypting STATUS");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].up_addr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, pending->file_id,
"Error sending STATUS");
} else {
log(group_list[listidx].group_id, pending->file_id,
"Sent %d NAKs for pass %d section %d seq %d", nak_count,
pending->pass, pending->section, pending->seq);
}
group_list[listidx].last_seq++;
set_timeout(listidx, 1);
free(buf);
free(encrypted);
}
/**
* Sends a COMPLETE to the server for all pending clients.
*/
void send_complete(int listidx, int pendidx)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct complete_h *complete;
uint32_t *addrlist;
struct pr_pending_info_t *pending;
int payloadlen, destcount;
buf = calloc(group_list[listidx].mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
pending = &group_list[listidx].pending[pendidx];
header = (struct uftp_h *)buf;
complete = (struct complete_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)complete + sizeof(struct complete_h));
set_uftp_header(header, COMPLETE, listidx);
complete->func = COMPLETE;
complete->file_id = htons(pending->file_id);
destcount = load_pending(listidx, pendidx, COMPLETE, addrlist,
max_msg_dest(listidx, COMPLETE));
payloadlen = sizeof(struct complete_h) + (destcount * sizeof(uint32_t));
complete->destcount = htons(destcount);
if (group_list[listidx].keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen,
group_list[listidx].mtu, group_list[listidx].keytype,
group_list[listidx].groupkey, group_list[listidx].groupsalt,
group_list[listidx].ivlen, group_list[listidx].hashtype,
group_list[listidx].grouphmackey,
group_list[listidx].hmaclen, group_list[listidx].sigtype,
group_list[listidx].proxykey,
group_list[listidx].proxy_keylen)) {
log(group_list[listidx].group_id, pending->file_id,
"Error encrypting COMPLETE");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group_list[listidx].up_addr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(group_list[listidx].group_id, pending->file_id,
"Error sending COMPLETE");
} else {
log(group_list[listidx].group_id, pending->file_id, "Sent COMPLETE");
}
set_timeout(listidx, 1);
free(buf);
free(encrypted);
}
uftp-3.5/server_announce.c 0000644 0003316 0000550 00000076102 11577014247 014722 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_common.h"
#include "server_announce.h"
/**
* Send the ANNOUNCE message
* For open group membership, just send one. For closed group membership,
* list as many destinations as will fit and send multiple packets so that
* each receiver is listed.
* Returns 1 on success, 0 on fail.
*/
int send_announce(const struct finfo_t *finfo, int attempt, int open)
{
int packetlen, rval;
unsigned char *buf, *keymod;
struct uftp_h *header;
struct announce_h *announce;
uint32_t *addrlist;
uint32_t keyexp;
uint16_t keylen;
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
keymod = (unsigned char *)announce + sizeof(struct announce_h);
set_uftp_header(header, ANNOUNCE, finfo, &listen_dest);
announce->func = ANNOUNCE;
if (restart_groupid) {
announce->flags |= FLAG_RESTART;
}
if (sync_mode) {
announce->flags |= FLAG_SYNC_MODE;
if (sync_preview) {
announce->flags |= FLAG_SYNC_PREVIEW;
}
}
announce->announce_int = htons(announce_int);
announce->status_int = htons(status_int);
announce->register_int = htons(register_int);
announce->done_int = htons(done_int);
announce->announce_time = announce_time;
announce->status_time = status_time;
announce->privatemcast = unicast ? 0 : receive_dest.sin_addr.s_addr;
announce->mtu = htons(mtu);
announce->client_auth = client_auth;
announce->sigtype = sigtype;
announce->hashtype = hashtype;
announce->keytype = keytype;
if (keytype != KEY_NONE) {
if (!export_RSA_key(privkey, &keyexp, keymod, &keylen)) {
log0(0, 0, "Error exporting server public key");
free(buf);
return 0;
}
announce->keylen = htons(keylen);
announce->keyexp = htonl(keyexp);
} else {
keylen = 0;
}
memcpy(announce->rand1, rand1, sizeof(rand1));
addrlist = (uint32_t *)(keymod + keylen);
if (open) {
packetlen = sizeof(struct uftp_h) + sizeof(struct announce_h) + keylen;
header->blsize = htons(sizeof(struct announce_h) + keylen);
announce->destcount = 0;
log(0, 0, "Sending ANNOUNCE %d", attempt);
if (nb_sendto(sock, buf, packetlen, 0, (struct sockaddr *)&listen_dest,
sizeof(listen_dest)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending ANNOUNCE");
// So we don't spin our wheels...
sleep(1);
free(buf);
return 0;
}
free(buf);
return 1;
} else {
rval = send_multiple(finfo, buf, ANNOUNCE, attempt, addrlist,
DEST_MUTE, &announce->destcount, 0, &listen_dest, 0);
free(buf);
return rval;
}
}
/**
* Send out REG_CONF messages specifiying all registered clients.
* Sent when encryption is disabled, or if the client is behind a proxy.
* Returns 1 on success, 0 on fail
*/
int send_regconf(const struct finfo_t *finfo, int attempt, int do_regconf)
{
int rval;
unsigned char *buf;
struct uftp_h *header;
struct regconf_h *regconf;
uint32_t *addrlist;
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
regconf = (struct regconf_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)regconf + sizeof(struct regconf_h));
set_uftp_header(header, REG_CONF, finfo, &receive_dest);
regconf->func = REG_CONF;
rval = send_multiple(finfo, buf, REG_CONF, attempt, addrlist, DEST_ACTIVE,
®conf->destcount, 0, &receive_dest, do_regconf);
free(buf);
return rval;
}
/**
* Send a KEYINFO message. Sent during the Announce phase for a group
* with encryption enabled.
* Returns 1 on success, 0 on fail.
*/
int send_keyinfo(const struct finfo_t *finfo, int attempt)
{
unsigned char *buf, *iv;
struct uftp_h *header;
struct keyinfo_h *keyinfo;
struct destkey *keylist;
struct timeval tv;
unsigned int hsize, payloadlen, len;
int maxdest, packetcnt, dests, i;
buf = calloc(mtu, 1);
iv = calloc(ivlen, 1);
if ((buf == NULL) || (iv == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));
set_uftp_header(header, KEYINFO, finfo, &receive_dest);
keyinfo->func = KEYINFO;
hsize = sizeof(struct keyinfo_h);
maxdest = (payloadsize - hsize) / sizeof(struct destkey);
packetcnt = 1;
gettimeofday(&tv, NULL);
keyinfo->tstamp_sec = htonl(tv.tv_sec);
keyinfo->tstamp_usec = htonl(tv.tv_usec);
for (i = 0, dests = 0; i < destcount; i++) {
if (destlist[i].status == DEST_REGISTERED) {
keylist[dests].destaddr = destlist[i].addr.s_addr;
build_iv(iv, destlist[i].salt, ivlen, htonl(finfo->group_id),
out_addr.s_addr, htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(keytype, iv, destlist[i].key,
&groupmaster[1], sizeof(groupmaster) - 1,
keylist[dests].groupmaster, &len)) {
log0(0, 0, "Error encrypting KEYINFO for %s", destlist[i].name);
free(buf);
free(iv);
return 0;
}
if (!keyinfo->groupmaster_len) keyinfo->groupmaster_len = len;
dests++;
}
if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) {
payloadlen = hsize + (dests * sizeof(struct destkey));
header->blsize = htons(payloadlen);
keyinfo->destcount = dests;
log(0, 0, "Sending KEYINFO %d.%d", attempt, packetcnt);
if (nb_sendto(sock, buf, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&receive_dest,
sizeof(receive_dest)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending KEYINFO");
sleep(1);
free(buf);
free(iv);
return 0;
}
if (packet_wait) usleep(packet_wait);
memset(keylist, 0, maxdest * sizeof(struct destkey));
dests = 0;
packetcnt++;
}
}
free(buf);
free(iv);
return 1;
}
/**
* Send a FILEINFO message. Sent for each individual file.
* Returns 1 on success, 0 on fail.
*/
int send_fileinfo(const struct finfo_t *finfo, int attempt)
{
int rval;
unsigned char *buf;
struct uftp_h *header;
struct fileinfo_h *fileinfo;
uint32_t *addrlist;
char *linkname;
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
fileinfo = (struct fileinfo_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)fileinfo + sizeof(struct fileinfo_h));
set_uftp_header(header, FILEINFO, finfo, &receive_dest);
fileinfo->func = FILEINFO;
fileinfo->ftype = finfo->ftype;
fileinfo->file_id = htons(finfo->file_id);
fileinfo->block_total = htonl(finfo->blocks);
fileinfo->section_total = htons(finfo->sections);
fileinfo->lofsize = htonl((finfo->size & 0xFFFFFFFF));
fileinfo->hifsize = htonl((finfo->size >> 32));
if (sync_mode) {
fileinfo->ftstamp = htonl(finfo->tstamp);
} else {
fileinfo->ftstamp = 0;
}
strncpy(fileinfo->name, finfo->destfname, sizeof(fileinfo->name));
fileinfo->name[strlen(finfo->destfname)] = '\x0';
if (finfo->ftype == FTYPE_LINK) {
linkname = fileinfo->name + strlen(finfo->destfname) + 1;
strncpy(linkname, finfo->linkname,
sizeof(fileinfo->name) - (fileinfo->name - linkname));
linkname[strlen(finfo->linkname)] = '\x0';
}
rval = send_multiple(finfo, buf, FILEINFO, attempt, addrlist,
DEST_REGISTERED, &fileinfo->destcount, (keytype != KEY_NONE),
&receive_dest, 0);
free(buf);
return rval;
}
/**
* Adds a registered host to the hostlist. Returns the list index.
*/
int add_dest_by_addr(uint32_t addr, struct finfo_t *finfo,
int state, int proxyidx, int clientcnt)
{
struct hostent *hp;
struct in_addr inaddr;
inaddr.s_addr = addr;
if (ntohl(addr) <= 0xffffff) {
sprintf(destlist[destcount].name, "0x%06X", ntohl(addr));
} else {
if (!noname && (hp = gethostbyaddr((char *)&inaddr,
sizeof(struct in_addr), AF_INET))) {
strncpy(destlist[destcount].name, hp->h_name,
sizeof(destlist[destcount].name)-1);
} else {
strncpy(destlist[destcount].name, inet_ntoa(inaddr),
sizeof(destlist[destcount].name)-1);
}
destlist[destcount].name[sizeof(destlist[destcount].name)-1] = '\x0';
}
destlist[destcount].addr = inaddr;
destlist[destcount].status = state;
destlist[destcount].proxyidx = proxyidx;
destlist[destcount].clientcnt = clientcnt;
return destcount++;
}
/**
* When a proxy registers, process the clients the proxy is serving
*/
void add_proxy_dests(struct finfo_t *finfo, const uint32_t *addr,
const struct sockaddr_in *sin, int clientcnt,
int proxyidx, int open)
{
int startcnt, hostidx, i, dupmsg;
if (destlist[proxyidx].clientcnt == -1) {
// True when using open group membership and
// we get a CLIENT_KEY before the REGSITER for a proxy
destlist[proxyidx].clientcnt = 0;
}
startcnt = destlist[proxyidx].clientcnt;
for (i = 0; i < clientcnt; i++) {
dupmsg = 0;
hostidx = find_client(addr[i]);
if (hostidx == -1) {
if (open) {
hostidx = add_dest_by_addr(addr[i], finfo, DEST_ACTIVE,
proxyidx, -1);
} else {
struct in_addr inaddr = to_addr(addr[i]);
log1(0, 0, "Host %s not in host list", inet_ntoa(inaddr));
send_abort(finfo, "Not in host list", sin, &inaddr, 0);
continue;
}
} else {
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
destlist[hostidx].status = DEST_ACTIVE;
destlist[hostidx].proxyidx = proxyidx;
}
finfo->deststate[hostidx].conf_sent = 0;
log1(0, 0, " For client%s %s", dupmsg ? "+" : "",
destlist[hostidx].name);
destlist[proxyidx].clients[i + startcnt] = hostidx;
}
destlist[proxyidx].clientcnt += clientcnt;
}
/**
* Returns the verify_data string used in certain messages. This value
* is then run through the PRF with the result going into the message
*/
uint8_t *build_verify_data(const struct finfo_t *finfo, int hostidx,
int *verifylen)
{
uint8_t *verifydata;
uint32_t blank;
uint32_t exponent;
uint16_t modlen;
uint8_t modulus[PUBKEY_LEN];
uint32_t n_group_id;
memset(&blank, 0, sizeof(blank));
*verifylen = 0;
if (destlist[hostidx].status == DEST_MUTE) {
verifydata = calloc(sizeof(finfo->group_id) +
sizeof(receive_dest.sin_addr.s_addr) + sizeof(rand1) +
sizeof(destlist[hostidx].rand2) +
sizeof(destlist[hostidx].premaster), 1);
} else {
verifydata = calloc(sizeof(finfo->group_id) +
sizeof(receive_dest.sin_addr.s_addr) + sizeof(rand1) +
sizeof(destlist[hostidx].rand2) +
sizeof(destlist[hostidx].premaster) + PUBKEY_LEN + 4 +
sizeof(groupmaster), 1);
}
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
n_group_id = htonl(finfo->group_id);
memcpy(verifydata, &n_group_id, sizeof(n_group_id));
*verifylen += sizeof(n_group_id);
memcpy(verifydata + *verifylen,
unicast ? &blank : &receive_dest.sin_addr.s_addr,
sizeof(receive_dest.sin_addr.s_addr));
*verifylen += sizeof(receive_dest.sin_addr.s_addr);
memcpy(verifydata + *verifylen, rand1, sizeof(rand1));
*verifylen += sizeof(rand1);
memcpy(verifydata + *verifylen, destlist[hostidx].rand2,
sizeof(destlist[hostidx].rand2));
*verifylen += sizeof(destlist[hostidx].rand2);
memcpy(verifydata + *verifylen, destlist[hostidx].premaster,
sizeof(destlist[hostidx].premaster));
*verifylen += sizeof(destlist[hostidx].premaster);
if (destlist[hostidx].status != DEST_MUTE) {
if (destlist[hostidx].pubkey) {
if (!export_RSA_key(destlist[hostidx].pubkey,
&exponent, modulus, &modlen)) {
free(verifydata);
return NULL;
}
exponent = htonl(exponent);
memcpy(verifydata + *verifylen, &exponent, sizeof(exponent));
*verifylen += sizeof(exponent);
memcpy(verifydata + *verifylen, modulus, modlen);
*verifylen += modlen;
}
memcpy(verifydata + *verifylen, groupmaster, sizeof(groupmaster));
*verifylen += sizeof(groupmaster);
}
return verifydata;
}
/**
* Verifies the data in a CLIENT_KEY message signed by the client's public key
*/
int verify_client_key(struct finfo_t *finfo, int hostidx)
{
uint8_t *verifydata;
int verifylen;
// build_verify_data should never fail in this case
verifydata = build_verify_data(finfo, hostidx, &verifylen);
if (!verify_RSA_sig(destlist[hostidx].pubkey, hashtype, verifydata,
verifylen, destlist[hostidx].verifydata,
destlist[hostidx].verifylen)) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: verify data mismatch",
destlist[hostidx].name);
free(verifydata);
return 0;
}
destlist[hostidx].status = DEST_REGISTERED;
free(verifydata);
return 1;
}
/**
* For a given client, calculate the master key and do key expansion
* to determine the symmetric cypher key and IV salt, and hash key
*/
void calculate_client_keys(int hostidx)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
explen = keylen + ivlen + hmaclen;
seedlen = sizeof(rand1) * 2;
seed = calloc(seedlen, 1);
prf_buf = calloc(MASTER_LEN + explen + hmaclen, 1);
if ((seed == NULL) || (prf_buf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(seed, rand1, sizeof(rand1));
memcpy(seed + sizeof(rand1), destlist[hostidx].rand2,
sizeof(destlist[hostidx].rand2));
PRF(hashtype, MASTER_LEN, destlist[hostidx].premaster,
sizeof(destlist[hostidx].premaster),
"master secret", seed, seedlen, prf_buf, &len);
memcpy(destlist[hostidx].master,prf_buf,
sizeof(destlist[hostidx].master));
PRF(hashtype, explen, destlist[hostidx].master,
sizeof(destlist[hostidx].master), "key expansion",
seed, seedlen, prf_buf, &len);
memcpy(destlist[hostidx].hmackey, prf_buf, hmaclen);
memcpy(destlist[hostidx].key, prf_buf + hmaclen, keylen);
memcpy(destlist[hostidx].salt, prf_buf + hmaclen + keylen, ivlen);
free(seed);
free(prf_buf);
}
/**
* Processes encryption key information received in a REGISTER message
*/
int handle_register_keys(const struct register_h *reg,
const unsigned char *enckey, struct finfo_t *finfo,
int hostidx)
{
unsigned char *decrypted;
int len;
decrypted = calloc(PUBKEY_LEN, 1);
if (decrypted == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(destlist[hostidx].rand2, reg->rand2,
sizeof(destlist[hostidx].rand2));
if (!RSA_decrypt(privkey, enckey, ntohs(reg->premaster_len),
decrypted, &len)) {
log1(0, 0, "Rejecting REGISTER from %s: failed to decrypt "
"premaster secret", destlist[hostidx].name);
free(decrypted);
return 0;
}
if (len > ntohs(reg->premaster_len)) {
log1(0, 0, "Rejecting REGISTER from %s: decrypted "
"premaster secret too long", destlist[hostidx].name);
free(decrypted);
return 0;
}
memcpy(destlist[hostidx].premaster, decrypted, len);
calculate_client_keys(hostidx);
free(decrypted);
if (destlist[hostidx].pubkey) {
if (!verify_client_key(finfo, hostidx)) {
return 0;
}
}
return 1;
}
/**
* Process an expected REGISTER with open group membership
*/
void handle_open_register(const unsigned char *message, int meslen,
struct finfo_t *finfo, const struct sockaddr_in *sin,
const struct in_addr *hostaddr, int regconf)
{
struct register_h *reg;
uint32_t *addrlist;
unsigned char *enckey;
int clientcnt, hostidx;
reg = (struct register_h *)message;
enckey = (unsigned char *)reg + sizeof(struct register_h);
addrlist = (uint32_t *)(enckey + ntohs(reg->premaster_len));
if (destcount == MAXDEST) {
log1(0, 0, "Rejecting REGISTER from %s: max destinations exceeded",
inet_ntoa(*hostaddr));
send_abort(finfo, "Max destinations exceeded", sin, hostaddr, 0);
return;
}
clientcnt = ntohs(reg->destcount);
if (meslen != sizeof(struct register_h) + ntohs(reg->premaster_len) +
(clientcnt * sizeof(uint32_t))) {
log1(0, 0, "Rejecting REGISTER from %s: invalid message size",
inet_ntoa(*hostaddr));
send_abort(finfo, "Invalid message size", sin, hostaddr, 0);
return;
}
hostidx = add_dest_by_addr(hostaddr->s_addr, finfo, DEST_MUTE, -1,
(clientcnt > 0) ? 0 : -1);
if (keytype != KEY_NONE) {
if (!handle_register_keys(reg, enckey, finfo, hostidx)) {
return;
}
}
if (regconf) {
finfo->deststate[hostidx].conf_sent = 0;
}
destlist[hostidx].registered = 1;
destlist[hostidx].status =
regconf ? DEST_ACTIVE : (client_auth ? DEST_MUTE : DEST_REGISTERED);
log1(0, 0, "Received REGISTER from %s %s",
(clientcnt > 0) ? "proxy" : "client", destlist[hostidx].name);
if (clientcnt > 0) {
add_proxy_dests(finfo, addrlist, sin, clientcnt, hostidx, 1);
}
}
/**
* Process an expected REGISTER with closed group membership,
* or with open group membership if CLIENT_KEY was received first.
*/
void handle_register(const unsigned char *message, int meslen,
struct finfo_t *finfo, const struct sockaddr_in *sin,
const struct in_addr *hostaddr, int hostidx,
int regconf, int open)
{
struct register_h *reg;
uint32_t *addrlist;
unsigned char *enckey;
int clientcnt, dupmsg, isproxy;
reg = (struct register_h *)message;
enckey = (unsigned char *)reg + sizeof(struct register_h);
addrlist = (uint32_t *)(enckey + ntohs(reg->premaster_len));
clientcnt = ntohs(reg->destcount);
if ((clientcnt > 0) && (destlist[hostidx].clientcnt == -1) && (!open)) {
log1(0, 0, "Rejecting REGISTER from %s: specified multiple clients "
"but not a proxy", destlist[hostidx].name);
send_abort(finfo, "specified multiple clients but not a proxy", sin,
hostaddr, 0);
destlist[hostidx].status = DEST_ABORT;
return;
}
if (meslen != sizeof(struct register_h) + ntohs(reg->premaster_len) +
(clientcnt * sizeof(uint32_t))) {
log1(0, 0, "Rejecting REGISTER from %s: invalid message size",
destlist[hostidx].name);
send_abort(finfo, "Invalid message size", sin, hostaddr, 0);
return;
}
if (finfo->file_id != 0) {
log1(0, 0, "Received REGISTER+ from %s", destlist[hostidx].name);
return;
}
if (destlist[hostidx].status == DEST_MUTE) {
if (keytype != KEY_NONE) {
if (!handle_register_keys(reg, enckey, finfo, hostidx)) {
return;
}
}
destlist[hostidx].status = regconf ? DEST_ACTIVE :
((client_auth && (!destlist[hostidx].pubkey))
? DEST_MUTE : DEST_REGISTERED);
}
dupmsg = (destlist[hostidx].registered);
destlist[hostidx].registered = 1;
if (regconf) {
finfo->deststate[hostidx].conf_sent = 0;
}
isproxy = (destlist[hostidx].clientcnt != -1);
log1(0,0, "Received REGISTER%s from %s %s", (dupmsg && !isproxy) ? "+" : "",
(isproxy) ? "proxy" : "client", destlist[hostidx].name);
if (clientcnt > 0) {
add_proxy_dests(finfo, addrlist, sin, clientcnt, hostidx, open);
}
}
/**
* Verifies a client's public key fingerprint
*/
int verify_client_fingerprint(const struct client_key_h *clientkey,
const unsigned char *keymod,
const unsigned char *verify, int hostidx)
{
unsigned char *verifydata, fingerprint[HMAC_LEN];
unsigned int verifylen, fplen;
if (destlist[hostidx].verified) {
return 1;
}
if (!import_RSA_key(&destlist[hostidx].pubkey, ntohl(clientkey->keyexp),
keymod, ntohs(clientkey->keylen))) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: failed to import key",
destlist[hostidx].name);
return 0;
}
destlist[hostidx].pubkeylen = RSA_keylen(destlist[hostidx].pubkey);
if (destlist[hostidx].has_fingerprint) {
verifylen = 0;
verifydata = calloc(sizeof(clientkey->keyexp) +
ntohs(clientkey->keylen), 1);
if (verifydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(verifydata, &clientkey->keyexp, sizeof(clientkey->keyexp));
verifylen += sizeof(clientkey->keyexp);
memcpy(verifydata + verifylen, keymod,
ntohs(clientkey->keylen));
verifylen += ntohs(clientkey->keylen);
hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen);
if (memcmp(destlist[hostidx].keyfingerprint, fingerprint, fplen)) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: key fingerprint mismatch",
destlist[hostidx].name);
free(verifydata);
free_RSA_key(destlist[hostidx].pubkey);
destlist[hostidx].pubkey = (RSA_key_t)NULL;
destlist[hostidx].pubkeylen = 0;
return 0;
}
free(verifydata);
}
destlist[hostidx].verified = 1;
return 1;
}
/**
* Process an expected CLIENT_KEY with open group membership
*/
void handle_open_clientkey(const unsigned char *message, int meslen,
struct finfo_t *finfo, const struct sockaddr_in *sin,
const struct in_addr *hostaddr)
{
struct client_key_h *clientkey;
unsigned char *keymod, *verify;
int hostidx;
clientkey = (struct client_key_h *)message;
keymod = (unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keymod + ntohs(clientkey->keylen);
if (destcount == MAXDEST) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: max destinations exceeded",
inet_ntoa(*hostaddr));
send_abort(finfo, "Max destinations exceeded", sin, hostaddr, 0);
return;
}
if (meslen != sizeof(struct client_key_h) + ntohs(clientkey->keylen) +
ntohs(clientkey->verifylen)) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: invalid message size",
inet_ntoa(*hostaddr));
send_abort(finfo, "Invalid message size", sin, hostaddr, 0);
return;
}
hostidx = add_dest_by_addr(hostaddr->s_addr, finfo, DEST_MUTE, -1, -1);
if (!verify_client_fingerprint(clientkey, keymod, verify, hostidx)) {
return;
}
memcpy(destlist[hostidx].verifydata, verify, ntohs(clientkey->verifylen));
destlist[hostidx].verifylen = ntohs(clientkey->verifylen);
log(0, 0, "Received CLIENT_KEY from %s", destlist[hostidx].name);
}
/**
* Process an expected CLIENT_KEY with closed group membership,
* or with open group membersip if REGISTER was received first.
*/
void handle_clientkey(const unsigned char *message, int meslen,
struct finfo_t *finfo, const struct sockaddr_in *sin,
const struct in_addr *hostaddr, int hostidx)
{
struct client_key_h *clientkey;
unsigned char *keymod, *verify;
clientkey = (struct client_key_h *)message;
keymod = (unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keymod + ntohs(clientkey->keylen);
if (meslen != sizeof(struct client_key_h) + ntohs(clientkey->keylen) +
ntohs(clientkey->verifylen)) {
log1(0, 0, "Rejecting CLIENT_KEY from %s: invalid message size",
destlist[hostidx].name);
send_abort(finfo, "Invalid message size", sin, hostaddr, 0);
return;
}
if (finfo->file_id != 0) {
log(0, 0, "Received CLIENT_KEY+ from %s", destlist[hostidx].name);
return;
}
if (!verify_client_fingerprint(clientkey, keymod, verify, hostidx)) {
return;
}
memcpy(destlist[hostidx].verifydata, verify, ntohs(clientkey->verifylen));
destlist[hostidx].verifylen = ntohs(clientkey->verifylen);
if (destlist[hostidx].registered) {
if (!verify_client_key(finfo, hostidx)) {
return;
}
}
log(0, 0, "Received CLIENT_KEY from %s", destlist[hostidx].name);
}
/**
* Process an expected INFO_ACK. If received in response to a KEYINFO
* message, validate the verify_data field.
*/
void handle_info_ack(const unsigned char *message, int meslen,
struct finfo_t *finfo, const struct sockaddr_in *sin,
const struct in_addr *hostaddr, int hostidx, int announce)
{
struct infoack_h *infoack;
uint32_t *addr;
unsigned char *verifydata, *verify_hash, *verify_test;
int verifylen, len, clientcnt, dupmsg, isproxy, clientidx, i;
unsigned int hashlen;
infoack = (struct infoack_h *)message;
addr = (uint32_t *)((char *)infoack + sizeof(struct infoack_h));
clientcnt = ntohs(infoack->destcount);
if (meslen != sizeof(struct infoack_h) +
(clientcnt * sizeof(uint32_t))) {
log1(0, 0, "Rejecting INFO_ACK from %s: invalid message size",
destlist[hostidx].name);
send_abort(finfo, "Invalid message size", sin, hostaddr, 0);
return;
}
if ((clientcnt > 0) && (destlist[hostidx].clientcnt == -1)) {
log1(0, 0, "Rejecting INFO_ACK from %s: specified multiple clients "
"but not a proxy", destlist[hostidx].name);
send_abort(finfo, "specified multiple clients but not a proxy", sin,
hostaddr, 0);
return;
}
// If in response to a KEYINFO, check the verify data
if ((keytype != KEY_NONE) && announce) {
if (!(verifydata = build_verify_data(finfo, hostidx, &verifylen))) {
log1(0, 0, "Rejecting INFO_ACK from %s: error exporting "
"client public key", destlist[hostidx].name);
return;
}
verify_hash = calloc(hmaclen, 1);
verify_test = calloc(VERIFY_LEN + hmaclen, 1);
if ((verify_hash == NULL) || (verify_test == NULL)){
syserror(0, 0, "calloc failed!");
exit(1);
}
hash(hashtype, verifydata, verifylen, verify_hash, &hashlen);
PRF(hashtype, VERIFY_LEN, groupmaster, sizeof(groupmaster),
"client finished", verify_hash, hashlen, verify_test, &len);
if (memcmp(infoack->verify_data, verify_test, VERIFY_LEN)) {
log1(0, 0, "Rejecting INFO_ACK from %s: verify data mismatch",
destlist[hostidx].name);
free(verifydata);
free(verify_hash);
free(verify_test);
return;
}
free(verifydata);
free(verify_hash);
free(verify_test);
} else if (!announce) {
if (ntohs(infoack->file_id) != finfo->file_id) {
log1(0, 0, "Rejecting INFO_ACK from %s: invalid file ID %04X, "
"expected %04X ", destlist[hostidx].name,
ntohs(infoack->file_id), finfo->file_id );
return;
}
finfo->partial = finfo->partial &&
((infoack->flags & FLAG_PARTIAL) != 0);
}
isproxy = (destlist[hostidx].clientcnt != -1);
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
log(0, 0, "Received INFO_ACK%s from %s %s", (dupmsg && !isproxy) ? "+" : "",
(isproxy) ? "proxy" : "client", destlist[hostidx].name);
destlist[hostidx].status = DEST_ACTIVE;
clientcnt = ntohs(infoack->destcount);
if (clientcnt > 0) {
addr = (uint32_t *)((char *)infoack + sizeof(struct infoack_h));
for (i = 0; i < clientcnt; i++) {
dupmsg = 0;
clientidx = find_client(addr[i]);
if (clientidx == -1) {
log(0, 0, "Host %s not in host list",
inet_ntoa(to_addr(addr[i])));
continue;
} else {
dupmsg = (destlist[clientidx].status == DEST_ACTIVE);
destlist[clientidx].status = DEST_ACTIVE;
}
log(0, 0, " For client%s %s", dupmsg ? "+" : "",
destlist[clientidx].name);
}
}
return;
}
uftp-3.5/server_common.c 0000644 0003316 0000550 00000026065 11577014250 014401 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#ifdef WINDOWS
// none
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_common.h"
/**
* Initializes the uftp header of an outgoing packet
* blsize is initialized to 0, but must be set later before sending
*/
void set_uftp_header(struct uftp_h *header, int func,
const struct finfo_t *finfo,
const struct sockaddr_in *dest)
{
header->uftp_id = UFTP_VER_NUM;
header->func = func;
header->blsize = 0;
header->group_id = htonl(finfo->group_id);
header->srcaddr = out_addr.s_addr;
header->destaddr = dest->sin_addr.s_addr;
}
/**
* Sends an ABORT message to one or more clients
*/
void send_abort(const struct finfo_t *finfo, const char *message,
const struct sockaddr_in *destaddr,
const struct in_addr *dest, int encrypt)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct abort_h *abort;
int payloadlen;
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
abort = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, finfo, destaddr);
abort->func = ABORT;
if (dest) {
abort->host = dest->s_addr;
}
strncpy(abort->message, message, sizeof(abort->message) - 1);
payloadlen = sizeof(struct abort_h);
if (encrypt) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, mtu, keytype,
groupkey, groupsalt, ivlen, hashtype, grouphmackey, hmaclen,
sigtype, privkey, rsalen)) {
log0(0, 0, "Error encrypting ABORT");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
encrypted = NULL;
outpacket = buf;
header->blsize = htons(payloadlen);
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)destaddr,
sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending ABORT");
}
free(buf);
free(encrypted);
}
/**
* For messages that send a list of clients in the body, append all clients
* in the specified state to the packet, then send the message of the given
* type when either the body is full or the end of the client list has been
* reached. All header fields besides uftp_h.blsize and message.destcount
* must be populated before calling.
* Returns 1 on success, 0 on fail.
*/
int send_multiple(const struct finfo_t *finfo, unsigned char *packet,
int message, int attempt, uint32_t *addrlist, int state,
uint16_t *mes_destcount, int encrypt,
const struct sockaddr_in *destaddr, int regconf)
{
struct uftp_h *header;
int hsize, payloadlen;
int maxdest, packetcnt, dests, i;
unsigned char *mheader, *encpacket, *outpacket;
header = (struct uftp_h *)packet;
mheader = packet + sizeof(struct uftp_h);
hsize = (unsigned char *)addrlist - mheader;
maxdest = ((encrypt ? encpayloadsize : payloadsize) - hsize) /
sizeof(struct in_addr);
packetcnt = 1;
if (encrypt) {
encpacket = calloc(mtu + keylen, 1);
if (encpacket == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
} else {
encpacket = NULL;
}
for (i = 0, dests = 0; i < destcount; i++) {
if (message == REG_CONF) {
// Only send REG_CONF for a particular client if either it's
// behind a proxy or we're sending them to everyone.
// Also, don't send if we already sent one and we haven't
// gotten another REGISTER
if ((destlist[i].status == state) &&
(!finfo->deststate[i].conf_sent) &&
(regconf || (destlist[i].proxyidx != -1))) {
addrlist[dests++] = destlist[i].addr.s_addr;
finfo->deststate[i].conf_sent = 1;
}
} else if (message == DONE_CONF) {
// As with REG_CONF, don't send a DONE_CONF for a client
// if we already sent one and we haven't gotten another COMPLETE
if ((destlist[i].status == state) &&
(!finfo->deststate[i].conf_sent)) {
addrlist[dests++] = destlist[i].addr.s_addr;
finfo->deststate[i].conf_sent = 1;
}
} else if (destlist[i].status == state) {
addrlist[dests++] = destlist[i].addr.s_addr;
}
if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) {
payloadlen = hsize + (dests * sizeof(uint32_t));
*mes_destcount = htons(dests);
if (encrypt) {
if (!encrypt_and_sign(packet, &encpacket, payloadlen, mtu,
keytype, groupkey, groupsalt, ivlen, hashtype,
grouphmackey, hmaclen, sigtype, privkey, rsalen)) {
log0(0, 0, "Error encrypting %s", func_name(message));
free(encpacket);
return 0;
}
outpacket = encpacket;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
outpacket = packet;
header->blsize = htons(payloadlen);
}
log(0, 0, "Sending %s %d.%d", func_name(message),
attempt, packetcnt);
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h),0,
(struct sockaddr *)destaddr,
sizeof(*destaddr)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending %s", func_name(message));
sleep(1);
free(encpacket);
return 0;
}
if (packet_wait) usleep(packet_wait);
memset(addrlist, 0, maxdest * sizeof(uint32_t));
dests = 0;
packetcnt++;
}
}
free(encpacket);
return 1;
}
/**
* Do basic checking on a received packet, like checking the version
* and making sure the size matches the size in the header.
* Returns 1 on success, 0 on fail.
*/
int validate_packet(const unsigned char *packet, int len,
const struct finfo_t *finfo)
{
struct uftp_h *header;
header = (struct uftp_h *)packet;
if ((header->uftp_id != UFTP_VER_NUM) &&
(header->uftp_id != UFTP_3_0_VER)) {
log1(0, 0, "Invalid version %02X", header->uftp_id);
return 0;
}
if (len - sizeof(struct uftp_h) != ntohs(header->blsize)) {
log1(0, 0, "Invalid packet size %d, expected %d",
len - sizeof(struct uftp_h), ntohs(header->blsize));
return 0;
}
if (ntohl(header->group_id) != finfo->group_id) {
log1(0, 0, "Invalid group ID %08X, expected %08X",
ntohl(header->group_id), finfo->group_id);
return 0;
}
if ((header->func == ENCRYPTED) && (keytype == KEY_NONE)) {
log1(0, 0, "Received encrypted packet with encryption disabled");
return 0;
}
return 1;
}
/**
* Look for a given client in the global client list
* Returns the client's index in the list, or -1 if not found
*/
int find_client(uint32_t addr)
{
int i;
// TODO: This can be a lot more efficient. Should probably sort by
// addr and keep an index, then do a binary search.
for (i = 0; i < destcount; i++) {
if (destlist[i].addr.s_addr == addr) {
return i;
}
}
return -1;
}
/**
* Check to see if a client is in an error state
* Returns 1 if true, 0 if false
*/
int client_error(int listidx)
{
return ((destlist[listidx].status == DEST_MUTE) ||
(destlist[listidx].status == DEST_LOST) ||
(destlist[listidx].status == DEST_ABORT));
}
/**
* Process an ABORT message
*/
void handle_abort(const unsigned char *message, int meslen, int idx,
struct finfo_t *finfo, const struct in_addr *hostaddr)
{
struct abort_h *abort;
int i;
abort = (struct abort_h *)message;
if (meslen != sizeof(struct abort_h)) {
log1(0, 0, "Rejecting ABORT from %s: invalid message size",
(idx == -1) ? inet_ntoa(*hostaddr) : destlist[idx].name);
return;
}
if (idx == -1) {
log1(0, 0, "Transfer aborted by %s: %s",
inet_ntoa(*hostaddr), abort->message);
return;
}
if (abort->host != 0) {
idx = find_client(abort->host);
}
if (idx == -1) {
log1(0, 0, "Transfer aborted by %s: %s",
inet_ntoa(*hostaddr), abort->message);
} else {
destlist[idx].status = DEST_ABORT;
log1(0, 0, "Transfer aborted by %s: %s",
destlist[idx].name, abort->message);
}
if (quit_on_error) {
log0(0, 0, "Aboring all clients");
send_abort(finfo, "A client aborted, aborting all",
&receive_dest, NULL, 0);
// If encryption enabled, send ABORT both encrypted and unencrypted
// since we can't be sure what phase we're currently in.
if (keytype != KEY_NONE) {
send_abort(finfo, "A client aborted, aborting all",
&receive_dest, NULL, 1);
}
for (i = 0; i < destcount; i++) {
if ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_REGISTERED) ||
(destlist[i].status == DEST_STATUS)) {
destlist[i].status = DEST_ABORT;
}
}
}
}
uftp-3.5/LICENSE.txt 0000644 0003316 0000550 00000104513 11346327713 013202 0 ustar bush alumni GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
uftp-3.5/server_config.c 0000644 0003316 0000550 00000066672 11577014247 014374 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_config.h"
/**
* Global command line values and sockets
*/
SOCKET sock;
struct sockaddr_in listen_dest, receive_dest;
int weight, min_time, max_rate, rate, port, rcvbuf, packet_wait;
int noname, unicast, client_auth, quit_on_error, dscp, follow_links;
int save_fail, restart_groupid, sync_mode, sync_preview, dest_is_dir;
int announce_time, status_time;
int announce_int, status_int, register_int, done_int;
unsigned char ttl;
char pub_multi[IPSTR_LEN], priv_multi[IPSTR_LEN], destfname[MAXPATHNAME];
char logfile[MAXPATHNAME], keyfile[MAXPATHNAME];
char filelist[MAXFILES][MAXPATHNAME], exclude[MAXFILES][MAXPATHNAME];
char basedir[MAXDIR][MAXDIRNAME];
struct iflist ifl[MAX_INTERFACES];
int keytype, hashtype, sigtype, newkeylen;
int mtu, blocksize, encpayloadsize, payloadsize;
int ifl_len, destcount, filecount, excludecount, basedircount, cc_count;
struct cc_config_t cc_list[MAXCC];
struct in_addr out_addr;
struct destinfo_t destlist[MAXDEST];
/**
* Encryption variables
*/
RSA_key_t privkey;
unsigned char rand1[RAND_LEN], groupmaster[MASTER_LEN];
uint8_t groupsalt[MAXIV], groupkey[MAXKEY], grouphmackey[HMAC_LEN];
int ivlen, keylen, hmaclen, rsalen, sys_keys;
extern char *optarg;
extern int optind;
/**
* Add a destination or proxy to the list as specified by -H or -j
*/
void add_dest_by_name(const char *destname, const char *fingerprint, int proxy)
{
struct hostent *hp;
struct in_addr *addr;
if (destcount == MAXDEST) {
fprintf(stderr,"Exceeded maximum destination count\n");
exit(1);
}
if (inet_addr(destname) == INADDR_NONE) {
if ((hp = gethostbyname(destname)) == NULL) {
uint32_t uid = strtol(destname, NULL, 16);
if ((uid > 0xffffff) || (uid <= 0)) {
fprintf(stderr, "Invalid host name / UID\n");
exit(1);
}
destlist[destcount].addr.s_addr = htonl(uid);
strncpy(destlist[destcount].name, destname,
sizeof(destlist[destcount].name));
destlist[destcount].name[sizeof(destlist[destcount].name)-1]='\x0';
destlist[destcount].proxyidx = -1;
destlist[destcount].clientcnt = proxy ? 0 : -1;
destlist[destcount].has_fingerprint =
parse_fingerprint(destlist[destcount].keyfingerprint,
fingerprint);
destcount++;
} else {
addr = (struct in_addr *)hp->h_addr_list[0];
destlist[destcount].addr = *addr;
strncpy(destlist[destcount].name, destname,
sizeof(destlist[destcount].name));
destlist[destcount].name[sizeof(destlist[destcount].name)-1]='\x0';
destlist[destcount].proxyidx = -1;
destlist[destcount].clientcnt = proxy ? 0 : -1;
destlist[destcount].has_fingerprint =
parse_fingerprint(destlist[destcount].keyfingerprint,
fingerprint);
destcount++;
}
} else {
destlist[destcount].addr.s_addr = inet_addr(destname);
strncpy(destlist[destcount].name, destname,
sizeof(destlist[destcount].name));
destlist[destcount].name[sizeof(destlist[destcount].name)-1] = '\x0';
destlist[destcount].proxyidx = -1;
destlist[destcount].clientcnt = proxy ? 0 : -1;
destlist[destcount].has_fingerprint =
parse_fingerprint(destlist[destcount].keyfingerprint,
fingerprint);
destcount++;
}
}
/**
* Set defaults for all command line arguments
*/
void set_defaults()
{
memset(destlist, 0, sizeof(destlist));
memset(filelist, 0, sizeof(filelist));
memset(exclude, 0, sizeof(exclude));
memset(destfname, 0, sizeof(destfname));
destcount = 0;
port = DEF_PORT;
rate = DEF_RATE;
max_rate = DEF_RATE;
weight = DEF_WEIGHT;
min_time = DEF_MIN_TIME;
out_addr.s_addr = INADDR_NONE;
ttl = DEF_TTL;
dscp = DEF_DSCP;
mtu = DEF_MTU;
log_level = DEF_LOG_LEVEL;
unicast = DEF_UNICAST;
noname = DEF_NONAME;
rcvbuf = 0;
memset(keyfile, 0, sizeof(keyfile));
announce_time = DEF_ANNOUNCE_TIME;
status_time = DEF_STATUS_TIME;
announce_int = DEF_ANNOUNCE_INTERVAL;
status_int = DEF_STATUS_INTERVAL;
register_int = DEF_REGISTER_INTERVAL;
done_int = DEF_DONE_INTERVAL;
client_auth = 0;
quit_on_error = 0;
save_fail = 0;
restart_groupid = 0;
keytype = DEF_KEYTYPE;
hashtype = DEF_HASHTYPE;
sigtype = DEF_SIGTYPE;
strncpy(pub_multi, DEF_PUB_MULTI, sizeof(pub_multi)-1);
pub_multi[sizeof(pub_multi)-1] = '\x0';
strncpy(priv_multi, DEF_PRIV_MULTI, sizeof(priv_multi)-1);
priv_multi[sizeof(priv_multi)-1] = '\x0';
strncpy(logfile, "", sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
filecount = 0;
excludecount = 0;
basedircount = 0;
newkeylen = 0;
follow_links = 0;
showtime = 0;
sys_keys = 0;
sync_mode = 0;
sync_preview = 0;
cc_count = 0;
dest_is_dir = 0;
}
/**
* Reads in the contents of the restart file.
* Contains a server_restart_t header, followed by
* one or more server_restart_host_t entries.
*/
void read_restart_file(const char *restart_name)
{
struct server_restart_t header;
struct server_restart_host_t host;
int fd, i, rval;
if ((fd = open(restart_name, OPENREAD)) == -1) {
syserror(0, 0, "Failed to open restart file");
exit(1);
}
if (file_read(fd, &header, sizeof(header), 0) == -1) {
log(0, 0, "Failed to read header from restart file");
exit(1);
}
restart_groupid = header.group_id;
if ((header.filecount > MAXFILES) || (header.filecount <= 0)) {
log(0, 0, "Too many files listed in restart file");
exit(1);
}
for (i = 0; i < header.filecount; i++) {
if (file_read(fd, filelist[i], sizeof(filelist[i]), 0) == -1) {
log(0, 0, "Failed to read filename from restart file");
exit(1);
}
}
filecount = header.filecount;
while ((rval = file_read(fd, &host, sizeof(host), 1)) != 0) {
if (rval == -1) {
log(0, 0, "Failed to read host from restart file");
exit(1);
}
memcpy(destlist[destcount].name, host.name, sizeof(host.name));
destlist[destcount].addr = host.addr;
destlist[destcount].proxyidx = -1;
destlist[destcount].clientcnt = host.is_proxy ? 0 : -1;
destlist[destcount].has_fingerprint = host.has_fingerprint;
if (host.has_fingerprint) {
memcpy(destlist[destcount].keyfingerprint, host.keyfingerprint,
sizeof(destlist[destcount].keyfingerprint));
}
destcount++;
}
}
/**
* Reads in the congestion control config file.
* Each line contains a percentage (0-100) followed by a scaling factor.
* Sample:
* 0;1.3
* 5;1.1
* 10;0.9
* 25;0.7
* 50;0.4
*/
void read_cc_config(const char *cc_config)
{
FILE *cc_file;
char line[100], *p;
int last_percentage, percentage;
double scaling_factor;
if ((cc_file = fopen(cc_config, "rt")) == NULL) {
fprintf(stderr, "Couldn't open congestion control file %s: %s\n",
cc_config, strerror(errno));
exit(1);
}
last_percentage = 0;
while (fgets(line, sizeof(line), cc_file)) {
while ((strlen(line) > 0) && ((line[strlen(line)-1] == '\r') ||
(line[strlen(line)-1] == '\n'))) {
line[strlen(line)-1] = '\x0';
}
if (strlen(line) == 0) continue;
p = strtok(line, ";");
if (p == NULL) {
fprintf(stderr, "Error reading congestion control entry\n");
exit(1);
}
percentage = strtol(p, NULL, 10);
if ((percentage < 0) || percentage > 100) {
fprintf(stderr, "Invalid percentage in congestion control entry\n");
exit(1);
}
if (percentage < last_percentage) {
fprintf(stderr, "Congestion control entries must be in ascending "
"order by percentage\n");
exit(1);
}
p = strtok(NULL, ";");
if (p == NULL) {
fprintf(stderr, "Error reading congestion control entry\n");
exit(1);
}
errno = 0;
scaling_factor = strtod(p, NULL);
if (errno) {
fprintf(stderr, "Error reading congestion control entry: %s\n",
strerror(errno));
exit(1);
}
if (scaling_factor <= 0) {
fprintf(stderr, "Invalid scaling factor "
"in congestion control entry\n");
exit(1);
}
last_percentage = percentage;
cc_list[cc_count].percentage = percentage;
cc_list[cc_count].scaling_factor = scaling_factor;
cc_count++;
}
fclose(cc_file);
// Make sure we have a top end entry, make scaling same as last specified
cc_list[cc_count].percentage = 100;
cc_list[cc_count].scaling_factor = cc_list[cc_count - 1].scaling_factor;
cc_count++;
}
/**
* Set argument defaults, read and validate command line options
*/
void process_args(int argc, char *argv[])
{
int c, i, listidx, rval;
long tmpval;
struct hostent *hp;
struct in_addr *paddr, addr;
char line[1000], *dest, *destname, filename[MAXPATHNAME], *fingerprint, *p;
FILE *destfile, *excludefile, *listfile;
stat_struct statbuf;
const char opts[] =
"Ux:R:W:m:nL:B:Y:h:w:ck:K:lTA:S:a:s:r:d:"
"b:t:Q:zZI:p:j:qfyH:F:X:M:P:C:D:oE:i:";
set_defaults();
// read lettered arguments
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 'U':
unicast = 1;
break;
case 'x':
log_level = atoi(optarg);
if (log_level < 0) {
fprintf(stderr,"Invalid log level\n");
exit(1);
}
break;
case 'R':
rate = atoi(optarg);
if ((rate <= 0) && (rate != -1)) {
fprintf(stderr,"Invalid rate\n");
exit(1);
}
if ((rate == -1) && (cc_count > 0)) {
fprintf(stderr,"Can't specify -R -1 with -C\n");
exit(1);
}
max_rate = rate;
break;
case 'W':
weight = atoi(optarg);
if ((weight < 110) || (weight > 10000)) {
fprintf(stderr, "Invalid weight\n");
exit(1);
}
break;
case 'm':
min_time = atoi(optarg);
if ((min_time <= 0) || (min_time > 3600)) {
fprintf(stderr, "Invalid min time\n");
exit(1);
}
break;
case 'n':
noname = 1;
break;
case 'L':
strncpy(logfile, optarg, sizeof(logfile)-1);
logfile[sizeof(logfile)-1] = '\x0';
break;
case 'B':
rcvbuf = atoi(optarg);
if ((rcvbuf < 65536) || (rcvbuf > 104857600)) {
fprintf(stderr, "Invalid receive buffer size\n");
exit(1);
}
break;
case 'Y':
if (!strcmp(optarg, "none")) {
keytype = KEY_NONE;
} else if (!strcmp(optarg, "des")) {
keytype = KEY_DES;
} else if (!strcmp(optarg, "3des")) {
keytype = KEY_DES_EDE3;
} else if (!strcmp(optarg, "aes128")) {
keytype = KEY_AES128;
} else if (!strcmp(optarg, "aes256")) {
keytype = KEY_AES256;
} else {
fprintf(stderr, "Invalid keytype\n");
exit(1);
}
if (keytype != KEY_NONE && !cipher_supported(keytype)) {
fprintf(stderr, "Keytype not supported\n");
exit(1);
}
break;
case 'h':
if (!strcmp(optarg, "sha1")) {
hashtype = HASH_SHA1;
} else if (!strcmp(optarg, "sha256")) {
hashtype = HASH_SHA256;
} else {
fprintf(stderr, "Invalid hashtype\n");
exit(1);
}
if (!hash_supported(hashtype)) {
fprintf(stderr, "Hashtype not supported\n");
exit(1);
}
break;
case 'w':
if (!strcmp(optarg, "hmac")) {
sigtype = SIG_HMAC;
} else if (!strcmp(optarg, "rsa")) {
sigtype = SIG_RSA;
} else {
fprintf(stderr, "Invalid sigtype\n");
exit(1);
}
break;
case 'c':
client_auth = 1;
break;
case 'k':
strncpy(keyfile, optarg, sizeof(keyfile)-1);
keyfile[sizeof(keyfile)-1] = '\x0';
break;
case 'K':
newkeylen = atoi(optarg);
if ((newkeylen < 512) || (newkeylen > 2048)) {
fprintf(stderr, "Invalid new key length\n");
exit(1);
}
break;
case 'l':
follow_links = 1;
break;
case 'T':
showtime = 1;
break;
case 'A':
announce_time = atoi(optarg);
if ((announce_time < 1) || (announce_time > 240)) {
fprintf(stderr, "Invalid announce time\n");
exit(1);
}
break;
case 'S':
status_time = atoi(optarg);
if ((status_time < 1) || (status_time > 240)) {
fprintf(stderr, "Invalid status time\n");
exit(1);
}
break;
case 'a':
announce_int = atoi(optarg);
if ((announce_int < 500) || (announce_int > 20000)) {
fprintf(stderr, "Invalid announce interval\n");
exit(1);
}
break;
case 's':
status_int = atoi(optarg);
if ((status_int < 500) || (status_int > 20000)) {
fprintf(stderr, "Invalid status interval\n");
exit(1);
}
break;
case 'r':
register_int = atoi(optarg);
if ((register_int < 500) || (register_int > 60000)) {
fprintf(stderr, "Invalid register interval\n");
exit(1);
}
break;
case 'd':
done_int = atoi(optarg);
if ((done_int < 500) || (done_int > 60000)) {
fprintf(stderr, "Invalid done interval\n");
exit(1);
}
break;
case 'b':
mtu = atoi(optarg);
if ((mtu < 576) || (mtu > 9000)) {
fprintf(stderr, "Invalid mtu\n");
exit(1);
}
break;
case 't':
tmpval = atoi(optarg);
if ((tmpval <= 0) || (tmpval > 255)) {
fprintf(stderr, "Invalid ttl\n");
exit(1);
}
ttl = (char)tmpval;
break;
case 'Q':
tmpval = strtol(optarg, NULL, 0);
if ((tmpval < 0) || (tmpval > 63)) {
fprintf(stderr, "Invalid dscp\n");
exit(1);
}
dscp = (tmpval & 0xFF) << 2;
break;
case 'I':
if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) {
out_addr = ifl[listidx].addr;
break;
}
if (inet_addr(optarg) == INADDR_NONE) {
if ((hp = gethostbyname(optarg)) == NULL) {
fprintf(stderr, "Invalid host name: %s\n", optarg);
exit(1);
} else {
paddr = (struct in_addr *)hp->h_addr_list[0];
}
} else {
addr.s_addr = inet_addr(optarg);
paddr = &addr;
}
if ((listidx = getifbyaddr(*paddr, ifl, ifl_len)) != -1) {
out_addr = ifl[listidx].addr;
} else {
fprintf(stderr, "Interface %s not found\n", optarg);
exit(1);
}
break;
case 'z':
sync_mode = 1;
break;
case 'Z':
sync_preview = 1;
sync_mode = 1;
break;
case 'p':
port = atoi(optarg);
if (port == 0) {
fprintf(stderr, "Invalid port\n");
exit(1);
}
break;
case 'j':
if ((destfile = fopen(optarg, "rt")) == NULL) {
fprintf(stderr,"Couldn't open proxy list %s: %s\n",
optarg, strerror(errno));
exit(1);
}
while (fgets(line, sizeof(line), destfile)) {
while ((strlen(line) > 0) && ((line[strlen(line)-1] == '\r') ||
(line[strlen(line)-1] == '\n'))) {
line[strlen(line)-1] = '\x0';
}
destname = strtok(line, " \t");
if (!destname) continue;
if (destname[0] == '#') continue;
if (strlen(destname) > DESTNAME_LEN) {
fprintf(stderr, "Proxylist: name too long\n");
exit(1);
}
fingerprint = strtok(NULL, " \t");
add_dest_by_name(destname, fingerprint, 1);
}
if (!feof(destfile) && ferror(destfile)) {
perror("Failed to read from proxylist file");
exit(1);
}
fclose(destfile);
break;
case 'q':
quit_on_error = 1;
break;
case 'f':
save_fail = 1;
break;
case 'y':
sys_keys = 1;
break;
case 'H':
if (destcount != 0) {
fprintf(stderr,"Can't specify both -H and -F\n");
exit(1);
}
if (optarg[0] == '@') {
dest = &optarg[1];
if ((destfile = fopen(dest, "rt")) == NULL) {
fprintf(stderr,"Couldn't open destination list %s: %s\n",
dest, strerror(errno));
exit(1);
}
while (fgets(line, sizeof(line), destfile)) {
while ((strlen(line) > 0) &&
((line[strlen(line)-1] == '\r') ||
(line[strlen(line)-1] == '\n'))) {
line[strlen(line)-1] = '\x0';
}
destname = strtok(line, " \t");
if (!destname) continue;
if (destname[0] == '#') continue;
if (strlen(destname) > DESTNAME_LEN) {
fprintf(stderr, "Hostlist: name too long\n");
exit(1);
}
fingerprint = strtok(NULL, " \t");
add_dest_by_name(destname, fingerprint, 0);
}
if (!feof(destfile) && ferror(destfile)) {
perror("Failed to read from hostlist file");
exit(1);
}
fclose(destfile);
} else {
dest = strtok(optarg, ",");
while (dest != NULL) {
add_dest_by_name(dest, NULL, 0);
dest = strtok(NULL, ",");
}
}
break;
case 'F':
if (destcount != 0) {
fprintf(stderr,"Can't specify both -H and -F\n");
exit(1);
}
save_fail = 1;
read_restart_file(optarg);
break;
case 'X':
if ((excludefile = fopen(optarg, "rt")) == NULL) {
fprintf(stderr,"Couldn't open exclude list %s: %s\n",
optarg, strerror(errno));
exit(1);
}
while (fgets(filename, sizeof(filename), excludefile)) {
while ((strlen(filename) > 0) &&
((filename[strlen(filename)-1] == '\r') ||
(filename[strlen(filename)-1] == '\n'))) {
filename[strlen(filename)-1] = '\x0';
}
if (strlen(filename) == 0) continue;
if (excludecount == MAXFILES) {
fprintf(stderr,"Exceeded maximum exclude file count\n");
exit(1);
}
strncpy(exclude[excludecount], filename, sizeof(exclude[0]));
exclude[excludecount][sizeof(exclude[0])-1] = '\x0';
excludecount++;
}
if (!feof(excludefile) && ferror(excludefile)) {
perror("Failed to read from exclude file");
exit(1);
}
fclose(excludefile);
break;
case 'M':
strncpy(pub_multi, optarg, sizeof(pub_multi)-1);
pub_multi[sizeof(pub_multi)-1] = '\x0';
break;
case 'P':
strncpy(priv_multi, optarg, sizeof(priv_multi)-1);
priv_multi[sizeof(priv_multi)-1] = '\x0';
break;
case 'C':
if (rate == -1) {
fprintf(stderr,"Can't specify -C with -R -1\n");
exit(1);
}
read_cc_config(optarg);
break;
case 'D':
strncpy(destfname, optarg, sizeof(destfname)-1);
destfname[sizeof(destfname)-1] = '\x0';
while (destfname[strlen(destfname)-1] == PATH_SEP) {
destfname[strlen(destfname)-1] = '\x0';
}
break;
case 'o':
dest_is_dir = 1;
break;
case 'E':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(basedir[basedircount], p,
sizeof(basedir[basedircount])-1);
basedir[basedircount][sizeof(basedir[basedircount])-1] = '\x0';
basedircount++;
p = strtok(NULL, ",");
}
break;
case 'i':
if (filecount != 0) {
fprintf(stderr,"Can't specify both -i and -F\n");
exit(1);
}
if (strcmp(optarg, "-") == 0) {
listfile = stdin;
} else if ((listfile = fopen(optarg, "rt")) == NULL) {
fprintf(stderr,"Couldn't open file list %s: %s\n",
optarg, strerror(errno));
exit(1);
}
while (fgets(filename, sizeof(filename), listfile)) {
if (filecount == MAXFILES) {
fprintf(stderr, "Exceeded maximum file count\n");
exit(1);
}
while ((strlen(filename) > 0) &&
((filename[strlen(filename)-1] == '\r') ||
(filename[strlen(filename)-1] == '\n'))) {
filename[strlen(filename)-1] = '\x0';
}
if (strlen(filename) == 0) continue;
strncpy(filelist[filecount], filename, sizeof(filelist[0])-1);
filelist[filecount][sizeof(filelist[0])-1] = '\x0';
if (follow_links) {
rval = stat_func(filelist[filecount], &statbuf);
} else {
rval = lstat_func(filelist[filecount], &statbuf);
}
if (rval == -1) {
fprintf(stderr, "Error getting status of %s: %s\n",
filelist[filecount], strerror(errno));
exit(1);
}
filecount++;
}
if (!feof(listfile) && ferror(listfile)) {
perror("Failed to read from file list");
exit(1);
}
fclose(listfile);
break;
case '?':
fprintf(stderr, USAGE);
exit(1);
}
}
argc -= optind;
argv += optind;
if ((argc == 0) && (filecount == 0)) {
fprintf(stderr, USAGE);
exit(1);
}
if (unicast && (destcount != 1)) {
fprintf(stderr, "Must specify exactly one host for unicast\n");
exit(1);
}
if (announce_int > register_int) {
fprintf(stderr, "Error: Register interval %d is less than "
"announce interval %d\n", register_int, announce_int);
exit(1);
}
if (status_int > done_int) {
fprintf(stderr, "Error: Done interval %d is less than "
"status interval %d\n", done_int, status_int);
exit(1);
}
if (save_fail && sync_mode) {
fprintf(stderr, "Error: Cannot use restart mode "
"and sync mode together\n");
exit(1);
}
if (filecount != 0) {
if (argc > 0) {
fprintf(stderr, "Warning: ignoring paths "
"specified on command line\n");
}
return;
}
// Read list of files. Make sure each exists.
for (i = 0; i < argc; i++) {
if (filecount == MAXFILES) {
fprintf(stderr, "Exceeded maximum file count\n");
exit(1);
}
strncpy(filelist[filecount], argv[i], sizeof(filelist[0])-1);
filelist[filecount][sizeof(filelist[0])-1] = '\x0';
if (follow_links) {
rval = stat_func(filelist[filecount], &statbuf);
} else {
rval = lstat_func(filelist[filecount], &statbuf);
}
if (rval == -1) {
fprintf(stderr, "Error getting status of %s: %s\n",
filelist[filecount], strerror(errno));
exit(1);
}
filecount++;
}
if (keytype == KEY_NONE) {
hashtype = HASH_NONE;
sigtype = SIG_NONE;
}
}
uftp-3.5/server_init.c 0000644 0003316 0000550 00000025041 11577014250 014045 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_config.h"
#include "server_init.h"
/**
* Cleanup routine set up by atexit
*/
void cleanup(void)
{
int i, j;
closesocket(sock);
if (keytype != KEY_NONE) {
for (i = 0; i < destcount; i++) {
if (destlist[i].pubkey) {
free_RSA_key(destlist[i].pubkey);
}
free(destlist[i].last_status);
for (j = 0; j < destlist[i].last_prstatus_cnt; j++) {
free(destlist[i].last_prstatus[j]);
}
}
free_RSA_key(privkey);
}
crypto_cleanup();
#ifdef WINDOWS
WSACleanup();
#endif
}
/**
* Do initial setup before parsing arguments, including getting interface list
*/
void pre_initialize()
{
#ifdef WINDOWS
struct WSAData data;
if (WSAStartup(2, &data)) {
fprintf(stderr, "Error in WSAStartup: %d\n", WSAGetLastError());
exit(1);
}
#endif
applog = stderr;
ifl_len = sizeof(ifl) / sizeof(struct iflist);
getiflist(ifl, &ifl_len);
srand((unsigned int)time(NULL) ^ getpid());
crypto_init(0);
}
/**
* Do all socket creation and initialization
*/
void create_sockets()
{
struct sockaddr_in sin;
char *p, tmp_multi[IPSTR_LEN];
int found_if, fdflag, i;
// Create and bind socket
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
sockerror(0, 0, "Error creating socket");
exit(1);
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = 0;
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR) {
sockerror(0, 0, "Error binding socket");
exit(1);
}
// Set send/receive buffer size, ttl, and multicast interface
if (rcvbuf) {
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting send buffer size");
exit(1);
}
} else {
rcvbuf = DEF_RCVBUF;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
rcvbuf = DEF_BSD_RCVBUF;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting receive buffer size");
exit(1);
}
}
rcvbuf = DEF_RCVBUF;
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
rcvbuf = DEF_BSD_RCVBUF;
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&rcvbuf,
sizeof(rcvbuf)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting send buffer size");
exit(1);
}
}
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl,
sizeof(ttl)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting ttl");
closesocket(sock);
exit(1);
}
if (setsockopt(sock, IPPROTO_IP, IP_TOS, (char *)&dscp,
sizeof(dscp)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting dscp");
closesocket(sock);
exit(1);
}
if (out_addr.s_addr == INADDR_NONE) {
for (i = 0, found_if = 0; (i < ifl_len) && !found_if; i++) {
if (!ifl[i].isloopback) {
found_if = 1;
out_addr = ifl[i].addr;
}
}
if (!found_if) {
if (ifl_len > 0) {
out_addr = ifl[0].addr;
} else {
fprintf(stderr, "ERROR: no network interfaces found!\n");
exit(1);
}
}
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,
sizeof(out_addr)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting outgoing interface");
closesocket(sock);
exit(1);
}
// Make socket non-blocking
#ifndef BLOCKING
#ifdef WINDOWS
fdflag = 1;
if (ioctlsocket(sock, FIONBIO, &fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(sock);
exit(1);
}
#else
if ((fdflag = fcntl(sock, F_GETFL)) == SOCKET_ERROR) {
sockerror(0, 0, "Error getting socket descriptor flags");
closesocket(sock);
exit(1);
}
fdflag |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, fdflag) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting non-blocking option");
closesocket(sock);
exit(1);
}
#endif
#endif // BLOCKING
// Set up global sockaddr_in structs for public and private addresses
// Perform octet substitution on private multicast address
listen_dest.sin_family = AF_INET;
listen_dest.sin_addr.s_addr = inet_addr(pub_multi);
listen_dest.sin_port = htons(port);
while ((p = strchr(priv_multi,'x')) != NULL) {
memset(tmp_multi, 0, sizeof(tmp_multi));
snprintf(tmp_multi, sizeof(tmp_multi), "%.*s%d%s",
(int)(p - priv_multi), priv_multi, rand() & 0xFF, p + 1);
strcpy(priv_multi, tmp_multi);
}
receive_dest.sin_family = AF_INET;
receive_dest.sin_addr.s_addr = inet_addr(priv_multi);
receive_dest.sin_port = htons(port);
if (unicast) {
listen_dest.sin_addr = destlist[0].addr;
receive_dest.sin_addr = destlist[0].addr;
}
if (listen_dest.sin_addr.s_addr == INADDR_NONE) {
log0(0, 0, "Invalid public address\n");
exit(1);
}
if (receive_dest.sin_addr.s_addr == INADDR_NONE) {
log0(0, 0, "Invalid private address\n");
exit(1);
}
}
/**
* Initialize crypto library, generate keys
*/
void key_init()
{
unsigned char *prf_buf;
time_t t;
uint32_t t2;
int explen, len;
if (keytype == KEY_NONE) {
return;
}
set_sys_keys(sys_keys);
get_key_info(keytype, &keylen, &ivlen);
hmaclen = get_hash_len(hashtype);
memset(groupkey, 0, sizeof(groupkey));
memset(groupsalt, 0, sizeof(groupsalt));
memset(grouphmackey, 0, sizeof(grouphmackey));
if (!get_random_bytes(groupmaster, sizeof(groupmaster))) {
log0(0, 0, "Failed to generate group master");
exit(1);
}
groupmaster[0] = UFTP_VER_NUM;
if (!get_random_bytes(rand1, sizeof(rand1))) {
log0(0, 0, "Failed to generate rand1");
exit(1);
}
// Sets the first 4 bytes of rand1 to the current time
t = time(NULL);
t2 = (uint32_t)(t & 0xFFFFFFFF);
*(uint32_t *)rand1 = t2;
explen = hmaclen + keylen + ivlen;
prf_buf = calloc(explen + hmaclen, 1);
if (prf_buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
PRF(hashtype, explen, groupmaster, sizeof(groupmaster), "key expansion",
rand1, sizeof(rand1), prf_buf, &len);
memcpy(grouphmackey, prf_buf, hmaclen);
memcpy(groupkey, prf_buf + hmaclen, keylen);
memcpy(groupsalt, prf_buf + hmaclen + keylen, ivlen);
free(prf_buf);
if ((!strcmp(keyfile, "")) || (newkeylen != 0)) {
privkey = gen_RSA_key(newkeylen, RSA_EXP, keyfile);
} else {
privkey = read_RSA_key(keyfile);
}
if (!privkey) {
log0(0, 0, "Failed to read/generate private key");
exit(1);
}
rsalen = RSA_keylen(privkey);
}
/**
* Initialization based on command line args
*/
void initialize()
{
atexit(cleanup);
if (strcmp(logfile, "")) {
int fd;
if ((fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) {
perror("Can't open log file");
exit(1);
}
dup2(fd, 2);
close(fd);
showtime = 1;
}
applog = stderr;
key_init();
create_sockets();
// Size of non-data packets including message specific header.
// sizeof(ip) + sizeof(udp) = 20 + 8 = 28
payloadsize = mtu - 28 - sizeof(struct uftp_h);
// Size of encrypted packets
// Leaves room for expansion due to symmetric key block size
encpayloadsize = payloadsize - sizeof(struct encrypted_h) - KEYBLSIZE -
((sigtype == SIG_RSA) ? rsalen : hmaclen );
// Size of data block
blocksize = ((keytype != KEY_NONE) ? encpayloadsize : payloadsize) -
sizeof(struct fileseg_h);
// Never ask for a client key with no encryption,
// and always ask with RSA signatures
if (keytype == KEY_NONE) {
client_auth = 0;
} else if (sigtype == SIG_RSA) {
client_auth = 1;
}
if (rate == -1) {
packet_wait = 0;
} else {
packet_wait = (int32_t)(1000000.0 * mtu / ((float)rate * 1024 / 8));
}
}
uftp-3.5/server_main.c 0000644 0003316 0000550 00000003104 11346327251 014024 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2010 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "server_config.h"
#include "server_init.h"
#include "server_send.h"
int main(int argc, char *argv[])
{
int rval;
pre_initialize();
process_args(argc, argv);
initialize();
rval = send_files();
return rval ? 0 : 1;
}
uftp-3.5/server_phase.c 0000644 0003316 0000550 00000100156 11577014250 014203 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_common.h"
#include "server_announce.h"
#include "server_transfer.h"
/**
* Check an announce phase message and pass to appropriate message handler,
* decrypting first if necessary
* Returns 1 on success, 0 on error
*/
int handle_announce_phase(const unsigned char *packet, unsigned char *decrypted,
const struct sockaddr_in *receiver,
struct finfo_t *finfo,
int announce, int open, int regconf)
{
struct uftp_h *header;
const unsigned char *message;
int hostidx;
unsigned decryptlen, meslen;
uint8_t *func;
struct in_addr srcaddr;
header = (struct uftp_h *)packet;
hostidx = find_client(header->srcaddr);
if (header->srcaddr == 0) {
srcaddr = receiver->sin_addr;
} else {
srcaddr.s_addr = header->srcaddr;
}
if ((keytype != KEY_NONE) && (header->func == ENCRYPTED)) {
if (hostidx == -1) {
log(0, 0, "Got encrypted packet from unknown receiver %s",
inet_ntoa(srcaddr));
return 0;
}
if (!validate_and_decrypt(packet, &decrypted, &decryptlen, mtu,
keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, destlist[hostidx].pubkey,
destlist[hostidx].pubkeylen)) {
log1(0, 0, "Rejecting message from %s: decrypt/validate failed",
destlist[hostidx].name);
return 0;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((keytype != KEY_NONE) && (header->func == INFO_ACK)) {
log1(0, 0, "Rejecting %s message from %s: not encrypted",
func_name(header->func), inet_ntoa(srcaddr));
return 0;
}
func = (uint8_t *)&header->func;
message = packet + sizeof(struct uftp_h);
meslen = ntohs(header->blsize);
}
if (*func == ABORT) {
handle_abort(message, meslen, hostidx, finfo, &srcaddr);
return 1;
}
if (hostidx == -1) {
if (open) {
if (*func == REGISTER) {
handle_open_register(message, meslen, finfo, receiver,
&srcaddr, regconf);
} else if (*func == CLIENT_KEY) {
handle_open_clientkey(message, meslen, finfo, receiver,
&srcaddr);
} else {
log1(0, 0, "Invalid function: expected "
"REGISTER or CLIENT_KEY, got %s", func_name(*func));
}
} else {
log1(0, 0, "Host %s not in host list", inet_ntoa(srcaddr));
send_abort(finfo, "Not in host list", receiver, &srcaddr, 0);
}
} else {
switch (destlist[hostidx].status) {
case DEST_MUTE:
if (*func == REGISTER) {
handle_register(message, meslen, finfo, receiver,
&srcaddr, hostidx, regconf, open);
} else if (*func == CLIENT_KEY) {
handle_clientkey(message, meslen, finfo, receiver,
&srcaddr, hostidx);
} else {
log1(0, 0, "Invalid function: expected "
"REGISTER or CLIENT_KEY, got %s", func_name(*func));
}
break;
case DEST_REGISTERED:
if (*func == INFO_ACK) {
handle_info_ack(message, meslen, finfo, receiver,
&srcaddr, hostidx, announce);
} else if (*func == REGISTER) {
handle_register(message, meslen, finfo, receiver,
&srcaddr, hostidx, regconf, open);
} else if (*func == CLIENT_KEY) {
log(0, 0, "Received CLIENT_KEY+ from %s",
destlist[hostidx].name);
} else if (!announce && (*func == COMPLETE)) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
case DEST_ACTIVE:
if (*func == REGISTER) {
handle_register(message, meslen, finfo, receiver,
&srcaddr, hostidx, regconf, open);
} else if (*func == CLIENT_KEY) {
log(0, 0, "Received CLIENT_KEY+ from %s",
destlist[hostidx].name);
} else if (*func == INFO_ACK) {
finfo->deststate[hostidx].conf_sent = 0;
handle_info_ack(message, meslen, finfo, receiver,
&srcaddr, hostidx, announce);
} else if (!announce && (*func == COMPLETE)) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
default:
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
break;
}
}
return 1;
}
/**
* Perform the Announce/Register phase for a particular group/file
* Group & encryption: ->ANNOUNCE <-REGISTER ->KEYINFO <-INFO_ACK
* Group & no encryption: ->ANNOUNCE <-REGISTER ->REG_CONF
* Files within a group: ->FILEINFO <-INFO_ACK
* If client_key == 1, REGISTER is followed by CLIENT_KEY
* Returns 1 if at least one client responded, 0 if none responded
*/
int announce_phase(struct finfo_t *finfo)
{
time_t endtime;
int attempt, resend, announce, regconf, keyinfo, fileinfo, open, anyerror;
int len, rval, rcv_status, last_pass, gotall, gotone, allreg, regdone, i;
unsigned char *packet, *decrypted;
struct uftp_h *header;
struct timeval timeout;
struct sockaddr_in receiver;
if (finfo->file_id) {
log1(0, 0, "File ID: %04X Name: %s", finfo->file_id, finfo->filename);
log1(0, 0, " sending as: %s", finfo->destfname);
switch (finfo->ftype) {
case FTYPE_REG:
log(0, 0, "Bytes: %s Blocks: %d Sections: %d",
printll(finfo->size), finfo->blocks, finfo->sections);
break;
case FTYPE_DIR:
log(0, 0, "Empty directory");
break;
case FTYPE_LINK:
log(0, 0, "Symbolic link to %s", finfo->linkname);
break;
}
} else {
log(0, 0, "Initializing group");
if (sync_mode) {
log0(0, 0, "- Connect -");
}
}
rval = 1;
packet = calloc(mtu, 1);
decrypted = calloc(mtu, 1);
if ((packet == NULL) || (decrypted == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
endtime = time(NULL) + announce_time;
announce = (finfo->file_id == 0);
regconf = (announce && (keytype == KEY_NONE));
keyinfo = (announce && (keytype != KEY_NONE));
fileinfo = (finfo->file_id != 0);
open = (destcount == 0);
for (i = 0; i < destcount; i++) {
// At start of group, initialize all clients/proxies to DEST_MUTE.
// At start of file, initialize proxies to DEST_ACTIVE (since they
// don't respond directly to a FILEINFO) and clients to DEST_REGISTERED.
if (announce) {
destlist[i].status = DEST_MUTE;
} else if (!client_error(i)) {
if (destlist[i].clientcnt != -1) {
destlist[i].status = DEST_ACTIVE;
} else {
destlist[i].status = DEST_REGISTERED;
}
}
}
timeout.tv_sec = announce_int / 1000;
timeout.tv_usec = (announce_int % 1000) * 1000;
resend = 1;
attempt = 1;
last_pass = 0;
regdone = 0;
while (time(NULL) < endtime) {
// On the initial pass, or when the announce timeout trips,
// send any necessary messages.
if (resend) {
if (keyinfo && !send_keyinfo(finfo, attempt)) {
continue;
}
if (announce && !send_regconf(finfo, attempt, regconf)) {
continue;
}
if (fileinfo && !send_fileinfo(finfo, attempt)) {
continue;
}
if (announce && !send_announce(finfo, attempt, open)) {
continue;
}
resend = 0;
}
// TODO: Currently, the interval between sends is really an inactivity
// timer, not the actual time between sends. We might want to change
// it to that, and perhaps add an extra "overage" timer in case we're
// still processing responses when we're due to resend, that way we'll
// always wait some minimum amount of time.
if ((rcv_status = read_packet(sock, &receiver, packet, &len,
mtu, &timeout)) == -1) {
continue;
} else if (rcv_status == 0) {
attempt++;
resend = 1;
if (last_pass) break;
continue;
}
if (!validate_packet(packet, len, finfo)) {
continue;
}
if (!handle_announce_phase(packet, decrypted, &receiver, finfo,
announce, open, regconf)) {
continue;
}
if (!open) {
for (i = 0, gotall = 1, allreg = 1;
(i < destcount) && (gotall || allreg); i++) {
if (announce) {
gotall = gotall && ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_ABORT));
allreg = allreg && ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_REGISTERED) ||
(destlist[i].status == DEST_ABORT));
} else {
gotall = gotall && ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_DONE) ||
(client_error(i)));
}
}
if (gotall) {
// Break out right away if this is a file registration.
// For group registration, do one last wait, even if
// encryption is enabled since we can still send a
// REG_CONF for a client behind a proxy.
// Change the wait interval to the client's register_int * 1.5
// to allow for late registers.
// Be careful not to overrun the phase timeout!
if (finfo->file_id != 0) break;
timeout.tv_sec = (int)(register_int / 1000 * 1.5);
timeout.tv_usec = (int)((register_int % 1000) * 1000 * 1.5);
if (timeout.tv_sec > endtime) {
#ifdef WINDOWS
timeout.tv_sec = (long)endtime;
#else
timeout.tv_sec = endtime;
#endif
timeout.tv_usec = 0;
}
if (!last_pass) {
log(0, 0, "Late registers:");
}
last_pass = 1;
send_regconf(finfo, attempt + 1, regconf);
} else if (announce && allreg && !regdone) {
// All have registered, so don't wait to send the next message
resend = 1;
regdone = 1;
}
}
}
for (i = 0, gotone = 0, anyerror = 0; i < destcount; i++) {
gotone = gotone || (((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_DONE)) &&
(destlist[i].clientcnt == -1));
if (destlist[i].status == DEST_REGISTERED) {
log1(0, 0, "Couldn't get INFO_ACK from %s", destlist[i].name);
destlist[i].status = DEST_LOST;
anyerror = 1;
}
if ((destlist[i].status == DEST_MUTE) ||
(destlist[i].status == DEST_ABORT)) {
anyerror = 1;
}
}
if (anyerror && quit_on_error) {
log0(0, 0, "Aboring all clients");
send_abort(finfo, "A client dropped out, aborting all",
&receive_dest, NULL, (keytype != KEY_NONE));
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
destlist[i].status = DEST_ABORT;
}
}
rval = 0;
}
if (!gotone) {
log0(0, 0, "Announce timed out");
rval = 0;
}
if (open) {
send_regconf(finfo, attempt, regconf);
}
if ((finfo->file_id == 0) && sync_mode) {
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
log0(0, 0, "CONNECT;success;%s", destlist[i].name);
} else {
log0(0, 0, "CONNECT;failed;%s", destlist[i].name);
}
}
log0(0, 0, "- Transfer -");
}
free(packet);
free(decrypted);
return rval;
}
/**
* Check a transfer phase message and pass to appropriate message handler,
* decrypting first if necessary
* Returns 1 on success, 0 on error
*/
int handle_transfer_phase(const unsigned char *packet, unsigned char *decrypted,
const struct sockaddr_in *receiver,
int blocks_this_sec, int section_offset,
int pass, int section, struct finfo_t *finfo)
{
struct uftp_h *header;
const unsigned char *message;
int hostidx;
unsigned int decryptlen, meslen;
uint8_t *func;
struct in_addr srcaddr;
header = (struct uftp_h *)packet;
hostidx = find_client(header->srcaddr);
srcaddr.s_addr = header->srcaddr;
if ((keytype != KEY_NONE) && (header->func == ENCRYPTED)) {
if (hostidx == -1) {
log1(0, 0, "Host %s not in host list", inet_ntoa(srcaddr));
send_abort(finfo, "Not in host list", receiver, &srcaddr, 0);
return 0;
}
if (!validate_and_decrypt(packet, &decrypted, &decryptlen, mtu,
keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, destlist[hostidx].pubkey,
destlist[hostidx].pubkeylen)) {
log1(0, 0, "Rejecting message from %s: decrypt/validate failed",
destlist[hostidx].name);
return 0;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((keytype != KEY_NONE) && ((header->func == PRSTATUS) ||
(header->func == STATUS) || (header->func == COMPLETE) ||
(header->func == ABORT))) {
log1(0, 0, "Rejecting %s message from %s: not encrypted",
func_name(header->func), inet_ntoa(srcaddr));
return 0;
}
func = (uint8_t *)&header->func;
message = packet + sizeof(struct uftp_h);
meslen = ntohs(header->blsize);
}
if (*func == ABORT) {
handle_abort(message, meslen, hostidx, finfo, &srcaddr);
} else if (hostidx == -1) {
log1(0, 0, "Host %s not in host list", inet_ntoa(srcaddr));
send_abort(finfo, "Not in host list", receiver, &srcaddr, 0);
} else {
switch (destlist[hostidx].status) {
case DEST_ACTIVE:
if (*func == STATUS) {
handle_status(message, meslen, finfo, hostidx,
blocks_this_sec, section_offset, pass, section);
} else if (*func == COMPLETE) {
handle_complete(message, meslen, finfo, hostidx);
} else if ((destlist[hostidx].clientcnt != -1) &&
(*func == PRSTATUS)) {
handle_prstatus(message, meslen, finfo, hostidx,
blocks_this_sec, section_offset, pass, section);
} else {
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
case DEST_STATUS:
if (*func == COMPLETE) {
handle_complete(message, meslen, finfo, hostidx);
} else if (*func == STATUS) {
handle_status(message, meslen, finfo, hostidx,
blocks_this_sec, section_offset, pass, section);
} else {
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
case DEST_DONE:
if (*func == COMPLETE) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(0, 0, "Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
}
}
return 1;
}
/**
* Request NAKs from all clients.
* Returns the aggregate number of NAKs for the section.
*/
int get_naks(struct finfo_t *finfo, unsigned int pass, unsigned int section,
int *alldone)
{
unsigned char *packet, *decrypted;
struct uftp_h *header;
struct timeval timeout;
struct sockaddr_in receiver;
int resend, attempt, last_pass, gotall, anyerror;
int blocks_this_sec, section_offset;
int rcv_status, len, nakidx, numnaks, i, j;
time_t endtime;
packet = calloc(mtu, 1);
decrypted = calloc(mtu, 1);
if ((packet == NULL) || (decrypted == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
section_offset = (blocksize * 8) * (section - 1);
blocks_this_sec = ((section < finfo->sections) ? (blocksize * 8) :
(finfo->blocks % (blocksize * 8)));
if (finfo->sections && !blocks_this_sec) blocks_this_sec = blocksize * 8;
for (i = 0; i < blocks_this_sec; i++) {
nakidx = i + section_offset;
finfo->naklist[nakidx] = 0;
}
for (i = 0; i < destcount; i++) {
if (client_error(i)) {
continue;
}
if (destlist[i].clientcnt != -1) {
destlist[i].status = DEST_ACTIVE;
} else if (destlist[i].status == DEST_ACTIVE) {
destlist[i].status = DEST_STATUS;
}
free(destlist[i].last_status);
destlist[i].last_status = NULL;
for (j = 0; j < destlist[i].last_prstatus_cnt; j++) {
free(destlist[i].last_prstatus[j]);
destlist[i].last_prstatus[j] = NULL;
}
destlist[i].last_prstatus_cnt = 0;
}
endtime = time(NULL) + status_time;
timeout.tv_sec = status_int / 1000;
timeout.tv_usec = (status_int % 1000) * 1000;
resend = 1;
attempt = 1;
gotall = 0;
last_pass = 0;
while ((time(NULL) < endtime) && (!gotall)) {
if (resend) {
if (!send_doneconf(finfo, attempt)) {
continue;
}
if (!send_done(finfo, attempt, pass, section)) {
continue;
}
resend = 0;
}
// See comments in announce_phase regarding timing
if ((rcv_status = read_packet(sock, &receiver, packet, &len,
mtu, &timeout)) == -1) {
continue;
} else if (rcv_status == 0) {
attempt++;
resend = 1;
if (last_pass) break;
continue;
}
if (!validate_packet(packet, len, finfo)) {
continue;
}
if (!handle_transfer_phase(packet, decrypted, &receiver,
blocks_this_sec, section_offset, pass, section, finfo)) {
continue;
}
for (i = 0, gotall = 1, *alldone = 1;
(i < destcount) && (gotall || *alldone); i++) {
gotall = gotall && (destlist[i].status != DEST_STATUS);
*alldone = *alldone && ((destlist[i].status == DEST_DONE) ||
(client_error(i)) || (destlist[i].clientcnt != -1));
}
if (*alldone) {
if (finfo->file_id != 0) break;
// Change the wait interval to the client's done_int * 1.5
// to allow for late completions
timeout.tv_sec = (int)(done_int / 1000 * 1.5);
timeout.tv_usec = (int)((done_int % 1000) * 1000 * 1.5);
if (!last_pass) {
log(0, 0, "Late completions:");
}
last_pass = 1;
gotall = 0;
send_doneconf(finfo, attempt + 1);
}
}
anyerror = 0;
if (*alldone) {
send_doneconf(finfo, attempt + 1);
} else if (!gotall) {
for (i = 0, *alldone = 1; i < destcount; i++) {
if (destlist[i].status == DEST_STATUS) {
log1(0, 0, "Couldn't get STATUS from %s", destlist[i].name);
destlist[i].status = DEST_LOST;
anyerror = 1;
}
*alldone = *alldone && ((destlist[i].status == DEST_DONE) ||
(client_error(i)) || (destlist[i].clientcnt != -1));
}
}
if (anyerror && quit_on_error) {
log0(0, 0, "Aboring all clients");
send_abort(finfo, "A client dropped out, aborting all",
&receive_dest, NULL, (keytype != KEY_NONE));
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
destlist[i].status = DEST_ABORT;
}
}
*alldone = 1;
}
for (i = 0, numnaks = 0; i < blocks_this_sec; i++) {
nakidx = i + section_offset;
if (finfo->naklist[nakidx]) {
numnaks++;
}
}
free(packet);
free(decrypted);
return numnaks;
}
/**
* Adjust the sending rate based on percentage of NAKs received to blocks sent
*/
void adjust_rate(int naks, int blocks)
{
int percentage, i;
percentage = naks * 100 / blocks;
for (i = 0; i < cc_count; i++) {
if (cc_list[i].percentage >= percentage) {
rate = (int)(rate * cc_list[i].scaling_factor);
if (rate > max_rate) rate = max_rate;
packet_wait = (int32_t)(1000000.0 * mtu / ((float)rate * 1024 / 8));
log(0, 0, "Updated rate: %d Kbps (%d KB/s)", rate, rate / 8);
log(0, 0, "Wait between packets: %d us", packet_wait);
break;
}
}
}
/**
* Seeks to a particular block in a file
* Returns 1 on success, 0 on error
*/
int seek_block(int file, int block, f_offset_t *offset, f_offset_t curr_offset)
{
if ((*offset = lseek_func(file,
((f_offset_t)block * blocksize) - curr_offset, SEEK_CUR)) == -1) {
syserror(0, 0, "lseek failed for file");
return 0;
}
if (*offset != (f_offset_t)block * blocksize) {
log0(0, 0, "block %d: offset is %s", block, printll(*offset));
log0(0, 0, " should be %s", printll((f_offset_t)block * blocksize));
if ((*offset = lseek_func(file,
((f_offset_t)block * blocksize) - (*offset), SEEK_CUR)) == -1) {
syserror(0, 0, "lseek failed for file");
return 0;
}
}
return 1;
}
/**
* Performs the Transfer phase for a particular file
* Returns 1 if at least one client finished, 0 if all are dropped or aborted
*/
int transfer_phase(struct finfo_t *finfo)
{
unsigned char *packet, *encpacket, *data;
char path[MAXPATHNAME];
struct uftp_h *header;
struct fileseg_h *fileseg;
int max_time, alldone, numbytes, sent_blocks, current_naks;
unsigned int pass, section, numnaks, block;
struct timeval start_time, last_sent, current_sent;
int64_t avgwait, waitcnt, overage, tdiff;
f_offset_t offset, curr_offset;
int file, i;
if (finfo->file_id != 0) {
// First check to see if all clients are already done for this file.
// This can happen on a restart when the file finished on the
// last attempt and responded to the FILEINFO with a COMPLETE
for (i = 0, alldone = 1; (i < destcount) && alldone; i++) {
alldone = alldone && ((destlist[i].status == DEST_DONE) ||
(client_error(i)) || (destlist[i].clientcnt != -1));
}
if (alldone) {
gettimeofday(&start_time, NULL);
print_status(finfo, start_time);
return 1;
}
}
// If rate is -1, use 100Mbps for purpose of calculating max time
max_time = (int)floor(((double)weight / 100) *
((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024));
if (max_time < min_time) {
max_time = min_time;
}
if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
log(0, 0, "Maximum file transfer time: %d seconds", max_time);
snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP,
finfo->filename);
if ((file = open(path, OPENREAD, 0)) == -1) {
syserror(0, 0, "Error opening file");
return 1;
}
} else {
// At end of group, all non-errored client are DEST_DONE from the
// last file, so reset them to DEST_ACTIVE to get the final COMPLETE.
for (i = 0; i < destcount; i++) {
if (!client_error(i)) {
destlist[i].status = DEST_ACTIVE;
}
}
}
packet = calloc(mtu, 1);
encpacket = calloc(mtu, 1);
if ((packet == NULL) || (encpacket == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)packet;
fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
data = (unsigned char *)fileseg + sizeof(struct fileseg_h);
set_uftp_header(header, FILESEG, finfo, &receive_dest);
pass = 1;
alldone = 0;
gettimeofday(&start_time, NULL);
do {
avgwait = 0;
waitcnt = 0;
numnaks = 0;
overage = 0;
section = 1;
curr_offset = 0;
sent_blocks = 0;
gettimeofday(&last_sent, NULL);
if (finfo->file_id != 0) {
log(0, 0, "Sending file...pass %d", pass);
lseek_func(file, 0, SEEK_SET);
} else {
log(0, 0, "Finishing group");
}
fileseg->func = FILESEG;
fileseg->file_id = htons(finfo->file_id);
fileseg->pass = pass;
fileseg->section = htons(section);
for (block = 0; block < finfo->blocks; block++) {
// If all clients received this file partially on a prior attempt
// and it's the first pass, request NAKs for all sections
// right away and don't send any data packets.
if (((pass == 1) || finfo->naklist[block]) &&
!((pass == 1) && finfo->partial)) {
if (diff_sec(last_sent, start_time) > max_time) {
log0(0, 0, "Max file transfer time exceeded");
send_abort(finfo, "Max file transfer time exceeded",
&receive_dest, NULL, (keytype != KEY_NONE));
alldone = 1;
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
destlist[i].status = DEST_ABORT;
}
}
break;
}
// On the first pass, go straight through the file.
// On later passes, seek to the next packet.
if (pass != 1) {
log4(0, 0, "Resending %d", block);
if (!seek_block(file, block, &offset, curr_offset)) {
continue;
}
}
if ((numbytes = read(file, data, blocksize)) == -1) {
syserror(0, 0, "read failed");
continue;
}
if (pass != 1) {
curr_offset = offset + numbytes;
}
// Keep track of how long we really slept compared to how
// long we expected to sleep. If we went over, subtract the
// time over from the next sleep time. This way we maintain
// the proper average sleep time. This can result in multiple
// packets going out at once, potentially losing packets.
if (packet_wait > overage) {
usleep(packet_wait - (int32_t)overage);
}
gettimeofday(¤t_sent, NULL);
tdiff = diff_usec(current_sent, last_sent);
avgwait += tdiff;
waitcnt++;
if (packet_wait) overage += tdiff - packet_wait;
last_sent = current_sent;
fileseg->seq_num = htonl(block);
send_data(finfo, packet, numbytes, encpacket);
sent_blocks++;
}
if ((block % (blocksize * 8) == (blocksize * 8) - 1) ||
(block == finfo->blocks - 1)) {
if ((pass == 1) || sent_blocks) {
current_naks = get_naks(finfo, pass, section, &alldone);
numnaks += current_naks;
if ((rate != -1) && (cc_count > 0)) {
adjust_rate(current_naks, sent_blocks);
}
overage = 0;
if (alldone) break;
}
sent_blocks = 0;
gettimeofday(&last_sent, NULL);
fileseg->section = htons(++section);
}
}
if ((finfo->size == 0) && !alldone) {
// If it's the end of the group, or an empty file, a DONE was
// never sent, so send it now
numnaks += get_naks(finfo, pass, section, &alldone);
}
if (finfo->file_id != 0) {
log(0, 0, "Average wait time = %.2f us",
(waitcnt == 0) ? 0 : (float)avgwait / waitcnt);
log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass);
}
pass++;
} while (!alldone);
if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
close(file);
}
print_status(finfo, start_time);
free(packet);
free(encpacket);
for (i = 0; i < destcount; i++) {
if (quit_on_error) {
// Check to see that all finished
if (destlist[i].status != DEST_DONE) {
return 0;
}
} else {
// Check to see if at least one finished
if (destlist[i].status == DEST_DONE) {
return 1;
}
}
}
if (quit_on_error) {
return 1;
} else {
return 0;
}
}
uftp-3.5/server_send.c 0000644 0003316 0000550 00000041374 11577014247 014050 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_send.h"
#include "server_phase.h"
#include "server_common.h"
/**
* Checks to see if a file/directory is in the exclude list
*/
int file_excluded(const char *filename)
{
int found, i;
for (found = 0, i = 0; (i < excludecount) && !found; i++) {
if (!strcmp(filename, exclude[i])) {
found = 1;
}
}
return found;
}
/**
* Performs the send for a particular file/directory. If a directory is
* specified, get the list of files and call recursively for each.
* Returns 0 if a file was sent and none received it, 1 otherwise
*/
int send_file(const char *basedir, const char *filename,
const char *n_destfname, uint32_t group_id)
{
static uint16_t file_id = 1;
struct finfo_t finfo;
stat_struct statbuf;
char path[MAXPATHNAME], destpath[MAXPATHNAME];
int len, rval, fd, emptydir;
log1(0, 0, "----- %s -----", filename);
len = snprintf(path, sizeof(path), "%s%c%s", basedir, PATH_SEP, filename);
if ((len >= sizeof(path)) || (len == -1)) {
log1(0, 0, "Max pathname length exceeded: %s%c%s",
basedir, PATH_SEP, filename);
return 1;
}
if (follow_links) {
rval = stat_func(path, &statbuf);
} else {
rval = lstat_func(path, &statbuf);
}
if (rval == -1) {
syserror(0, 0, "Error getting file status for %s", filename);
return 1;
}
if (file_excluded(filename)) {
log0(0, 0, "Skipping %s", filename);
return 1;
}
rval = 1;
if (S_ISREG(statbuf.st_mode)) {
if ((fd = open(path, OPENREAD, 0)) == -1) {
syserror(0, 0, "Error reading file %s", filename);
return 1;
}
close(fd);
memset(&finfo, 0, sizeof(struct finfo_t));
finfo.ftype = FTYPE_REG;
finfo.basedir = basedir;
finfo.filename = filename;
finfo.destfname = n_destfname;
finfo.group_id = group_id;
finfo.file_id = file_id++;
if (file_id == 0) {
file_id = 1;
}
finfo.size = statbuf.st_size;
finfo.tstamp = statbuf.st_mtime;
finfo.blocks = (int32_t)((finfo.size / blocksize) +
(finfo.size % blocksize ? 1 :0));
finfo.sections = (finfo.blocks / (blocksize * 8)) +
(finfo.blocks % (blocksize * 8) ? 1 : 0);
finfo.naklist = calloc(finfo.blocks, 1);
finfo.deststate = calloc(destcount ? destcount : MAXDEST,
sizeof(struct deststate_t));
if ((finfo.naklist == NULL) || (finfo.deststate == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
finfo.partial = 1;
rval = announce_phase(&finfo);
if (rval) {
rval = transfer_phase(&finfo);
}
free(finfo.deststate);
free(finfo.naklist);
#ifndef WINDOWS
} else if (S_ISLNK(statbuf.st_mode)) {
char linkname[MAXPATHNAME];
memset(linkname, 0, sizeof(linkname));
if (readlink(path, linkname, sizeof(linkname)-1) == -1) {
syserror(0, 0, "Failed to read symbolic link %s", path);
return 1;
}
// Both the file name and the link have to fit into a fileinfo_h.name
if (strlen(linkname) + strlen(filename) + 2 > MAXPATHNAME) {
log0(0, 0, "Combined file name %s and link %s too long",
filename, linkname);
return 1;
}
memset(&finfo, 0, sizeof(struct finfo_t));
finfo.ftype = FTYPE_LINK;
finfo.basedir = basedir;
finfo.filename = filename;
finfo.destfname = n_destfname;
finfo.linkname = linkname;
finfo.group_id = group_id;
finfo.file_id = file_id++;
if (file_id == 0) {
file_id = 1;
}
finfo.deststate = calloc(destcount ? destcount : MAXDEST,
sizeof(struct deststate_t));
if (finfo.deststate == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
finfo.partial = 1;
rval = announce_phase(&finfo);
if (rval) {
rval = transfer_phase(&finfo);
}
#endif
} else if (S_ISDIR(statbuf.st_mode)) {
// read directory and do recursive send
#ifdef WINDOWS
intptr_t ffhandle;
struct _finddatai64_t ffinfo;
char dirglob[MAXPATHNAME];
snprintf(dirglob, sizeof(dirglob), "%s%c%s%c*", basedir, PATH_SEP,
filename, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &ffinfo)) == -1) {
syserror(0, 0, "Failed to open directory %s%c%s", basedir, PATH_SEP,
filename);
return 1;
}
emptydir = 1;
do {
len = snprintf(path, sizeof(path), "%s/%s", filename, ffinfo.name);
log3(0, 0, "Checking file %s", path);
if ((len >= sizeof(path)) || (len == -1)) {
log0(0, 0, "Max pathname length exceeded: %s/%s",
filename, ffinfo.name);
continue;
}
len = snprintf(destpath, sizeof(destpath), "%s/%s",
n_destfname, ffinfo.name);
if ((len >= sizeof(destpath)) || (len == -1)) {
log0(0, 0, "Max pathname length exceeded: %s/%s",
n_destfname, ffinfo.name);
continue;
}
if (strcmp(ffinfo.name, ".") && strcmp(ffinfo.name, "..")) {
emptydir = 0;
if (!send_file(basedir, path, destpath, group_id)) {
rval = 0;
break;
}
}
} while (_findnexti64(ffhandle, &ffinfo) == 0);
_findclose(ffhandle);
#else
DIR *dir;
struct dirent *de;
char dirname[MAXPATHNAME];
snprintf(dirname, sizeof(dirname), "%s%c%s", basedir,PATH_SEP,filename);
if ((dir = opendir(dirname)) == NULL) {
syserror(0, 0, "Failed to open directory %s", dirname);
return 1;
}
// errno needs to be set to 0 before calling readdir, otherwise
// we'll report a false error when we exhaust the directory
emptydir = 1;
while ((errno = 0, de = readdir(dir)) != NULL) {
len = snprintf(path, sizeof(path), "%s/%s", filename, de->d_name);
if ((len >= sizeof(path)) || (len == -1)) {
log0(0, 0, "Max pathname length exceeded: %s/%s",
filename, de->d_name);
continue;
}
len = snprintf(destpath, sizeof(destpath), "%s/%s",
n_destfname, de->d_name);
if ((len >= sizeof(destpath)) || (len == -1)) {
log0(0, 0, "Max pathname length exceeded: %s/%s",
n_destfname, de->d_name);
continue;
}
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
emptydir = 0;
if (!send_file(basedir, path, destpath, group_id)) {
rval = 0;
break;
}
}
}
if (errno && (errno != ENOENT)) {
syserror(0, 0, "Failed to read directory %s", filename);
}
closedir(dir);
#endif
if (emptydir) {
memset(&finfo, 0, sizeof(struct finfo_t));
finfo.ftype = FTYPE_DIR;
finfo.basedir = basedir;
finfo.filename = filename;
finfo.destfname = n_destfname;
finfo.group_id = group_id;
finfo.file_id = file_id++;
if (file_id == 0) {
file_id = 1;
}
finfo.deststate = calloc(destcount ? destcount : MAXDEST,
sizeof(struct deststate_t));
if (finfo.deststate == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
finfo.partial = 1;
rval = announce_phase(&finfo);
if (rval) {
rval = transfer_phase(&finfo);
}
}
} else {
log0(0, 0, "Skipping special file %s", filename);
}
return rval;
}
/**
* Write a restart file entry for a particular client.
* Returns 1 on success, o on fail.
*/
int write_restart_host(int fd, int i)
{
struct server_restart_host_t host;
memset(&host, 0, sizeof(host));
strcpy(host.name, destlist[i].name);
host.addr = destlist[i].addr;
if (destlist[i].has_fingerprint) {
host.has_fingerprint = 1;
memcpy(host.keyfingerprint, destlist[i].keyfingerprint,
HMAC_LEN);
}
host.is_proxy = (destlist[i].clientcnt != -1);
if (file_write(fd, &host, sizeof(host)) == -1) {
log0(0, 0, "Failed to write host for restart file");
return 0;
}
return 1;
}
/**
* Save the state of a failed transfer so it can restarted later.
*/
void write_restart_file(uint32_t group_id)
{
struct server_restart_t header;
char restart_name[MAXFILENAME];
char proxy_listed[MAXPROXYDEST];
int fd, opened, i, j, proxycnt, found;
memset(proxy_listed, 0, sizeof(proxy_listed));
opened = 0;
proxycnt = 0;
for (i = 0; i < destcount; i++) {
if ((destlist[i].clientcnt == -1) && client_error(i)) {
if (!opened) {
snprintf(restart_name, sizeof(restart_name),
"_group_%08X_restart", group_id);
if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC,
0644)) == -1) {
syserror(0, 0, "Failed to create restart file");
return;
}
// Write header
header.group_id = group_id;
header.filecount = filecount;
if (file_write(fd, &header, sizeof(header)) == -1) {
log0(0, 0, "Failed to write header for restart file");
goto errexit;
}
// Write file list
for (j = 0; j < filecount; j++) {
if (file_write(fd, filelist[j],sizeof(filelist[j])) == -1) {
log0(0, 0, "Failed to write filename for restart file");
goto errexit;
}
}
opened = 1;
}
if (!write_restart_host(fd, i)) {
goto errexit;
}
if (destlist[i].proxyidx != -1) {
for (j = 0, found = 0; (j < proxycnt) && !found; j++) {
if (proxy_listed[j] == destlist[i].proxyidx) {
found = 1;
}
}
if (!found) {
if (!write_restart_host(fd, destlist[i].proxyidx)) {
goto errexit;
}
proxy_listed[proxycnt++] = destlist[i].proxyidx;
}
}
}
}
if (opened) {
close(fd);
}
return;
errexit:
close(fd);
unlink(restart_name);
}
/**
* The main sending function. Goes through all files/diectories specified on
* the command line and initializes the group for multiple files.
*/
int send_files()
{
int i, j, rval, len, found_base;
struct finfo_t group_info;
char *dir, *base;
time_t t;
char path[MAXPATHNAME], l_destfname[MAXPATHNAME];
t = time(NULL);
if (!showtime) fprintf(applog, "\n");
log0(0, 0, "%s", VERSIONSTR);
if (!showtime) clog0(0, 0, "Starting at %s", ctime(&t));
if (privkey) {
log1(0, 0, "Loaded %d bit key with fingerprint %s",
RSA_keylen(privkey) * 8, print_key_fingerprint(privkey));
}
if (rate == -1) {
log(0, 0, "Transfer rate: full interface speed");
} else {
log(0, 0, "Transfer rate: %d Kbps (%d KB/s)", rate, rate / 8);
log(0, 0, "Wait between packets: %d us", packet_wait);
}
memset(&group_info, 0, sizeof(struct finfo_t));
if (restart_groupid) {
group_info.group_id = restart_groupid;
} else {
group_info.group_id = rand() & 0xFFFF;
group_info.group_id |= (rand() & 0xFFFF) << 16;
}
group_info.deststate = calloc(destcount ? destcount : MAXDEST,
sizeof(struct deststate_t));
if (group_info.deststate == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
log(0, 0, "Using private multicast address %s Group ID: %08X",
inet_ntoa(receive_dest.sin_addr), group_info.group_id);
rval = announce_phase(&group_info);
if (rval) {
rval = 0;
for (i = 0; i < filecount; i++) {
split_path(filelist[i], &dir, &base);
if (basedircount > 0) {
for (found_base = 0, j = 0; j < basedircount; j++) {
#ifdef WINDOWS
if (!strnicmp(basedir[j],filelist[i], strlen(basedir[j]))) {
#else
if (!strncmp(basedir[j], filelist[i], strlen(basedir[j]))) {
#endif
found_base = 1;
break;
}
}
if (!found_base) {
log0(0, 0, "Skipping %s: doesn't match any base",
filelist[i]);
free(dir);
free(base);
continue;
}
strncpy(l_destfname, filelist[i] + strlen(basedir[j]),
sizeof(l_destfname)-1);
} else {
strncpy(l_destfname, base, sizeof(l_destfname)-1);
}
#if PATH_SEP != '/'
// Translate any PATH_SEP in the sent file name to '/'
{
char *p;
while ((p = strchr(l_destfname, PATH_SEP)) != NULL) {
*p = '/';
}
}
#endif
if (strcmp(destfname, "")) {
if ((filecount > 1) || dest_is_dir) {
len = snprintf(path, sizeof(path), "%s/%s",
destfname, l_destfname);
if ((len >= sizeof(path)) || (len == -1)) {
log0(0, 0, "Max pathname length exceeded: %s/%s",
destfname, base);
free(dir);
free(base);
continue;
}
rval = send_file(dir, base, path, group_info.group_id);
} else {
rval = send_file(dir, base, destfname, group_info.group_id);
}
} else {
rval = send_file(dir, base, l_destfname, group_info.group_id);
}
free(dir);
free(base);
if (!rval) {
break;
}
}
if (rval) {
log1(0, 0, "-----------------------------");
transfer_phase(&group_info);
}
}
if (save_fail) {
write_restart_file(group_info.group_id);
}
free(group_info.deststate);
t = time(NULL);
if (!showtime) clog0(0, 0, "uftp: Finishing at %s", ctime(&t));
return rval;
}
uftp-3.5/server_transfer.c 0000644 0003316 0000550 00000051676 11577014247 014751 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#ifdef WINDOWS
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#endif
#include "server.h"
#include "server_common.h"
#include "server_transfer.h"
/**
* Send out DONE_CONF messages specifiying all completed clients.
* Returns 1 on success, 0 on fail
*/
int send_doneconf(const struct finfo_t *finfo, int attempt)
{
unsigned char *buf;
struct uftp_h *header;
struct doneconf_h *doneconf;
uint32_t *addrlist;
int rval;
if (finfo->file_id != 0) {
return 1;
}
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
doneconf = (struct doneconf_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)doneconf + sizeof(struct doneconf_h));
set_uftp_header(header, DONE_CONF, finfo, &receive_dest);
doneconf->func = DONE_CONF;
doneconf->file_id = htons(finfo->file_id);
rval = send_multiple(finfo, buf, DONE_CONF, attempt, addrlist, DEST_DONE,
&doneconf->destcount, (keytype != KEY_NONE), &receive_dest, 0);
free(buf);
return rval;
}
/**
* Send out DONE messages specifiying active clients that haven't yet responded.
* Returns 1 on success, 0 on fail
*/
int send_done(const struct finfo_t *finfo, int attempt, int pass, int section)
{
unsigned char *buf;
struct uftp_h *header;
struct done_h *done;
uint32_t *addrlist;
int rval;
buf = calloc(mtu, 1);
if (buf == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
header = (struct uftp_h *)buf;
done = (struct done_h *)(buf + sizeof(struct uftp_h));
addrlist = (uint32_t *)((char *)done + sizeof(struct done_h));
set_uftp_header(header, DONE, finfo, &receive_dest);
done->func = DONE;
done->file_id = htons(finfo->file_id);
done->pass = pass;
done->section = htons(section);
rval = send_multiple(finfo, buf, DONE, attempt, addrlist, DEST_STATUS,
&done->destcount, (keytype != KEY_NONE), &receive_dest, 0);
free(buf);
return rval;
}
/**
* Process an expected COMPLETE message
*/
void handle_complete(const unsigned char *message, int meslen,
struct finfo_t *finfo, int hostidx)
{
struct complete_h *complete;
uint32_t *addrlist;
int clientcnt, clientidx, dupmsg, isproxy, i;
char status[20];
complete = (struct complete_h *)message;
addrlist = (uint32_t *)((char *)complete + sizeof(struct complete_h));
clientcnt = ntohs(complete->destcount);
if (meslen != sizeof(struct complete_h) +
(clientcnt * sizeof(uint32_t))) {
log1(0, 0, "Rejecting COMPLETE from %s: invalid message size",
destlist[hostidx].name);
return;
}
if (ntohs(complete->file_id) != finfo->file_id) {
log1(0, 0, "Rejecting COMPLETE from %s: invalid file ID %04X, "
"expected %04X ", destlist[hostidx].name,
ntohs(complete->file_id), finfo->file_id);
return;
}
dupmsg = (destlist[hostidx].status == DEST_DONE);
isproxy = (destlist[hostidx].clientcnt != -1);
destlist[hostidx].comp_status = complete->status;
switch (complete->status) {
case COMP_STAT_NORMAL:
strcpy(status,"");
break;
case COMP_STAT_SKIPPED:
strcpy(status,"(skipped)");
break;
case COMP_STAT_OVERWRITE:
strcpy(status,"(overwritten)");
break;
case COMP_STAT_REJECTED:
strcpy(status,"(rejected)");
break;
}
log(0, 0, "Got COMPLETE%s%s from %s %s", status,
(dupmsg && !isproxy) ? "+" : "",
(isproxy) ? "proxy" : "client", destlist[hostidx].name);
if (destlist[hostidx].clientcnt != -1) {
for (i = 0; i < clientcnt; i++) {
clientidx = find_client(addrlist[i]);
if (clientidx == -1) {
log1(0, 0, "Client %s via proxy %s not found",
inet_ntoa(to_addr(addrlist[i])),
destlist[hostidx].name);
} else if (destlist[clientidx].proxyidx != hostidx) {
log1(0, 0, "Client %s found via proxy %s, expected proxy %s",
destlist[clientidx].name,
destlist[destlist[clientidx].proxyidx].name,
destlist[hostidx].name);
} else {
dupmsg = (destlist[clientidx].status == DEST_DONE);
log(0, 0, " For client%s %s", dupmsg ? "+" : "",
destlist[clientidx].name);
finfo->deststate[clientidx].conf_sent = 0;
destlist[clientidx].status = DEST_DONE;
gettimeofday(&finfo->deststate[clientidx].time, NULL);
}
}
} else {
finfo->deststate[hostidx].conf_sent = 0;
destlist[hostidx].status = DEST_DONE;
gettimeofday(&finfo->deststate[hostidx].time, NULL);
}
}
/**
* Apply the given NAKs to the naklist
*/
void apply_naks(unsigned char *naklist, struct finfo_t *finfo,
int blocks_this_sec, int section_offset)
{
int nakidx, listidx, i;
for (i = 0; i < blocks_this_sec; i++) {
// Each bit represents a NAK; check each one
// Simplified: (naklist[listidx / 8] & (1 << (listidx % 8)))
nakidx = i + section_offset;
listidx = i;
if ((naklist[listidx >> 3] & (1 << (listidx & 7))) != 0) {
log4(0, 0, "Got NAK for %d", nakidx);
finfo->naklist[nakidx] = 1;
}
}
}
/**
* Mark clients as having sent status as specified by PRSTATUS
*/
void set_proxy_status(struct finfo_t *finfo, const unsigned char *data,
int hostidx, int nak_count)
{
struct prstatus_h *prstatus;
uint32_t *addrlist;
int clientcnt, clientidx, dupmsg, i;
prstatus = (struct prstatus_h *)data;
addrlist = (uint32_t *)(data + sizeof(struct prstatus_h));
clientcnt = ntohs(prstatus->destcount);
for (i = 0; i < clientcnt; i++) {
clientidx = find_client(addrlist[i]);
if (clientidx == -1) {
log1(0, 0, "Client %s via proxy %s not found",
inet_ntoa(to_addr(addrlist[i])), destlist[hostidx].name);
} else if (destlist[clientidx].proxyidx != hostidx) {
log1(0, 0, "Client %s found via proxy %s, expected proxy %s",
destlist[clientidx].name,
destlist[destlist[clientidx].proxyidx].name,
destlist[hostidx].name);
} else if (!client_error(clientidx)) {
dupmsg = (destlist[clientidx].status == DEST_ACTIVE);
destlist[clientidx].status = DEST_ACTIVE;
log(0, 0, " For client%s %s", dupmsg ? "+" : "",
destlist[clientidx].name);
if (!dupmsg) {
finfo->deststate[clientidx].naks += nak_count;
}
}
}
}
/**
* Process an expected PRSTATUS message
*/
void handle_prstatus(const unsigned char *message, int meslen,
struct finfo_t *finfo, int hostidx,
int blocks_this_sec, int section_offset,
int pass, int section)
{
struct status_h *status;
struct prstatus_h *prstatus, *tmp_prstatus;
unsigned char *naklist;
int clientcnt, maxseq;
int mes_pass, mes_section, nak_count, i;
if (destlist[hostidx].clientcnt == -1) {
log1(0, 0, "Rejecting PRSTATUS from %s: not a proxy",
destlist[hostidx].name);
return;
}
prstatus = (struct prstatus_h *)message;
mes_pass = prstatus->pass;
mes_section = ntohs(prstatus->section);
clientcnt = ntohs(prstatus->destcount);
if (meslen != sizeof(struct prstatus_h) +
(clientcnt * sizeof(uint32_t))) {
log1(0, 0, "Rejecting PRSTATUS from %s: invalid message size",
destlist[hostidx].name);
return;
}
if (ntohs(prstatus->file_id) != finfo->file_id) {
log1(0, 0, "Rejecting PRSTATUS from %s: invalid file ID %04X, "
"expected %04X ", destlist[hostidx].name,
ntohs(prstatus->file_id), finfo->file_id );
return;
}
if ((mes_pass == pass) && (mes_section == section)) {
if (destlist[hostidx].last_status) {
// If we have a STATUS with matching pass/section/seq,
// apply the NAKs to all listed clients and mark the clients
// as having received status
status = (struct status_h *)destlist[hostidx].last_status;
nak_count = ntohl(status->nak_count);
naklist = ((unsigned char *)status) + sizeof(struct status_h);
if ((status->pass == pass) &&
(ntohs(status->section) == section) &&
(status->seq == prstatus->seq)) {
log(0, 0, "Got %d NAKs for pass %d section %d from proxy %s",
nak_count, mes_pass, mes_section,
destlist[hostidx].name);
set_proxy_status(finfo, (unsigned char *)prstatus, hostidx,
nak_count);
if (nak_count) {
apply_naks(naklist, finfo, blocks_this_sec, section_offset);
}
}
}
// Add this PRSTATUS to the last_prstatus list, clearing the list
// if existing messages don't match the pass/section/seq of this one
maxseq = destlist[hostidx].last_prstatus_cnt;
if (maxseq > 0) {
tmp_prstatus =
(struct prstatus_h *)destlist[hostidx].last_prstatus[0];
if ((tmp_prstatus->pass != pass) ||
(ntohs(tmp_prstatus->section) != section) ||
(tmp_prstatus->seq != prstatus->seq)) {
for (i = 0; i < maxseq; i++) {
free(destlist[hostidx].last_prstatus[i]);
}
maxseq = 0;
}
}
destlist[hostidx].last_prstatus[maxseq] = calloc(meslen, 1);
if (destlist[hostidx].last_prstatus[maxseq] == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(destlist[hostidx].last_prstatus[maxseq], prstatus, meslen);
destlist[hostidx].last_prstatus_cnt = maxseq + 1;
}
}
/**
* Process an expected STATUS message
*/
void handle_status(const unsigned char *message, int meslen,
struct finfo_t *finfo, int hostidx,
int blocks_this_sec, int section_offset,
int pass, int section)
{
struct status_h *status;
struct prstatus_h *prstatus;
unsigned char *naklist;
int mes_pass, mes_section, nak_count, i, dupmsg;
status = (struct status_h *)message;
naklist = ((unsigned char *)status) + sizeof(struct status_h);
mes_pass = status->pass;
mes_section = ntohs(status->section);
nak_count = ntohl(status->nak_count);
if (meslen != sizeof(struct status_h) + (nak_count ? blocksize : 0)) {
log1(0, 0, "Rejecting STATUS from %s: invalid message size",
destlist[hostidx].name);
return;
}
if (ntohs(status->file_id) != finfo->file_id) {
log1(0, 0, "Rejecting STATUS from %s: invalid file ID %04X, "
"expected %04X ", destlist[hostidx].name,
ntohs(status->file_id), finfo->file_id );
return;
}
if ((mes_pass == pass) && (mes_section == section)) {
if (destlist[hostidx].clientcnt != -1) {
// If we have one or more PRSTATUS with matching pass/section/seq,
// apply the NAKs to all listed clients and mark the clients
// as having received status
if (destlist[hostidx].last_prstatus_cnt > 0) {
prstatus =
(struct prstatus_h *)destlist[hostidx].last_prstatus[0];
if ((prstatus->pass == pass) &&
(ntohs(prstatus->section) == section) &&
(prstatus->seq == status->seq)) {
log(0, 0,"Got %d NAKs for pass %d section %d from proxy %s",
nak_count, mes_pass, mes_section,
destlist[hostidx].name);
for (i = 0; i < destlist[hostidx].last_prstatus_cnt; i++) {
set_proxy_status(finfo,
destlist[hostidx].last_prstatus[i],
hostidx, nak_count);
}
if (nak_count) {
apply_naks(naklist, finfo,
blocks_this_sec, section_offset);
}
}
}
free(destlist[hostidx].last_status);
destlist[hostidx].last_status = calloc(meslen, 1);
if (destlist[hostidx].last_status == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(destlist[hostidx].last_status, status, meslen);
} else {
log(0, 0, "Got %d NAKs for pass %d section %d from client %s",
nak_count, mes_pass, mes_section, destlist[hostidx].name);
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
destlist[hostidx].status = DEST_ACTIVE;
if (!dupmsg) {
finfo->deststate[hostidx].naks += nak_count;
}
if (nak_count) {
apply_naks(naklist, finfo, blocks_this_sec, section_offset);
}
}
} else {
log(0, 0, "Got %d NAKs for pass %d section %d from %s",
nak_count, mes_pass, mes_section, destlist[hostidx].name);
}
}
/**
* Sends out a data packet. All headers should be populated except blsize
*/
int send_data(const struct finfo_t *finfo, unsigned char *packet, int datalen,
unsigned char *encpacket)
{
struct uftp_h *header;
struct fileseg_h *fileseg;
int payloadlen;
unsigned char *outpacket;
header = (struct uftp_h *)packet;
fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
payloadlen = sizeof(struct fileseg_h) + datalen;
if (keytype != KEY_NONE) {
if (!encrypt_and_sign(packet, &encpacket, payloadlen, mtu, keytype,
groupkey, groupsalt, ivlen, hashtype, grouphmackey, hmaclen,
sigtype, privkey, rsalen)) {
log0(0, 0, "Error encrypting FILESEG");
return 0;
}
outpacket = encpacket;
payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize);
} else {
outpacket = packet;
header->blsize = htons(payloadlen);
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&receive_dest,
sizeof(receive_dest)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending FILESEG");
return 0;
}
return 1;
}
/**
* Print the final statistics for the given file while in sync mode
*/
void print_sync_status(const struct finfo_t *finfo, struct timeval start_time)
{
double elapsed_time, throughput;
int i;
if (finfo->file_id == 0) {
log0(0, 0, "- Status -");
log0(0, 0, "HSTATS;target;copy;overwrite;"
"skip;totalKB;time;speedKB/s");
for (i = 0; i < destcount; i++) {
if (destlist[i].total_time > 0) {
throughput = destlist[i].total_size /
destlist[i].total_time / 1024;
} else {
throughput = 0;
}
log0(0, 0, "STATS;%s;%d;%d;%d;%sKB;%.3f;%.2fKB/s",
destlist[i].name, destlist[i].num_copy,
destlist[i].num_overwrite, destlist[i].num_skip,
printll(destlist[i].total_size), destlist[i].total_time,
throughput);
}
return;
}
for (i = 0; i < destcount; i++) {
clog0(0, 0, "RESULT;%s;%s;%sKB;", destlist[i].name,
finfo->destfname, printll(finfo->size / 1024));
switch (destlist[i].status) {
case DEST_MUTE:
slog0("mute;");
break;
case DEST_LOST:
slog0("lost;");
break;
case DEST_ABORT:
slog0("aborted;");
break;
case DEST_DONE:
if (sync_preview) {
throughput = rate / 8;
elapsed_time = finfo->size / (throughput * 1024);
} else {
elapsed_time = (double)diff_usec(finfo->deststate[i].time,
start_time) / 1000000;
if (elapsed_time > 0) {
throughput = finfo->size / elapsed_time / 1024;
} else {
throughput = 0;
}
}
switch (destlist[i].comp_status) {
case COMP_STAT_NORMAL:
slog0("copy;%.2fKB/s", throughput);
destlist[i].num_copy++;
destlist[i].total_time += elapsed_time;
destlist[i].total_size += finfo->size;
break;
case COMP_STAT_SKIPPED:
slog0("skipped;");
destlist[i].num_skip++;
break;
case COMP_STAT_OVERWRITE:
slog0("overwritten;%.2fKB/s", throughput);
destlist[i].num_overwrite++;
destlist[i].total_time += elapsed_time;
destlist[i].total_size += finfo->size;
break;
case COMP_STAT_REJECTED:
slog0("rejected;");
break;
default:
slog0("Unknown;");
break;
}
break;
default:
slog0("Unknown;");
break;
}
}
}
/**
* Print the final statistics for the given file
*/
void print_status(const struct finfo_t *finfo, struct timeval start_time)
{
struct timeval done_time;
double elapsed_time;
int i;
if (sync_mode) {
print_sync_status(finfo, start_time);
return;
}
if (finfo->file_id == 0) {
log0(0, 0, "Group complete");
return;
}
log0(0, 0, "Transfer status:");
for (done_time = start_time, i = 0; i < destcount; i++) {
if (destlist[i].clientcnt >= 0) {
continue;
}
clog0(0, 0, "Host: %-15s Status: ", destlist[i].name);
switch (destlist[i].status) {
case DEST_MUTE:
slog0("Mute");
break;
case DEST_LOST:
slog0("Lost connection");
break;
case DEST_ABORT:
slog0("Aborted");
break;
case DEST_DONE:
if (destlist[i].comp_status == COMP_STAT_REJECTED) {
slog0("Rejected");
break;
}
if (diff_usec(finfo->deststate[i].time, done_time) > 0) {
done_time = finfo->deststate[i].time;
}
elapsed_time = (double)diff_usec(finfo->deststate[i].time,
start_time) / 1000000;
slog0("Completed time: %7.3f seconds NAKs: %d",
elapsed_time, finfo->deststate[i].naks);
break;
default:
slog0("Unknown code: %d", destlist[i].status);
break;
}
}
elapsed_time = (double)diff_usec(done_time, start_time) / 1000000;
log1(0, 0, "Total elapsed time: %.3f seconds", elapsed_time);
log1(0, 0, "Overall throughput: %.2f KB/s",
(elapsed_time != 0) ? (finfo->size / elapsed_time / 1024) : 0);
}
uftp-3.5/uftp_common.c 0000644 0003316 0000550 00000103660 11577014247 014054 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2011 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, the copyright holder
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include
#include "uftp.h"
#include "uftp2.h"
#include "uftp_common.h"
#include "encryption.h"
#include "win_func.h"
void getiflist(struct iflist *list, int *len)
{
SOCKET s;
int rval, rsize, count, i;
INTERFACE_INFO *iflist;
if (*len <= 0) return;
count = *len;
iflist = malloc(sizeof(INTERFACE_INFO) * count);
if (iflist == NULL) {
syserror(0, 0, "malloc failed!");
exit(1);
}
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
sockerror(0, 0, "Error creating socket for interface list");
free(iflist);
*len = 0;
return;
}
if ((rval = WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, iflist,
sizeof(INTERFACE_INFO) * count, &rsize, NULL, NULL)) ==
SOCKET_ERROR) {
sockerror(0, 0, "Error getting interface list");
closesocket(s);
free(iflist);
*len = 0;
return;
}
count = rsize / sizeof(INTERFACE_INFO);
for (i = 0, *len = 0; i < count; i++) {
if ((iflist[i].iiFlags & IFF_UP) != 0) {
memset(&list[*len], 0, sizeof(struct iflist));
strcpy(list[*len].name, "");
list[*len].addr.s_addr =
iflist[i].iiAddress.AddressIn.sin_addr.s_addr;
list[*len].isloopback = (iflist[i].iiFlags&IFF_LOOPBACK) != 0;
list[*len].ismulti = (iflist[i].iiFlags&IFF_MULTICAST) != 0;
(*len)++;
}
}
free(iflist);
closesocket(s);
}
#else /*if WINDOWS*/
#include
#include
#include
#include
#include
#include
#include
#include "uftp.h"
#include "uftp2.h"
#include "uftp_common.h"
#include "encryption.h"
#ifdef HAS_GETIFADDRS
#include
void getiflist(struct iflist *list, int *len)
{
struct ifaddrs *ifa, *ifa_tmp;
struct sockaddr_in *in;
int count;
if (getifaddrs(&ifa) == -1) {
syserror(0, 0, "getifaddrs failed");
*len = 0;
return;
}
ifa_tmp = ifa;
count = *len;
*len = 0;
while (ifa_tmp && (*len < count)) {
in = (struct sockaddr_in *)ifa_tmp->ifa_addr;
if (in && (in->sin_family == AF_INET) &&
((ifa_tmp->ifa_flags & IFF_UP) != 0)) {
memset(&list[*len], 0, sizeof(struct iflist));
strncpy(list[*len].name, ifa_tmp->ifa_name,
sizeof(list[*len].name) - 1);
list[*len].addr.s_addr = in->sin_addr.s_addr;
list[*len].isloopback = (ifa_tmp->ifa_flags & IFF_LOOPBACK) != 0;
list[*len].ismulti = (ifa_tmp->ifa_flags & IFF_MULTICAST) != 0;
(*len)++;
}
ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);
}
#else
void getiflist(struct iflist *list, int *len)
{
int s, i, count;
struct ifconf ifc;
struct ifreq *ifr;
struct ifreq ifr_tmp;
struct sockaddr_in *in;
if (*len <= 0) return;
count = *len;
ifr = malloc(sizeof(struct ifreq) * count);
if (ifr == NULL) {
syserror(0, 0, "malloc failed!");
exit(1);
}
ifc.ifc_len = sizeof(struct ifreq) * count;
ifc.ifc_req = ifr;
if ((s = socket(AF_INET,SOCK_DGRAM, 0)) == -1) {
sockerror(0, 0, "Error creating socket for interface list");
free(ifr);
*len = 0;
return;
}
if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
syserror(0, 0, "Error getting interface list");
free(ifr);
close(s);
*len = 0;
return;
}
count = ifc.ifc_len / sizeof(struct ifreq);
for (i = 0, *len = 0; i < count; i++) {
in = (struct sockaddr_in *)&ifr[i].ifr_addr;
strcpy(ifr_tmp.ifr_name, ifr[i].ifr_name);
if (ioctl(s, SIOCGIFFLAGS, &ifr_tmp) == -1) {
continue;
}
if (in && (in->sin_family == AF_INET) &&
((ifr_tmp.ifr_flags & IFF_UP) != 0)) {
memset(&list[*len], 0, sizeof(struct iflist));
strncpy(list[*len].name, ifr[i].ifr_name, sizeof(list[i].name) - 1);
list[*len].addr.s_addr = in->sin_addr.s_addr;
list[*len].isloopback = (ifr_tmp.ifr_flags & IFF_LOOPBACK) != 0;
list[*len].ismulti = (ifr_tmp.ifr_flags & IFF_MULTICAST) != 0;
(*len)++;
}
}
free(ifr);
close(s);
}
#endif /*if Sun*/
#ifdef VMS
pid_t GENERIC_SETSID(void) { return(0); }
#endif
#endif /*if WINDOWS*/
const char *printll(int64_t n)
{
static char str[50];
#ifdef WINDOWS
snprintf(str, sizeof(str) - 1, "%I64d", n);
#else
snprintf(str, sizeof(str) - 1, "%lld", (long long)n);
#endif
str[sizeof(str) - 1] = '\x0';
return &str[0];
}
int32_t diff_sec(struct timeval t2, struct timeval t1)
{
return t2.tv_sec - t1.tv_sec;
}
int64_t diff_usec(struct timeval t2, struct timeval t1)
{
return (t2.tv_usec - t1.tv_usec) +
(int64_t)1000000 * (t2.tv_sec - t1.tv_sec);
}
int cmptimestamp(struct timeval t1, struct timeval t2)
{
if (t1.tv_sec > t2.tv_sec) {
return 1;
} else if (t1.tv_sec < t2.tv_sec) {
return -1;
} else if (t1.tv_usec > t2.tv_usec) {
return 1;
} else if (t1.tv_usec < t2.tv_usec) {
return -1;
} else {
return 0;
}
}
const char *func_name(int func)
{
switch (func) {
case ANNOUNCE:
return "ANNOUNCE";
case REGISTER:
return "REGISTER";
case CLIENT_KEY:
return "CLIENT_KEY";
case REG_CONF:
return "REG_CONF";
case KEYINFO:
return "KEYINFO";
case FILEINFO:
return "FILEINFO";
case INFO_ACK:
return "INFO_ACK";
case FILESEG:
return "FILESEG";
case DONE:
return "DONE";
case PRSTATUS:
return "PRSTATUS";
case STATUS:
return "STATUS";
case COMPLETE:
return "COMPLETE";
case DONE_CONF:
return "DONE_CONF";
case HB_REQ:
return "HB_REQ";
case HB_RESP:
return "HB_RESP";
case KEY_REQ:
return "KEY_REQ";
case PROXY_KEY:
return "PROXY_KEY";
case ENCRYPTED:
return "ENCRYPTED";
case ABORT:
return "ABORT";
default:
return "UNKNOWN";
}
}
const char *v2_func_name(int func)
{
switch (func) {
case V2_ANNOUNCE:
return "ANNOUNCE";
case V2_REGISTER:
return "REGISTER";
case V2_REG_CONF:
return "REG_CONF";
case V2_FILESEG:
return "FILESEG";
case V2_DONE:
return "DONE";
case V2_STATUS:
return "STATUS";
case V2_DONE_CONF:
return "DONE_CONF";
case V2_ABORT:
return "ABORT";
default:
return "UNKNOWN";
}
}
int showtime;
FILE *applog;
int log_level;
void logfunc(uint32_t group_id, uint16_t file_id, int level, int _showtime,
int newline, int err, int sockerr, const char *str, ...)
{
struct tm *timeval;
struct timeval tv;
time_t t;
va_list args;
if (level > log_level) return;
if (_showtime) {
gettimeofday(&tv, NULL);
// In Windows, tv.tv_sec is long, not time_t
t = tv.tv_sec;
timeval = localtime(&t);
fprintf(applog, "%04d/%02d/%02d %02d:%02d:%02d.%06d: ",
timeval->tm_year + 1900, timeval->tm_mon + 1, timeval->tm_mday,
timeval->tm_hour, timeval->tm_min, timeval->tm_sec,
(int)tv.tv_usec);
}
if (group_id && file_id) {
fprintf(applog, "[%08X:%04X]: ", group_id, file_id);
} else if (group_id && !file_id) {
fprintf(applog, "[%08X:0]: ", group_id);
} else if (!group_id && file_id) {
fprintf(applog, "[%04X]: ", file_id);
}
va_start(args, str);
vfprintf(applog, str, args);
va_end(args);
if (sockerr) {
#ifdef WINDOWS
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(),
0, errbuf, sizeof(errbuf), NULL);
fprintf(applog, ": (%d) %s", WSAGetLastError(), errbuf);
newline = 0;
#else
fprintf(applog, ": %s", strerror(err));
#endif
} else if (err) {
fprintf(applog, ": %s", strerror(err));
}
if (newline) {
fprintf(applog, "\n");
}
fflush(applog);
}
/**
* Takes a pathname and splits it into a directory part and file part.
* The caller is expected to clean up *dir and *file.
*/
void split_path(const char *path, char **dir, char **file)
{
#ifdef WINDOWS
char *result, *filename;
DWORD len, len2;
if (strlen(path) == 0) {
*dir = NULL;
*file = NULL;
return;
}
// GetFullPathNameA doens't handle trailing slashes well, so disallow
if ((path[strlen(path)-1] == '/') || (path[strlen(path)-1] == '\\')) {
log(0, 0, "bad path, trailing / or \\ not allowed");
*dir = NULL;
*file = NULL;
return;
}
len = GetFullPathNameA(path, 0, NULL, &filename);
*dir = NULL;
*file = NULL;
result = malloc(len);
if (!result) {
syserror(0, 0, "malloc failed!");
exit(1);
}
if ((len2 = GetFullPathNameA(path, len, result, &filename)) <= len) {
*dir = strdup(result);
*file = strdup(filename);
if (!*dir || (filename && !*file)) {
syserror(0, 0, "strdup failed!");
exit(1);
}
(*dir)[strlen(*dir) - strlen(*file) - 1] = '\x0';
}
free(result);
#else
char *dirc, *filec;
dirc = strdup(path);
filec = strdup(path);
if (!dirc || !filec) {
syserror(0, 0, "strdup failed!");
exit(1);
}
*dir = strdup(dirname(dirc));
*file = strdup(basename(filec));
if (!*dir || !*file) {
syserror(0, 0, "strdup failed!");
exit(1);
}
free(dirc);
free(filec);
#endif
}
/**
* Parses a key fingerprint string and saves it to the specified buffer
* Returns 1 on success, 0 on fail
*/
int parse_fingerprint(unsigned char *fingerprint, const char *fingerprint_str)
{
char *p, *tmp;
int num, len;
if (fingerprint_str == NULL) {
return 0;
}
tmp = strdup(fingerprint_str);
len = 0;
p = strtok(tmp, ":");
if (p == NULL) {
log(0, 0, "Invalid fingerprint %s", fingerprint_str);
return 0;
}
do {
if (len >= HMAC_LEN) {
log(0, 0, "Key fingerprint %s too long", fingerprint_str);
free(tmp);
return 0;
}
errno = 0;
num = strtol(p, NULL, 16);
if (errno) {
syserror(0, 0, "Parse of host key fingerprint %s failed",
fingerprint_str);
free(tmp);
return 0;
} else if ((num > 255) || (num < 0)) {
log(0, 0, "Parse of host key fingerprint %s failed",
fingerprint_str);
free(tmp);
return 0;
}
fingerprint[len++] = (uint8_t)num;
p = strtok(NULL, ":");
} while (p);
free(tmp);
return 1;
}
/**
* Tests a struct in_addr to see if it's a valid multicast address
*/
int is_multicast(struct in_addr addr, int ssm)
{
int val;
val = ntohl(addr.s_addr) >> 24;
if (ssm && (val != 232)) {
return 0;
} else if ((val >= 224) && (val < 240)) {
return 1;
} else {
return 0;
}
}
/**
* Returns a struct in_addr based on the given 32-bit value
*/
struct in_addr to_addr(uint32_t addr)
{
struct in_addr inaddr;
inaddr.s_addr = addr;
return inaddr;
}
/**
* Returns whether the last socket operation would have blocked
*/
int would_block_err()
{
#ifdef WINDOWS
return (WSAGetLastError() == WSAEWOULDBLOCK);
#else
return (errno == EAGAIN);
#endif
}
/**
* Calls sendto, retrying if the send would block.
* The calling function should check for and log any other errors.
*/
int nb_sendto(SOCKET s, const void *msg, int len, int flags,
const struct sockaddr *to, int tolen)
{
int retry, sentlen;
retry = 1;
while (retry) {
if ((sentlen = sendto(s, msg, len, flags, to, tolen)) == SOCKET_ERROR) {
if (!would_block_err()) {
return -1;
}
} else {
retry = 0;
}
}
return sentlen;
}
/**
* Reads a packet off the network with a possible timeout.
* The socket must be non-blocking.
* Returns 1 on success, 0 on timeout, -1 on fail.
*/
int read_packet(SOCKET sock, struct sockaddr_in *sin, unsigned char *buffer,
int *len, int bsize, const struct timeval *timeout)
{
fd_set fdin;
struct timeval tv;
int rval;
socklen_t addr_len;
#ifdef BLOCKING
FD_ZERO(&fdin);
FD_SET(sock,&fdin);
if (timeout) tv = *timeout;
if ((rval = select(FD_SETSIZE-1, &fdin, NULL, NULL,
(timeout ? &tv : NULL))) == SOCKET_ERROR) {
sockerror(0, 0, "Select failed");
return -1;
}
if (rval == 0) {
return 0;
} else if (FD_ISSET(sock, &fdin)) {
addr_len = sizeof(struct sockaddr_in);
if ((*len = recvfrom(sock, buffer, bsize, 0, (struct sockaddr *)sin,
&addr_len)) == SOCKET_ERROR) {
#ifdef WINDOWS
if (WSAGetLastError() != WSAECONNRESET) {
#else
if (errno != ECONNRESET) {
#endif
sockerror(0, 0, "Error receiving");
}
return -1;
}
return 1;
} else {
log(0, 0, "Unknown select error");
return -1;
}
#else // BLOCKING
while (1) {
addr_len = sizeof(struct sockaddr_in);
if ((*len = recvfrom(sock, buffer, bsize, 0, (struct sockaddr *)sin,
&addr_len)) == SOCKET_ERROR) {
if (!would_block_err()) {
#ifdef WINDOWS
if (WSAGetLastError() != WSAECONNRESET) {
#else
if (errno != ECONNRESET) {
#endif
sockerror(0, 0, "Error receiving");
}
return -1;
}
} else {
return 1;
}
FD_ZERO(&fdin);
FD_SET(sock,&fdin);
if (timeout) tv = *timeout;
if ((rval = select(FD_SETSIZE-1, &fdin, NULL, NULL,
(timeout ? &tv : NULL))) == SOCKET_ERROR) {
sockerror(0, 0, "Select failed");
return -1;
}
if (rval == 0) {
return 0;
} else if (!FD_ISSET(sock, &fdin)) {
log(0, 0, "Unknown select error");
return -1;
}
}
#endif // BLOCKING
}
/**
* Performs an XOR between p1 and p2, storing the result in p1
*/
void memxor(void *p1, const void *p2, int len)
{
int i;
for (i = 0; i < len; i++) {
((unsigned char *)p1)[i] ^= ((unsigned char *)p2)[i];
}
}
/**
* Constructs an initialization vector (IV) from a salt value
* and encrypted packet as follows:
* For a 128-bit IV: IV = S XOR (group_ID + IP + T)
* For a 64-bit IV: IV = S XOR (group_ID + IP) XOR T
* All values should be in network byte order.
*/
void build_iv(uint8_t *iv, const uint8_t *salt, int saltlen, uint32_t group_id,
uint32_t addr, uint32_t time_sec, uint32_t time_usec)
{
char tmp[16], tmp2[8];
int tmplen, tmp2len;
memcpy(tmp, &group_id, sizeof(uint32_t));
tmplen = sizeof(uint32_t);
memcpy(tmp + tmplen, &addr, sizeof(uint32_t));
tmplen += sizeof(uint32_t);
if (saltlen == 8) {
memcpy(tmp2, &time_sec, sizeof(uint32_t));
tmp2len = sizeof(uint32_t);
memcpy(tmp2 + tmp2len, &time_usec, sizeof(uint32_t));
tmp2len += sizeof(uint32_t);
memxor(tmp, tmp2, tmp2len);
} else {
memcpy(tmp + tmplen, &time_sec, sizeof(uint32_t));
tmplen = sizeof(uint32_t);
memcpy(tmp + tmplen, &time_usec, sizeof(uint32_t));
tmplen += sizeof(uint32_t);
}
memcpy(iv, salt, saltlen);
memxor(iv, tmp, tmplen);
}
/**
* Outputs data buffers to log in hex.
* Used only for debugging
*/
void printhex(const char *name, const unsigned char *data, int len)
{
int i;
sclog("%s:", name);
for (i = 0; i < len; i++) {
sclog(" %02X", data[i]);
if (i % 16 == 15) sclog("\n");
}
sclog("\n");
}
/**
* Verify the signature of an encrypted message and decrypt.
* The decrypted message is returned without a uftp_h header.
* Returns 1 on success, 0 on fail
*/
int validate_and_decrypt(const unsigned char *encpacket,
unsigned char **decpacket, unsigned int *declen,
int mtu, int keytype, const uint8_t *key,
const uint8_t *salt, int ivlen, int hashtype,
const uint8_t *hmackey, uint8_t hmaclen, int sigtype,
RSA_key_t rsakey, int rsalen)
{
struct uftp_h *header;
struct encrypted_h *encrypted;
unsigned char *payload, *sig, *sigcopy, *sigtest, *iv;
unsigned int hsiglen, siglen, len, rval, allocdec;
if (sigtype == SIG_HMAC) {
siglen = hmaclen;
} else if (sigtype == SIG_RSA) {
siglen = rsalen;
} else {
log(0, 0, "Invalid signature type");
return 0;
}
header = (struct uftp_h *)encpacket;
encrypted = (struct encrypted_h *)(encpacket + sizeof(struct uftp_h));
hsiglen = ntohs(encrypted->sig_len);
sig = (unsigned char *)encrypted + sizeof(struct encrypted_h);
payload = sig + hsiglen;
if (header->func != ENCRYPTED) {
log(0, 0, "Attempt to decrypt non-encrypted message");
return 0;
}
if (ntohs(header->blsize) != (sizeof(struct encrypted_h) +
hsiglen + ntohs(encrypted->payload_len))) {
log(0, 0, "Invalid signature and/or encrypted payload length");
return 0;
}
if (hsiglen != siglen) {
log(0, 0, "Signature length incorrect: got %d, expected %d",
hsiglen, siglen);
return 0;
}
sigcopy = calloc(siglen, 1);
sigtest = calloc(siglen, 1);
iv = calloc(ivlen, 1);
if ((sigcopy == NULL) || (sigtest == NULL) || (iv == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(sigcopy, sig, hsiglen);
memset(sig, 0, hsiglen);
if (sigtype == SIG_HMAC) {
if (!create_hmac(hashtype, hmackey, hsiglen, encpacket,
sizeof(struct uftp_h) + ntohs(header->blsize),
sigtest, &len)) {
log(0, 0, "HMAC creation failed");
rval = 0;
goto end;
}
if (memcmp(sigtest, sigcopy, len)) {
log(0, 0, "HMAC verification failed");
rval = 0;
goto end;
}
} else {
if (!verify_RSA_sig(rsakey, hashtype, encpacket,
sizeof(struct uftp_h) + ntohs(header->blsize),
sigcopy, ntohs(encrypted->sig_len))) {
log(0, 0, "RSA signature verification failed");
rval = 0;
goto end;
}
}
allocdec = 0;
if (*decpacket == NULL) {
allocdec = 1;
*decpacket = calloc(mtu + KEYBLSIZE, 1);
if (*decpacket == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
}
build_iv(iv, salt, ivlen, header->group_id, header->srcaddr,
encrypted->tstamp_sec, encrypted->tstamp_usec);
if (!decrypt_block(keytype, iv, key, payload,
ntohs(encrypted->payload_len), *decpacket, declen)) {
log(0, 0, "Decrypt failed");
if (allocdec) {
free(*decpacket);
*decpacket = NULL;
}
rval = 0;
goto end;
}
rval = 1;
end:
free(sigcopy);
free(sigtest);
free(iv);
return rval;
}
/**
* Encrypts a message and attaches a signature to the encrypted message.
* The incoming message should include a uftp_h header.
* Returns 1 on success, 0 on fail
*/
int encrypt_and_sign(const unsigned char *decpacket, unsigned char **encpacket,
int declen, int mtu, int keytype, uint8_t *key,
const uint8_t *salt, int ivlen,
int hashtype, uint8_t *hmackey, uint8_t hmaclen,
int sigtype, RSA_key_t rsakey, int rsalen)
{
struct uftp_h *header;
struct encrypted_h *encrypted;
const unsigned char *mheader;
unsigned char *payload, *sig, *sigcopy, *iv;
unsigned int enclen, siglen, len, allocenc;
struct timeval tv;
if (sigtype == SIG_HMAC) {
siglen = hmaclen;
} else if (sigtype == SIG_RSA) {
siglen = rsalen;
} else {
log(0, 0, "Invalid signature type");
return 0;
}
allocenc = 0;
if (*encpacket == NULL) {
allocenc = 1;
*encpacket = calloc(mtu + KEYBLSIZE, 1);
if (*encpacket == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
}
iv = calloc(ivlen, 1);
if (iv == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
mheader = decpacket + sizeof(struct uftp_h);
header = (struct uftp_h *)*encpacket;
encrypted = (struct encrypted_h *)(*encpacket + sizeof(struct uftp_h));
sig = (unsigned char *)encrypted + sizeof(struct encrypted_h);
payload = sig + siglen;
memcpy(*encpacket, decpacket, sizeof(struct uftp_h));
gettimeofday(&tv, NULL);
build_iv(iv, salt, ivlen, header->group_id, header->srcaddr,
htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(keytype, iv, key, mheader, declen, payload, &enclen)) {
// Called function should log
free(iv);
if (allocenc) {
free(*encpacket);
*encpacket = NULL;
}
return 0;
}
header->func = ENCRYPTED;
header->blsize = htons(sizeof(struct encrypted_h) + enclen + siglen);
encrypted->tstamp_sec = htonl(tv.tv_sec);
encrypted->tstamp_usec = htonl(tv.tv_usec);
encrypted->sig_len = htons(siglen);
encrypted->payload_len = htons(enclen);
sigcopy = calloc(siglen, 1);
if (sigcopy == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memset(sig, 0, siglen);
if (sigtype == SIG_HMAC) {
if (!create_hmac(hashtype, hmackey, siglen, *encpacket,
sizeof(struct uftp_h) + ntohs(header->blsize),
sigcopy, &len)) {
log(0, 0, "HMAC creation failed");
free(iv);
free(sigcopy);
if (allocenc) {
free(*encpacket);
*encpacket = NULL;
}
return 0;
}
} else {
if (!create_RSA_sig(rsakey, hashtype, *encpacket,
sizeof(struct uftp_h) + ntohs(header->blsize),
sigcopy, &len)) {
// Called function should log
free(iv);
free(sigcopy);
if (allocenc) {
free(*encpacket);
*encpacket = NULL;
}
return 0;
}
}
memcpy(sig, sigcopy, len);
free(iv);
free(sigcopy);
return 1;
}
/**
* Psedo-random function for an individual hashing algorithm
* as defined in RFC 4346 and RFC 5246
*/
static void P_hash(int hash, int bytes,
const unsigned char *secret, int secret_len,
const char *label, const unsigned char *seed, int seed_len,
unsigned char *outbuf, int *outbuf_len)
{
unsigned char *newseed, *inbuf, *tmpbuf;
unsigned newseed_len, inbuf_len;
unsigned int tmpbuf_len, outbuf_len_new;
newseed = calloc(strlen(label) + seed_len, 1);
inbuf = calloc(get_hash_len(hash) + strlen(label) + seed_len, 1);
tmpbuf = calloc(get_hash_len(hash) + strlen(label) + seed_len, 1);
if ((newseed == NULL) || (inbuf == NULL) || (tmpbuf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
*outbuf_len = 0;
newseed_len = 0;
memcpy(newseed, label, strlen(label));
newseed_len += strlen(label);
memcpy(newseed + newseed_len, seed, seed_len);
newseed_len += seed_len;
memcpy(inbuf, newseed, newseed_len);
inbuf_len = newseed_len;
while (*outbuf_len < bytes)
{
// TODO: create_hmac can fail under CrypoAPI.
// Figure out how to handle this.
create_hmac(hash, secret, secret_len, inbuf, inbuf_len,
tmpbuf, &tmpbuf_len);
memcpy(tmpbuf + tmpbuf_len, newseed, newseed_len);
tmpbuf_len += newseed_len;
create_hmac(hash, secret, secret_len, tmpbuf, tmpbuf_len,
outbuf + *outbuf_len, &outbuf_len_new);
*outbuf_len += outbuf_len_new;
memcpy(inbuf,tmpbuf,tmpbuf_len);
inbuf_len = tmpbuf_len;
}
free(newseed);
free(inbuf);
free(tmpbuf);
}
/**
* Psedo-random function
* as defined in RFC 4346 and RFC 5246
*/
void PRF(int hash, int bytes, const unsigned char *secret, int secret_len,
const char *label, const unsigned char *seed, int seed_len,
unsigned char *outbuf, int *outbuf_len)
{
int i, s_len, sha_buf_len, md5_buf_len;
unsigned char *sha_buf, *md5_buf;
if (hash == HASH_SHA256) {
P_hash(HASH_SHA256, bytes, secret, secret_len, label,
seed, seed_len, outbuf, outbuf_len);
} else if (hash == HASH_SHA1) {
// TLS 1.1 MD5/SHA1 combination
md5_buf = calloc(bytes + get_hash_len(HASH_MD5), 1);
sha_buf = calloc(bytes + get_hash_len(HASH_SHA1), 1);
if ((md5_buf == NULL) || (sha_buf == NULL)) {
syserror(0, 0, "calloc failed!");
exit(1);
}
// if secret_len is even integer division truncates the result
// if secret_len is odd, the + 1 effectively rounds up
s_len = (secret_len + 1) / 2;
P_hash(HASH_MD5, bytes, secret, s_len, label,
seed, seed_len, md5_buf, &md5_buf_len);
P_hash(HASH_SHA1, bytes, secret + (secret_len - s_len), s_len, label,
seed, seed_len, sha_buf, &sha_buf_len);
for (i = 0; i < bytes; i++) {
outbuf[i] = md5_buf[i] ^ sha_buf[i];
}
*outbuf_len = bytes;
free(md5_buf);
free(sha_buf);
} else {
*outbuf_len = 0;
}
}
/**
* Outputs a key's fingerprint
*/
const char *print_key_fingerprint(const RSA_key_t key)
{
static char fpstr[100];
char *p;
unsigned char keymod[PUBKEY_LEN], fingerprint[HMAC_LEN], *keydata;
uint32_t exponent;
uint16_t modlen;
unsigned int keylen, fplen, i, cnt;
if (!export_RSA_key(key, &exponent, keymod, &modlen)) {
return NULL;
}
exponent = htonl(exponent);
keylen = 0;
keydata = calloc(sizeof(exponent) + modlen, 1);
if (keydata == NULL) {
syserror(0, 0, "calloc failed!");
exit(1);
}
memcpy(keydata, &exponent, sizeof(exponent));
keylen += sizeof(exponent);
memcpy(keydata + keylen, keymod, modlen);
keylen += modlen;
hash(HASH_SHA1, keydata, keylen, fingerprint, &fplen);
for (i = 0, p = fpstr; i < fplen; i++) {
if (i != 0) {
*p = ':';
p++;
}
cnt = sprintf(p, "%02X", fingerprint[i]);
p += cnt;
}
free(keydata);
return fpstr;
}
/**
* Join the specified multicast group on the specified list of interfaces.
* If source specific multicast is supported and we're given a list of servers,
* join source specific multicast groups for those servers.
* Returns 1 on success, 0 on fail
*/
int multicast_join(SOCKET s, uint32_t group_id, const struct in_addr *multi,
const struct iflist *addrlist, int addrlen,
const struct fp_list_t *fplist, int fplist_len)
{
struct ip_mreq mreq;
int i;
for (i = 0; i < addrlen; i++) {
if (!addrlist[i].ismulti) {
continue;
}
#ifdef IP_ADD_SOURCE_MEMBERSHIP
if (fplist_len != 0) {
int j;
for (j = 0; j < fplist_len; j++) {
struct ip_mreq_source srcmreq;
srcmreq.imr_multiaddr = *multi;
srcmreq.imr_sourceaddr = fplist[j].addr;
srcmreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
(char *)&srcmreq, sizeof(srcmreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error joining multicast group");
return 0;
}
}
} else {
mreq.imr_multiaddr = *multi;
mreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error joining multicast group");
return 0;
}
}
#else
mreq.imr_multiaddr = *multi;
mreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error joining multicast group");
return 0;
}
#endif
}
return 1;
}
/**
* Leave the specified multicast group on the specified list of interfaces.
* If source specific multicast is supported and we're given a list of servers,
* leave source specific multicast groups for those servers.
*/
void multicast_leave(SOCKET s, uint32_t group_id, const struct in_addr *multi,
const struct iflist *addrlist, int addrlen,
const struct fp_list_t *fplist, int fplist_len)
{
struct ip_mreq mreq;
int i;
for (i = 0; i < addrlen; i++) {
if (!addrlist[i].ismulti) {
continue;
}
#ifdef IP_DROP_SOURCE_MEMBERSHIP
if (fplist_len != 0) {
int j;
for (j = 0; j < fplist_len; j++) {
struct ip_mreq_source srcmreq;
srcmreq.imr_multiaddr = *multi;
srcmreq.imr_sourceaddr = fplist[j].addr;
srcmreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
(char *)&srcmreq, sizeof(srcmreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error leaving multicast group");
}
}
} else {
mreq.imr_multiaddr = *multi;
mreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error leaving multicast group");
}
}
#else
mreq.imr_multiaddr = *multi;
mreq.imr_interface = addrlist[i].addr;
if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof(mreq)) == SOCKET_ERROR) {
sockerror(group_id, 0, "Error leaving multicast group");
}
#endif
}
}
/**
* Search for a network interface in a list with the matching name.
* Returns the index in the list if found, -1 if not found.
*/
int getifbyname(const char *name, const struct iflist *list, int len)
{
int i;
for (i = 0; i < len; i++) {
if (!strcmp(name, list[i].name)) {
return i;
}
}
return -1;
}
/**
* Search for a network interface in a list with the matching IP address.
* Returns the index in the list if found, -1 if not found.
*/
int getifbyaddr(struct in_addr addr, const struct iflist *list, int len)
{
int i;
for (i = 0; i < len; i++) {
if (addr.s_addr == list[i].addr.s_addr) {
return i;
}
}
return -1;
}
/**
* Reads buflen bytes into buf from the given file descriptor.
* If buflen bytes are read, returns buflen.
* If 0 bytes are read, returns 0 if allow_eof is true, otherwise returns -1.
* If less that buflen bytes are read, or on error, returns -1.
*/
int file_read(int fd, void *buf, int buflen, int allow_eof)
{
int read_len;
if ((read_len = read(fd, buf, buflen)) == -1) {
syserror(0, 0, "Read failed");
return -1;
}
if ((read_len != buflen) && (!allow_eof || (read_len != 0))) {
log(0, 0, "Read error: read %d bytes, expected %d", read_len, buflen);
return -1;
}
return read_len;
}
/**
* Writes buflen bytes from buf to the given file descriptor.
* If buflen bytes are written, returns buflen.
* If less that buflen bytes are written, or on error, returns -1.
*/
int file_write(int fd, const void *buf, int buflen)
{
int write_len;
if ((write_len = write(fd, buf, buflen)) == -1) {
syserror(0, 0, "Write failed");
return -1;
}
if (write_len != buflen) {
log(0, 0, "Write error: wrote %d bytes, expected %d", write_len,buflen);
return -1;
}
return write_len;
}
uftp-3.5/win_func.c 0000644 0003316 0000550 00000004773 11503275503 013334 0 ustar bush alumni /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2010 Dennis A. Bush, Jr. bush@tcnj.edu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see