uftp-4.1.5/ 0000755 0000764 0000764 00000000000 12304524273 011505 5 ustar dbush dbush uftp-4.1.5/ReadMe.txt 0000644 0000764 0000764 00000014027 12176066071 013413 0 ustar dbush dbush --== UFTP - Encrypted UDP based FTP with multicast ==--
UFTP is an encrypted multicast file transfer program, designed to securely,
reliably, and efficiently transfer files to multiple receivers simultaneously.
This is useful for distributing large files to a large number of receivers,
and is especially useful for data distribution over a satellite link (with two
way communication), where the inherent delay makes any TCP based communication
highly inefficient. The multicast encryption scheme is based on TLS with
extensions to allow multiple receivers to share a common key.
UFTP also has the capability to communicate over disjoint networks separated
by one or more firewalls (NAT traversal) and without full end-to-end multicast
capability (multicast tunneling) through the use of a UFTP proxy server.
These proxies also provide scalability by aggregating responses from a group
of receivers.
--== Building ==--
UNIX-like systems require GNU make and a C compiler such as GCC or
equivalent. Windows systems require Visual Studio Express 2008 or later.
Non-Windows systems require OpenSSL to be installed if encryption support is
enabled. On Linux, Solaris, and BSD systems (including MacOSX), this should
be included with the OS.
To compile for UNIX-like systems, including MacOSX:
make [ OPENSSL={install directory for OpenSSL} ] [ NO_ENCRYPTION=1 ] [ NO_EC=1 ]
To compile for Windows (from a Visual Studio command prompt):
nmake -f makefile.mak [ OPENSSL={install directory for OpenSSL} ] [ NO_ENCRYPTION=1 ] [ WINXP=1 ]
The OPENSSL parameter to make should only need to be specified if OpenSSL is
installed in a non-standard location, or on systems where it isn't
preinstalled.
The NO_ENCRYPTION flag compiles with no encryption support. This can be
useful in embedded environments that don't need encryption and want to
keep the size of the executable down, and for use in a Windows service
that doesn't require encryption.
The NO_EC flag compiles without support for Elliptic Curve crypto. This may
be required if either you have an older version of OpenSSL or the build of
OpenSSL installed wasn't configured with ECC support.
The WINXP flag is used on Windows systems to disable features requiring
Windows Vista or Windows 7. This includes dual stack IPv4/IPv6 sockets as
well as the CNG API which supports Elliptic Curve crypto, larger hash sizes,
and authenticated mode ciphers.
To install for UNIX-like systems, including MacOSX:
make [ DESTDIR={install directory} ] install
The DESTDIR parameter allows installing into a fake root directory, which
can be useful for packaging utilities such as rpm.
--== Tuning ==--
If you find that clients can't receive data fast enough, or if servers
communicating with several hundred clients can't handle the flood of
STATUS messages that come in, you can increase the UDP send/receive buffer
size to help with this. This is set using the -B option on server,
client, or proxy. However, many operating systems impose a maximum value
of 256K (262144). This limit can be increased, although the method is OS
specific. Here are a few common ones:
Solaris: ndd -set /dev/udp udp_max_buf {value}
Linux: sysctl -w net.core.rmem_max={value}
sysctl -w net.core.wmem_max={value}
MacOSX / FreeBSD: sysctl -w kern.ipc.maxsockbuf={value}
(The actual maximum you can use is 8/9 of this value)
While Windows does not have this limitation, Windows XP Service Pack 3 and
later does throttle back UDP datagrams larger that 1024 bytes by default
(a UFTP packet is 1472 bytes by default). This is most commonly seen when
attempting to move data at 100 Mbps or more. You can change this by
adding/modifying the following registry value:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters\FastSendDatagramThreshold
Set this DWORD value to 1500 and reboot, and it should take care of the issue.
If you're making use of the DSCP/TOS byte in the IP header, this can be
modified via the -Q option to the server, client, or proxy. On Windows XP
systems, this can't be modified by default using portable libriaries. This
can be overridden by adding/modifying the following registry value:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DisableUserTOSSetting
Set this DWORD value to 0 and reboot, and the -Q option should work.
Windows Vista and Windows 7 don't support this particular mechanism at all,
and each has it's own way of doing it. For now, this option does not work on
these OSs.
--== Running under Windows ==--
UFTP was originally written to run in a UNIX/Linux environment, so it doens't
natively do things that other Windows servers do such as run in the background
or run as a Windows service. Included with UFTP are hidedos.exe, instsrv.exe,
and srvany.exe which allows these things to happen. Hidedos is a utility
originally written by LANDesk to allow command line windows programs to run
in the background. Instsrv and srvany are components of the Windows Resource
Kit which. Instsrv is used to install and remove services, and srvany allows
programs not written as windows services to run as such.
To run the uftpd client or proxy without leaving a command prompt open
(the -d option tells uftpd to run in the foreground, so don't use this
option in this case):
hidedos uftpd [options]
-- or --
hidedos uftpproxyd [options]
To run the uftpd client as a Windows service:
(steps for the proxy are similar)
Copy instsrv.exe and srvany.exe to the system folder (usually
C:\Windows\system32)
Install the service:
instsrv UFTPD c:\windows\system32\srvany.exe
Configure the service:
Open regedit and go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UFTPD
Right-click the UFTPD key and choose New > Key.
Name the key Parameters.
Right-click the new Parameters key under UFTP and choose New > String Value.
Name the string Application.
Double-click the Application value and fill in the full command line to uftpd
Once configured, the service should start. In the task list, you'll see
both srvany.exe and uftpd.exe running while the service is up.
If you need to remove the service:
instsrv UFTPD remove
uftp-4.1.5/win_func.c 0000644 0000764 0000764 00000005567 12137013343 013471 0 ustar dbush dbush /*
* 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 "win_func.h"
int optind=1;
char *optarg;
char getopt(int argc, char *argv[], const char options[])
{
char *loc;
if (optind >= argc) {
return -1;
} else if (strlen(argv[optind]) <= 1) {
return -1;
} else if (argv[optind][0] != '-') {
return -1;
} else if (argv[optind][1] == '-') {
optind++;
return -1;
} else if (!isalnum(argv[optind][1])) {
return '?';
} else if ((loc = strchr(options, argv[optind][1])) == NULL) {
return '?';
} else {
optind++;
if (loc[1] == ':') {
optarg = argv[optind++];
}
return loc[0];
}
}
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
const unsigned __int64 epoch_diff = 11644473600000000;
unsigned __int64 tmp;
FILETIME t;
if (tv) {
GetSystemTimeAsFileTime(&t);
tmp = 0;
tmp |= t.dwHighDateTime;
tmp <<= 32;
tmp |= t.dwLowDateTime;
tmp /= 10;
tmp -= epoch_diff;
tv->tv_sec = (long)(tmp / 1000000);
tv->tv_usec = (long)(tmp % 1000000);
}
return 0;
}
int get_win_priority(int priority)
{
switch (priority) {
case -2:
return HIGH_PRIORITY_CLASS;
case -1:
return ABOVE_NORMAL_PRIORITY_CLASS;
case 0:
return NORMAL_PRIORITY_CLASS;
case 1:
return BELOW_NORMAL_PRIORITY_CLASS;
case 2:
return IDLE_PRIORITY_CLASS;
default:
return NORMAL_PRIORITY_CLASS;
}
} uftp-4.1.5/client_main.c 0000644 0000764 0000764 00000003041 12137013343 014124 0 ustar dbush dbush /*
* 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-4.1.5/encrypt_cng.c 0000644 0000764 0000764 00000123303 12250244021 014154 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
// Use this #define to pull in neccesary declarations from uftp_common.h
// in encryption.h instead. We don't include uftp_common.h because it contains
// an include for winsock2.h which is needed pretty much everyplace else.
// Including winsock2.h prevents BCryptEncrypt and BCryptDecrypt from working
// with GCM and CCM mode ciphers.
#define NO_UFTP_COMMON_H
#include "uftp.h"
#include "encryption.h"
#define MAXLIST 100
#define BLOBLEN 1000
static struct providers_t {
LPCWSTR alg;
LPCWSTR mode;
int hmac;
BCRYPT_ALG_HANDLE handle;
} providers[MAXLIST];
static int provlen;
static const struct keyinfo_t {
int keytype;
int keysize;
int blocksize;
} keyinfo[] = {
{ KEY_DES, 8, 8 },
{ KEY_DES_EDE3, 24, 8 },
{ KEY_AES128_CBC, 16, 16 },
{ KEY_AES128_GCM, 16, 12 },
{ KEY_AES128_CCM, 16, 12 },
{ KEY_AES256_CBC, 32, 16 },
{ KEY_AES256_GCM, 32, 12 },
{ KEY_AES256_CCM, 32, 12 }
};
static int machine_keyset = 0;
static int init_done = 0;
/**
* Prints Microsoft specific error messages to log
*/
static void mserror(const char *str, int err)
{
char errbuf[300];
HMODULE Hand = LoadLibrary("NTDLL.DLL");
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS, Hand, err,
0, errbuf, sizeof(errbuf), NULL);
clog0(0, 0, "%s: (0x%08X) %s", str, err, errbuf);
FreeLibrary(Hand);
}
/**
* Performs all necessary steps to initialize the crypto library
*/
void crypto_init(int set_sys_key)
{
provlen = 0;
if (set_sys_key) {
machine_keyset = NCRYPT_MACHINE_KEY_FLAG;
} else {
machine_keyset = 0;
}
init_done = 1;
}
/**
* Performs all necessary steps to clean up the crypto library
*/
void crypto_cleanup(void)
{
NTSTATUS status;
int i;
if (!init_done) {
return;
}
for (i = 0; i < provlen; i++) {
status = BCryptCloseAlgorithmProvider(providers[i].handle, 0);
if (!BCRYPT_SUCCESS(status)) {
mserror("BCryptCloseAlgorithmProvider failed", status);
}
}
}
/**
* Returns the next key for the current user
*/
const char *get_next_container(void)
{
static NCRYPT_PROV_HANDLE prov = 0;
static NCryptKeyName *keyitem = NULL;
static void *ptr = NULL;
static char name[256];
SECURITY_STATUS sstatus;
if (!prov) {
sstatus = NCryptOpenStorageProvider(&prov, NULL, 0);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptOpenStorageProvider failed", sstatus);
return NULL;
}
}
if (keyitem) {
NCryptFreeBuffer(keyitem);
}
sstatus = NCryptEnumKeys(prov, NULL, &keyitem, &ptr,
NCRYPT_SILENT_FLAG | machine_keyset);
if (sstatus == ERROR_SUCCESS) {
wcstombs(name, keyitem->pszName, sizeof(name));
return name;
} else {
if (sstatus != NTE_NO_MORE_ITEMS) {
mserror("NCryptEnumKeys failed", sstatus);
}
NCryptFreeBuffer(keyitem);
keyitem = NULL;
NCryptFreeBuffer(ptr);
ptr = NULL;
NCryptFreeObject(prov);
prov = 0;
return NULL;
}
}
/**
* Deletes the key container with the given name
*/
void delete_container(const char *container)
{
NCRYPT_PROV_HANDLE prov;
NCRYPT_KEY_HANDLE key;
SECURITY_STATUS sstatus;
wchar_t wcontainer[256];
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
}
memset(wcontainer, 0, sizeof(wcontainer));
mbstowcs(wcontainer, container, strlen(container));
sstatus = NCryptOpenKey(prov, &key, wcontainer, 0,
NCRYPT_SILENT_FLAG | machine_keyset);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptOpenKey failed", sstatus);
NCryptFreeObject(prov);
}
if (!BCRYPT_SUCCESS(sstatus = NCryptDeleteKey(key, NCRYPT_SILENT_FLAG))) {
NCryptFreeObject(prov);
mserror("NCryptDeleteKey failed", sstatus);
}
NCryptFreeObject(prov);
}
/**
* Gets an algorithm provider handle for the given hash or cipher.
* Check the provider list to see if it exists. If so, return it,
* otherwise get a new one and put it in the list.
*/
static BCRYPT_ALG_HANDLE get_alg_handle(LPCWSTR alg, LPCWSTR mode, int hmac)
{
BCRYPT_ALG_HANDLE handle;
NTSTATUS status;
int i;
for (i=0; i siglen) {
log0(0, 0, "Invalid signature length");
return 0;
}
sstatus = NCryptVerifySignature(ec, NULL, meshash, meshashlen, rsval,
ntohs(*rlen) + ntohs(*slen), 0);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptVerifySignature failed", sstatus);
return 0;
}
return 1;
}
/**
* Creates an ECDH key based on two EC keys, one public and one private
*/
int get_ECDH_key(EC_key_t pubkey, EC_key_t privkey, unsigned char *key,
unsigned int *keylen)
{
SECURITY_STATUS sstatus;
NCRYPT_SECRET_HANDLE secret;
int _len;
sstatus = NCryptSecretAgreement(privkey, pubkey, &secret, 0);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptSecretAgreement failed", sstatus);
return 0;
}
sstatus = NCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, NULL, 0, &_len, 0);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptDeriveKey failed", sstatus);
return 0;
}
sstatus = NCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, key, _len,
keylen, 0 );
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptDeriveKey failed", sstatus);
return 0;
}
return 1;
}
/**
* Imports an RSA keyblob into an RSA public key
*/
int import_RSA_key(RSA_key_t *rsa, const unsigned char *keyblob,
uint16_t bloblen)
{
SECURITY_STATUS sstatus;
NCRYPT_PROV_HANDLE prov;
BCRYPT_RSAKEY_BLOB *blobheader;
char *buf, *buf_exp, *buf_mod;
int buflen;
const struct rsa_blob_t *rsablob;
const unsigned char *modulus;
rsablob = (struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
if (sizeof(struct rsa_blob_t) + ntohs(rsablob->modlen) != bloblen) {
log0(0, 0, "Error importing RSA key: invalid length");
return 0;
}
buflen = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(rsablob->exponent) +
ntohs(rsablob->modlen);
buf = safe_calloc(buflen, 1);
blobheader = (BCRYPT_RSAKEY_BLOB *)buf;
buf_exp = buf + sizeof(BCRYPT_RSAKEY_BLOB);
buf_mod = buf_exp + sizeof(rsablob->exponent);
blobheader->Magic = BCRYPT_RSAPUBLIC_MAGIC;
blobheader->BitLength = ntohs(rsablob->modlen) * 8;
blobheader->cbPublicExp = sizeof(rsablob->exponent);
blobheader->cbModulus = ntohs(rsablob->modlen);
buf_exp[0] = (char)((ntohl(rsablob->exponent) & 0xFF000000) >> 24);
buf_exp[1] = (char)((ntohl(rsablob->exponent) & 0x00FF0000) >> 16);
buf_exp[2] = (char)((ntohl(rsablob->exponent) & 0x0000FF00) >> 8);
buf_exp[3] = (char)(ntohl(rsablob->exponent) & 0x000000FF);
memcpy(buf_mod, modulus, ntohs(rsablob->modlen));
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0 ))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
free(buf);
return 0;
}
sstatus = NCryptImportKey(prov, 0, BCRYPT_RSAPUBLIC_BLOB, NULL, rsa,
buf, buflen, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptImportKey failed", sstatus);
free(buf);
NCryptFreeObject(prov);
return 0;
}
free(buf);
NCryptFreeObject(prov);
return 1;
}
/**
* Exports an RSA public key into an RSA keyblob
*/
int export_RSA_key(const RSA_key_t rsa, unsigned char *keyblob,
uint16_t *bloblen)
{
SECURITY_STATUS sstatus;
DWORD len;
BCRYPT_RSAKEY_BLOB *blobheader;
char *buf, *buf_exp, *buf_mod;
struct rsa_blob_t *rsablob;
unsigned char *modulus;
uint32_t exponent;
int i;
rsablob = (struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
sstatus = NCryptExportKey(rsa, (NCRYPT_KEY_HANDLE)NULL,
BCRYPT_RSAPUBLIC_BLOB, NULL, NULL, 0, &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptExportKey failed", sstatus);
return 0;
}
buf = safe_malloc(len);
sstatus = NCryptExportKey(rsa, (NCRYPT_KEY_HANDLE)NULL,
BCRYPT_RSAPUBLIC_BLOB, NULL, buf, len, &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptExportKey failed", sstatus);
free(buf);
return 0;
}
blobheader = (BCRYPT_RSAKEY_BLOB *)buf;
buf_exp = buf + sizeof(BCRYPT_RSAKEY_BLOB);
buf_mod = buf_exp + blobheader->cbPublicExp;
if (blobheader->cbPublicExp > 4) {
log0(0, 0, "unexpected size %d of public exponent\n",
blobheader->cbPublicExp);
free(buf);
return 0;
}
for (exponent = 0, i = blobheader->cbPublicExp - 1; i >= 0; i--) {
exponent |= buf_exp[i] << (8 * (blobheader->cbPublicExp - 1 - i));
}
rsablob->blobtype = KEYBLOB_RSA;
rsablob->reserved = 0;
rsablob->modlen = htons((uint16_t)blobheader->cbModulus);
rsablob->exponent = htonl(exponent);
memcpy(modulus, buf_mod, blobheader->cbModulus);
*bloblen = (uint16_t)(sizeof(struct rsa_blob_t) + blobheader->cbModulus);
free(buf);
return 1;
}
/**
* Imports an EC keyblob into an EC public key
*/
int import_EC_key(EC_key_t *ec, const unsigned char *keyblob, uint16_t bloblen,
int isdh)
{
SECURITY_STATUS sstatus;
NCRYPT_PROV_HANDLE prov;
BCRYPT_ECCKEY_BLOB *blobheader;
char *buf, *buf_key;
int buflen;
const struct ec_blob_t *ecblob;
const unsigned char *keyval;
ecblob = (struct ec_blob_t *)keyblob;
keyval = keyblob + sizeof(struct ec_blob_t);
if (sizeof(struct ec_blob_t) + ntohs(ecblob->keylen) > bloblen) {
log0(0, 0, "Error importing EC key: invalid length");
return 0;
}
buflen = sizeof(BCRYPT_ECCKEY_BLOB) + ntohs(ecblob->keylen);
buf = safe_calloc(buflen, 1);
blobheader = (BCRYPT_ECCKEY_BLOB *)buf;
buf_key = buf + sizeof(BCRYPT_ECCKEY_BLOB);
blobheader->dwMagic = get_curve_magic(ecblob->curve, isdh);
blobheader->cbKey = ntohs(ecblob->keylen) / 2;
memcpy(buf_key, keyval, ntohs(ecblob->keylen));
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0 ))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
free(buf);
return 0;
}
sstatus = NCryptImportKey(prov, 0, BCRYPT_ECCPUBLIC_BLOB,
NULL, ec, buf, buflen, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptImportKey failed", sstatus);
free(buf);
NCryptFreeObject(prov);
return 0;
}
free(buf);
NCryptFreeObject(prov);
return 1;
}
/**
* Exports an EC public key into an EC keyblob
*/
int export_EC_key(const EC_key_t ec, unsigned char *keyblob, uint16_t *bloblen)
{
SECURITY_STATUS sstatus;
DWORD len;
BCRYPT_ECCKEY_BLOB *blobheader;
char *buf, *buf_key;
struct ec_blob_t *ecblob;
unsigned char *keyval;
ecblob = (struct ec_blob_t *)keyblob;
keyval = keyblob + sizeof(struct ec_blob_t);
sstatus = NCryptExportKey(ec, 0, BCRYPT_ECCPUBLIC_BLOB, NULL, NULL, 0,
&len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptExportKey failed", sstatus);
return 0;
}
buf = safe_malloc(len);
sstatus = NCryptExportKey(ec, 0, BCRYPT_ECCPUBLIC_BLOB, NULL,
buf, len, &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptExportKey failed", sstatus);
free(buf);
return 0;
}
blobheader = (BCRYPT_ECCKEY_BLOB *)buf;
buf_key = buf + sizeof(BCRYPT_ECCKEY_BLOB);
ecblob->blobtype = KEYBLOB_EC;
ecblob->curve = get_EC_curve(ec);
ecblob->keylen = htons((uint16_t)(blobheader->cbKey * 2));
memcpy(keyval, buf_key, blobheader->cbKey * 2);
*bloblen = (uint16_t)(sizeof(struct ec_blob_t) + (blobheader->cbKey * 2));
free(buf);
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)
{
NCRYPT_PROV_HANDLE prov;
NCRYPT_KEY_HANDLE key;
SECURITY_STATUS sstatus;
wchar_t wcontainer[256];
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
return 0;
}
memset(wcontainer, 0, sizeof(wcontainer));
if (container && strcmp(container, "")) {
mbstowcs(wcontainer, container, strlen(container));
}
sstatus = NCryptCreatePersistedKey(prov, &key, BCRYPT_RSA_ALGORITHM,
(!wcscmp(wcontainer, L"") ? NULL : wcontainer), 0, machine_keyset);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptCreatePersistedKey failed", sstatus);
NCryptFreeObject(prov);
return 0;
}
if (!bits) bits = DEF_RSA_LEN;
sstatus = NCryptSetProperty(key, NCRYPT_LENGTH_PROPERTY, (PBYTE)&bits,
sizeof(bits), NCRYPT_PERSIST_FLAG | NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptSetProperty failed", sstatus);
NCryptFreeObject(prov);
return 0;
}
sstatus = NCryptFinalizeKey(key, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptFinalizeKey failed", sstatus);
NCryptFreeObject(prov);
return 0;
}
NCryptFreeObject(prov);
return key;
}
/**
* Loads an RSA private key from the specified key container
*/
RSA_key_t read_RSA_key(const char *container)
{
union key_t key;
int keytype;
key = read_private_key(container, &keytype);
if (keytype != KEYBLOB_RSA) {
log0(0, 0, "%s not an RSA key", container);
NCryptFreeObject(key.rsa);
return 0;
}
return key.rsa;
}
EC_key_t gen_EC_key(uint8_t curve, int isdh, const char *container)
{
NCRYPT_PROV_HANDLE prov;
NCRYPT_KEY_HANDLE key;
SECURITY_STATUS sstatus;
LPCWSTR alg;
wchar_t wcontainer[256];
alg = get_curve_alg(curve, isdh);
if (!alg) {
log0(0, 0, "curve not supported\n");
return 0;
}
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
return 0;
}
memset(wcontainer, 0, sizeof(wcontainer));
if (container && strcmp(container, "")) {
mbstowcs(wcontainer, container, strlen(container));
}
sstatus = NCryptCreatePersistedKey(prov, &key, alg,
(!wcscmp(wcontainer, L"") || isdh ? NULL : wcontainer),
0, machine_keyset);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptCreatePersistedKey failed", sstatus);
NCryptFreeObject(prov);
return 0;
}
if (!BCRYPT_SUCCESS(sstatus = NCryptFinalizeKey(key, NCRYPT_SILENT_FLAG))) {
mserror("NCryptFinalizeKey failed", sstatus);
NCryptFreeObject(prov);
return 0;
}
NCryptFreeObject(prov);
return key;
}
EC_key_t read_EC_key(const char *container)
{
union key_t key;
int keytype;
key = read_private_key(container, &keytype);
if (keytype != KEYBLOB_EC) {
log0(0, 0, "%s not an EC key", container);
NCryptFreeObject(key.ec);
return 0;
}
return key.ec;
}
union key_t read_private_key(const char *container, int *keytype)
{
union key_t tmp;
NCRYPT_PROV_HANDLE prov;
NCRYPT_KEY_HANDLE key;
SECURITY_STATUS sstatus;
wchar_t algtype[20];
wchar_t wcontainer[256];
int len;
tmp.key = 0;
*keytype = 0;
if (!BCRYPT_SUCCESS(sstatus = NCryptOpenStorageProvider(&prov, NULL, 0))) {
mserror("NCryptOpenStorageProvider failed", sstatus);
return tmp;
}
memset(wcontainer, 0, sizeof(wcontainer));
mbstowcs(wcontainer, container, strlen(container));
sstatus = NCryptOpenKey(prov, &key, wcontainer, 0,
NCRYPT_SILENT_FLAG | machine_keyset);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptOpenKey failed", sstatus);
NCryptFreeObject(prov);
return tmp;
}
sstatus = NCryptGetProperty(key, NCRYPT_ALGORITHM_GROUP_PROPERTY, NULL,
0, &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptGetProperty failed", sstatus);
NCryptFreeObject(key);
NCryptFreeObject(prov);
return tmp;
}
sstatus = NCryptGetProperty(key, NCRYPT_ALGORITHM_GROUP_PROPERTY,
(PBYTE)algtype, sizeof(algtype), &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptGetProperty failed", sstatus);
NCryptFreeObject(key);
NCryptFreeObject(prov);
return tmp;
}
if (!wcscmp(algtype, NCRYPT_ECDSA_ALGORITHM_GROUP)) {
tmp.ec = key;
*keytype = KEYBLOB_EC;
} else if (!wcscmp(algtype, NCRYPT_RSA_ALGORITHM_GROUP)) {
tmp.rsa = key;
*keytype = KEYBLOB_RSA;
} else {
log0(0, 0, "Unexpected key type: %ws", algtype);
NCryptFreeObject(key);
}
NCryptFreeObject(prov);
return tmp;
}
uint8_t get_EC_curve(const EC_key_t ec)
{
SECURITY_STATUS sstatus;
wchar_t alg[20];
int len;
sstatus = NCryptGetProperty(ec, NCRYPT_ALGORITHM_PROPERTY, NULL,
0, &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptGetProperty failed", sstatus);
return 0;
}
sstatus = NCryptGetProperty(ec, NCRYPT_ALGORITHM_PROPERTY,
(PBYTE)alg, sizeof(alg), &len, NCRYPT_SILENT_FLAG);
if (!BCRYPT_SUCCESS(sstatus)) {
mserror("NCryptGetProperty failed", sstatus);
return 0;
}
if (!wcscmp(alg, BCRYPT_ECDH_P256_ALGORITHM) ||
!wcscmp(alg, BCRYPT_ECDSA_P256_ALGORITHM)) {
return CURVE_prime256v1;
} else if (!wcscmp(alg, BCRYPT_ECDH_P384_ALGORITHM) ||
!wcscmp(alg, BCRYPT_ECDSA_P384_ALGORITHM)) {
return CURVE_secp384r1;
} else if (!wcscmp(alg, BCRYPT_ECDH_P521_ALGORITHM) ||
!wcscmp(alg, BCRYPT_ECDSA_P521_ALGORITHM)) {
return CURVE_secp521r1;
} else {
log0(0, 0, "Unexpected key type: %ws", alg);
return 0;
}
}
void free_RSA_key(RSA_key_t rsa)
{
SECURITY_STATUS sstatus;
if (!BCRYPT_SUCCESS(sstatus = NCryptFreeObject(rsa))) {
mserror("NCryptFreeObject failed", sstatus);
}
}
void free_EC_key(EC_key_t ec)
{
SECURITY_STATUS sstatus;
if (!BCRYPT_SUCCESS(sstatus = NCryptFreeObject(ec))) {
mserror("NCryptFreeObject failed", sstatus);
}
}
void set_sys_keys(int set_sys_key)
{
if (set_sys_key) {
machine_keyset = NCRYPT_MACHINE_KEY_FLAG;
} else {
machine_keyset = 0;
}
}
uftp-4.1.5/client_loop.h 0000644 0000764 0000764 00000002651 12250244021 014157 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_LOOP_H
#define _CLIENT_LOOP_H
void mainloop(void);
#endif // _CLIENT_LOOP_H
uftp-4.1.5/encryption.h 0000644 0000764 0000764 00000014455 12250244021 014047 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _ENCRYPTION_H
#define _ENCRYPTION_H
// This section includes items normally listed in uftp_common.h
// that are required in encrypt_cng.c. See encrypt_cng.c for more details.
#ifdef NO_UFTP_COMMON_H
#include
extern int showtime;
extern FILE *applog;
extern 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, ...);
#define clog0(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 0, 0, 0, __VA_ARGS__)
#define log0(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog2(...) \
logfunc(0, 0, 2, 0, 0, 0, 0, __VA_ARGS__)
#define syserror(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 1, errno, 0, __VA_ARGS__)
int is_auth_enc(int keytype);
int is_gcm_mode(int keytype);
int is_ccm_mode(int keytype);
void *safe_malloc(size_t size);
void *safe_calloc(size_t num, size_t size);
#endif
#ifdef NO_ENCRYPTION
typedef void *RSA_key_t;
typedef void *EC_key_t;
#elif defined WINDOWS && !defined OPENSSL &&\
(_WIN32_WINNT >= _WIN32_WINNT_LONGHORN)
#include
#include
#include
typedef NCRYPT_KEY_HANDLE RSA_key_t;
typedef NCRYPT_KEY_HANDLE EC_key_t;
#elif defined WINDOWS && !defined OPENSSL
#include
#include
typedef HCRYPTKEY RSA_key_t;
typedef void *EC_key_t;
#else
#include
typedef RSA *RSA_key_t;
#ifndef NO_EC
#include
#include
#include
typedef EC_KEY *EC_key_t;
#else
typedef void *EC_key_t;
#endif
#endif
union key_t {
uint64_t key;
RSA_key_t rsa;
EC_key_t ec;
};
void crypto_init(int set_sys_key);
void crypto_cleanup(void);
int cipher_supported(int keytype);
int hash_supported(int hashtype);
void get_key_info(int keytype, int *keylen, int *ivlen);
int get_hash_len(int hashtype);
int get_random_bytes(unsigned char *buf, int num);
int encrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *aad, unsigned int aadlen,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen);
int decrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *aad, unsigned int aadlen,
unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen);
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);
int hash(int hashtype, const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen);
int RSA_keylen(const RSA_key_t rsa);
int EC_keylen(const EC_key_t ec);
int ECDSA_siglen(const EC_key_t ec);
int RSA_encrypt(RSA_key_t rsa, const unsigned char *from, unsigned int fromlen,
unsigned char *to, unsigned int *tolen);
int RSA_decrypt(RSA_key_t rsa, const unsigned char *from, unsigned int fromlen,
unsigned char *to, unsigned int *tolen);
int create_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen);
int verify_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int siglen);
int create_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen);
int verify_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
const unsigned char *sig, unsigned int siglen);
int get_ECDH_key(EC_key_t pubkey, EC_key_t privkey, unsigned char *key,
unsigned int *keylen);
int import_RSA_key(RSA_key_t *rsa, const unsigned char *keyblob,
uint16_t bloblen);
int export_RSA_key(const RSA_key_t rsa, unsigned char *keyblob,
uint16_t *bloblen);
int import_EC_key(EC_key_t *ec, const unsigned char *keyblob, uint16_t bloblen,
int isdh);
int export_EC_key(const EC_key_t ec, unsigned char *keyblob, uint16_t *bloblen);
RSA_key_t gen_RSA_key(int bits, int exponent, const char *filename);
RSA_key_t read_RSA_key(const char *filename);
EC_key_t gen_EC_key(uint8_t curve, int isdh, const char *filename);
EC_key_t read_EC_key(const char *filename);
union key_t read_private_key(const char *filename, int *keytype);
uint8_t get_EC_curve(const EC_key_t ec);
void free_RSA_key(RSA_key_t rsa);
void free_EC_key(EC_key_t ec);
const char *get_next_container(void);
void delete_container(const char *name);
void set_sys_keys(int set);
#endif // _ENCRYPTION_H
uftp-4.1.5/server_config.h 0000644 0000764 0000764 00000006217 12176066071 014523 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_CONFIG_H
#define _SERVER_CONFIG_H
/**
* Default command line values
*/
#define DEF_RATE 128000
#define DEF_PORT "1044"
#define DEF_SRCPORT "0"
#define DEF_PUB_MULTI "230.4.4.1"
#define DEF_PRIV_MULTI "230.5.5.x"
#define DEF_TTL 1
#define DEF_DSCP 0
#define DEF_RCVBUF 262144
#define DEF_BSD_RCVBUF 233016
#define DEF_BLOCKSIZE 1300
#define DEF_GRTT 0.5
#define DEF_MIN_GRTT 0.01
#define DEF_MAX_GRTT 15.0
#define DEF_ROBUST 20
#define DEF_TXWEIGHT 0
#define DEF_MAX_NAK_PCT 100
#define DEF_MAX_NAK_CNT 1
#define DEF_KEYEXTYPE KEYEX_RSA
#define DEF_KEYTYPE KEY_NONE
#define DEF_HASHTYPE HASH_SHA1
#define DEF_SIGTYPE SIG_HMAC
#define USAGE "uftp [ -R txrate ] [ -L logfile ] [ -B udp_buf_size ]\n\
[ -g max_log_size ] [ -n max_log_count ] [ -m max_nak_count ]\n\
[ -Y keytype ] [ -h hashtype ] [ -w sigtype ] [ -e keyextype[:curve] ]\n\
[ -c ] [ -k key_file ] [ -K new_key_length | curve ] [ -l ] [ -T ]\n\
[ -b block_size ] [ -t ttl ] [ -Q dscp ] [ -z | -Z ] [ -I interface ]\n\
[ -p port ] [ -u source_port ] [ -j proxylist_file ]\n\
[ -q ] [ -f ] [ -y ] [ -x log_level ]\n\
[ -H host[,host...] | -H @hostlist_file | -F restart_file ] [ -o ]\n\
[ -X exclude_file ] [ -M pub_multicast_addr ] [ -P priv_multicast_addr ]\n\
[ -C cc_type ] [ -D dest_name ] [ -E base_dir[,base_dir... ] ]\n\
[ -S status_file ] [ -r init_grtt[:min_grtt:max_grtt] ] [ -s robust ] \n\
[ -W txweight ] [ -N max_nak_pct ] { -i list_file | file [ file... ] }\n"
void process_args(int argc, char *argv[]);
int read_cc_config(const char *filename);
#endif // _SERVER_CONFIG_H
uftp-4.1.5/server_phase.c 0000644 0000764 0000764 00000145011 12250244021 014327 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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"
#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(unsigned char *packet, unsigned char *decrypted,
int packetlen, const union sockaddr_u *receiver,
struct finfo_t *finfo,
int announce, int open_group, int regconf)
{
struct uftp_h *header;
unsigned char *message;
int hostidx;
unsigned decryptlen, meslen;
uint8_t *func;
header = (struct uftp_h *)packet;
hostidx = find_client(header->src_id);
if ((keytype != KEY_NONE) && (header->func == ENCRYPTED)) {
if (hostidx == -1) {
log2(finfo->group_id, finfo->file_id, "Got encrypted packet "
"from unknown receiver %08X", ntohl(header->src_id));
return 0;
}
if (!validate_and_decrypt(packet, packetlen, &decrypted, &decryptlen,
keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, destlist[hostidx].encinfo->pubkey,
destlist[hostidx].encinfo->pubkeylen)) {
log1(finfo->group_id, finfo->file_id, "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 == KEYINFO_ACK) ||
(header->func == FILEINFO_ACK))) {
log1(finfo->group_id, finfo->file_id,
"Rejecting %s message from %08X: not encrypted",
func_name(header->func), ntohl(header->src_id));
return 0;
}
func = (uint8_t *)&header->func;
message = packet + sizeof(struct uftp_h);
meslen = packetlen - sizeof(struct uftp_h);
}
if (*func == ABORT) {
handle_abort(message, meslen, hostidx, finfo, header->src_id);
return 1;
}
if (hostidx == -1) {
if (open_group) {
if (*func == REGISTER) {
handle_open_register(message, meslen, finfo, receiver,
header->src_id, regconf);
} else if (*func == CLIENT_KEY) {
handle_open_clientkey(message, meslen, finfo, receiver,
header->src_id);
} else {
log1(finfo->group_id, finfo->file_id, "Invalid function: "
"expected REGISTER or CLIENT_KEY, got %s",
func_name(*func));
}
} else {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(header->src_id));
send_abort(finfo, "Not in host list", receiver, header->src_id,0,0);
}
} else {
switch (destlist[hostidx].status) {
case DEST_MUTE:
if (*func == REGISTER) {
handle_register(message, meslen, finfo, receiver,
hostidx, regconf, open_group);
} else if (*func == CLIENT_KEY) {
handle_clientkey(message, meslen, finfo, receiver, hostidx);
} else {
log1(finfo->group_id, finfo->file_id, "Invalid function: "
"expected REGISTER or CLIENT_KEY, got %s",
func_name(*func));
}
break;
case DEST_REGISTERED:
if (announce && (*func == KEYINFO_ACK)) {
handle_keyinfo_ack(message, meslen, finfo, receiver, hostidx);
} else if (!announce && (*func == FILEINFO_ACK)) {
handle_fileinfo_ack(message, meslen, finfo, hostidx);
} else if (*func == REGISTER) {
handle_register(message, meslen, finfo, receiver,
hostidx, regconf, open_group);
} else if (*func == CLIENT_KEY) {
log2(finfo->group_id, finfo->file_id, "Received CLIENT_KEY+ "
"from %s", destlist[hostidx].name);
} else if (!announce && (*func == COMPLETE)) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(finfo->group_id, finfo->file_id,
"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,
hostidx, regconf, open_group);
} else if (*func == CLIENT_KEY) {
log2(finfo->group_id, finfo->file_id, "Received CLIENT_KEY+ "
"from %s", destlist[hostidx].name);
} else if (announce && (*func == KEYINFO_ACK)) {
finfo->deststate[hostidx].conf_sent = 0;
handle_keyinfo_ack(message, meslen, finfo, receiver, hostidx);
} else if (!announce && (*func == FILEINFO_ACK)) {
handle_fileinfo_ack(message, meslen, finfo, hostidx);
} else if (!announce && (*func == COMPLETE)) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(finfo->group_id, finfo->file_id,
"Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
default:
log1(finfo->group_id, finfo->file_id,
"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 <-KEYINFO_ACK
* Group & no encryption: ->ANNOUNCE <-REGISTER ->REG_CONF
* Files within a group: ->FILEINFO <-FILEINFO_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)
{
int attempt, resend, announce, regconf, keyinfo, fileinfo;
int open_group, anyerror;
int len, rval, rcv_status, last_pass, gotall, gotone, allreg, regdone, i;
unsigned char *packet, *decrypted;
struct timeval timeout, next_send, now;
union sockaddr_u receiver;
int grtt_set;
if (finfo->file_id) {
log1(finfo->group_id, finfo->file_id,
"File ID: %04X Name: %s", finfo->file_id, finfo->filename);
log1(finfo->group_id, finfo->file_id,
" sending as: %s", finfo->destfname);
switch (finfo->ftype) {
case FTYPE_REG:
log2(finfo->group_id, finfo->file_id,
"Bytes: %s Blocks: %d Sections: %d",
printll(finfo->size), finfo->blocks, finfo->sections);
log3(finfo->group_id, finfo->file_id, "small section size: %d, "
"big section size: %d, " "# big sections: %d",
finfo->secsize_small, finfo->secsize_big, finfo->big_sections);
break;
case FTYPE_DIR:
log2(finfo->group_id, finfo->file_id, "Empty directory");
break;
case FTYPE_LINK:
log2(finfo->group_id, finfo->file_id,
"Symbolic link to %s", finfo->linkname);
break;
case FTYPE_DELETE:
log2(finfo->group_id, finfo->file_id,
"Delete file/directory with this name");
break;
case FTYPE_FREESPACE:
log2(finfo->group_id, finfo->file_id, "Free disk space query");
break;
}
} else {
log2(finfo->group_id, finfo->file_id, "Initializing group");
}
rval = 1;
packet = safe_calloc(MAXMTU, 1);
decrypted = safe_calloc(MAXMTU, 1);
announce = (finfo->file_id == 0);
regconf = (announce && (keytype == KEY_NONE));
keyinfo = (announce && (keytype != KEY_NONE));
fileinfo = (finfo->file_id != 0);
open_group = (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;
}
destlist[i].freespace = -1;
}
}
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, 3 * grtt);
resend = 1;
attempt = 1;
last_pass = 0;
regdone = 0;
grtt_set = !announce;
while (attempt <= robust) {
if (user_abort) break;
// On the initial pass, or when the 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_group)) {
continue;
}
resend = 0;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, next_send) >= 0) {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
} else {
timeout = diff_timeval(next_send, now);
}
if ((rcv_status = read_packet(sock, &receiver, packet, &len,
MAXMTU, &timeout)) == -1) {
continue;
} else if (rcv_status == 0) {
attempt++;
grtt_set |= recalculate_grtt(finfo, grtt_set, 0);
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, 3 * grtt);
resend = 1;
if (last_pass) break;
continue;
}
if (!validate_packet(packet, len, finfo)) {
continue;
}
if (!handle_announce_phase(packet, decrypted, len, &receiver, finfo,
announce, open_group, regconf)) {
continue;
}
if (!open_group) {
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 timeout to 1 * grtt
// to allow for late registers.
if (finfo->file_id != 0) break;
recalculate_grtt(finfo, grtt_set, 0);
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, grtt);
if (!last_pass) {
log2(finfo->group_id, finfo->file_id, "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;
}
}
}
recalculate_grtt(finfo, 1, 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) {
if (announce) {
log1(finfo->group_id, finfo->file_id,
"Couldn't get KEYINFO_ACK from %s", destlist[i].name);
} else {
log1(finfo->group_id, finfo->file_id,
"Couldn't get FILEINFO_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(finfo->group_id, finfo->file_id, "Aboring all clients");
send_abort(finfo, "A client dropped out, aborting all",
&receive_dest, 0, (keytype != KEY_NONE), 0);
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
destlist[i].status = DEST_ABORT;
}
}
rval = 0;
}
if (!gotone) {
log0(finfo->group_id, finfo->file_id, "Announce timed out");
rval = 0;
}
if (open_group) {
send_regconf(finfo, attempt, regconf);
}
if ((finfo->file_id == 0) && status_file) {
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
fprintf(status_file, "CONNECT;success;%s\n", destlist[i].name);
} else {
fprintf(status_file, "CONNECT;failed;%s\n", destlist[i].name);
}
}
log0(finfo->group_id, finfo->file_id, "- Transfer -");
}
free(packet);
free(decrypted);
return rval;
}
/**
* Check a transfer phase message and pass to appropriate message handler,
* decrypting first if necessary
*/
void handle_transfer_phase(unsigned char *packet,unsigned char *decrypted,
int packetlen, const union sockaddr_u *receiver,
struct finfo_t *finfo, int *got_naks)
{
struct uftp_h *header;
unsigned char *message;
int hostidx;
unsigned int decryptlen, meslen;
uint8_t *func;
header = (struct uftp_h *)packet;
hostidx = find_client(header->src_id);
if ((keytype != KEY_NONE) && (header->func == ENCRYPTED)) {
if (hostidx == -1) {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(header->src_id));
send_abort(finfo, "Not in host list", receiver, header->src_id,0,0);
return;
}
if (!validate_and_decrypt(packet, packetlen, &decrypted, &decryptlen,
keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, destlist[hostidx].encinfo->pubkey,
destlist[hostidx].encinfo->pubkeylen)) {
log1(finfo->group_id, finfo->file_id, "Rejecting message from %s: "
"decrypt/validate failed", destlist[hostidx].name);
return;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((keytype != KEY_NONE) && ( (header->func == STATUS) ||
(header->func == COMPLETE) || (header->func == ABORT))) {
log1(finfo->group_id, finfo->file_id,
"Rejecting %s message from %08X: not encrypted",
func_name(header->func), ntohl(header->src_id));
return;
}
func = (uint8_t *)&header->func;
message = packet + sizeof(struct uftp_h);
meslen = packetlen - sizeof(struct uftp_h);
}
if (*func == ABORT) {
handle_abort(message, meslen, hostidx, finfo, header->src_id);
} else if (hostidx == -1) {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(header->src_id));
send_abort(finfo, "Not in host list", receiver, header->src_id, 0, 0);
} else {
switch (destlist[hostidx].status) {
case DEST_ACTIVE:
case DEST_ACTIVE_NAK:
if (*func == STATUS) {
handle_status(message, meslen, finfo, hostidx, got_naks);
} else if (*func == COMPLETE) {
handle_complete(message, meslen, finfo, hostidx);
} else if (*func == CC_ACK) {
handle_cc_ack(message, meslen, finfo, hostidx);
} else {
log1(finfo->group_id, finfo->file_id,
"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(finfo->group_id, finfo->file_id,
"Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
break;
}
}
return;
}
/**
* Adjust the sending rate based on percentage of NAKs received to blocks sent
* TODO: this has to change to accomodate lost percentage as received from CC
*/
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 * datapacketsize / rate);
log2(0, 0, "Updated rate: %d Kbps (%d KB/s)",
rate * 8 / 1024, rate / 1024);
log2(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(const struct finfo_t *finfo, int file, int block,
f_offset_t *offset)
{
f_offset_t new_offset;
if ((new_offset = lseek_func(file,
((f_offset_t)block * blocksize) - *offset, SEEK_CUR)) == -1) {
syserror(finfo->group_id, finfo->file_id, "lseek failed for file");
return 0;
}
if (new_offset != (f_offset_t)block * blocksize) {
log0(finfo->group_id, finfo->file_id,
"block %d: offset is %s", block, printll(new_offset));
log0(finfo->group_id, finfo->file_id,
" should be %s", printll((f_offset_t)block * blocksize));
if ((new_offset = lseek_func(file,
((f_offset_t)block * blocksize) - (new_offset),
SEEK_CUR)) == -1) {
syserror(finfo->group_id, finfo->file_id, "lseek failed for file");
return 0;
}
}
*offset = new_offset;
return 1;
}
/*
TODO: Have the server echo back any received NAKs, and have the
clients suppress their own NAKs based on that.
*/
/**
* Variables shared between the sending and receiving threads
*/
mux_t mux_main;
struct cc_queue_item {
unsigned char *data;
int len;
};
#define CC_QUEUE_LEN 100
static struct cc_queue_item cc_queue[CC_QUEUE_LEN];
static int cc_queue_start, cc_queue_end, end_of_pass;
static int file_done_flag, rewind_flag, rewind_pending_flag, max_time_timeout;
int rate_change;
uint32_t current_position;
uint16_t cc_seq;
uint32_t cc_rate;
double adv_grtt;
// TODO: not shared between threads, move?
int slowstart, clr, clr_drop, new_rate;
struct timeval last_clr_time;
static int file;
/**
* Read item from cc_queue. The calling thread must be holding the lock.
* Returns the head of the list, or NULL if the list is empty.
*/
void get_cc_queue(unsigned char **item, int *len)
{
if ((cc_queue_start == cc_queue_end) &&
(cc_queue[cc_queue_end].data == NULL)) {
*item = NULL;
*len = 0;
return;
}
*item = cc_queue[cc_queue_start].data;
*len = cc_queue[cc_queue_start].len;
cc_queue[cc_queue_start].data = NULL;
cc_queue[cc_queue_start++].len = 0;
if (cc_queue_start == CC_QUEUE_LEN) {
cc_queue_start = 0;
}
}
/**
* Put item on cc_queue. The calling thread must be holding the lock.
* Returns true if the item was added, false if the list is full.
*/
int put_cc_queue(const struct finfo_t *finfo, unsigned char *item, int len)
{
if ((cc_queue_start == cc_queue_end) &&
(cc_queue[cc_queue_end].data != NULL)) {
log1(finfo->group_id, finfo->file_id, "cc_queue full");
return 0;
}
cc_queue[cc_queue_end].data = item;
cc_queue[cc_queue_end++].len = len;
if (cc_queue_end == CC_QUEUE_LEN) {
cc_queue_end = 0;
}
return 1;
}
/**
* Thread for sending all packets during the transfer phase
*/
THREAD_FUNC transfer_send_thread(void *infop)
{
unsigned char *packet, *encpacket, *data, *cc_body;
struct uftp_h *header;
struct fileseg_h *fileseg;
struct tfmcc_data_info_he *tfmcc;
int numbytes, attempt, l_file_done_flag, current_nak, cc_len, done_sent;
double l_adv_grtt;
uint16_t l_cc_seq, pass, section, last_section;
uint32_t l_cc_rate, block;
int l_packet_wait, l_rate_change, max_time;
struct timeval last_sent, current_sent, now, start_time;
int64_t overage, tdiff;
f_offset_t offset, curr_offset;
struct finfo_t *finfo;
finfo = infop;
// Not mutexed, but should be OK when the thread first starts
l_cc_seq = cc_seq;
l_cc_rate = cc_rate;
l_packet_wait = packet_wait;
l_adv_grtt = adv_grtt;
l_rate_change = 0;
packet = safe_calloc(MAXMTU, 1);
encpacket = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)packet;
fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
if (cc_type == CC_TFMCC) {
tfmcc = (struct tfmcc_data_info_he *)((unsigned char *)fileseg +
sizeof(struct fileseg_h));
tfmcc->exttype = EXT_TFMCC_DATA_INFO;
tfmcc->extlen = sizeof(struct tfmcc_data_info_he) / 4;
fileseg->hlen = (sizeof(struct fileseg_h) +
sizeof(struct tfmcc_data_info_he)) / 4;
} else {
tfmcc = NULL;
fileseg->hlen = sizeof(struct fileseg_h) / 4;
}
data = (unsigned char *)fileseg + (fileseg->hlen * 4);
set_uftp_header(header, FILESEG, finfo->group_id, finfo->group_inst,
l_adv_grtt, destcount);
gettimeofday(&start_time, NULL);
gettimeofday(&last_sent, NULL);
overage = 0;
offset = 0;
lseek_func(file, 0, SEEK_SET);
fileseg->func = FILESEG;
fileseg->file_id = htons(finfo->file_id);
// If all clients received this file partially on a prior attempt,
// set the block counter at the end so we start by sending a DONE
if (finfo->partial) {
block = finfo->blocks;
} else {
block = 0;
}
done_sent = 0;
current_nak = 1;
attempt = 1;
l_file_done_flag = 0;
if ((cc_type == CC_NONE) && (rate != -1) && (txweight != 0)) {
max_time = (int)(0 + floor(((double)txweight / 100) *
((double)finfo->size / rate)));
log2(finfo->group_id, finfo->file_id,
"Maximum file transfer time: %d seconds", max_time);
} else {
max_time = 0;
}
pass = 1;
section = 0;
last_section = (uint16_t)-1;
log2(finfo->group_id, finfo->file_id, "Sending file");
log2(finfo->group_id, finfo->file_id, "Starting pass %u", pass);
do {
if (block < finfo->blocks) {
if (current_nak) {
log5(finfo->group_id, finfo->file_id,
"Sending %d, wait=%d", block, l_packet_wait);
attempt = 1;
// TODO: try to avoid seek on consecutive packets?
curr_offset = offset;
if (!seek_block(finfo, file, block, &curr_offset)) {
continue;
}
offset = curr_offset;
if ((numbytes = read(file, data, blocksize)) == -1) {
syserror(finfo->group_id, finfo->file_id, "read failed");
continue;
}
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.
if (l_packet_wait > overage) {
usleep(l_packet_wait - (int32_t)overage);
}
gettimeofday(¤t_sent, NULL);
tdiff = diff_usec(current_sent, last_sent);
if (l_packet_wait) overage += tdiff - l_packet_wait;
last_sent = current_sent;
if (log_level >= 5) {
clog5(finfo->group_id, finfo->file_id,
"tdiff=%s, ", printll(tdiff));
slog5("overage=%s", printll(overage));
}
// When rate changes significantly, clear the overage counter
if (l_rate_change) {
overage = 0;
}
header->grtt = quantize_grtt(l_adv_grtt);
if (cc_type == CC_TFMCC) {
tfmcc->send_rate = htons(quantize_rate(rate));
tfmcc->cc_seq = htons(l_cc_seq);
tfmcc->cc_rate = htons(quantize_rate(l_cc_rate));
}
if (block >= finfo->big_sections * finfo->secsize_big) {
fileseg->section = htons(((block -
(finfo->big_sections * finfo->secsize_big)) /
finfo->secsize_small) + finfo->big_sections);
fileseg->sec_block = htons((block -
(finfo->big_sections * finfo->secsize_big)) %
finfo->secsize_small);
} else {
fileseg->section = htons(block / finfo->secsize_big);
fileseg->sec_block = htons(block % finfo->secsize_big);
}
section = ntohs(fileseg->section);
if (last_section != section) {
log2(finfo->group_id, finfo->file_id,
"Sending section %u", section);
last_section = section;
}
send_data(finfo, packet, numbytes, encpacket);
done_sent = 0;
}
} else {
gettimeofday(&now, NULL);
if (!done_sent ||
(diff_usec(now, last_sent) > (3 * l_adv_grtt * 1000000))) {
if (attempt < robust) {
if (!send_done(finfo, attempt, finfo->sections ?
finfo->sections - 1 : 0, l_adv_grtt)) {
log2(finfo->group_id, finfo->file_id,
"Error sending DONE");
}
}
attempt++;
gettimeofday(&last_sent, NULL);
done_sent = 1;
}
usleep(l_packet_wait);
overage = 0;
}
// Access anything used by both threads under one mutex all at once
if (mux_lock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to lock mutex in transfer_send_thread");
continue;
}
if (max_time) {
gettimeofday(&now, NULL);
if (diff_sec(now, start_time) > max_time) {
log1(finfo->group_id, finfo->file_id,
"Maximum file transfer time exceeded");
max_time_timeout = 1;
file_done_flag = 1;
}
}
if (block < finfo->blocks) {
finfo->naklist[block] = 0;
block++;
} else {
end_of_pass = 1;
}
if (rewind_flag) {
log2(finfo->group_id, finfo->file_id, "Starting pass %u", ++pass);
block = 0;
rewind_flag = 0;
end_of_pass = 0;
last_section = (uint16_t)-1;
}
if (block < finfo->blocks) {
current_nak = finfo->naklist[block];
} else {
current_nak = 0;
}
current_position = block;
if ((attempt > robust) && !rewind_pending_flag) {
log1(finfo->group_id, finfo->file_id, "Sending thread timed out");
file_done_flag = 1;
}
l_cc_seq = cc_seq;
l_cc_rate = cc_rate;
l_adv_grtt = adv_grtt;
l_packet_wait = packet_wait;
l_file_done_flag = file_done_flag;
l_rate_change = rate_change;
rate_change = 0;
get_cc_queue(&cc_body, &cc_len);
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in transfer_send_thread");
// TODO: if we can't unlock, kill the thread and fail the file
continue;
}
if (cc_body) {
send_cong_ctrl(finfo, l_adv_grtt, l_cc_seq, l_cc_rate,
cc_body, cc_len);
free(cc_body);
cc_body = NULL;
}
} while (!l_file_done_flag);
free(packet);
free(encpacket);
THREAD_RETURN;
}
/**
* Thread for receiving all packets during the transfer phase
* Called directly from transfer_phase
*/
void transfer_receive_thread(struct finfo_t *finfo)
{
unsigned char *packet, *decrypted, *cc_body;
union sockaddr_u receiver;
struct timeval now, timeout, rewind_time, fb_end, next_cc;
struct timeval min_tstamp;
int l_file_done_flag, got_naks, cc_len;
int alldone, found_error, i, len, rcv_status, found_timeout;
int do_rewind, do_nextcc, do_halfrate;
int64_t last_clr;
double l_adv_grtt;
packet = safe_calloc(MAXMTU, 1);
decrypted = safe_calloc(MAXMTU, 1);
alldone = 0;
rewind_time.tv_sec = 0;
rewind_time.tv_usec = 0;
got_naks = 0;
next_cc.tv_sec = 0;
next_cc.tv_usec = 0;
// Not mutexed, but should be OK when the thread first starts
l_adv_grtt = adv_grtt;
log4(finfo->group_id, finfo->file_id, "adv_grtt=%.3f", l_adv_grtt);
if (cc_type == CC_TFMCC) {
// Start a feedback round
gettimeofday(&last_clr_time, NULL);
gettimeofday(&fb_end, NULL);
add_timeval_d(&fb_end, 6 * l_adv_grtt);
} else {
fb_end.tv_sec = 0;
fb_end.tv_usec = 0;
}
l_file_done_flag = 0;
do {
do_rewind = 0;
do_nextcc = 0;
do_halfrate = 0;
gettimeofday(&now, NULL);
if ((cc_type == CC_TFMCC) && (cmptimestamp(now, next_cc) > 0)) {
create_cc_list(&cc_body, &cc_len);
if (!put_cc_queue(finfo, cc_body, cc_len)) {
log1(finfo->group_id, finfo->file_id,
"Couldn't queue up CONG_CTRL: list full!");
free(cc_body);
} else {
gettimeofday(&next_cc, NULL);
add_timeval_d(&next_cc, 1 * l_adv_grtt);
}
log3(finfo->group_id, finfo->file_id, "CONG_CTRL queued");
// TODO: bypass this check if selected less that 10 RTT ago?
if (diff_usec(now, last_clr_time) > 1000000 * 4 * l_adv_grtt) {
do_halfrate = 1;
log5(finfo->group_id, finfo->file_id, "Halfing rate");
}
if (diff_usec(now, last_clr_time) > 1000000 * robust * l_adv_grtt) {
clr = -1;
clr_drop = 1;
log5(finfo->group_id, finfo->file_id, "Lost clr");
}
}
if ((rewind_time.tv_sec) && (cmptimestamp(now, rewind_time) >= 0)) {
do_rewind = 1;
}
if ((fb_end.tv_sec) && (cmptimestamp(now, fb_end) >= 0)) {
do_nextcc = 1;
}
if (do_rewind || do_nextcc || do_halfrate) {
if (mux_lock(mux_main)) {
log0(finfo->group_id, finfo->file_id, "Failed to lock mutex "
"in transfer_receive_thread for rewind / feedback");
continue;
}
}
if (do_rewind) {
log3(finfo->group_id, finfo->file_id, "Rewind timer tripped");
rewind_flag = 1;
rewind_time.tv_sec = 0;
rewind_time.tv_usec = 0;
got_naks = 0;
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE_NAK) {
destlist[i].status = DEST_ACTIVE;
}
}
}
if (do_halfrate) {
rate /= 2;
if (rate < datapacketsize / grtt) {
slowstart = 1;
rate = (int)(datapacketsize / grtt);
}
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
rate_change = 1;
last_clr = diff_usec(now, last_clr_time);
if ((last_clr > 1000000 * 2 * robust * l_adv_grtt) &&
(last_clr > 1000000 * 5)) {
// No new CLR chosen in 2*robust RTTs (or 5 seconds) since the
// prior one dropped, meaning no feedback from anyone, so quit
log2(finfo->group_id, finfo->file_id,
"No feedback in %d GRTTs", 2 * robust);
file_done_flag = 1;
}
}
if (do_nextcc) {
// TODO: Handle extended feedback round
if (clr_drop) {
clr_drop = 0;
if (new_rate) {
rate = new_rate;
}
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
rate_change = 1;
}
recalculate_grtt(finfo, 1, 1);
adv_grtt = (double)datapacketsize / rate;
if (adv_grtt < grtt) {
adv_grtt = grtt;
}
l_adv_grtt = adv_grtt;
log5(finfo->group_id, finfo->file_id, "adv_grtt=%.3f", l_adv_grtt);
if (rate < datapacketsize / grtt) {
slowstart = 1;
rate = (int)(datapacketsize / grtt);
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
rate_change = 1;
}
gettimeofday(&fb_end, NULL);
add_timeval_d(&fb_end, 6 * l_adv_grtt);
cc_rate = 0xFFFFFFFF;
cc_seq++;
log4(finfo->group_id, finfo->file_id,
"Starting feedback round %d", cc_seq);
}
if (do_rewind || do_nextcc || do_halfrate) {
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id, "Failed to unlock mutex "
"in transfer_receive_thread for rewind / feedback");
continue;
}
}
found_timeout = 0;
if (rewind_time.tv_sec) {
min_tstamp = rewind_time;
found_timeout = 1;
}
if (fb_end.tv_sec) {
if (!found_timeout || (cmptimestamp(fb_end, min_tstamp) < 0)) {
min_tstamp = fb_end;
found_timeout = 1;
}
}
if (cc_type == CC_TFMCC) {
if (!found_timeout || (cmptimestamp(next_cc, min_tstamp) < 0)) {
min_tstamp = next_cc;
found_timeout = 1;
}
}
if (found_timeout) {
timeout = diff_timeval(min_tstamp, now);
} else {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
add_timeval_d(&timeout, 1 * l_adv_grtt);
}
if ((rcv_status = read_packet(sock, &receiver, packet, &len,
MAXMTU, &timeout)) == -1) {
continue;
} else if (rcv_status == 0) {
// Timeouts get handled at the top of the loop
} else if (validate_packet(packet, len, finfo)) {
handle_transfer_phase(packet, decrypted, len, &receiver, finfo,
&got_naks);
for (i = 0, alldone = 1; (i < destcount) && alldone; i++) {
alldone = alldone && ((destlist[i].status == DEST_DONE) ||
(client_error(i)) || (destlist[i].clientcnt != -1));
}
}
if (mux_lock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to lock mutex in transfer_receive_thread");
continue;
}
if (alldone || user_abort) {
file_done_flag = 1;
} else if (got_naks && end_of_pass && (rewind_time.tv_sec == 0)) {
gettimeofday(&rewind_time, NULL);
add_timeval_d(&rewind_time, 3 * l_adv_grtt);
log3(finfo->group_id, finfo->file_id, "Starting rewind timer");
}
l_file_done_flag = file_done_flag;
l_adv_grtt = adv_grtt;
log5(finfo->group_id, finfo->file_id, "adv_grtt=%.3f", l_adv_grtt);
rewind_pending_flag = (rewind_time.tv_sec != 0);
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in transfer_receive_thread");
continue;
}
} while (!l_file_done_flag);
found_error = 0;
if (user_abort || max_time_timeout) {
log0(finfo->group_id, finfo->file_id, "Aboring all clients");
if (user_abort) {
send_abort(finfo, "Server quit, aborting all",
&receive_dest, 0, (keytype != KEY_NONE), 0);
} else if (max_time_timeout) {
send_abort(finfo, "Max file transfer time exceeded",
&receive_dest, 0, (keytype != KEY_NONE), 0);
}
for (i = 0; i < destcount; i++) {
if ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_ACTIVE_NAK)) {
destlist[i].status = DEST_ABORT;
}
}
} else if (!alldone) {
for (i = 0; i < destcount; i++) {
if ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_ACTIVE_NAK)) {
log1(finfo->group_id, finfo->file_id,
"No response from %s", destlist[i].name);
destlist[i].status = DEST_LOST;
if (quit_on_error && !found_error) {
found_error = 1;
log0(finfo->group_id,finfo->file_id, "Aboring all clients");
send_abort(finfo, "A client dropped out, aborting all",
&receive_dest, 0, (keytype != KEY_NONE), 0);
}
}
}
}
free(packet);
free(decrypted);
}
/**
* Performs the Transfer phase for a particular file.
* It sits in a loop to do all reads, and it starts a thread to do all writes
* Returns 1 if at least one client finished, 0 if all are dropped or aborted
*/
int transfer_phase(struct finfo_t *finfo)
{
thread_t tid;
int alldone, i;
double tmp_rtt;
uint32_t nak;
char path[MAXPATHNAME];
struct timeval start_time;
// 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;
}
// TODO: if not a regular file, error any clients that didn't
// respond to the FILEINFO with a COMPLETE
if (finfo->ftype == FTYPE_REG) {
snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP,
finfo->filename);
// Open the file now so we don't start the sending thread if it fails
if ((file = open(path, OPENREAD, 0)) == -1) {
syserror(finfo->group_id, finfo->file_id, "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;
destlist[i].max_nak_exceed = 0;
}
}
}
gettimeofday(&start_time, NULL);
max_time_timeout = 0;
file_done_flag = 0;
rewind_flag = 0;
cc_queue_start = 0;
cc_queue_end = 0;
rate_change = 0;
end_of_pass = 0;
if (cc_type == CC_TFMCC) {
// Initialize rate to 1 packet per GRTT
rate = (int32_t)(((double)datapacketsize / grtt));
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
adv_grtt = (double)datapacketsize / rate;
if (adv_grtt < grtt) {
adv_grtt = grtt;
}
cc_seq = 0;
cc_rate = 0xFFFFFFFF;
slowstart = 1;
// Pick an initial CLR based on who has the highest RTT
for (tmp_rtt = 9999, clr = -1, i = 0; i < destcount; i++) {
if ((destlist[i].rtt < tmp_rtt) && (destlist[i].clientcnt == -1)) {
clr = i;
tmp_rtt = destlist[i].rtt;
}
}
} else {
adv_grtt = grtt;
}
for (nak = 0; nak < finfo->blocks; nak++) {
if (finfo->partial) {
finfo->naklist[nak] = 0;
} else {
finfo->naklist[nak] = 1;
}
}
if (mux_create(mux_main)) {
syserror(finfo->group_id, finfo->file_id, "Failed to create mutex");
if (finfo->ftype == FTYPE_REG) {
close(file);
}
return 1;
}
use_log_mux = 1;
if (start_thread(tid, transfer_send_thread, finfo) != 0) {
syserror(finfo->group_id, finfo->file_id,
"Failed to create sender thread");
if (finfo->ftype == FTYPE_REG) {
close(file);
}
mux_destroy(mux_main);
return 1;
}
transfer_receive_thread(finfo);
if (join_thread(tid) != 0) {
syserror(finfo->group_id,finfo->file_id,"Failed to join sender thread");
} else {
destroy_thread(tid);
}
use_log_mux = 0;
if (finfo->ftype == FTYPE_REG) {
close(file);
}
mux_destroy(mux_main);
print_status(finfo, start_time);
if (user_abort) {
return 0;
}
for (i = 0; i < destcount; i++) {
if (quit_on_error) {
// Check to see that all finished
if ((destlist[i].status != DEST_DONE) &&
(destlist[i].clientcnt == -1)) {
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;
}
}
/**
* Check a completion phase message and pass to appropriate message handler,
* decrypting first if necessary
*/
void handle_completion_phase(unsigned char *packet,
unsigned char *decrypted, int packetlen,
const union sockaddr_u *receiver,
struct finfo_t *finfo)
{
struct uftp_h *header;
unsigned char *message;
int hostidx;
unsigned int decryptlen, meslen;
uint8_t *func;
header = (struct uftp_h *)packet;
hostidx = find_client(header->src_id);
if ((keytype != KEY_NONE) && (header->func == ENCRYPTED)) {
if (hostidx == -1) {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(header->src_id));
send_abort(finfo, "Not in host list", receiver, header->src_id,0,0);
return;
}
if (!validate_and_decrypt(packet, packetlen, &decrypted, &decryptlen,
keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, destlist[hostidx].encinfo->pubkey,
destlist[hostidx].encinfo->pubkeylen)) {
log1(finfo->group_id, finfo->file_id, "Rejecting message from %s: "
"decrypt/validate failed", destlist[hostidx].name);
return;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((keytype != KEY_NONE) && ( (header->func == STATUS) ||
(header->func == COMPLETE) || (header->func == ABORT))) {
log1(finfo->group_id, finfo->file_id,
"Rejecting %s message from %08X: not encrypted",
func_name(header->func), ntohl(header->src_id));
return;
}
func = (uint8_t *)&header->func;
message = packet + sizeof(struct uftp_h);
meslen = packetlen - sizeof(struct uftp_h);
}
if (*func == ABORT) {
handle_abort(message, meslen, hostidx, finfo, header->src_id);
} else if (hostidx == -1) {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(header->src_id));
send_abort(finfo, "Not in host list", receiver, header->src_id, 0, 0);
} else if (*func == COMPLETE) {
handle_complete(message, meslen, finfo, hostidx);
} else {
log1(finfo->group_id, finfo->file_id,
"Received invalid message %s from %s",
func_name(*func), destlist[hostidx].name);
}
return;
}
/**
* Performs the Completion/Confirmaton phase at the end of a group
*/
void completion_phase(struct finfo_t *finfo)
{
unsigned char *packet, *decrypted;
struct timeval timeout, next_send, now, start_time;
union sockaddr_u receiver;
int resend, attempt, last_pass, alldone;
int rcv_status, len, i;
packet = safe_calloc(MAXMTU, 1);
decrypted = safe_calloc(MAXMTU, 1);
// 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;
}
}
log2(finfo->group_id, finfo->file_id, "Finishing group");
gettimeofday(&start_time, NULL);
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, 3 * grtt);
resend = 1;
attempt = 1;
last_pass = 0;
while (attempt <= robust) {
if (user_abort) break;
if (resend) {
if (!send_doneconf(finfo, attempt)) {
continue;
}
if (!send_done(finfo, attempt, 0, grtt)) {
continue;
}
resend = 0;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, next_send) >= 0) {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
} else {
timeout = diff_timeval(next_send, now);
}
if ((rcv_status = read_packet(sock, &receiver, packet, &len,
MAXMTU, &timeout)) == -1) {
continue;
} else if (rcv_status == 0) {
attempt++;
recalculate_grtt(finfo, 1, 0);
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, 3 * grtt);
resend = 1;
if (last_pass) break;
continue;
}
if (!validate_packet(packet, len, finfo)) {
continue;
}
handle_completion_phase(packet, decrypted, len, &receiver, finfo);
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) {
// Change the timeout to 1 * grtt
// to allow for late completions
recalculate_grtt(finfo, 1, 0);
gettimeofday(&next_send, NULL);
add_timeval_d(&next_send, grtt);
if (!last_pass) {
log2(finfo->group_id, finfo->file_id, "Late completions:");
}
last_pass = 1;
send_doneconf(finfo, attempt + 1);
}
}
for (i = 0; i < destcount; i++) {
if (destlist[i].status == DEST_ACTIVE) {
log1(finfo->group_id, finfo->file_id, "Couldn't get COMPLETE "
"for group from %s", destlist[i].name);
destlist[i].status = DEST_LOST;
}
}
send_doneconf(finfo, attempt + 1);
print_status(finfo, start_time);
free(packet);
free(decrypted);
}
uftp-4.1.5/server.h 0000644 0000764 0000764 00000022243 12176066071 013173 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_H
#define _SERVER_H
#include "uftp_common.h"
#include "encryption.h"
#define MAXDEST 100000
#define MAXFILES 10000
#define MAXEXCLUDE 500
#define MAXCC 20
#define CLIENT_RTT_MIN 0.001
/**
* Values for deststate_t.status
*/
enum client_status {
DEST_MUTE = -4, /// Expected client that hasn't registered
DEST_LOST = -3, /// Client that timed out responding to a DONE
DEST_ABORT = -2, /// An ABORT was either sent to or received from
DEST_REGISTERED = -1, /// Registered but haven't received INFO_ACK
DEST_ACTIVE = 0, /// Ready to receive data
DEST_ACTIVE_NAK = 1, /// Ready to receive data, sent back NAKs
DEST_DONE = 2 /// Client finished successfully, sent COMPLETE
};
/**
* Destination state for a particular destination and file
*/
struct deststate_t {
int conf_sent; /// False if REG_CONF or DONE_CONF needs to be sent
struct timeval time; /// Time that this client finished
};
/**
* File info struct
*/
struct finfo_t {
int ftype; /// File type (regular, directory, symlink)
const char *basedir; /// Base pathname of file in filesystem
const char *filename; /// Local name of file (may include path)
const char *linkname; /// For symbolic links, the text of the link
const char *destfname; /// Transmitted name of file (may include path)
f_offset_t size; /// File size in bytes
int32_t tstamp; /// Timestamp of file
uint32_t blocks; /// Total blocks in file
uint16_t sections; /// Total sections in file
uint16_t big_sections; /// Number of larger sized sections
uint32_t secsize_small, secsize_big; /// Size of sections
uint32_t group_id; /// Group ID
uint8_t group_inst; /// Group instance ID (restart number)
uint16_t file_id; /// File ID
char *naklist; /// Aggregate NAK list
int partial; /// True if all clients partially received last run
struct deststate_t *deststate; /// Status array for each client
};
/**
* Encryption info for a particilar destination
* Only allocated when encryption is enabled and only for proxies and clients
* talking directly to the server. Clients communicating through one or
* more proxies maintain encryption state with the proxy, not the server.
*/
struct encinfo_t {
union key_t pubkey; /// The client's RSA or ECDSA public key
int pubkeylen; /// The client's public key length in bytes
union key_t dhkey; /// The client's ECDH public key
uint8_t verifydata[PUBKEY_LEN]; /// The verify data from a CLIENT_KEY
uint16_t verifylen; /// The length of verifydata in bytes
uint8_t rand2[RAND_LEN]; /// Client's random number
uint8_t premaster[MASTER_LEN]; /// Premaster secret sent by client
int premaster_len; /// Length of premaster secret
uint8_t master[MASTER_LEN]; /// Master key for client
uint8_t hmackey[HMAC_LEN]; /// HMAC key for client
uint8_t key[MAXKEY]; /// Symmetric encryption key for client
uint8_t salt[MAXIV]; /// Salt for block cypher IV for client
};
/**
* Destination info
*/
struct destinfo_t {
char name[DESTNAME_LEN]; /// Hostname of client
uint32_t id; /// UID of client (network byte order)
int proxyidx; /// Index of the proxy serving this client
int clientcnt; /// The number of clients a proxy serves
uint32_t *clients; /// Indexes of clients served by this proxy
int64_t freespace; /// Free disk space reported by client
int8_t status; /// Specified by a client_status value
int8_t comp_status; /// Completion status as given by COMPLETE
int8_t registered; /// True if we received a REGISTER
int8_t verified; /// True if we have a verified CLIENT_KEY
int num_copy; /// Number of copied files in sync mode
int num_skip; /// Number of skipped files in sync mode
int num_overwrite; /// Number of overwritten files in sync mode
double total_time; /// Total elasped sending time in sync mode
f_offset_t total_size; /// Total number of bytes sent in sync mode
double rtt; /// RTT to this client
int8_t rtt_measured; /// True if RTT measured this round
int8_t rtt_sent; /// True if RTT sent in a CONG_CTRL
int8_t max_nak_exceed; /// How often client exceeded max naks
int8_t has_fingerprint; /// True if we have client's key fingerprint
uint8_t keyfingerprint[HMAC_LEN]; /// Fingerprint of RSA key
struct encinfo_t *encinfo; /// If encryption enabled, encryption info
};
/**
* Header of server restart file.
* Followed in the file by filecount file names of MAXPATHNAME length,
* followed by an arbitrary number of server_restart_host_t entries.
*/
struct server_restart_t {
uint32_t group_id; /// Group ID of failed transfer
uint8_t group_inst; /// Group instance ID of failed transfer
int filecount; /// Number of files specified on cmdline
};
/**
* Server restart file entry for a particular host.
*/
struct server_restart_host_t {
char name[DESTNAME_LEN]; /// Hostname of client
uint32_t id; /// UID of client (network byte order)
int has_fingerprint; /// True if we have client's key fingerprint
uint8_t keyfingerprint[HMAC_LEN]; /// Fingerprint of RSA key
int is_proxy; /// True if this is a proxy
};
/**
* Congestion control configuration entrie.
*/
struct cc_config_t {
double scaling_factor; /// The given scaling factor
int percentage; /// NAK percentage for the scaling factor
};
/**
* Global command line values and sockets
*/
extern SOCKET sock;
extern union sockaddr_u listen_dest, receive_dest;
extern int max_rate, rate, rcvbuf, packet_wait, txweight, max_nak_pct;
extern int client_auth, quit_on_error, dscp, follow_links, max_nak_cnt;
extern int save_fail, restart_groupid, restart_groupinst;
extern int sync_mode, sync_preview, dest_is_dir, cc_type, user_abort;
extern unsigned int ttl;
extern char port[PORTNAME_LEN], srcport[PORTNAME_LEN];
extern char pub_multi[INET6_ADDRSTRLEN], priv_multi[INET6_ADDRSTRLEN];
extern char keyfile[MAXPATHNAME], cc_config[MAXPATHNAME];
extern char filelist[MAXFILES][MAXPATHNAME], exclude[MAXEXCLUDE][MAXPATHNAME];
extern char basedir[MAXDIR][MAXDIRNAME], destfname[MAXPATHNAME];
extern char statusfilename[MAXPATHNAME];
extern FILE *status_file;
extern struct iflist ifl[MAX_INTERFACES];
extern int keytype, hashtype, sigtype, keyextype, newkeylen, sys_keys;
extern int blocksize, datapacketsize;
extern int ifl_len, destcount, filecount, excludecount, basedircount, cc_count;
extern struct cc_config_t cc_list[MAXCC];
extern struct iflist out_if;
extern struct destinfo_t destlist[MAXDEST];
extern int robust;
extern double grtt, min_grtt, max_grtt;
extern uint16_t send_seq;
extern uint32_t server_id;
/**
* Encryption variables
*/
extern union key_t privkey, dhkey;
extern unsigned char rand1[RAND_LEN], groupmaster[MASTER_LEN];
extern uint8_t groupsalt[MAXIV], groupkey[MAXKEY], grouphmackey[HMAC_LEN];
extern uint64_t ivctr;
extern int ivlen, keylen, hmaclen, privkeylen;
extern uint8_t ecdh_curve, ecdsa_curve;
/**
* Variables shared between the sending and receiving threads
* Defined in server_phase.c
*/
extern mux_t mux_main;
extern int rate_change;
extern uint32_t current_position;
extern uint32_t rewind_to;
extern uint16_t cc_seq;
extern uint32_t cc_rate;
extern double adv_grtt;
extern int slowstart, clr, clr_drop, new_rate;
extern struct timeval last_clr_time;
#endif // _SERVER_H
uftp-4.1.5/client_init.h 0000644 0000764 0000764 00000002707 12250244021 014153 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_INIT_H
#define _CLIENT_INIT_H
void pre_initialize(void);
void initialize(void);
#endif // _CLIENT_INIT_H
uftp-4.1.5/heartbeat_send.c 0000644 0000764 0000764 00000016715 12250244021 014621 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#endif
#include "uftp_common.h"
#include "heartbeat_send.h"
/**
* Process an HB_RESP message
*/
void handle_hb_response(SOCKET s, const union sockaddr_u *src,
const unsigned char *message, unsigned meslen,
union sockaddr_u hb_hosts[], int num_hosts,
union key_t privkey, int keytype, uint32_t uid)
{
const struct hb_resp_h *hbresp;
char addrname[INET6_ADDRSTRLEN];
int hostidx, rval;
hbresp = (const struct hb_resp_h *)message;
if (meslen < (hbresp->hlen * 4U) ||
((hbresp->hlen * 4U) < sizeof(struct hb_resp_h))) {
log2(0, 0, "Rejecting HB_RESP: invalid message size");
return;
}
if ((rval = getnameinfo((const struct sockaddr *)src,
sizeof(union sockaddr_u), addrname, sizeof(addrname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Received HB_RESP from %s", addrname);
if (hbresp->authenticated == HB_AUTH_CHALLENGE) {
log1(0, 0, "Heartbeat authentication required");
for (hostidx = 0; hostidx < num_hosts; hostidx++) {
if (addr_equal(src, &hb_hosts[hostidx])) {
send_auth_hb_request(s, &hb_hosts[hostidx],
ntohl(hbresp->nonce), privkey, keytype, uid);
break;
}
}
} else if (hbresp->authenticated == HB_AUTH_FAILED) {
log1(0, 0, "Heartbeat authentication failed");
} else if (hbresp->authenticated == HB_AUTH_OK) {
log2(0, 0, "Heartbeat authentication successful");
}
}
/**
* Sends an authenticated HB_REQ message to the given host.
*/
void send_auth_hb_request(SOCKET s, union sockaddr_u *hbhost, uint32_t nonce,
union key_t privkey, int keytype, uint32_t uid)
{
unsigned char *packet, *keyblob, *sig;
struct uftp_h *header;
struct hb_req_h *hbreq;
uint32_t n_nonce;
unsigned int meslen, siglen, rval;
uint16_t bloblen;
char addrname[INET6_ADDRSTRLEN], portstr[PORTNAME_LEN];
packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h) +
(PUBKEY_LEN * 2) , 1);
header = (struct uftp_h *)packet;
hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h));
keyblob = (unsigned char *)hbreq + sizeof(struct hb_req_h);
header->version = UFTP_VER_NUM;
header->func = HB_REQ;
header->src_id = uid;
hbreq->func = HB_REQ;
n_nonce = htonl(nonce);
hbreq->nonce = n_nonce;
if (keytype == KEYBLOB_RSA) {
if (!export_RSA_key(privkey.rsa, keyblob, &bloblen)) {
log0(0, 0, "Error exporting public key");
free(packet);
return;
}
sig = keyblob + bloblen;
if (!create_RSA_sig(privkey.rsa, HASH_SHA1, (unsigned char *)&n_nonce,
sizeof(n_nonce), sig, &siglen)) {
log0(0, 0, "Error signing nonce");
free(packet);
return;
}
} else {
if (!export_EC_key(privkey.ec, keyblob, &bloblen)) {
log0(0, 0, "Error exporting public key");
free(packet);
return;
}
sig = keyblob + bloblen;
if (!create_ECDSA_sig(privkey.ec, HASH_SHA1, (unsigned char *)&n_nonce,
sizeof(n_nonce), sig, &siglen)) {
log0(0, 0, "Error signing nonce");
free(packet);
return;
}
}
hbreq->bloblen = htons(bloblen);
hbreq->siglen = htons(siglen);
hbreq->hlen = (sizeof(struct hb_req_h) + bloblen + siglen) / 4;
meslen = sizeof(struct uftp_h) + (hbreq->hlen * 4);
if (nb_sendto(s, packet, meslen, 0, (struct sockaddr *)hbhost,
family_len(*hbhost)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending HB_REQ");
} else {
if ((rval = getnameinfo((struct sockaddr *)hbhost,
sizeof(union sockaddr_u), addrname, sizeof(addrname), portstr,
sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Sent authenticated HB_REQ to %s:%s", addrname, portstr);
}
free(packet);
}
/**
* Sends an HB_REQ message to each host listed in the hb_host list
*/
void send_hb_request(SOCKET s, union sockaddr_u hb_hosts[], int num_hosts,
struct timeval *next_hb_time, int hb_interval,
uint32_t uid)
{
unsigned char *packet;
struct uftp_h *header;
struct hb_req_h *hbreq;
char addrname[INET6_ADDRSTRLEN], portstr[PORTNAME_LEN];
int meslen, rval, i;
packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h), 1);
header = (struct uftp_h *)packet;
hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h));
header->version = UFTP_VER_NUM;
header->func = HB_REQ;
header->src_id = uid;
hbreq->func = HB_REQ;
hbreq->hlen = sizeof(struct hb_req_h) / 4;
for (i = 0; i < num_hosts; i++) {
hbreq->nonce = 0;
hbreq->bloblen = 0;
hbreq->siglen = 0;
meslen = sizeof(struct uftp_h) + (hbreq->hlen * 4);
if (nb_sendto(s, packet, meslen, 0, (struct sockaddr *)&hb_hosts[i],
family_len(hb_hosts[i])) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending HB_REQ");
} else {
if ((rval = getnameinfo((struct sockaddr *)&hb_hosts[i],
sizeof(union sockaddr_u), addrname, sizeof(addrname),
portstr, sizeof(portstr),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Sent HB_REQ to %s:%s", addrname, portstr);
}
}
free(packet);
gettimeofday(next_hb_time, NULL);
next_hb_time->tv_sec += hb_interval;
}
uftp-4.1.5/uftp.h 0000644 0000764 0000764 00000040457 12304524207 012643 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _UFTP_H
#define _UFTP_H
#ifdef WINDOWS
// same as passing /D _CRT_SECURE_NO_WARNINGS to cl
#pragma warning(disable: 4996)
#include
#include
typedef unsigned __int8 uint8_t;
typedef __int8 int8_t;
typedef unsigned __int16 uint16_t;
typedef __int16 int16_t;
typedef unsigned __int32 uint32_t;
typedef __int32 int32_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
#define open(name, ...) _open(name, __VA_ARGS__)
#define read(fd, buf, count) _read(fd, buf, count)
#define close(fd) _close(fd)
#define write(fd, buf, count) _write(fd, buf, count)
#define dup2(fd1, fd2) _dup2(fd1, fd2)
#define unlink(file) _unlink(file)
#define rmdir(dir) _rmdir(dir)
#define getpid() _getpid()
#define mkdir(dir, mode) _mkdir(dir)
#define usleep(t) Sleep((t)/1000)
#define sleep(t) Sleep((t)*1000)
#define strdup(p) _strdup(p)
#define utime(f, t) _utime(f, t)
#define isfullpath(str) (((str[1] == ':') && (str[2] == '\\')) || \
((str[0] == '\\') && (str[1] == '\\')))
#define PATH_SEP '\\'
typedef int64_t f_offset_t;
typedef struct _stat32i64 stat_struct;
typedef struct _utimbuf utim_buf;
#define stat_func(name, buf) _stat32i64(name, buf)
#define lstat_func(name, buf) _stat32i64(name, buf)
#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
#define lseek_func(fd, offset, whence) _lseeki64(fd, offset, whence)
#define snprintf(buf, cnt, ...) _snprintf(buf, cnt, __VA_ARGS__)
typedef int socklen_t;
#define OPENREAD (O_RDONLY | O_BINARY)
#define OPENWRITE (O_WRONLY | O_BINARY)
typedef HANDLE mux_t;
#define mux_create(mux) (((mux=CreateMutex(NULL,FALSE,NULL))!=NULL)?0:-1)
#define mux_destroy(mux) ((CloseHandle(mux)!=0)?0:-1)
#define mux_lock(mux) ((WaitForSingleObject(mux,INFINITE)!=WAIT_FAILED)?0:-1)
#define mux_unlock(mux) ((ReleaseMutex(mux)!=0)?0:-1)
typedef unsigned long thread_t;
#define start_thread(id,func,arg) (((id=_beginthread(func,0,arg))!=-1)?0:-1)
#define end_thread() _endthread()
#define thread_id() GetCurrentThreadId()
#define join_thread(id) WaitForSingleObject((HANDLE)(id), INFINITE)
#define destroy_thread(id) CloseHandle((HANDLE)(id))
#define THREAD_FUNC void
#define THREAD_RETURN return
#else // if WINDOWS
#include
#include
#define closesocket(s) close(s)
#ifdef VMS
pid_t GENERIC_SETSID(void);
#define setsid GENERIC_SETSID
#define fork vfork
typedef unsigned int socklen_t;
#define isfullpath(str) (1)
#define PATH_SEP ':'
#define open(name, flag, mode) open(name, flag, mode, "ctx=stm")
#else
#define isfullpath(str) (str[0] == '/')
#define PATH_SEP '/'
#endif
typedef int64_t f_offset_t;
typedef struct stat stat_struct;
typedef struct utimbuf utim_buf;
#define stat_func(name, buf) stat(name, buf)
#define lstat_func(name, buf) lstat(name, buf)
#define lseek_func(fd, offset, whence) lseek(fd, offset, whence)
typedef int SOCKET;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#ifndef INADDR_NONE
#define INADDR_NONE -1
#endif
#define OPENREAD (O_RDONLY)
#define OPENWRITE (O_WRONLY)
typedef pthread_mutex_t mux_t;
#define mux_create(mux) pthread_mutex_init(&(mux),NULL)
#define mux_destroy(mux) pthread_mutex_destroy(&(mux))
#define mux_lock(mux) pthread_mutex_lock(&(mux))
#define mux_unlock(mux) pthread_mutex_unlock(&(mux))
typedef pthread_t thread_t;
#define start_thread(id,func,arg) pthread_create(&(id),NULL,func,arg)
#define end_thread() pthread_exit(NULL)
#define thread_id() pthread_self()
#define join_thread(id) pthread_join(id, NULL)
#define destroy_thread(id)
#define THREAD_FUNC void *
#define THREAD_RETURN return NULL
#endif // if WINDOWS
#define VERSIONSTR "UFTP version 4.1.5 Copyright (C) 2001-2014 Dennis A. Bush"
#define UFTP_VER_NUM 0x40
#define ANNOUNCE 1
#define REGISTER 2
#define CLIENT_KEY 3
#define REG_CONF 4
#define KEYINFO 5
#define KEYINFO_ACK 6
#define FILEINFO 7
#define FILEINFO_ACK 8
#define FILESEG 9
#define DONE 10
#define STATUS 11
#define COMPLETE 12
#define DONE_CONF 13
#define HB_REQ 14
#define HB_RESP 15
#define KEY_REQ 16
#define PROXY_KEY 17
#define ENCRYPTED 18
#define ABORT 19
#define CONG_CTRL 20
#define CC_ACK 21
#define FTYPE_REG 0
#define FTYPE_DIR 1
#define FTYPE_LINK 2
#define FTYPE_DELETE 3
#define FTYPE_FREESPACE 4
#define KEY_NONE 0
#define KEY_DES 1
#define KEY_DES_EDE3 2
#define KEY_AES128_CBC 3
#define KEY_AES256_CBC 4
#define KEY_AES128_GCM 5
#define KEY_AES256_GCM 6
#define KEY_AES128_CCM 7
#define KEY_AES256_CCM 8
#define HASH_NONE 0
#define HASH_MD5 1
#define HASH_SHA1 2
#define HASH_SHA256 3
#define HASH_SHA384 4
#define HASH_SHA512 5
#define SIG_NONE 0
#define SIG_HMAC 1
#define SIG_KEYEX 2
#define SIG_AUTHENC 3
#define KEYEX_NONE 0
#define KEYEX_RSA 1
#define KEYEX_ECDH_RSA 2
#define KEYEX_ECDH_ECDSA 3
#define KEYBLOB_RSA 1
#define KEYBLOB_EC 2
#define CURVE_sect163k1 1
#define CURVE_sect163r1 2
#define CURVE_sect163r2 3
#define CURVE_sect193r1 4
#define CURVE_sect193r2 5
#define CURVE_sect233k1 6
#define CURVE_sect233r1 7
#define CURVE_sect239k1 8
#define CURVE_sect283k1 9
#define CURVE_sect283r1 10
#define CURVE_sect409k1 11
#define CURVE_sect409r1 12
#define CURVE_sect571k1 13
#define CURVE_sect571r1 14
#define CURVE_secp160k1 15
#define CURVE_secp160r1 16
#define CURVE_secp160r2 17
#define CURVE_secp192k1 18
#define CURVE_secp192r1 19
#define CURVE_secp224k1 20
#define CURVE_secp224r1 21
#define CURVE_secp256k1 22
#define CURVE_secp256r1 23
#define CURVE_secp384r1 24
#define CURVE_secp521r1 25
#define CURVE_prime192v1 CURVE_secp192r1
#define CURVE_prime256v1 CURVE_secp256r1
#define CC_NONE 0
#define CC_UFTP3 1
#define CC_TFMCC 2
#define CC_PGMCC 3
#define EXT_ENC_INFO 1
#define EXT_TFMCC_DATA_INFO 2
#define EXT_TFMCC_ACK_INFO 3
#define EXT_PGMCC_DATA_INFO 4
#define EXT_PGMCC_NAK_INFO 5
#define EXT_PGMCC_ACK_INFO 6
#define EXT_FREESPACE_INFO 7
#define FLAG_SYNC_MODE 0x01
#define FLAG_SYNC_PREVIEW 0x02
#define FLAG_IPV6 0x04
#define FLAG_CLIENT_AUTH 0x01
#define FLAG_PARTIAL 0x01
#define FLAG_CURRENT_FILE 0x01
#define FLAG_CC_CLR 0x01
#define FLAG_CC_RTT 0x02
#define FLAG_CC_START 0x04
#define FLAG_CC_LEAVE 0x08
#define COMP_STAT_NORMAL 0
#define COMP_STAT_SKIPPED 1
#define COMP_STAT_OVERWRITE 2
#define COMP_STAT_REJECTED 3
#define HB_AUTH_FAILED 0
#define HB_AUTH_OK 1
#define HB_AUTH_CHALLENGE 2
#define MAXFILENAME 100
#define MAXDIRNAME 200
#define MAXPATHNAME 300
#define MAXBACKUPPATHNAME 600
#define MAXPROXYDEST 1000
#define MAXDIR 10
#define MAXSECTION 65536
#define DESTNAME_LEN 80
#define IFNAME_LEN 25
#define PORTNAME_LEN 20
#define MAX_INTERFACES 100
#define MAXMTU 9000
#define PUBKEY_LEN 264 // big enough for a keyblob with RSA-2048
#define RAND_LEN 32 // RFC 5246
#define HMAC_LEN 64 // big enough for SHA-512
#define VERIFY_LEN 12 // RFC 5246
#define MASTER_LEN 48 // RFC 5246
#define MAXIV 16 // big enough for AES256
#define MAXKEY 32 // big enough for AES256
#define KEYBLSIZE 16 // Maximum symetric key blocksize
#define DEF_RSA_LEN 512 // Default length of generated RSA keys
#define DEF_CURVE CURVE_prime256v1 // Default EC curve
#define RSA_EXP 65537 // Public key exponent of generated RSA keys
#define SALT_LEN 4 // Length of salt for IV
#define GCM_IV_LEN 12 // Length of IV for ciphers in GCM mode
#define CCM_IV_LEN 12 // Length of IV for ciphers in CCM mode
#define GCM_TAG_LEN 16 // Length of tag for ciphers in GCM mode
#define CCM_TAG_LEN 16 // Length of tag for ciphers in CCM mode
struct uftp_h {
uint8_t version;
uint8_t func;
uint16_t seq;
uint32_t src_id; // ID of sender
uint32_t group_id;
uint8_t group_inst; // Group restart number
uint8_t grtt; // RFC 5401, Unused in upstream messages
uint8_t gsize; // Unused in upstream messages
uint8_t reserved;
}; // sizeof = 16
struct encrypted_h {
uint32_t iv_ctr_hi;
uint32_t iv_ctr_lo;
uint16_t sig_len;
uint16_t payload_len;
}; // sizeof = 12 + sig_len + payload_len
struct announce_h {
uint8_t func; // always ANNOUNCE
uint8_t hlen;
uint8_t flags;
uint8_t robust;
uint8_t cc_type;
uint8_t reserved;
uint16_t blocksize;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
//uint32_t publicmcast; // for IPv4
//uint8_t publicmcast[16]; // for IPv6
//uint32_t privatemcast; // for IPv4
//uint8_t privatemcast[16]; // for IPv6
}; // sizeof = 16 + 2*iplen + (encrypted?sizeof(enc_info_he):0) + {uint32_t[]}
struct enc_info_he {
uint8_t exttype; // always EXT_ENC_INFO
uint8_t extlen;
uint8_t flags;
uint8_t keyextype_sigtype; // & 0xF0 = keyextype, & 0x0F = sigtype
uint8_t keytype;
uint8_t hashtype;
uint16_t keylen;
uint16_t dhlen;
uint16_t siglen;
uint8_t rand1[RAND_LEN];
//uint8_t keyblob[];
//uint8_t dhkey[];
//uint8_t sig[];
}; //sizeof = 44 + keylen + dhlen + siglen
struct rsa_blob_t {
uint8_t blobtype; // always KEYBLOB_RSA
uint8_t reserved;
uint16_t modlen;
uint32_t exponent;
//uint8_t modulus[];
}; //sizeof = 8 + modlen
struct ec_blob_t {
uint8_t blobtype; // always KEYBLOB_EC
uint8_t curve;
uint16_t keylen;
//uint8_t key[];
}; //sizeof = 4 + keylen
struct client_key_h {
uint8_t func; // always CLIENT_KEY
uint8_t hlen;
uint16_t reserved;
uint16_t bloblen;
uint16_t siglen;
//uint8_t keyblob[];
//uint8_t verify[];
}; // sizeof = 8 + bloblen + siglen
struct register_h {
uint8_t func; // always REGISTER
uint8_t hlen;
uint16_t keyinfo_len;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
uint8_t rand2[RAND_LEN];
//uint8_t keyinfo[]; // Either an EC key or an RSA encrypted premaster
}; // sizeof = 44 + (server_key_len or ec_len) + {uint32_t[]}
struct regconf_h {
uint8_t func; // always REG_CONF
uint8_t hlen;
uint16_t reserved;
}; // sizeof = 4 + {uint32_t[]}
struct keyinfo_h {
uint8_t func; // always KEYINFO
uint8_t hlen;
uint16_t reserved;
uint32_t iv_ctr_hi;
uint32_t iv_ctr_lo;
}; // sizeof = 12 + {destkey[]}
struct destkey {
uint32_t dest_id;
uint8_t groupmaster[MASTER_LEN]; // based on 16 byte blocksize
}; // sizeof = 52 */
struct keyinfoack_h {
uint8_t func; // always KEYINFO_ACK
uint8_t hlen;
uint16_t reserved;
uint8_t verify_data[VERIFY_LEN];
}; // sizeof = 16
struct fileinfo_h {
uint8_t func; // always FILEINFO
uint8_t hlen;
uint16_t file_id;
uint8_t ftype;
uint8_t reserved1;
uint16_t reserved2;
uint8_t namelen;
uint8_t linklen;
uint16_t hifsize;
uint32_t lofsize;
uint32_t ftstamp;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
//char name[MAXPATHNAME];
}; // sizeof = 28 + namelen + {uint32_t[]}
struct fileinfoack_h {
uint8_t func; // always FILEINFO_ACK
uint8_t hlen;
uint16_t file_id;
uint8_t flags;
uint8_t reserved1;
uint16_t reserved2;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
}; // sizeof = 16 + {uint32_t[]}
struct fileseg_h {
uint8_t func; // always FILESEG
uint8_t hlen;
uint16_t file_id;
uint16_t section;
uint16_t sec_block;
}; // sizeof = 8
// Append to FILESEG
struct tfmcc_data_info_he {
uint8_t exttype; // always EXT_TFMCC_DATA_INFO
uint8_t extlen;
uint16_t send_rate;
uint16_t cc_seq;
uint16_t cc_rate;
}; // sizeof = 8
// Append to FILESEG
struct pgmcc_data_info_he {
uint8_t exttype; // always EXT_PGMCC_DATA_INFO
uint8_t extlen;
uint16_t reserved;
uint32_t acker;
}; // sizeof = 8
struct done_h {
uint8_t func; // always DONE
uint8_t hlen;
uint16_t file_id;
uint16_t section;
uint16_t reserved;
}; // sizeof = 8 + {uint32_t[]}
struct status_h {
uint8_t func; // always STATUS
uint8_t hlen;
uint16_t file_id;
uint16_t section;
uint16_t reserved;
}; // sizeof = 8
// Append to STATUS or CC_ACK
struct tfmcc_ack_info_he {
uint8_t exttype; // always EXT_TFMCC_ACK_INFO
uint8_t extlen;
uint8_t flags; // RTT, SS, LEAVE
uint8_t reserved;
uint16_t cc_seq;
uint16_t cc_rate;
uint32_t client_id;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
}; // sizeof = 20
// Append to STATUS
struct pgmcc_nak_info_he {
uint8_t exttype; // always EXT_PGMCC_NAK_INFO
uint8_t extlen;
uint16_t loss;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
}; // sizeof = 12
// Append to CC_ACK
struct pgmcc_ack_info_he {
uint8_t exttype; // always EXT_PGMCC_ACK_INFO
uint8_t extlen;
uint16_t reserved;
uint16_t loss;
uint16_t max_seq;
uint32_t ack_bitmap;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
}; // sizeof = 20
struct complete_h {
uint8_t func; // always COMPLETE
uint8_t hlen;
uint16_t file_id;
uint8_t status;
uint8_t reserved1;
uint16_t reserved2;
}; // sizeof = 8 + {uint32_t[]}
struct freespace_info_he {
uint8_t exttype; // always EXT_FREESPACE_INFO
uint8_t extlen;
uint16_t reserved;
uint32_t freespace_hi;
uint32_t freespace_lo;
}; // sizeof = 12
struct doneconf_h {
uint8_t func; // always DONE_CONF
uint8_t hlen;
uint16_t reserved;
}; // sizeof = 4 + {uint32_t[]}
struct abort_h {
uint8_t func; // always ABORT
uint8_t hlen;
uint8_t flags;
uint8_t reserved;
uint32_t host;
char message[300]; // TODO: define error codes
}; // sizeof = 308
struct hb_req_h {
uint8_t func; // always HB_REQ
uint8_t hlen;
uint16_t reserved;
uint16_t bloblen;
uint16_t siglen;
uint32_t nonce;
//uint8_t keyblob[];
//uint8_t verify[];
}; // sizeof = 12 + bloblen + siglen
struct hb_resp_h {
uint8_t func; // always HB_RESP
uint8_t hlen;
uint8_t authenticated;
uint8_t reserved;
uint32_t nonce;
}; // sizeof = 8
struct key_req_h {
uint8_t func; // always KEY_REQ
uint8_t hlen;
uint16_t reserved;
}; // sizeof = 4
struct proxy_key_h {
uint8_t func; // always PROXY_KEY
uint8_t hlen;
uint16_t bloblen;
uint16_t dhlen;
uint16_t siglen;
uint32_t nonce;
//uint8_t keyblob[];
//uint8_t dhkey[];
//uint8_t verify[];
}; // sizeof = 12 + bloblen + dhlen + siglen
struct cong_ctrl_h {
uint8_t func; // always CONG_CTRL
uint8_t hlen;
uint16_t reserved;
uint16_t cc_seq;
uint16_t cc_rate;
uint32_t tstamp_sec;
uint32_t tstamp_usec;
}; // sizeof = 16 + {cc_item[]}
struct cc_item {
uint32_t dest_id;
uint8_t flags; // CLR, RTT, START, LEAVE
uint8_t rtt; // RFC 5401
uint16_t reserved;
}; // sizeof = 8
struct cc_ack_h {
uint8_t func; // always CC_ACK
uint8_t hlen;
uint16_t reserved;
}; // sizeof = 4 + tfmcc_info_he or pgmcc_info_ack_he
#endif // _UFTP_H
uftp-4.1.5/proxy_upstream.h 0000644 0000764 0000764 00000004251 12250244021 014747 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _PROXY_UPSTREAM_H
#define _PROXY_UPSTREAM_H
void handle_announce(struct pr_group_list_t *group,
const union sockaddr_u *src, unsigned char *packet,
unsigned packetlen);
void handle_regconf(struct pr_group_list_t *group, const unsigned char *message,
unsigned meslen);
void handle_keyinfo(struct pr_group_list_t *group, unsigned char *message,
unsigned meslen, uint32_t src_id);
void send_register(struct pr_group_list_t *group, int pendidx);
void send_clientkey(struct pr_group_list_t *group);
void send_keyinfo_ack(struct pr_group_list_t *group);
void send_fileinfo_ack(struct pr_group_list_t *group, int pendidx);
void send_status(struct pr_group_list_t *group, int pendidx);
void send_complete(struct pr_group_list_t *group, int pendidx);
#endif // _PROXY_UPSTREAM_H
uftp-4.1.5/server_transfer.c 0000644 0000764 0000764 00000074151 12304524207 015070 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2014 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 *idlist;
int rval;
if (finfo->file_id != 0) {
return 1;
}
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
doneconf = (struct doneconf_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, DONE_CONF, finfo->group_id, finfo->group_inst,
grtt, destcount);
doneconf->func = DONE_CONF;
doneconf->hlen = sizeof(struct doneconf_h) / 4;
idlist = (uint32_t *)((uint8_t *)doneconf + (doneconf->hlen * 4));
rval = send_multiple(finfo, buf, DONE_CONF, attempt, idlist, DEST_DONE,
(keytype != KEY_NONE), &receive_dest, 0);
free(buf);
return rval;
}
/**
* Send out DONE messages specifiying active clients that haven't yet responded.
* The grtt is being passed in because multiple threads could be touching it.
* Returns 1 on success, 0 on fail
*/
int send_done(const struct finfo_t *finfo, int attempt, int section,
double l_grtt)
{
unsigned char *buf;
struct uftp_h *header;
struct done_h *done;
uint32_t *idlist;
int rval;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
done = (struct done_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, DONE, finfo->group_id, finfo->group_inst,
l_grtt, destcount);
done->func = DONE;
done->hlen = sizeof(struct done_h) / 4;
done->file_id = htons(finfo->file_id);
done->section = htons(section);
idlist = (uint32_t *)((uint8_t *)done + (done->hlen * 4));
rval = send_multiple(finfo, buf, DONE, attempt, idlist, DEST_ACTIVE,
(keytype != KEY_NONE), &receive_dest, 0);
free(buf);
return rval;
}
/**
* Creates the body of a CONG_CTRL message
* This is done separate from sending the message so that the receiving thread
* can perform this part. We do this because the receiving thread checks
* timeouts, and because the referenced data structures are now only
* read/written in one thread, so we don't have to lock when we do this part.
*/
void create_cc_list(unsigned char **body, int *len)
{
struct cc_item *list;
int *has_rtt, *no_rtt, has_rtt_len, no_rtt_len, maxlist, count, i;
*body = safe_calloc(blocksize, 1);
has_rtt = safe_calloc(MAXDEST, sizeof(int));
no_rtt = safe_calloc(MAXDEST, sizeof(int));
for (has_rtt_len = 0, no_rtt_len = 0, i = 0; i < destcount; i++) {
if (i == clr) continue;
if (destlist[i].rtt_sent) {
has_rtt[has_rtt_len++] = i;
} else {
no_rtt[no_rtt_len++] = i;
}
}
maxlist = blocksize / sizeof(struct cc_item);
list = (struct cc_item *)*body;
count = 0;
if (clr != -1) {
list[count].dest_id = destlist[clr].id;
list[count].flags =
FLAG_CC_CLR | FLAG_CC_RTT | (slowstart ? FLAG_CC_START : 0);
list[count].rtt = quantize_grtt(destlist[clr].rtt);
destlist[clr].rtt_sent = 1;
count++;
}
for (i = 0; (i < no_rtt_len) && (count < maxlist); i++) {
list[count].dest_id = destlist[no_rtt[i]].id;
list[count].flags = FLAG_CC_RTT | (slowstart ? FLAG_CC_START : 0);
list[count].rtt = quantize_grtt(destlist[no_rtt[i]].rtt);
count++;
destlist[i].rtt_sent = 1;
}
for (i = 0; (i < has_rtt_len) && (count < maxlist); i++) {
list[count].dest_id = destlist[has_rtt[i]].id;
list[count].flags = FLAG_CC_RTT | (slowstart ? FLAG_CC_START : 0);
list[count].rtt = quantize_grtt(destlist[has_rtt[i]].rtt);
count++;
destlist[i].rtt_sent = 1;
}
*len = count * sizeof(struct cc_item);
free(has_rtt);
free(no_rtt);
}
/**
* Send out a CONG_CTRL message
*/
void send_cong_ctrl(const struct finfo_t *finfo, double l_grtt,
uint16_t l_cc_seq, uint32_t l_cc_rate,
unsigned char *body, int len)
{
unsigned char *buf, *bodyptr, *encrypted, *outpacket;
struct uftp_h *header;
struct cong_ctrl_h *cong_ctrl;
struct timeval now;
int payloadlen, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
cong_ctrl = (struct cong_ctrl_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, CONG_CTRL, finfo->group_id, finfo->group_inst,
l_grtt, destcount);
header->seq = htons(send_seq++);
cong_ctrl->func = CONG_CTRL;
cong_ctrl->hlen = sizeof(struct cong_ctrl_h) / 4;
cong_ctrl->cc_seq = htons(l_cc_seq);
cong_ctrl->cc_rate = htons(quantize_rate(l_cc_rate));
gettimeofday(&now, NULL);
cong_ctrl->tstamp_sec = htonl(now.tv_sec);
cong_ctrl->tstamp_usec = htonl(now.tv_usec);
bodyptr = (unsigned char *)cong_ctrl + (cong_ctrl->hlen * 4);
memcpy(bodyptr, body, len);
payloadlen = (cong_ctrl->hlen * 4) + len;
if (keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, keytype,
groupkey, groupsalt, &ivctr, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, privkey, privkeylen)) {
log0(finfo->group_id, finfo->file_id, "Error encrypting CONG_CTRL");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&receive_dest,
family_len(receive_dest)) == SOCKET_ERROR) {
sockerror(finfo->group_id, finfo->file_id, "Error sending CONG_CTRL");
}
log4(finfo->group_id, finfo->file_id, "Sent CONG_CTRL, seq %d", l_cc_seq);
free(buf);
free(encrypted);
}
/**
* Handle a EXT_FREESPACE_INFO extension in a COMPLETE
*/
void handle_freespace_info(const struct freespace_info_he *freespace,
int hostidx)
{
destlist[hostidx].freespace = ntohl(freespace->freespace_lo);
destlist[hostidx].freespace |= (int64_t)ntohl(freespace->freespace_hi)<<32;
}
/**
* Process an expected COMPLETE message
*/
void handle_complete(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx)
{
const struct complete_h *complete;
const struct freespace_info_he *freespace;
const uint8_t *he;
const uint32_t *idlist;
int clientcnt, clientidx, dupmsg, isproxy, i;
unsigned extlen;
char status[20];
complete = (const struct complete_h *)message;
idlist = (const uint32_t *)(message + (complete->hlen * 4));
clientcnt = (meslen - (complete->hlen * 4)) / 4;
if ((meslen < (complete->hlen * 4U)) ||
((complete->hlen * 4U) < sizeof(struct complete_h))) {
log1(finfo->group_id, finfo->file_id, "Rejecting COMPLETE from %s: "
"invalid message size", destlist[hostidx].name);
return;
}
if (ntohs(complete->file_id) != finfo->file_id) {
if (finfo->file_id == 0) {
return; // Reject silently
}
log1(finfo->group_id, finfo->file_id, "Rejecting COMPLETE from %s: "
"invalid file ID %04X, expected %04X ", destlist[hostidx].name,
ntohs(complete->file_id), finfo->file_id);
if (clientcnt > 0) {
for (i = 0; i < clientcnt; i++) {
clientidx = find_client(idlist[i]);
if (clientidx == -1) {
log2(finfo->group_id, finfo->file_id,
" For client %08X", ntohl(idlist[i]));
} else {
log2(finfo->group_id, finfo->file_id,
" For client %s", destlist[clientidx].name);
}
}
}
return;
}
freespace = NULL;
if (complete->hlen * 4U > sizeof(struct complete_h)) {
he = (const uint8_t *)complete + sizeof(struct complete_h);
if (*he == EXT_FREESPACE_INFO) {
freespace = (const struct freespace_info_he *)he;
extlen = freespace->extlen * 4U;
if ((extlen > (complete->hlen * 4U) - sizeof(struct complete_h)) ||
extlen < sizeof(struct freespace_info_he)) {
log1(finfo->group_id, finfo->file_id,
"Rejecting COMPLETE from %s: invalid extension size",
destlist[hostidx].name);
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:
strncpy(status, "", sizeof(status));
break;
case COMP_STAT_SKIPPED:
strncpy(status, "(skipped)", sizeof(status));
break;
case COMP_STAT_OVERWRITE:
strncpy(status, "(overwritten)", sizeof(status));
break;
case COMP_STAT_REJECTED:
strncpy(status, "(rejected)", sizeof(status));
break;
}
log2(finfo->group_id, finfo->file_id, "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(idlist[i]);
if (clientidx == -1) {
log1(finfo->group_id, finfo->file_id,
"Client %08X via proxy %s not found",
ntohl(idlist[i]),
destlist[hostidx].name);
} else if (destlist[clientidx].proxyidx != hostidx) {
log1(finfo->group_id, finfo->file_id,
"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);
log2(finfo->group_id, finfo->file_id, " For client%s %s",
dupmsg ? "+" : "", destlist[clientidx].name);
finfo->deststate[clientidx].conf_sent = 0;
destlist[clientidx].status = DEST_DONE;
destlist[clientidx].comp_status = complete->status;
gettimeofday(&finfo->deststate[clientidx].time, NULL);
}
}
} else {
finfo->deststate[hostidx].conf_sent = 0;
destlist[hostidx].status = DEST_DONE;
gettimeofday(&finfo->deststate[hostidx].time, NULL);
}
if (freespace) {
handle_freespace_info(freespace, hostidx);
}
}
/**
* Handle a EXT_TFMCC_ACK_INFO extension in a STATUS or CC_ACK
* The mux_main mutex should already be locked
*/
void handle_tfmcc_ack_info(const struct finfo_t *finfo,
const struct tfmcc_ack_info_he *tfmcc, int hostidx)
{
struct timeval now, msgtime;
int flag_ss, flag_rtt, rate_1grtt;
unsigned client_rate;
double l_adv_grtt;
gettimeofday(&now, NULL);
msgtime.tv_sec = ntohl(tfmcc->tstamp_sec);
msgtime.tv_usec = ntohl(tfmcc->tstamp_usec);
destlist[hostidx].rtt = diff_usec(now, msgtime) / 1000000.0;
if (destlist[hostidx].rtt < CLIENT_RTT_MIN) {
destlist[hostidx].rtt = CLIENT_RTT_MIN;
}
destlist[hostidx].rtt_measured = 1;
destlist[hostidx].rtt_sent = 0;
log4(finfo->group_id,finfo->file_id, " rtt = %.6f", destlist[hostidx].rtt);
client_rate = unquantize_rate(ntohs(tfmcc->cc_rate));
flag_ss = ((tfmcc->flags & FLAG_CC_START) != 0);
flag_rtt = ((tfmcc->flags & FLAG_CC_RTT) != 0);
// TODO: should we be checking the advertised GRTT instead of the real GRTT?
if (destlist[hostidx].rtt > grtt) {
grtt = destlist[hostidx].rtt;
}
if (!flag_ss) {
slowstart = 0;
}
if (hostidx == clr) {
log3(finfo->group_id, finfo->file_id,
"Got clr CC response for round %d", ntohs(tfmcc->cc_seq));
rate_1grtt = (int)(datapacketsize / grtt);
if (!flag_ss && !flag_rtt) {
client_rate = (unsigned)((double)client_rate *
(adv_grtt / destlist[hostidx].rtt));
}
if ((client_rate > (unsigned)rate + rate_1grtt) && !slowstart) {
rate += rate_1grtt;
} else {
rate = client_rate;
}
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
last_clr_time = now;
} else {
log3(finfo->group_id, finfo->file_id,
"Got CC response for round %d", ntohs(tfmcc->cc_seq));
if (client_rate < cc_rate) {
cc_rate = (int)(client_rate * 0.9);
}
if (!flag_ss && !flag_rtt) {
client_rate = (unsigned)((double)client_rate *
(adv_grtt / destlist[hostidx].rtt));
}
if ((client_rate < (unsigned)rate) || (clr == -1)) {
log3(finfo->group_id, finfo->file_id,
"Selected new clr %s", destlist[hostidx].name);
if (!clr_drop) {
rate = client_rate;
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
rate_change = 1;
} else if (client_rate < (unsigned)rate) {
new_rate = client_rate;
} else {
new_rate = rate;
}
clr = hostidx;
last_clr_time = now;
}
}
l_adv_grtt = (double)datapacketsize / rate;
if (l_adv_grtt < grtt) {
l_adv_grtt = grtt;
}
if (l_adv_grtt > adv_grtt) {
adv_grtt = l_adv_grtt;
}
log4(finfo->group_id, finfo->file_id, "rate = %d", rate);
}
/**
* Process an expected STATUS message
* Sets *status_postion to the lowest numbered packet NAKed in this message
*/
void handle_status(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx, int *got_naks)
{
const struct status_h *status;
const struct tfmcc_ack_info_he *tfmcc;
const uint8_t *naklist, *he;
unsigned section, current_section, naks, section_offset, blocks_this_sec;
unsigned nakidx, listidx, nak_bytes, i, j;
unsigned extlen;
status = (const struct status_h *)message;
naklist = ((const uint8_t *)status) + (status->hlen * 4);
section = ntohs(status->section);
if ((meslen < (status->hlen * 4U)) ||
((status->hlen * 4U) < sizeof(struct status_h))) {
log1(finfo->group_id, finfo->file_id, "Rejecting STATUS from %s: "
"invalid message size", destlist[hostidx].name);
return;
}
if (ntohs(status->file_id) != finfo->file_id) {
log1(finfo->group_id, finfo->file_id, "Rejecting STATUS from %s: "
"invalid file ID %04X, expected %04X ", destlist[hostidx].name,
ntohs(status->file_id), finfo->file_id );
return;
}
tfmcc = NULL;
if (status->hlen * 4U > sizeof(struct status_h)) {
he = (const uint8_t *)status + sizeof(struct status_h);
if (*he == EXT_TFMCC_ACK_INFO) {
tfmcc = (const struct tfmcc_ack_info_he *)he;
extlen = tfmcc->extlen * 4U;
if ((extlen > (status->hlen * 4U) - sizeof(struct status_h)) ||
extlen < sizeof(struct tfmcc_ack_info_he)) {
log1(finfo->group_id, finfo->file_id,
"Rejecting STATUS from %s: invalid extension size",
destlist[hostidx].name);
return;
}
}
}
if (section >= finfo->big_sections) {
section_offset = (finfo->big_sections * finfo->secsize_big) +
((section - finfo->big_sections) * finfo->secsize_small);
blocks_this_sec = finfo->secsize_small;
} else {
section_offset = section * finfo->secsize_big;
blocks_this_sec = finfo->secsize_big;
}
if (meslen < (status->hlen * 4U) + (blocks_this_sec / 8) + 1) {
log1(finfo->group_id, finfo->file_id, "Rejecting STATUS from %s: "
"invalid message size", destlist[hostidx].name);
return;
}
if (mux_lock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to lock mutex in handle_status");
return;
}
if ((cc_type == CC_TFMCC) && tfmcc) {
handle_tfmcc_ack_info(finfo, tfmcc, hostidx);
}
if (current_position < finfo->blocks) {
if (current_position >= finfo->big_sections * finfo->secsize_big) {
current_section = ((current_position -
(finfo->big_sections * finfo->secsize_big)) /
finfo->secsize_small) + finfo->big_sections;
} else {
current_section = current_position / finfo->secsize_big;
}
if (section == current_section) {
// Don't accept if it's for the current section
log3(finfo->group_id, finfo->file_id,
"Dropping STATUS for section %d", section);
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in handle_status");
}
return;
}
}
// Count the NAKs first to see if there's an excessive amount
nak_bytes = blocks_this_sec / 8;
for (naks = 0, i = 0; i < nak_bytes; i++) {
for (j = 0; j < 8; j++) {
if ((naklist[i] & (1 << j)) != 0) {
naks++;
}
}
}
if ((naks * 100 / blocks_this_sec) > (unsigned)max_nak_pct) {
destlist[hostidx].max_nak_exceed++;
}
if (destlist[hostidx].max_nak_exceed >= max_nak_cnt) {
log1(finfo->group_id, finfo->file_id, "Got excessive NAKs (%d) "
"for section %d from %s %s, aborting", naks, section,
(destlist[hostidx].clientcnt != -1) ? "proxy" : "client",
destlist[hostidx].name);
destlist[hostidx].status = DEST_ABORT;
send_abort(finfo, "Excessive NAKs received",
&receive_dest, destlist[hostidx].id, (keytype != KEY_NONE), 0);
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in handle_status");
}
return;
}
// Now record the NAKs
for (naks = 0, 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(finfo->group_id, finfo->file_id, "Got NAK for %d", nakidx);
finfo->naklist[nakidx] = 1;
*got_naks = 1;
naks++;
}
}
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in handle_status");
}
log2(finfo->group_id, finfo->file_id,
"Got %d NAKs for section %d from %s %s", naks, section,
(destlist[hostidx].clientcnt != -1) ? "proxy" : "client",
destlist[hostidx].name);
destlist[hostidx].status = DEST_ACTIVE_NAK;
}
/**
* Process an expected CC_ACK message
*/
void handle_cc_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx)
{
const struct cc_ack_h *cc_ack;
const struct tfmcc_ack_info_he *tfmcc;
const uint8_t *he;
unsigned extlen;
cc_ack = (const struct cc_ack_h *)message;
if ((meslen < (cc_ack->hlen * 4U)) ||
((cc_ack->hlen * 4U) < sizeof(struct cc_ack_h))) {
log1(finfo->group_id, finfo->file_id, "Rejecting CC_ACK from %s: "
"invalid message size", destlist[hostidx].name);
return;
}
tfmcc = NULL;
if (cc_ack->hlen * 4U > sizeof(struct cc_ack_h)) {
he = (const uint8_t *)cc_ack + sizeof(struct cc_ack_h);
if (*he == EXT_TFMCC_ACK_INFO) {
tfmcc = (const struct tfmcc_ack_info_he *)he;
extlen = tfmcc->extlen * 4U;
if ((extlen > (cc_ack->hlen * 4U) - sizeof(struct cc_ack_h)) ||
extlen < sizeof(struct tfmcc_ack_info_he)) {
log1(finfo->group_id, finfo->file_id,
"Rejecting CC_ACK from %s: invalid extension size",
destlist[hostidx].name);
return;
}
}
}
log3(finfo->group_id, finfo->file_id,
"Got CC_ACK from %s", destlist[hostidx].name);
if ((cc_type == CC_TFMCC) && tfmcc) {
if (mux_lock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to lock mutex in handle_cc_ack");
return;
}
handle_tfmcc_ack_info(finfo, tfmcc, hostidx);
if (mux_unlock(mux_main)) {
log0(finfo->group_id, finfo->file_id,
"Failed to unlock mutex in handle_cc_ack");
return;
}
}
}
/**
* Sends out a data packet. All headers should be populated
*/
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, enclen;
unsigned char *outpacket;
header = (struct uftp_h *)packet;
fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
header->seq = htons(send_seq++);
payloadlen = (fileseg->hlen * 4) + datalen;
if (keytype != KEY_NONE) {
if (!encrypt_and_sign(packet, &encpacket, payloadlen, &enclen, keytype,
groupkey, groupsalt, &ivctr, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, privkey, privkeylen)) {
log0(finfo->group_id, finfo->file_id, "Error encrypting FILESEG");
return 0;
}
outpacket = encpacket;
payloadlen = enclen;
} else {
outpacket = packet;
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&receive_dest,
family_len(receive_dest)) == SOCKET_ERROR) {
sockerror(finfo->group_id, finfo->file_id, "Error sending FILESEG");
return 0;
}
return 1;
}
/**
* Print the final statistics for the given file to the status file
*/
void print_status_file(const struct finfo_t *finfo, struct timeval start_time)
{
double elapsed_time, throughput;
int i;
if (finfo->file_id == 0) {
fprintf(status_file, "HSTATS;target;copy;overwrite;"
"skip;totalMB;time;speedKB/s\n");
for (i = 0; i < destcount; i++) {
if (destlist[i].clientcnt >= 0) {
continue;
}
if (destlist[i].total_time > 0) {
throughput = destlist[i].total_size /
destlist[i].total_time / 1024;
} else {
throughput = 0;
}
fprintf(status_file, "STATS;%s;%d;%d;%d;%sMB;%.3f;%.2fKB/s\n",
destlist[i].name, destlist[i].num_copy,
destlist[i].num_overwrite, destlist[i].num_skip,
printll(destlist[i].total_size / 1048576),
destlist[i].total_time,
throughput);
}
return;
}
for (i = 0; i < destcount; i++) {
if (destlist[i].clientcnt >= 0) {
continue;
}
fprintf(status_file, "RESULT;%s;%s;%sKB;", destlist[i].name,
finfo->destfname, printll(finfo->size / 1024));
switch (destlist[i].status) {
case DEST_MUTE:
fprintf(status_file, "mute;\n");
break;
case DEST_LOST:
fprintf(status_file, "lost;\n");
break;
case DEST_ABORT:
fprintf(status_file, "aborted;\n");
break;
case DEST_DONE:
if (sync_preview) {
throughput = rate / 8;
elapsed_time = finfo->size / (throughput * 1024);
} else {
elapsed_time = diff_usec(finfo->deststate[i].time,
start_time) / 1000000.0;
if (elapsed_time > 0) {
throughput = finfo->size / elapsed_time / 1024;
} else {
throughput = 0;
}
}
switch (destlist[i].comp_status) {
case COMP_STAT_NORMAL:
fprintf(status_file, "copy;%.2fKB/s\n", throughput);
destlist[i].num_copy++;
destlist[i].total_time += elapsed_time;
destlist[i].total_size += finfo->size;
break;
case COMP_STAT_SKIPPED:
fprintf(status_file, "skipped;\n");
destlist[i].num_skip++;
break;
case COMP_STAT_OVERWRITE:
fprintf(status_file, "overwritten;%.2fKB/s\n", throughput);
destlist[i].num_overwrite++;
destlist[i].total_time += elapsed_time;
destlist[i].total_size += finfo->size;
break;
case COMP_STAT_REJECTED:
fprintf(status_file, "rejected;\n");
break;
default:
fprintf(status_file, "Unknown;\n");
break;
}
if (destlist[i].freespace != -1) {
fprintf(status_file, "FREESPACE;%s;%s\n", finfo->destfname,
printll(destlist[i].freespace));
}
break;
default:
fprintf(status_file, "Unknown;\n");
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 (status_file) {
print_status_file(finfo, start_time);
}
if (finfo->file_id == 0) {
log0(finfo->group_id, finfo->file_id, "Group complete");
return;
}
log0(finfo->group_id, finfo->file_id, "Transfer status:");
for (done_time = start_time, i = 0; i < destcount; i++) {
if (destlist[i].clientcnt >= 0) {
continue;
}
clog0(finfo->group_id, finfo->file_id,
"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:
switch (destlist[i].comp_status) {
case COMP_STAT_NORMAL:
if (diff_usec(finfo->deststate[i].time, done_time) > 0) {
done_time = finfo->deststate[i].time;
}
elapsed_time = diff_usec(finfo->deststate[i].time,
start_time) / 1000000.0;
slog0("Completed time: %7.3f seconds", elapsed_time);
break;
case COMP_STAT_SKIPPED:
slog0("Skipped");
break;
case COMP_STAT_OVERWRITE:
if (diff_usec(finfo->deststate[i].time, done_time) > 0) {
done_time = finfo->deststate[i].time;
}
elapsed_time = diff_usec(finfo->deststate[i].time,
start_time) / 1000000.0;
slog0("Completed(overwritten) time: %7.3f seconds",
elapsed_time);
break;
case COMP_STAT_REJECTED:
slog0("Rejected");
break;
default:
slog0("Unknown completion status: %d", destlist[i].comp_status);
break;
}
if (destlist[i].freespace != -1) {
log0(finfo->group_id, finfo->file_id,
" Free space on host: %s bytes",
printll(destlist[i].freespace));
}
break;
default:
slog0("Unknown code: %d", destlist[i].status);
break;
}
}
elapsed_time = diff_usec(done_time, start_time) / 1000000.0;
log1(finfo->group_id, finfo->file_id,
"Total elapsed time: %.3f seconds", elapsed_time);
log1(finfo->group_id, finfo->file_id, "Overall throughput: %.2f KB/s",
(elapsed_time != 0) ? (finfo->size / elapsed_time / 1024) : 0);
}
uftp-4.1.5/Changes.txt 0000644 0000764 0000764 00000066764 12304524207 013636 0 ustar dbush dbush Version 4.1.5 - 3/1/2014
Fixed casting bug in server TFMCC operations when calculating client rate.
Fixed server proxy bug where group round trip time was being read from
client messages when it should only be read from server messages.
Version 4.1.4 - 12/5/2013
During a restart session, a bug caused the full file to be resent on the
first pass, and client wouldn't send back NAKs for the first session.
This has been fixed.
Fixed incorrect reading of client ID lists in DONE, DONE_CONF, and CONG_CTRL
messages.
Added group ID and file ID to server logging when timestamps are enabled.
Added more warning checks for Linux and corrected warnings.
Cleaned up error checking code for malloc and calloc calls.
Version 4.1.3 - 10/13/2013
Since 4.0, compilation failed on MacOSX. This has been fixed.
Version 4.1.2 - 8/20/2013
Fixed client pathname checking that disallows ".." path elements.
Server wasn't writing clients to the restart file that didn't send a
COMPLETE message for the session. Bug fixed.
Version 4.1.1 - 8/8/2013
Fixed byte ordering issue in EXT_FREESPACE_INFO extension.
Version 4.1 - 7/30/2013
Changed the way the server handles NAKs from the clients. Instead of
rewinding its current position as soon as NAKs are received, the server
will wait until the end of the pass, then start a new pass. This more
closely resembles version 3.x behavior.
Added special metafiles @DELETE:{filename}, which deletes filename on the
client, and @FREESPACE, which reports the available free disk space on
the client's primary destination directory.
Upon receiving a FILEINFO, a client will abort if it doesn't have enough
free disk space to receive the file.
Added -S option to server to write parsable status information to a separate
log file. This information was previously available only with the -z
option, and -z will no longer print this info without -S.
Added -N option which specifies the maximum NAK percentage a client may
report before potentially getting dropped. Also added related -m option,
which specifies the number of times a client can exceed the maximum NAK
percentage before it is dropped.
Reinstated the -W option, which specifies the maximum file transfer time as
a percentage of the optimal time.
Added -i option to client to allow moving files one at a time from the temp
directory to the dest directory. This option is necessary to prevent
previously received files in dest from being deleted when full directories
are received.
Added -u option to server to specify outgoing UDP port number.
Added log rolling to the server, client, and proxy. The -g option specifies
the max log file size in MB before rolling, and the -n option specifies
the number of rolled log files to save.
The server may now specify clients by their IPv4 name/address as well as
their ID, assuming the clients in question use their IPv4 address as their
ID, making for cleaner backward compatibility with version 3.x.
Fixed several bugs in the TFMCC implementation:
Fixed counter wraparound issue.
Fixed crash when more than a few hundred clients are active.
Fixed bug which caused rate to be set to 0.
Fixed crash when sending an empty file.
Added signal handlers in server so that it will save restart info if the
user interrupts it.
Fixed issue with printing of client IDs in server.
Dependency fixes in makefile
Version 4.0 - 4/27/2013
Added support for IPv6. On systems that support dual stack IPv4/IPv6
sockets, clients and proxies may listen on multicast groups for both
IP versions.
Dynamically determine round trip time to clients and base control message
retransmissions and timeouts on that. Old server timing options
-A, -S, -a, -s, -r, -d, -W, and -m are no longer used, with -r and -s
options repurposed to put constraints on round trip time calculations and
control message retransmission counts.
The server no longer waits for client responses in the middle of sending a
file. Multiple threads are used to send data packets while reading client
responses, resulting in shorter overall transmission times. Also, clients
won't send back STATUS messages unless they require retranmissions of data
packets, reducing to amount of back traffic the server must handle.
Replaced simple congestion control scheme with TCP Friendly Multicast
Congestion Control (TFMCC) as specified in RFC 4654.
Upgraded available security protocols, including support for Elliptic Curve
Diffe-Hellman key exchange, Elliptic Curve DSA signatures, AES in GCM
and CCM authenticated modes, and SHA-384 and SHA-512 hashing. As a result
of this, the key fingerprinting algorithm has changed, so existing RSA
keys will have different signatures.
Clients and proxies are now always identified by a 32-bit ID instead of their
local IPv4 address. This ID will be derived from either the local IPv4 or
IPv6 address if not explicitly specified.
The format of the server list file on the client and the client/proxy list
files on the server have changed as a result of the new method of
identifying clients. See the man pages for more details.
Modified protocol to support the above new features and to be more
extendable in the face of future upgrades. While these changes break
backward compatibility with 3.x versions, the added support for header
extensions should prevent future instances of this.
Version 3.7.2 - 3/17/2013
When a client aborts and isn't using a temp directory, delete the current file
Version 3.7.1 - 10/21/2012
Fixed bug where clients would occasionally fail to register in open
group mode if the client doesn't have a UID assigned. First appeared
in version 3.4.3.
With a congestion control file in use, the speed wouldn't increase if
the max_rate parameter was not specified in the congestion control
config file. First appeared in version 3.6. Fixed.
Under Windows, a postreceive script with a .bat extension wouldn't
execute. Bug fixed.
Version 3.7 - 6/28/2012
Added the much-requested option to the client to run an external command
upon receiving a file. See the -s option.
Increased the maximum number of recognized network interfaces to 100.
Fixed data corruption bug caused by a file write error on the client.
Version 3.6.1 - 12/12/2011
Reverted the change in 3.6 that allowed the client and proxy to bind
only to one interface. Doing so breaks multicast on UNIX-like systems.
Version 3.6 - 12/3/2011
Added option (-N) to client and proxy to specify process priority.
Default behavior change: prior to this release on UNIX-like systems,
the client and proxy had a default nice() value of -20. The new
default is 0.
Added max_rate parameter to congestion control config file. Previously,
with congestion control enabled, the initial rate would be the max rate.
This parameter allows a max rate to be set separate from the initial rate.
On client and proxy, if only interface is specified to -I (or if -I is not
specified and only one non-loopback interface is detected), the
client/proxy will bind only to that interface. If more than one is
specified or detected, it will bind to all interfaces. Prior to this
release, it would always bind to all interfaces.
Explicitly disable MTU discovery on systems that support it. Since the
server specifies the MTU, it doesn't make sense to allow this.
When using a congestion control file, it is reread at each DONE interval.
This allows an external process to adjust the parameters on the fly based
on network conditions or business rules.
Fixed bug where a proxy wouldn't properly handle an ABORT from a client.
Fixed bug that would cause all clients to abort on when max file transfer
timeout was exceeded.
Fixed server bug where -j wouldn't work if specified before -H.
Version 3.5.1 - 7/10/2011
Send COMPLETE(rejected) instead of ABORT for file/path issues.
Allow server to send broadcast packets.
Improved memory management in server.
Proxy wasn't propigating COMPLETE status upsteam. Fixed.
Filenames passed to server are no longer checked on startup. They are
instead checked as they are handled.
Fixed proxy bug in open group mode where a client's address isn't detected.
Fixed a few proxy bounds checking issues.
Version 3.5 - 6/17/2011
Added sync mode - Incoming files won't overwrite existing files
unless the existing file is older. In this mode the server's output
displays whether a file is copied, overwritten, or skipped, and also
displays a count of each at the end of the session. See -z on the server.
Added sync preview mode - like sync mode, except no files are actually
transferred. The server displays the status of each file (copy,
overwrite, or skip) had it actually been copied. See -Z on the server.
When using -D on the server to specify the destination file name, an
absolute pathname may be specified. Clients must be configured to
allow incoming absolute pathnames.
Allow clients to specify multiple destination directories. The client will
allow an incoming file with an absolute pathname if it matches at least
one destination directory.
Added a simple congestion control method controled by a config file.
See the -C option to the server.
Added backup directory option to client. If an incoming file would overwrite
an existing file, the existing file is backed up. See -A on the client.
Added option to client to use an individual temp file for each incoming
file as opposed to a temp directory for all files. See -t on the client.
Added configurable logging levels to server, client, and proxy. See -x.
Added ability to send proxy heartbeat messages from clients. See -H and -h.
Fixed bug where clients assigned a UID don't register properly.
Fixed crash when reading from config files with blank lines.
Version 3.4.3 - 4/23/2011
Under Windows, allow use of the system key container for private keys.
See -y on the server, and -m on the client and proxy for more details.
Previously, when a client registered with a server in open group membership
mode, it responded using the IP of the first network interface, which is
not necessarily the interface the ANNOUNCE was received on. The client
now responds with a blank IP, telling the server/proxy to use the source
IP of the incoming packet as the client's IP.
Enabled NO_ENCRYPTION compile flag on Windows.
Version 3.4.2 - 3/29/2011
Fixed error when passing "-Y none" to the server
The pidfile option (-P) for the client and proxy didn't work under
Windows. It does now.
Improved internal error checking.
Version 3.4.1 - 2/21/2011
Fixed crash in Windows when receiving an empty file
Extended the late register and late done timers to 1.5 times the old values.
This gives clients a little extra time to send responses.
Clients now send a COMPLETE in response to a FILEINFO for a directory or
a symbolic link, elimitating an extra DONE/COMPLETE cycle.
Clients may now accept a FILEINFO in lieu of a REG_CONF. This eliminates
a timing issue when a REG_CONF gets lost.
Version 3.4 - 12/18/2010
Added new proxy mode: response proxy
Used when servers have direct multicast accessability to clients, but
response aggregation is still desired.
Fixed overflow bug when calculating packet rate with jumbo frames.
Fixed server bug when checking maximum size of file exclusion list.
Fixed bug in BSD systems where setting of DSCP/TOS value failed.
Updated documentation regarding Windows support for DSCP/TOS.
Version 3.3.3 - 11/26/2010
Added -Q option to server, client, and proxy to specify the DSCP
(formerly TOS) in the IP header for all outgoing packets.
Increased max announce time and status time from 10000 to 20000 ms.
Version 3.3.2 - 10/13/2010
Added DESTDIR option to makefile to allow installs in fake root directories.
Fixed overflow bug with session longer than 30 minutes.
Added examples to man pages
Version 3.3.1 - 7/18/2010
Added -i option to server to take list of file to send.
Added -T option to server to always print timestamps.
Fixed bug introduced in 3.3 where a single dropped REG_CONF
results in a client not being able to register.
Added makefile support for Darwin/OSX.
Improved error handling.
Version 3.3 - 7/8/2010
Added ability to restart failed file transfers (see server -f and -F).
Removed timeout option from proxy (-o) and client (-t)
The server specified status_time is used instead.
Allow proxies to keep track of multiple pending messages at once.
Added makefile flags to compile without encryption support.
Fixed handling of snprintf return codes under Windows.
Have proxies foward ABORTs after local handling.
Version 3.2.1 - 6/16/2010
Under Windows, setting the root directory of a drive (c:\, d:\, etc.) as
the destination directory caused errors. Fixed.
When trying to send unicast to a client beind a NAT, the client would
reject the transfer because the IP specified by the server doesn't
match the client's IP. Clients will now always accept in unicast mode.
Version 3.2 - 5/30/2010
Fixed compile bug on OSX (cleaned up signal setup)
Added detection of non-multicast interfaces
Added unique ID to clients and proxies. This allows for distinguising
clients between different NATs that might have the same IP address.
Version 3.1 - 4/6/2010
Added ability to send empty directories and symbolic links
Added -l option to server to follow / not follow links
Added -D option to server to specify destination file name
Fixed client bug when receiving a file with the same name as an
existing directory and vice versa
Version 3.0 - 3/11/2010
Added SSL derived encryption.
Uses RSA for host authentication and key exchange.
Uses DES, Triple DES, AES-128, and AES-256 for data encryption.
Ability to send multiple files or full directories at once.
Clients may be listed in a file instead of the server command line.
Added proxy daemon which allows:
NAT traversal
Aggregation of client responses
Multicast tunneling
Support for variable packet sizes.
Removed latency levels in favor of more fine grained control.
Support for source specific multicast.
Protocol heavily altered to support new features.
Code completely restructured and reformatted for ease of updates and support.
Version 2.10.3 - 1/21/2010
Fixed ttl bug under Solaris where no value was valid
Cleaned up handling of server timeout so an extra DONE request isn't sent
Changed Windows sockets error messages to use proper system message strings
Version 2.10.2 - 11/3/2009
The change in 2.10.1 that fixed the NAK issue caused a crash when sending
or receiving empty files. This has been fixed.
When specifying the list of interfaces for the client to listen on, if the
interfaces are listed by name only the first was used. Bug fixed.
Cleaned up more warnings on Windows
Version 2.10.1 - 8/20/2009
Fix for last packet wait bug, introduced in 2.3
Fix for NAKs with block counts a multiple of the section size
Fixed ttl bug - now uses actual number instead of ascii value of the
number. This also results in the default ttl changing from 49 to
the intended value of 1, so this may have an effect on existing apps.
Thanks to Luc Tanguay for finding these bugs
Cleaned up some warnings on Windows
Version 2.10 - 7/16/2009
Added -B option to client and server to set UDP receive buffer size.
Defaults to the old hardcoded value of 256K.
Added -z option to server, which tells clients to make responses as
small as possible. This minimizes backtraffic when you have a
large number of receivers.
Adjusted timeouts to work together better.
Server now exits if invalid hosts are specified.
Fixed some minor logging bugs.
Version 2.9.2 - 6/20/2009
Getting the list of network interfaces is no longer manditory. If
there's a problem getting the list, and you don't pass -I to the
client, it just uses the clients hostname interface as before
Fixed bounds checking on the server's -R and -c options, and
increased the maximum valid value for -c to 60000 (60 seconds).
Fixes to run properly under FreeBSD, including:
Use signal(2) instead of sigset(2) for signal processing
Use getifaddrs to get list of network interfaces
Try a smaller value for the receive buffer if the first fails
Version 2.9.1 - 6/13/2009
Allow the full 10000 receivers for closed group membership as well as
open. Multiple ANNOUNCE message are now sent to accomidate the
longer lists.
Minor modifications to compile under OpenVMS.
Fallback to listening on the client's hostname interface if interface
list contains no active non-loopback interfaces.
Got rid of debug message on startup on Windows boxes.
Version 2.9 - 6/8/2009
After numerous complaints from Linux users, the uftpd client now listens
for multicast traffic on all non-loopback interfaces by default. You
can still restrict the interfaces using the -I option.
The -I option for both the client and server will take interface names
(ex. eth0, bge1, etc.) as well as hostnames or IPs.
Broke out implementations in header files into separate source files
Commoned up all UNIX-like makefiles
Version 2.8.1 - 5/18/2009
Pidfile option to uftpd wasn't optional as documented. It is now.
Fixed bug in reporting of total time. Was previously looking for newest
end timestamp among all receivers, now only checking completed ones.
Version 2.8 - 4/26/2009
Functionality change: Instead of the server sending a REG_CONF separately
to each client, it sends one listing up to 300 clients, and more if
required. These are sent out before each subsequent ANNOUNCE and, in
closed group mode, after receiving REGISTERS from all clients. The
handling of DONE_CONF messages also changed to work in a similar manner.
As a result, the client side timeouts were increased to handle this.
Added -c option to uftp to explicitly set client timeouts in milliseconds
for both REG_CONF and DONE_CONF. Useful for when the server expects
to hear from a large number (>100) of receivers over a low speed link,
meaning it could take the server several seconds to process them all.
If this value is less than the time for the specified latency level,
it is ignored.
Version 2.7.1 - 3/31/2009
Added -P option to uftpd to write the daemon's pid to a pidfile.
Added latency level 4 with values 2x that of level 3.
Fixed bug when sending DONE to over 300 clients.
Brought man pages up to date.
Changed license to GPLv3.
Version 2.7 - 2/26/2009
Added -n option to uftp to prevent name lookups of clients (a similar
option exists in uftpd). This happens for both open group membership,
when clients register, and for closed group membership, when names are
specified on the command line via -H. For the latter to take effect, -n
must appear before -H on the command line.
Changed name of wait variable in uftp to get around conflict on OSX/BSD,
and added OSX/BSD makefile. Thanks to Jusin Venus for supplying these.
Version 2.6.6 - 1/16/2009
Fixed logging output to display correctly on 64 bit systems
Version 2.6.5 - 12/18/2008
Cleaned up a few more warnings
Version 2.6.4 - 12/16/2008
Cleaned up some warnings, added -Wall to linux makefile
Version 2.6.3 - 7/29/2008
Fixed the basename function for Windows, and made a proper makefile
for Windows. Thanks to Amol Deshpande for providing these.
Removed the call to basename from uftpd, since it has no effect.
Version 2.6.2 - 7/28/2008
Cleaned up the code a bit so file names/paths have a consistent size,
fixed bounds checking on several buffers
In-code max of 10000 receivers for open group membership, 100 for closed
Version 2.6.1 - 6/11/2007
Fixed wait bug in uftp
Version 2.6 - 2/25/2007
Changed all "short", "long", and "long long" types to the
appropriate fixed size types (16, 32, and 64, respectively) for
better compatibility between 32 bit and 64 bit environments.
Man pages added for Unix/Linux systems.
The -I option to uftpd is now optional. It defaults to the
interface assciated with the client's hostname.
Version 2.5.3 - 10/27/2006
In version 2.5.1, a fix was applied to uftpd to only close a file
if it was still open. But apparently, the old line of code
that closes it outright wasn't removed (doh!). It's gone now.
Version 2.5.2 - 10/26/2006
Bug fixes in uftpd - the -n option was actually causing the opposite
of the intended effect, and forgot to check for a null value from
gethostbyaddr when looking up the sender's name.
Version 2.5.1 - 9/25/2006
Fix in uftpd - don't close file if already closed. On most OS's,
this isn't an issue, but under MS Visual Studio it caused a crash.
Thanks to Mark Leavy for catching this one.
Made a similar fix in uftp. Actually, the file was never formally
closed. It is now.
Version 2.5 - 9/20/2006
Allowed the -I option to uftpd to take multiple parameters separated
by commas. This allows you to listen for multicast traffic on
multiple interfaces, and also allows you to receive data on one
interface and send responses back on another.
Version 2.4 - 9/16/2006
Added -L option to uftp to allow output to go to a log file. The
default is to write to stderr.
Modified all error routines in uftp to use logfunc. This is how
error logging is currently done in uftpd.
Version 2.3.1 - 7/25/2006
Fixed to allow zero length files to be transferred.
Version 2.3 - 7/16/2006
Fixed handling of large files in Windows
Added descriptive error messages for Windows socket errors
Functionality change: uftpd now runs in a single thread. This not
only resulted in a speed increase on most systems, but also gets
back the functionality of receiving multiple files at once in unicast
mode, which was lost in version 1.3. It is also no longer necessary
to have multiple sockets open on the same port, which caused an
issue where multiple instances could be running on the same port.
The code for uftpd was also restructured as a result, becoming
more modular.
Removed -s option from uftpd, since the conditions necessitating this
option no longer exist.
Version 2.2 - 7/7/2006
Added -n flag to uftpd prevent name lookups of transmitter. This can
eliminate issues with slow name lookups delaying registration.
Version 2.1.1 - 7/5/2006
Fixed handling of -H option to uftp
Version 2.1 - 6/22/2006
Made a few fixes specific to Linux machines:
When calling select(3c) with a timeout, save off a copy of the timeout
variable and reset it just before calling select. This gets around
Linux-specific behavior which modifies the timeout parameter after
the call to indicate the amount of time left to wait.
Added a -s option to uftpd. Apparently, when multiple UDP sockets are
open on the same port in Linux, any packet arriving on this port gets
passed to ALL open sockets, regardless of whether or not the multicast
destination of the packet matches one of the multicast addresses the
socket is bound to. This causes a slew of "invalid txID" or "invalid
function: ANNOUNCE" messages that otherwise shouldn't show up. The -s
option will suppress these messages. NOTE: This option should ONLY be
used on Linux boxes (or other UNIX-like OS's that exibit this behavior),
as it could hide a real problem. Windows and Solaris uses should NOT
use this option.
Version 2.0 - 5/4/2006
UFTP now runs under Windows! It will compile under both Visual C++ 6.0
and Visual Studio .NET, and run on Windows 2000/XP. No MFC or .NET
functionality was employed, only straight WIN32 calls. A few major
changes under the hood made it possible to have one version of the
code that compiles in both enivronments:
The receiver, uftpd, now uses multiple threads instead of multiple
processes. This was made necessary because Windows doesn't support
the UNIX fork function, which makes an exact copy of the calling process.
The logging functions in uftpd had to be modified to allow for a common
interface for UNIX and Windows boxes.
Changed timing routines in uftp. Previously, the gettimeofday function was
used to get microsecond resolution timestamps. Since Windows doesn't have
this function, calls to this function in addition to calculations of
timestamp differences were hidden away behind #defines, so that UNIX boxes
still call gettimeofday, and Windows boxes use QueryPerformanceCounter.
Several other functions, including perror, usleep, link, were abstracted
behind #defines to allow for a common interface for UNIX and Windows.
The Windows Sleep function only has millisecond timing, however it still
allows for proper wait times between packet transmissions.
Wrote my own versions of getopt and basename, since they don't exist in
Windows. When compliled for UNIX, the built in OS functions are used.
Currently, the Windows version of uftpd does not put itself in the
background. A Command Prompt window will remain open, even without the
-d option. This was a consequence of using main instead of WinMain for
the entry point to keep it consistent with UNIX. This can be worked
around by calling uftpd with hidedos.exe, which is a utility program
created by LanDesk. This utility can be found freely on several LanDesk
related forums.
Version 1.4.2 - 1/24/2006
Bug fix - missing htons() call when assigning port in uftpd. Not a big deal
for big-endian machines, BAD for little-endian machines.
Version 1.4.1 - 1/15/2006
Added a few missing header files
Version 1.4 - 11/16/2005
Added support for large (>2GB) files
On second and subsequent passes, status requests are not sent for a
particular section if there were no NAKs for that section. A status
request is always sent for the last section, however.
Version 1.3 - 9/25/2005
Add -l flag to uftp to set a predefined latency level 1-3
(1=low, 2=medium, 3=high (default))
In prior versions, during the initial handshaking and when requesting
NAK counts, the transmitter would wait 2 and 4 seconds respectively
before resending a request. This is fine for a high latency link
(satellite, WAN) but not a low latency link (local LAN). This setting
allows for shorter waits to handle a variety of network latencies.
This flag should be omitted for backward compatibility
Functionality change: The parent uftpd process no longer forwards data
to its children. The children now receive the data directly from the
transmitter. This greatly improves high end throughput. However, doing
so relies on a multicast feature (allowing multiple UDP sockets to listen
on the same port) that doesn't work in unicast. Therefore, in order
to retain the ability to receive files via unicast, uftpd cannot receive
files via unicast and multicast at the same time. When a request
for a unicast transfer is received, it is accepted only if no other
files are being received. The parent uftpd then handles the file
directly and will not accept any other files until it is done.
Version 1.2 - 9/14/2005
Improved error handling
Improved cross-platform compatibility
Version 1.1 - 8/15/2005
All integers in UFTP header converted to network byte order before
sending, and converted back upon receiving, allowing machines with
different hardware architechtures to communicate
Minor performance enhancements
Version 1.0
Initial Release
uftp-4.1.5/proxy_init.h 0000644 0000764 0000764 00000002704 12250244021 014053 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _PROXY_INIT_H
#define _PROXY_INIT_H
void pre_initialize(void);
void initialize(void);
#endif // _PROXY_INIT_H
uftp-4.1.5/proxy_upstream.c 0000644 0000764 0000764 00000122617 12250244021 014751 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#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 a pointer to the open slot, or NULL if none found.
*/
struct pr_group_list_t *find_open_slot(void)
{
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 &group_list[i];
}
}
return NULL;
}
/**
* 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(struct pr_group_list_t *group,
const struct enc_info_he *encinfo)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
time_t t;
uint32_t t2;
memcpy(group->rand1, encinfo->rand1, sizeof(encinfo->rand1));
if (!get_random_bytes(group->rand2, sizeof(group->rand2))) {
log2(group->group_id, 0, "Failed to get random bytes for rand2");
send_upstream_abort(group, 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->rand2) = t2;
if (group->keyextype == KEYEX_RSA) {
if (!get_random_bytes(group->premaster, MASTER_LEN)) {
log2(group->group_id,0, "Failed to get random bytes for premaster");
send_upstream_abort(group, 0,
"Failed to get random bytes for premaster");
return 0;
}
group->premaster_len = MASTER_LEN;
} else {
if (!get_ECDH_key(group->server_dhkey.ec, group->proxy_dhkey.ec,
group->premaster, &group->premaster_len)) {
log2(group->group_id,0, "Failed to calculate ECDH key");
send_upstream_abort(group, 0, "Failed to calculate ECDH key");
return 0;
}
}
get_key_info(group->keytype, &group->keylen, &group->ivlen);
group->hmaclen = get_hash_len(group->hashtype);
explen = group->keylen + SALT_LEN + group->hmaclen;
seedlen = RAND_LEN * 2;
seed = safe_calloc(seedlen, 1);
prf_buf = safe_calloc(MASTER_LEN + explen + group->hmaclen, 1);
memcpy(seed, group->rand1, sizeof(group->rand1));
memcpy(seed + sizeof(group->rand1), group->rand2, sizeof(group->rand2));
PRF(group->hashtype, MASTER_LEN, group->premaster, group->premaster_len,
"master secret", seed, seedlen, prf_buf, &len);
memcpy(group->master,prf_buf, sizeof(group->master));
PRF(group->hashtype, explen, group->master, sizeof(group->master),
"key expansion", seed, seedlen, prf_buf, &len);
memcpy(group->hmackey, prf_buf, group->hmaclen);
memcpy(group->key, prf_buf + group->hmaclen, group->keylen);
memcpy(group->salt, prf_buf + group->hmaclen + group->keylen, SALT_LEN);
free(seed);
free(prf_buf);
return 1;
}
/**
* Read encryption related fields from an ANNOUNCE
*/
int read_announce_encryption(struct pr_group_list_t *group,
struct enc_info_he *encinfo,
const unsigned char *packet, int packetlen)
{
int keyextype, sigtype, keytype, i;
unsigned char *keys;
keys = (unsigned char *)encinfo + sizeof(struct enc_info_he);
// Sanity check the selected encryption parameters
if (!cipher_supported(encinfo->keytype)) {
log2(group->group_id, 0, "Keytype invalid or not supported here");
send_upstream_abort(group, 0, "Keytype invalid or not supported here");
return 0;
}
if (!hash_supported(encinfo->hashtype)) {
log2(group->group_id, 0, "Hashtype invalid or not supported here");
send_upstream_abort(group, 0, "Hashtype invalid or not supported here");
return 0;
}
keyextype = (encinfo->keyextype_sigtype & 0xF0) >> 4;
sigtype = encinfo->keyextype_sigtype & 0x0F;
if (((sigtype != SIG_HMAC) && (sigtype != SIG_KEYEX) &&
(sigtype != SIG_AUTHENC)) ||
((sigtype == SIG_AUTHENC) && (!is_auth_enc(encinfo->keytype)))) {
log2(group->group_id, 0, "Invalid sigtype specified");
send_upstream_abort(group, 0, "Invalid sigtype specified");
return 0;
}
if ((keyextype != KEYEX_RSA) && (keyextype != KEYEX_ECDH_RSA) &&
(keyextype != KEYEX_ECDH_ECDSA)) {
log0(group->group_id, 0, "Invalid keyextype specified");
send_upstream_abort(group, 0, "Invalid keyextype specified");
return 0;
}
group->keyextype = keyextype;
group->keytype = encinfo->keytype;
group->hashtype = encinfo->hashtype;
group->sigtype = sigtype;
group->client_auth = ((encinfo->flags & FLAG_CLIENT_AUTH) != 0);
if (!verify_fingerprint(server_fp, server_fp_count, keys,
ntohs(encinfo->keylen), group, group->src_id)) {
log2(group->group_id, 0, "Failed to verify server key fingerprint");
send_upstream_abort(group,0, "Failed to verify server key fingerprint");
return 0;
}
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
keytype = KEYBLOB_RSA;
} else {
keytype = KEYBLOB_EC;
}
// Load server key and select a matching client key
if (keytype == KEYBLOB_RSA) {
if (!import_RSA_key(&group->server_pubkey.rsa, keys,
ntohs(encinfo->keylen))) {
log2(group->group_id, 0, "Failed to load server public key");
send_upstream_abort(group, 0, "Failed to load server public key");
return 0;
}
group->server_pubkeylen = RSA_keylen(group->server_pubkey.rsa);
for (i = 0; i < key_count; i++) {
if ((privkey_type[i] == KEYBLOB_RSA) &&
(group->server_pubkeylen) == RSA_keylen(privkey[i].rsa)) {
group->proxy_privkey = privkey[i];
group->proxy_privkeylen = RSA_keylen(privkey[i].rsa);
break;
}
}
} else {
if (!import_EC_key(&group->server_pubkey.ec, keys,
ntohs(encinfo->keylen), 0)) {
log0(group->group_id, 0, "Failed to load server public key");
send_upstream_abort(group, 0, "Failed to load server public key");
return 0;
}
group->server_pubkeylen = ECDSA_siglen(group->server_pubkey.ec);
for (i = 0; i < key_count; i++) {
if ((privkey_type[i] == KEYBLOB_EC) &&
(get_EC_curve(group->server_pubkey.ec) ==
get_EC_curve(privkey[i].ec))) {
group->proxy_privkey = privkey[i];
group->proxy_privkeylen = ECDSA_siglen(privkey[i].ec);
break;
}
}
}
if (!group->proxy_privkey.key) {
log2(group->group_id, 0, "No proxy key compatible with server key");
send_upstream_abort(group,0, "No proxy key compatible with server key");
return 0;
}
if ((group->keyextype == KEYEX_ECDH_ECDSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
unsigned char *sigcopy;
int siglen;
unsigned char *dhblob = keys + ntohs(encinfo->keylen);
unsigned char *sig = dhblob + ntohs(encinfo->dhlen);
if (!import_EC_key(&group->server_dhkey.ec, dhblob,
ntohs(encinfo->dhlen), 1)) {
log2(group->group_id, 0, "Failed to load server public ECDH key");
send_upstream_abort(group, 0,
"Failed to load server public ECDH key");
return 0;
}
if (proxy_type == RESPONSE_PROXY) {
if (get_EC_curve(group->server_pubkey.ec) ==
get_EC_curve(dhkey.ec)) {
group->proxy_dhkey = dhkey;
} else {
log2(group->group_id, 0,
"Proxy ECDH key not compatible with server key");
send_upstream_abort(group, 0,
"Proxy ECDH key not compatible with server key");
return 0;
}
} else {
group->proxy_dhkey.ec =
gen_EC_key(get_EC_curve(group->server_dhkey.ec), 1, NULL);
if (!group->proxy_dhkey.key) {
log2(group->group_id, 0, "Failed to generate proxy ECDH key");
send_upstream_abort(group, 0,
"Failed to generate proxy ECDH key");
return 0;
}
}
siglen = ntohs(encinfo->siglen);
sigcopy = safe_calloc(siglen, 1);
memcpy(sigcopy, sig, siglen);
memset(sig, 0, siglen);
if (keytype == KEYBLOB_RSA) {
if (!verify_RSA_sig(group->server_pubkey.rsa, group->hashtype,
packet, packetlen, sigcopy, siglen)) {
log2(group->group_id, 0, "Signature verification failed");
send_upstream_abort(group, 0, "Signature verification failed");
free(sigcopy);
return 0;
}
} else {
if (!verify_ECDSA_sig(group->server_pubkey.ec, group->hashtype,
packet, packetlen, sigcopy, siglen)) {
log2(group->group_id, 0, "Signature verification failed");
send_upstream_abort(group, 0, "Signature verification failed");
free(sigcopy);
return 0;
}
}
free(sigcopy);
}
// Calculate keys
if (!calculate_server_keys(group, encinfo)) {
return 0;
}
return 1;
}
/**
* Read in the contents of an ANNOUNCE.
*/
int read_announce(struct pr_group_list_t *group, unsigned char *packet,
const union sockaddr_u *src, int packetlen)
{
struct uftp_h *header;
struct announce_h *announce;
struct enc_info_he *encinfo;
uint8_t *publicmcast, *privatemcast;
uint8_t *he;
unsigned int iplen, extlen;
header = (struct uftp_h *)packet;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
encinfo = NULL;
group->version = header->version;
group->group_id = ntohl(header->group_id);
group->group_inst = header->group_inst;
group->up_addr = *src;
group->src_id = header->src_id;
group->grtt = unquantize_grtt(header->grtt);
group->robust = announce->robust;
group->cc_type = announce->cc_type;
group->gsize = unquantize_gsize(header->gsize);
group->blocksize = ntohs(announce->blocksize);
iplen = ((announce->flags & FLAG_IPV6) != 0) ?
sizeof(struct in6_addr) : sizeof(struct in_addr);
publicmcast = ((uint8_t *)announce) + sizeof(struct announce_h);
privatemcast = publicmcast + iplen;
if ((announce->flags & FLAG_IPV6) != 0) {
group->publicmcast.sin6.sin6_family = AF_INET6;
group->privatemcast.sin6.sin6_family = AF_INET6;
memcpy(&group->publicmcast.sin6.sin6_addr.s6_addr, publicmcast, iplen);
memcpy(&group->privatemcast.sin6.sin6_addr.s6_addr, privatemcast,iplen);
group->publicmcast.sin6.sin6_port = htons(out_port);
group->privatemcast.sin6.sin6_port = htons(out_port);
} else {
group->publicmcast.sin.sin_family = AF_INET;
group->privatemcast.sin.sin_family = AF_INET;
memcpy(&group->publicmcast.sin.sin_addr.s_addr, publicmcast, iplen);
memcpy(&group->privatemcast.sin.sin_addr.s_addr, privatemcast, iplen);
group->publicmcast.sin.sin_port = htons(out_port);
group->privatemcast.sin.sin_port = htons(out_port);
}
if ((announce->hlen * 4U) < sizeof(struct announce_h) + (2U * iplen)) {
log0(group->group_id, 0, "Rejecting ANNOUNCE from %08X: "
"invalid header size", ntohl(group->src_id));
send_upstream_abort(group, 0, "Invalid header size");
return 0;
}
if ((announce->hlen * 4U) > sizeof(struct announce_h) + (2U * iplen)) {
he = (uint8_t *)announce + sizeof(struct announce_h) + (2U * iplen);
if (*he == EXT_ENC_INFO) {
encinfo = (struct enc_info_he *)he;
extlen = encinfo->extlen * 4U;
if ((extlen > ((announce->hlen * 4U) -
sizeof(struct announce_h))) ||
(extlen < sizeof(struct enc_info_he)) ||
(extlen != (sizeof(struct enc_info_he) +
ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) +
ntohs(encinfo->siglen)))) {
log1(group->group_id, 0, "Rejecting ANNOUNCE from %08X: "
"invalid extension size", ntohl(group->src_id));
send_upstream_abort(group, 0, "Invalid extension size");
return 0;
}
}
}
if ((encinfo != NULL) && (proxy_type != SERVER_PROXY)) {
if (!read_announce_encryption(group, encinfo, packet, packetlen)) {
return 0;
}
} else {
group->keyextype = KEYEX_NONE;
group->keytype = KEY_NONE;
group->hashtype = HASH_NONE;
group->sigtype = SIG_NONE;
group->client_auth = 0;
}
gettimeofday(&group->phase_expire_time, NULL);
if (group->robust * group->grtt < 1.0) {
add_timeval_d(&group->phase_expire_time, 1.0);
} else {
add_timeval_d(&group->phase_expire_time, group->robust * group->grtt);
}
// Size of data packet, used in transmission speed calculations
group->datapacketsize = group->blocksize + sizeof(struct fileseg_h);
if (group->cc_type == CC_TFMCC) {
group->datapacketsize += sizeof(struct tfmcc_data_info_he);
}
if (group->keytype != KEY_NONE) {
group->datapacketsize += ((group->sigtype == SIG_KEYEX) ?
group->server_pubkeylen : (group->sigtype == SIG_HMAC) ?
group->hmaclen : 0) + KEYBLSIZE + sizeof(struct encrypted_h);
}
// 8 = UDP size, 20 = IPv4 size, 40 = IPv6 size
if ((announce->flags & FLAG_IPV6) != 0) {
group->datapacketsize += sizeof(struct uftp_h) + 8 + 40;
} else {
group->datapacketsize += sizeof(struct uftp_h) + 8 + 20;
}
return 1;
}
/**
* Inserts the proxy's public keys into an ANNOUNCE
* Returns 1 on success, 0 on fail
*/
int insert_pubkey_in_announce(struct pr_group_list_t *group,
unsigned char *packet, int packetlen)
{
struct announce_h *announce;
struct enc_info_he *encinfo;
unsigned char *keyblob, *dhkeyblob;
uint16_t bloblen;
unsigned int iplen;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
iplen = ((announce->flags & FLAG_IPV6) != 0) ? 16 : 4;
encinfo = (struct enc_info_he *)
((uint8_t *)announce + sizeof(struct announce_h) + (2U * iplen));
keyblob = ((unsigned char *)encinfo + sizeof(struct enc_info_he));
dhkeyblob = keyblob + ntohs(encinfo->keylen);
if ((group->keytype != KEY_NONE) && (proxy_type == CLIENT_PROXY)) {
// Plug in proxy's public key for server's
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(group->proxy_privkey.rsa, keyblob, &bloblen)) {
log2(group->group_id, 0, "Error exporting proxy public key");
return 0;
}
} else {
if (!export_EC_key(group->proxy_privkey.ec, keyblob, &bloblen)) {
log2(group->group_id, 0, "Error exporting proxy public key");
return 0;
}
}
if (bloblen != ntohs(encinfo->keylen)) {
log2(group->group_id, 0, "Incorrect exported proxy key size");
return 0;
}
if ((group->keyextype == KEYEX_ECDH_ECDSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_EC_key(group->proxy_dhkey.ec, dhkeyblob, &bloblen)) {
log2(group->group_id,0,"Error exporting proxy ECDH public key");
return 0;
}
if (bloblen != ntohs(encinfo->dhlen)) {
log2(group->group_id, 0,
"Incorrect exported proxy ECDH key size");
return 0;
}
}
}
return 1;
}
/**
* Handles an incoming ANNOUNCE message from a server.
* Sets up encryption if specified and forwards message.
*/
void handle_announce(struct pr_group_list_t *group,
const union sockaddr_u *src, unsigned char *packet,
unsigned packetlen)
{
struct uftp_h *header;
struct announce_h *announce;
char pubname[INET6_ADDRSTRLEN], privname[INET6_ADDRSTRLEN];
int rval;
header = (struct uftp_h *)packet;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
if ((packetlen < sizeof(struct uftp_h) + (announce->hlen * 4)) ||
((announce->hlen * 4) < sizeof(struct announce_h))) {
log1(group->group_id, 0, "Rejecting ANNOUNCE from %08X: "
"invalid message size", ntohl(header->src_id));
return;
}
if (group == NULL) {
if ((group = find_open_slot()) == NULL ) {
log1(ntohl(header->group_id), 0, "Error: maximum number of "
"incoming files exceeded: %d\n", MAXLIST);
return;
}
if (!read_announce(group, packet, src, packetlen)) {
return;
}
if ((rval = getnameinfo((struct sockaddr *)&group->publicmcast,
family_len(group->publicmcast), pubname, sizeof(pubname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
if ((rval = getnameinfo((struct sockaddr *)&group->privatemcast,
family_len(group->privatemcast), privname, sizeof(privname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log0(group->group_id, 0, "Received request from %08X",
ntohl(group->src_id));
log1(group->group_id, 0, "Using public multicast address %s", pubname);
log1(group->group_id, 0, "Using private multicast address %s",privname);
if (!addr_blank(&group->privatemcast) && (proxy_type != CLIENT_PROXY)) {
if (server_fp_count) {
if (!is_multicast(&group->privatemcast, 1)) {
log2(group->group_id, 0,
"Invalid source specific multicast address: %s",
privname);
send_upstream_abort(group, 0,
"Invalid source specific multicast address");
return;
}
} else {
if (!is_multicast(&group->privatemcast, 0)) {
log2(group->group_id, 0, "Invalid multicast address: %s",
privname);
send_upstream_abort(group, 0, "Invalid multicast address");
return;
}
}
if (!multicast_join(listener, group->group_id, &group->privatemcast,
m_interface, interface_count, server_fp, server_fp_count)) {
send_upstream_abort(group, 0, "Error joining multicast group");
return;
}
group->multi_join = 1;
}
group->phase = PR_PHASE_REGISTERED;
}
if (insert_pubkey_in_announce(group, packet, packetlen)) {
forward_message(group, src, packet, packetlen);
}
}
/**
* 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(struct pr_group_list_t *group, const unsigned char *message,
unsigned meslen)
{
const struct regconf_h *regconf;
const uint32_t *addrlist;
int hostidx, idx, addrcnt;
struct pr_destinfo_t *dest;
regconf = (const struct regconf_h *)message;
addrlist = (const uint32_t *)(message + (regconf->hlen * 4));
addrcnt = (meslen - (regconf->hlen * 4)) / 4;
if ((meslen < (regconf->hlen * 4U)) ||
((regconf->hlen * 4U) < sizeof(struct regconf_h))) {
log1(group->group_id, 0,
"Rejecting REG_CONF from server: invalid message size");
return;
}
log2(group->group_id, 0, "Received REG_CONF");
for (idx = 0; idx < addrcnt; idx++) {
hostidx = find_client(group, addrlist[idx]);
if (hostidx != -1) {
dest = &group->destinfo[hostidx];
log2(group->group_id, 0, " for %s", dest->name);
if (dest->state != PR_CLIENT_READY) {
dest->state = PR_CLIENT_CONF;
}
}
}
if (group->phase == PR_PHASE_READY) {
send_keyinfo(group, addrlist, addrcnt);
}
set_timeout(group, 0, 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(struct pr_group_list_t *group, unsigned char *message,
unsigned meslen, uint32_t src_id)
{
struct keyinfo_h *keyinfo_hdr;
struct destkey *keylist;
unsigned explen, declen;
int i, keyidx, len, keycount, unauth_keytype, unauth_keylen, unauth_ivlen;
uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv;
uint64_t ivctr;
keyinfo_hdr = (struct keyinfo_h *)message;
keylist = (struct destkey *)(message + (keyinfo_hdr->hlen * 4));
keycount = (meslen - (keyinfo_hdr->hlen * 4)) / sizeof(struct destkey);
if ((meslen < (keyinfo_hdr->hlen * 4U)) ||
((keyinfo_hdr->hlen * 4U) < sizeof(struct keyinfo_h))) {
log1(group->group_id, 0,
"Rejecting KEYINFO from server: invalid message size");
return;
}
if (group->keytype == KEY_NONE) {
log1(group->group_id, 0,
"Rejecting KEYINFO from server: encryption not enabled");
return;
}
for (i = 0, keyidx = -1; (i < keycount) && (keyidx == -1); i++) {
if (uid == keylist[i].dest_id) {
keyidx = i;
break;
}
}
// Don't use a cipher in an authentication mode to decrypt the group master
unauth_keytype = unauth_key(group->keytype);
get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);
if (keyidx != -1) {
log2(group->group_id, 0, "Received KEYINFO");
if (group->phase != PR_PHASE_REGISTERED) {
// We already got the KEYINFO, so no need to reprocess.
// Just resend the INFO_ACK and reset the timeout
send_keyinfo_ack(group);
return;
}
iv = safe_calloc(unauth_ivlen, 1);
ivctr = ntohl(keyinfo_hdr->iv_ctr_lo);
ivctr |= (uint64_t)ntohl(keyinfo_hdr->iv_ctr_hi) << 32;
build_iv(iv, group->salt, unauth_ivlen, uftp_htonll(ivctr), src_id);
if (!decrypt_block(unauth_keytype, iv, group->key, NULL, 0,
keylist[keyidx].groupmaster, MASTER_LEN,
decgroupmaster, &declen) ||
(declen != MASTER_LEN - 1)) {
log2(group->group_id, 0, "Decrypt failed for group master");
send_upstream_abort(group, 0, "Decrypt failed for group master");
free(iv);
return;
}
free(iv);
group->groupmaster[0] = group->version;
memcpy(&group->groupmaster[1], decgroupmaster, declen);
explen = group->keylen + SALT_LEN + group->hmaclen;
prf_buf = safe_calloc(explen + group->hmaclen, 1);
PRF(group->hashtype, explen, group->groupmaster,
sizeof(group->groupmaster), "key expansion",
group->rand1, sizeof(group->rand1), prf_buf, &len);
memcpy(group->grouphmackey, prf_buf, group->hmaclen);
memcpy(group->groupkey, prf_buf + group->hmaclen, group->keylen);
memcpy(group->groupsalt, prf_buf + group->hmaclen + group->keylen,
SALT_LEN);
free(prf_buf);
group->phase = PR_PHASE_READY;
// Respond to server, then send any pending REG_CONFs as KEYINFO
send_keyinfo_ack(group);
send_keyinfo(group, NULL, 0);
}
}
/**
* Sends a REGISTER to the server for all pending clients.
*/
void send_register(struct pr_group_list_t *group, int pendidx)
{
struct uftp_h *header;
struct register_h *reg;
unsigned char *buf, *keydata;
uint32_t *addrlist;
unsigned int len, meslen, destcount;
struct timeval now, send_time;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
reg = (struct register_h *)(buf + sizeof(struct uftp_h));
keydata = (unsigned char *)reg + sizeof(struct register_h);
set_uftp_header(header, REGISTER, group);
reg->func = REGISTER;
if (group->keytype != KEY_NONE) {
memcpy(reg->rand2, group->rand2, RAND_LEN);
if (group->keyextype == KEYEX_RSA) {
if (!RSA_encrypt(group->server_pubkey.rsa, group->premaster,
group->premaster_len, keydata, &len)) {
log2(group->group_id, 0, "Error encrypting premaster secret");
send_upstream_abort(group, 0,
"Error encrypting premaster secret");
free(buf);
return;
}
} else {
uint16_t keylen;
if (!export_EC_key(group->proxy_dhkey.ec, keydata, &keylen)) {
log2(group->group_id, 0, "Error exporting ECDH public key");
send_upstream_abort(group,0, "Error exporting ECDH public key");
free(buf);
return;
}
len = keylen;
}
reg->keyinfo_len = htons(len);
} else {
len = 0;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->pending[pendidx].rx_tstamp) <= 0) {
send_time = group->pending[pendidx].tstamp;
} else {
send_time = add_timeval(group->pending[pendidx].tstamp,
diff_timeval(now, group->pending[pendidx].rx_tstamp));
}
reg->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
reg->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
addrlist = (uint32_t *)(keydata + len);
reg->hlen = (sizeof(struct register_h) + len) / 4;
destcount = load_pending(group, pendidx, REGISTER, addrlist,
max_msg_dest(group, REGISTER, reg->hlen * 4));
meslen = sizeof(struct uftp_h) + (reg->hlen * 4) + (destcount * 4);
if (nb_sendto(listener, buf, meslen, 0, (struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error sending REGISTER");
} else {
log2(group->group_id, 0, "REGISTER sent");
}
if (group->client_auth) {
send_clientkey(group);
}
set_timeout(group, 1, 0);
free(buf);
}
/**
* Sends a CLIENT_KEY message to the server if requested.
*/
void send_clientkey(struct pr_group_list_t *group)
{
struct uftp_h *header;
struct client_key_h *client_key;
unsigned char *buf, *keyblob, *verify;
uint8_t *verifydata;
unsigned int siglen, meslen;
uint16_t bloblen;
int verifylen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h));
keyblob = (unsigned char *)client_key + sizeof(struct client_key_h);
verifydata = build_verify_data(group, -1, &verifylen, 0);
if (!verifydata) {
log2(group->group_id, 0, "Error getting verify data");
send_upstream_abort(group, 0, "Error getting verify data");
goto end;
}
set_uftp_header(header, CLIENT_KEY, group);
client_key->func = CLIENT_KEY;
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(group->proxy_privkey.rsa, keyblob, &bloblen)) {
log2(group->group_id, 0, "Error exporting public key");
send_upstream_abort(group, 0, "Error exporting public key");
goto end;
}
verify = keyblob + bloblen;
if (!create_RSA_sig(group->proxy_privkey.rsa, group->hashtype,
verifydata, verifylen, verify, &siglen) ||
(siglen > group->proxy_privkeylen)) {
log2(group->group_id, 0, "Error signing verify data");
send_upstream_abort(group, 0, "Error signing verify data");
goto end;
}
} else {
if (!export_EC_key(group->proxy_privkey.ec, keyblob, &bloblen)) {
log2(group->group_id, 0, "Error exporting public key");
send_upstream_abort(group, 0, "Error exporting public key");
goto end;
}
verify = keyblob + bloblen;
if (!create_ECDSA_sig(group->proxy_privkey.ec, group->hashtype,
verifydata, verifylen, verify, &siglen)) {
log2(group->group_id, 0, "Error signing verify data");
send_upstream_abort(group, 0, "Error signing verify data");
goto end;
}
}
client_key->bloblen = htons(bloblen);
client_key->siglen = htons(siglen);
client_key->hlen = (sizeof(struct client_key_h) + bloblen + siglen) / 4;
meslen = sizeof(struct uftp_h) + (client_key->hlen * 4);
if (nb_sendto(listener, buf, meslen, 0, (struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error sending CLIENT_KEY");
} else {
log2(group->group_id, 0, "CLIENT_KEY sent");
}
end:
free(verifydata);
free(buf);
}
/**
* Sends an KEYINFO_ACK to the server in response to a KEYINFO
*/
void send_keyinfo_ack(struct pr_group_list_t *group)
{
unsigned char *buf, *encrypted;
struct uftp_h *header;
struct keyinfoack_h *keyinfo_ack;
unsigned char *verifydata, *verify_hash, *verify_val;
unsigned int payloadlen, hashlen;
int verifylen, len, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
keyinfo_ack = (struct keyinfoack_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, KEYINFO_ACK, group);
keyinfo_ack->func = KEYINFO_ACK;
keyinfo_ack->hlen = sizeof(struct keyinfoack_h) / 4;
verifydata = build_verify_data(group, -1, &verifylen, 1);
if (!verifydata) {
log2(group->group_id, 0, "Error getting verify data");
send_upstream_abort(group, 0, "Error getting verify data");
free(buf);
return;
}
verify_hash = safe_calloc(group->hmaclen, 1);
verify_val = safe_calloc(VERIFY_LEN + group->hmaclen, 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_val, &len);
memcpy(keyinfo_ack->verify_data, verify_val, VERIFY_LEN);
free(verifydata);
free(verify_hash);
free(verify_val);
payloadlen = sizeof(struct keyinfoack_h);
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt, &group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen,
group->sigtype, group->keyextype, group->proxy_privkey,
group->proxy_privkeylen)) {
log2(group->group_id, 0, "Error encrypting KEYINFO_ACK");
free(buf);
return;
}
payloadlen = enclen + sizeof(struct uftp_h);
if (nb_sendto(listener, encrypted, payloadlen, 0,
(struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error sending KEYINFO_ACK");
} else {
log2(group->group_id, 0, "KEYINFO_ACK sent");
}
set_timeout(group, 0, 0);
free(encrypted);
free(buf);
}
/**
* Sends a FILEINFO_ACK to the server for all pending clients
*/
void send_fileinfo_ack(struct pr_group_list_t *group, int pendidx)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct fileinfoack_h *fileinfo_ack;
struct pr_pending_info_t *pending;
unsigned int payloadlen;
int destcount, enclen;
uint32_t *addrlist;
struct timeval now, send_time;
buf = safe_calloc(MAXMTU, 1);
pending = &group->pending[pendidx];
header = (struct uftp_h *)buf;
fileinfo_ack = (struct fileinfoack_h *)(buf + sizeof(struct uftp_h));
addrlist =(uint32_t *)((char *)fileinfo_ack + sizeof(struct fileinfoack_h));
payloadlen = sizeof(struct fileinfoack_h);
set_uftp_header(header, FILEINFO_ACK, group);
fileinfo_ack->func = FILEINFO_ACK;
fileinfo_ack->hlen = sizeof(struct fileinfoack_h) / 4;
fileinfo_ack->file_id = htons(pending->file_id);
if (pending->partial) {
fileinfo_ack->flags |= FLAG_PARTIAL;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->pending[pendidx].rx_tstamp) <= 0) {
send_time = group->pending[pendidx].tstamp;
} else {
send_time = add_timeval(group->pending[pendidx].tstamp,
diff_timeval(now, group->pending[pendidx].rx_tstamp));
}
fileinfo_ack->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
fileinfo_ack->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
destcount = load_pending(group, pendidx, FILEINFO_ACK, addrlist,
max_msg_dest(group, FILEINFO_ACK, fileinfo_ack->hlen * 4));
payloadlen += destcount * sizeof(uint32_t);
if (group->keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->proxy_privkey, group->proxy_privkeylen)) {
log2(group->group_id, pending->file_id,
"Error encrypting FILEINFO_ACK");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, pending->file_id,
"Error sending FILEINFO_ACK");
} else {
log2(group->group_id, pending->file_id, "FILEINFO_ACK sent");
}
set_timeout(group, 1, 0);
free(encrypted);
free(buf);
}
/**
* Counts the pending naks for the given group
*/
int count_naks(struct pr_group_list_t *group, int pendidx)
{
unsigned nak_count, i;
for (nak_count = 0, i = 0; i < group->blocksize * 8; i++) {
if ((group->pending[pendidx].naklist[i >> 3] & (1 << (i & 7))) != 0) {
nak_count++;
}
}
// Highly verbose debugging -- print aggregate NAKs before sending
if (log_level >= 5) {
for (i = 0; i < group->blocksize; i++) {
sclog5("%02X ", group->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(struct pr_group_list_t *group, 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;
struct pr_destinfo_t *dest;
int hostidx, payloadlen, enclen, nak_count;
buf = safe_calloc(MAXMTU, 1);
pending = &group->pending[pendidx];
// Since a STATUS doesn't contain a host list, we do this simplified
// cleanup instead of calling load_pending
for (hostidx = 0; hostidx < group->destcount; hostidx++) {
dest = &group->destinfo[hostidx];
if (dest->pending == pendidx) {
dest->pending = -1;
}
}
group->pending[pendidx].count = 0;
group->pending[pendidx].msg = 0;
header = (struct uftp_h *)buf;
status = (struct status_h *)(buf + sizeof(struct uftp_h));
nak_count = count_naks(group, pendidx);
set_uftp_header(header, STATUS, group);
status->func = STATUS;
status->hlen = sizeof(struct status_h) / 4;
status->file_id = htons(pending->file_id);
status->section = htons(pending->section);
payloadlen = group->blocksize;
sent_naks = (unsigned char *)status + sizeof(struct status_h);
memcpy(sent_naks, pending->naklist, payloadlen);
memset(pending->naklist, 0, payloadlen);
payloadlen += sizeof(struct status_h);
if (group->keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->proxy_privkey, group->proxy_privkeylen)) {
log2(group->group_id, pending->file_id, "Error encrypting STATUS");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, pending->file_id, "Error sending STATUS");
} else {
log2(group->group_id, pending->file_id,
"Sent %d NAKs for section %d", nak_count, pending->section);
}
set_timeout(group, 1, 0);
free(buf);
free(encrypted);
}
/**
* Sends a COMPLETE to the server for all pending clients.
*/
void send_complete(struct pr_group_list_t *group, 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, enclen;
buf = safe_calloc(MAXMTU, 1);
pending = &group->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, group);
complete->func = COMPLETE;
complete->hlen = sizeof(struct complete_h) / 4;
complete->file_id = htons(pending->file_id);
complete->status = pending->comp_status;
destcount = load_pending(group, pendidx, COMPLETE, addrlist,
max_msg_dest(group, COMPLETE, complete->hlen * 4));
payloadlen = sizeof(struct complete_h) + (destcount * sizeof(uint32_t));
if (group->keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->proxy_privkey, group->proxy_privkeylen)) {
log2(group->group_id, pending->file_id,
"Error encrypting COMPLETE");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, pending->file_id, "Error sending COMPLETE");
} else {
log2(group->group_id, pending->file_id, "Sent COMPLETE");
}
set_timeout(group, 1, 0);
free(buf);
free(encrypted);
}
uftp-4.1.5/client_common.c 0000644 0000764 0000764 00000114602 12250244021 014471 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
/**
* Look for a given group in the global group list
* Returns a pointer to the group in the list, or NULL if not found
*/
struct group_list_t *find_group(uint32_t group_id, uint8_t group_inst)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if ((group_list[i].group_id == group_id) &&
(group_list[i].group_inst == group_inst)) {
return &group_list[i];
}
}
return NULL;
}
/**
* Looks for the uid in a list of addresses.
* Returns 1 if found, 0 if not found
*/
int uid_in_list(const uint32_t *addrlist, int size)
{
int i;
for (i = 0; i < size; i++) {
if (addrlist[i] == 0) {
return 0;
}
if (uid == addrlist[i]) {
return 1;
}
}
return 0;
}
/**
* Reads in the contents of the restart file.
*/
void read_restart_file(struct group_list_t *group)
{
struct client_restart_t *restart;
char restart_name[MAXPATHNAME];
int fd, i, rval;
// Don't bother if we're not using a temp directory.
if (!strcmp(tempdir, "")) {
return;
}
// First abort any prior session with the same group_id.
// This creates the restart file.
for (i = 0; i < MAXLIST; i++) {
if ((group_list[i].group_id == group->group_id) &&
(group_list[i].group_inst < group->group_inst)) {
file_cleanup(&group_list[i], 1);
}
}
log1(group->group_id, 0, "Reading restart file");
snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
tempdir, PATH_SEP, group->group_id);
if ((fd = open(restart_name, OPENREAD, 0644)) == -1) {
syserror(group->group_id, 0, "Failed to read restart file");
return;
}
// Read header
restart = safe_calloc(sizeof(struct client_restart_t), 1);
if ((rval = file_read(fd, restart, sizeof(struct client_restart_t),
0)) == -1) {
log0(group->group_id, 0, "Failed to read header for restart file");
goto err1;
}
if (rval != sizeof(struct client_restart_t)) {
log0(group->group_id, 0,
"Failed to read header for restart file (read %d, expected %d)",
rval, sizeof(struct client_restart_t));
goto err1;
}
// Read NAK list
if (restart->blocks) {
restart->naklist = safe_calloc(restart->blocks, 1);
if (file_read(fd, restart->naklist, restart->blocks, 0) == -1) {
log0(group->group_id,0, "Failed to read NAK list for restart file");
goto err2;
}
}
// Read section_done list
if (restart->sections) {
restart->section_done = safe_calloc(restart->sections, 1);
if (file_read(fd, restart->section_done, restart->sections, 0) == -1) {
log0(group->group_id, 0,
"Failed to read section_done list for restart file");
goto err3;
}
}
close(fd);
unlink(restart_name);
group->restartinfo = restart;
log1(group->group_id, 0, "Reading restart file done");
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(struct group_list_t *group)
{
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->group_id, 0, "Writing restart file");
memset(&restart, 0, sizeof(restart));
fileinfo = &group->fileinfo;
if (group->phase != PHASE_MIDGROUP) {
restart.blocks = fileinfo->blocks;
restart.sections = fileinfo->sections;
restart.size = fileinfo->size;
strncpy(restart.name, fileinfo->name, sizeof(restart.name));
restart.name[sizeof(restart.name)-1] = '\x0';
}
snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
tempdir, PATH_SEP, group->group_id);
if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC, 0644)) == -1) {
syserror(group->group_id, 0, "Failed to create restart file");
return;
}
if (file_write(fd, &restart, sizeof(restart)) == -1) {
log0(group->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->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->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(struct group_list_t *group)
{
int i;
for (i = 0; i < pub_multi_count; i++) {
if (!memcmp(&group->multi, &pub_multi[i], sizeof(union sockaddr_u))) {
return 1;
}
}
for (i = 0; i < MAXLIST; i++) {
if (group_list[i].group_id == 0) {
return 0;
}
if ((&group_list[i] != group) && (!memcmp(&group->multi,
&group_list[i].multi, sizeof(union sockaddr_u)))) {
return 1;
}
}
return 0;
}
/**
* Run the postreceive script on list of received files
*/
void run_postreceive_multi(struct group_list_t *group, char *const *files,
int count)
{
char **params;
char gid_str[10];
char gid_param[] = "-I";
int i;
if (!strcmp(postreceive, "")) {
return;
}
params = safe_calloc(count + 4, sizeof(char *));
snprintf(gid_str, sizeof(gid_str), "%08X", group->group_id);
params[0] = postreceive;
params[1] = gid_param;
params[2] = gid_str;
for (i = 0; i < count; i++) {
params[i+3] = files[i];
}
params[count+4-1] = NULL;
if (log_level >= 2) {
clog2(group->group_id, group->file_id,
"Running postreceive: %s", postreceive);
for (i = 1; i < count + 3; i++) {
sclog2(" %s", params[i]);
}
slog2("");
}
#ifdef WINDOWS
{
char cmdline[0x8000]; // Windows max command line length
char cmdexe[MAXPATHNAME];
int too_long, rval, is_cmd;
strcpy(cmdline, "");
if ((!strncmp(&postreceive[strlen(postreceive)-4], ".cmd", 4)) ||
(!strncmp(&postreceive[strlen(postreceive)-4], ".bat", 4))) {
is_cmd = 1;
if (!GetEnvironmentVariable("SystemRoot", cmdexe, sizeof(cmdexe))) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
0, errbuf, sizeof(errbuf), NULL);
log0(group->group_id, group->file_id,
"Error getting sysroot: (%d) %s", GetLastError(), errbuf);
free(params);
return;
}
strcat(cmdexe, "\\system32\\cmd.exe");
strcat(cmdline, "/c \"");
} else {
is_cmd = 0;
}
for (too_long = 0, i = 0; i < count + 3; i++) {
int size = 0x8000 - strlen(cmdline);
if (size <= (int)strlen(params[i]) + 4) {
too_long = 1;
break;
}
// Quote everything except -I {group_id}
if (i == 1 || i == 2) {
strcat(cmdline, params[i]);
strcat(cmdline," ");
} else {
strcat(cmdline, "\"");
strcat(cmdline, params[i]);
strcat(cmdline,"\" ");
}
}
if (is_cmd) {
strcat(cmdline, "\"");
}
if (!too_long) {
STARTUPINFO startup_info;
PROCESS_INFORMATION proc_info;
GetStartupInfo(&startup_info);
rval = CreateProcess(is_cmd ? cmdexe : postreceive, cmdline,
NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL,
&startup_info, &proc_info);
if (!rval) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
0, errbuf, sizeof(errbuf), NULL);
log0(group->group_id, group->file_id,
"Error running script: (%d) %s", GetLastError(), errbuf);
}
}
}
#else
{
pid_t pid;
if ((pid = fork()) == -1) {
syserror(0, 0, "fork failed");
} else if (pid == 0) {
close(listener);
close(1);
close(2);
execv(postreceive, params);
syserror(0, 0, "exec failed");
exit(1);
}
}
#endif
free(params);
}
/**
* Run the postreceive script on a received file
*/
void run_postreceive(struct group_list_t *group, char *file)
{
char *files[] = { file };
run_postreceive_multi(group, files, 1);
}
/**
* 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(struct group_list_t *group, int abort_session)
{
if (group->fileinfo.fd >= 0) {
log2(group->group_id, group->file_id, "starting file close");
close(group->fileinfo.fd);
log2(group->group_id, group->file_id, "done file close");
group->fileinfo.fd = -1;
if (abort_session && !strcmp(tempdir, "")) {
if (tempfile) {
unlink(group->fileinfo.temppath);
} else {
unlink(group->fileinfo.filepath);
}
} else {
if (tempfile) {
move_to_backup(group);
if (rename(group->fileinfo.temppath,
group->fileinfo.filepath) == -1) {
syserror(group->group_id, group->file_id,
"Couldn't rename from %s to %s",
group->fileinfo.temppath,group->fileinfo.filepath);
}
}
if (group->fileinfo.tstamp) {
utim_buf utbuf;
utbuf.actime = group->fileinfo.tstamp;
utbuf.modtime = group->fileinfo.tstamp;
if (utime(group->fileinfo.filepath, &utbuf) == -1) {
syserror(group->group_id, group->file_id, "utime failed");
}
}
}
}
if (abort_session || (group->file_id == 0)) {
if (!addr_blank(&group->multi) && !other_mcast_users(group) &&
group->multi_join) {
if (server_count > 0) {
multicast_leave(listener, group->group_id, &group->multi,
m_interface, interface_count, server_keys,server_count);
if (has_proxy) {
multicast_leave(listener, group->group_id, &group->multi,
m_interface, interface_count, &proxy_info, 1);
}
} else {
multicast_leave(listener, group->group_id, &group->multi,
m_interface, interface_count, NULL, 0);
}
}
if (group->server_pubkey.key) {
if (group->keyextype == KEYEX_ECDH_ECDSA) {
free_EC_key(group->server_pubkey.ec);
} else {
free_RSA_key(group->server_pubkey.rsa);
}
}
if (group->server_dhkey.key) {
free_EC_key(group->server_dhkey.ec);
free_EC_key(group->client_dhkey.ec);
}
if (group->restartinfo &&
(strcmp(group->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->group_id, PATH_SEP,
group->restartinfo->name);
unlink(filepath);
}
if (abort_session) {
write_restart_file(group);
}
free(group->loss_history);
free(group->fileinfo.naklist);
free(group->fileinfo.section_done);
if (group->restartinfo) {
free(group->restartinfo->naklist);
free(group->restartinfo->section_done);
free(group->restartinfo);
}
memset(group, 0, sizeof(struct group_list_t));
} else {
// Don't clear the file_id in case we need to respond to late DONEs
if (!strcmp(tempdir, "")) {
run_postreceive(group, group->fileinfo.filepath);
}
group->phase = PHASE_MIDGROUP;
set_timeout(group, 0);
free(group->fileinfo.naklist);
free(group->fileinfo.section_done);
group->fileinfo.naklist = NULL;
group->fileinfo.section_done = NULL;
}
}
/**
* Initializes the uftp header of an outgoing packet
*/
void set_uftp_header(struct uftp_h *header, int func,
struct group_list_t *group)
{
header->version = group->version;
header->func = func;
header->seq = htons(group->send_seq++);
header->group_id = htonl(group->group_id);
header->group_inst = group->group_inst;
header->src_id = uid;
}
/**
* Sets the timeout time for a given group list member
*/
void set_timeout(struct group_list_t *group, int rescale)
{
if (!rescale) {
gettimeofday(&group->start_timeout_time, NULL);
}
group->timeout_time = group->start_timeout_time;
switch (group->phase) {
case PHASE_REGISTERED:
add_timeval_d(&group->timeout_time, 4 * group->grtt);
break;
case PHASE_RECEIVING:
case PHASE_MIDGROUP:
if (group->robust * group->grtt < 1.0) {
add_timeval_d(&group->timeout_time, 1.0);
} else {
add_timeval_d(&group->timeout_time, group->robust * group->grtt);
}
break;
case PHASE_COMPLETE:
add_timeval_d(&group->timeout_time, 4 * group->grtt);
break;
}
}
/**
* Sends an ABORT message to a server
*/
void send_abort(struct group_list_t *group, const char *message)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct abort_h *abort_hdr;
int payloadlen, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, group);
abort_hdr->func = ABORT;
abort_hdr->hlen = sizeof(struct abort_h) / 4;
abort_hdr->host = 0;
strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1);
payloadlen = sizeof(struct abort_h);
if ((group->phase != PHASE_REGISTERED) &&
(group->keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->client_privkey, group->client_privkeylen)) {
log0(group->group_id, group->file_id, "Error encrypting ABORT");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->replyaddr,
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending ABORT");
}
file_cleanup(group, 1);
free(buf);
free(encrypted);
}
/**
* Handles an ABORT message from a server
*/
void handle_abort(struct group_list_t *group, const unsigned char *message,
unsigned meslen)
{
const struct abort_h *abort_hdr;
int found;
abort_hdr = (const struct abort_h *)message;
if (meslen < (abort_hdr->hlen * 4U) ||
((abort_hdr->hlen * 4U) < sizeof(struct abort_h))) {
log1(group->group_id, group->file_id,
"Rejecting ABORT from server: invalid message size");
return;
}
found = 0;
if (abort_hdr->host == 0) {
if (((abort_hdr->flags & FLAG_CURRENT_FILE) != 0) &&
(group->phase == PHASE_MIDGROUP)) {
found = 0;
} else {
found = 1;
}
} else if (abort_hdr->host == uid) {
found = 1;
}
if (found) {
log0(group->group_id, group->file_id,
"Transfer aborted by server: %s", abort_hdr->message);
file_cleanup(group, 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;
union sockaddr_u proxyaddr;
char addrname[INET6_ADDRSTRLEN];
int meslen, rval;
packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct key_req_h), 1);
header = (struct uftp_h *)packet;
keyreq = (struct key_req_h *)(packet + sizeof(struct uftp_h));
header->version = UFTP_VER_NUM;
header->func = KEY_REQ;
header->src_id = uid;
keyreq->func = KEY_REQ;
keyreq->hlen = sizeof(struct key_req_h) / 4;
meslen = sizeof(struct uftp_h) + sizeof(struct key_req_h);
proxyaddr = proxy_info.addr;
if (nb_sendto(listener, packet, meslen, 0,
(struct sockaddr *)&proxyaddr,
family_len(proxyaddr)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending KEY_REQ");
} else {
if ((rval = getnameinfo((struct sockaddr *)&proxyaddr,
family_len(proxyaddr), addrname, sizeof(addrname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Sent KEY_REQ to %s:%s", addrname, portname);
}
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 union sockaddr_u *src,
unsigned char *message, unsigned meslen)
{
struct proxy_key_h *proxykey;
unsigned char *keyblob, *dhblob, *sig;
unsigned char fingerprint[HMAC_LEN];
unsigned int fplen, keylen, dhlen, siglen;
char addrname[INET6_ADDRSTRLEN];
int rval;
proxykey = (struct proxy_key_h *)message;
if (meslen < (proxykey->hlen * 4U) ||
((proxykey->hlen * 4U) < sizeof(struct proxy_key_h) +
ntohs(proxykey->bloblen) + ntohs(proxykey->dhlen) +
ntohs(proxykey->siglen))) {
log2(0, 0, "Rejecting PROXY_KEY: invalid message size");
return;
}
if ((rval = getnameinfo((const struct sockaddr *)src,
family_len(*src), addrname, sizeof(addrname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Received PROXY_KEY from %s", addrname);
if (!has_proxy) {
log2(0, 0, "No reply proxy specified");
return;
}
if (!addr_equal(&proxy_info.addr, src)) {
log2(0, 0, "PROXY_KEY not from specified reply proxy");
return;
}
keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
keylen = ntohs(proxykey->bloblen);
dhblob = keyblob + keylen;
dhlen = ntohs(proxykey->dhlen);
sig = dhblob + dhlen;
siglen = ntohs(proxykey->siglen);
if (keyblob[0] == KEYBLOB_RSA) {
if (!import_RSA_key(&proxy_pubkey.rsa, keyblob, keylen)) {
log0(0, 0, "Failed to import public key from PROXY_KEY");
return;
}
if (proxy_info.has_fingerprint) {
hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen);
if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) {
log0(0, 0, "Failed to verify PROXY_KEY fingerprint");
free_RSA_key(proxy_pubkey.rsa);
return;
}
}
if (!verify_RSA_sig(proxy_pubkey.rsa, HASH_SHA1,
(unsigned char *)&proxykey->nonce,
sizeof(proxykey->nonce), sig, siglen)) {
log0(0, 0, "Failed to verify PROXY_KEY signature");
free_RSA_key(proxy_pubkey.rsa);
return;
}
} else {
if (!import_EC_key(&proxy_pubkey.ec, keyblob, keylen, 0)) {
log0(0, 0, "Failed to import public key from PROXY_KEY");
return;
}
if (proxy_info.has_fingerprint) {
hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen);
if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) {
log0(0, 0, "Failed to verify PROXY_KEY fingerprint");
free_RSA_key(proxy_pubkey.rsa);
return;
}
}
if (!verify_ECDSA_sig(proxy_pubkey.ec, HASH_SHA1,
(unsigned char *)&proxykey->nonce,
sizeof(proxykey->nonce), sig, siglen)) {
log0(0, 0, "Failed to verify PROXY_KEY signature");
free_RSA_key(proxy_pubkey.rsa);
return;
}
}
if (dhlen) {
if (!import_EC_key(&proxy_dhkey.ec, dhblob, dhlen, 1)) {
log0(0, 0, "Failed to import ECDH public key from PROXY_KEY");
return;
}
}
}
/**
* Removes a full path from disk
*/
void clear_path(const char *path, struct group_list_t *group)
{
stat_struct statbuf;
char filename[MAXPATHNAME];
int len;
if (lstat_func(path, &statbuf) == -1) {
if (errno != ENOENT) {
syserror(group->group_id, group->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->group_id, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
syserror(group->group_id, group->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->group_id, group->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, group);
}
} while (_findnexti64(ffhandle, &finfo) == 0);
_findclose(ffhandle);
#else
DIR *dir;
struct dirent *de;
if ((dir = opendir(path)) == NULL) {
syserror(group->group_id, group->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->group_id, group->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, group);
}
}
if (errno && (errno != ENOENT)) {
syserror(group->group_id, group->file_id,
"Failed to read directory %s", path);
}
closedir(dir);
#endif
if (rmdir(path) == -1) {
syserror(group->group_id, group->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(struct group_list_t *group)
{
stat_struct statbuf;
char backup_file[MAXBACKUPPATHNAME], *trim_name;
int len;
if (lstat_func(group->fileinfo.filepath, &statbuf) == -1) {
return;
}
if (backupcnt == 0) {
clear_path(group->fileinfo.filepath, group);
return;
}
#ifdef WINDOWS
if ((group->fileinfo.filepath[1] == ':') &&
(group->fileinfo.filepath[2] == '\\')) {
trim_name = &group->fileinfo.filepath[3];
} else {
trim_name = group->fileinfo.filepath;
}
#else
trim_name = group->fileinfo.filepath;
#endif
len = snprintf(backup_file, sizeof(backup_file), "%s%c%s%c%s%c%s",
backupdir[group->fileinfo.destdiridx], PATH_SEP,
group->start_date, PATH_SEP,
group->start_time, PATH_SEP, trim_name);
if (len >= sizeof(backup_file)) {
log0(group->group_id, group->file_id,
"Max pathname length exceeded for backup file, deleting",
group->fileinfo.filepath);
clear_path(group->fileinfo.filepath, group);
return;
}
clear_path(backup_file, group);
if (!create_path_to_file(group, backup_file)) {
log0(group->group_id, group->file_id,
"Error creating path to backup file");
clear_path(group->fileinfo.filepath, group);
}
#ifdef WINDOWS
if (!MoveFile(group->fileinfo.filepath, backup_file)) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
log0(group->group_id, group->file_id,
"Couldn't rename from %s to %s, deleting: (%d): %s",
group->fileinfo.filepath, backup_file, GetLastError(), errbuf);
clear_path(group->fileinfo.filepath, group);
} else {
log1(group->group_id, group->file_id,
"Backed up existing file to %s", backup_file);
}
#else
if (rename(group->fileinfo.filepath, backup_file) == -1) {
syserror(group->group_id, group->file_id,
"Couldn't rename from %s to %s, deleting",
group->fileinfo.filepath, backup_file);
clear_path(group->fileinfo.filepath, group);
} else {
log1(group->group_id, group->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(struct group_list_t *group, const char *filename)
{
char *dir, *base;
stat_struct statbuf;
int rval;
split_path(filename, &dir, &base);
if (!dir) {
log1(group->group_id, group->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->group_id, group->file_id,
"Failed to delete path element %s", dir);
rval = 0;
goto end;
}
if (mkdir(dir, 0755) == -1) {
syserror(group->group_id, group->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(group, dir)) {
rval = 0;
goto end;
}
if (mkdir(dir, 0755) == -1) {
syserror(group->group_id, group->file_id,
"Failed to create path element %s", dir);
rval = 0;
goto end;
}
}
rval = 1;
end:
free(dir);
free(base);
return rval;
}
/**
* Updates the group's loss history
*
* Packets older than MAXMISORDER sequence numbers don't change the loss
* history, and packets aren't considered lost unless the sequence number is
* more than MAXMISORDER sequence numbers old. Works under the assumption
* that no more than 32K packets in a row get lost.
*/
void update_loss_history(struct group_list_t *group, uint16_t txseq, int size)
{
uint16_t i, count;
uint32_t i_long;
int tdiff, bytes, avgbytes, rate;
struct timeval tvdiff;
int grtt_usec;
group->loss_history[txseq].found = 1;
gettimeofday(&group->loss_history[txseq].t, NULL);
if (group->multi.ss.ss_family == AF_INET6) {
group->loss_history[txseq].size = size + 8 + 40;
} else {
group->loss_history[txseq].size = size + 8 + 20;
}
if ((int16_t)(txseq - group->max_txseq) > 0) {
log4(group->group_id, 0, "Got seq %d, max was %d",
txseq, group->max_txseq);
grtt_usec = (int)(group->grtt * 1000000);
if (txseq < group->max_txseq) {
log5(group->group_id, 0, "increasing seq_wrap, txseq=%u, maxseq=%u",
txseq, group->max_txseq);
group->seq_wrap++;
}
// First set nominal arrival times of missed packets
for (i = group->max_txseq + 1; i != txseq; i++) {
tdiff = (int)diff_usec(group->loss_history[txseq].t,
group->loss_history[group->max_txseq].t) *
((i - group->max_txseq) / (txseq - group->max_txseq));
tvdiff.tv_sec = 0;
tvdiff.tv_usec = tdiff;
while (tvdiff.tv_usec >= 1000000) {
tvdiff.tv_usec -= 1000000;
tvdiff.tv_sec++;
}
group->loss_history[i].found = 0;
group->loss_history[i].t =
add_timeval(group->loss_history[group->max_txseq].t,tvdiff);
}
// Then check for missed packets up to MAXMISORDER less than the current
// Don't do this part unless we have at least MAXMISORDER packets
// TODO: address issue of start_txseq being within MAXMISORDER sequence
// numbers from the maximum
if (group->seq_wrap ||((uint16_t)(group->max_txseq -
group->start_txseq) >= MAXMISORDER)) {
for (i = group->max_txseq - MAXMISORDER;
i != (uint16_t)(txseq - MAXMISORDER); i++) {
if (!group->loss_history[i].found &&
((diff_usec(group->loss_history[i].t,
group->loss_events[0].t) > grtt_usec) ||
group->slowstart)) {
log4(group->group_id, 0, "Seq %d starts new loss event", i);
// Found a new loss event
if (i < group->max_txseq - MAXMISORDER) {
log5(group->group_id, 0, "wrap check, i=%u, maxseq=%u",
i, group->max_txseq);
i_long = ((group->seq_wrap - 1) << 16) | i;
} else {
i_long = (group->seq_wrap << 16) | i;
}
if (group->slowstart) {
group->slowstart = 0;
// Initialize loss history
count = group->max_txseq;
bytes = 0;
while ((count != group->start_txseq) &&
(diff_usec(group->loss_history[i].t,
group->loss_history[count].t) < grtt_usec)) {
bytes += group->loss_history[count--].size;
}
rate = (int)(bytes / group->grtt);
log4(group->group_id, 0, "End slowstart, calculated "
"rate = %d", rate);
avgbytes= bytes / ((int16_t)(group->max_txseq - count));
group->loss_events[0].len = (group->rtt != 0)
? (int)(0 + pow((rate * group->rtt) /
(sqrt(1.5) * 8 * avgbytes), 2))
: (int)(0 + pow((rate * group->grtt) /
(sqrt(1.5) * 8 * avgbytes), 2));
log4(group->group_id, 0, "Calculated prior "
"event len = %d (rtt=%f, avgbytes=%d)",
group->loss_events[0].len, group->rtt,avgbytes);
} else {
group->loss_events[0].len =
i_long - group->loss_events[0].start_seq;
log4(group->group_id, 0, "Prior event length = %d "
"(i=%u, start=%u)", group->loss_events[0].len,
i_long, group->loss_events[0].start_seq);
}
memmove(&group->loss_events[1], &group->loss_events[0],
sizeof(struct loss_event_t) * 8);
group->loss_events[0].start_seq = i_long;
group->loss_events[0].len = 0;
group->loss_events[0].t = group->loss_history[i].t;
}
}
}
group->max_txseq = txseq;
}
group->loss_events[0].len = ((group->seq_wrap << 16) | group->max_txseq) -
group->loss_events[0].start_seq;
log5(group->group_id, 0, "current cc len = %d", group->loss_events[0].len);
log5(group->group_id, 0, "seq_wrap=%d, max_txseq=%u, start_seq=%u",
group->seq_wrap, group->max_txseq, group->loss_events[0].start_seq);
}
/**
* Calculates and returns the loss event rate
* TODO: add history discounting
*/
double loss_event_rate(struct group_list_t *group)
{
double weights[8] = { 1.0, 1.0, 1.0, 1.0, 0.8, 0.6, 0.4, 0.2 };
double loss_sum_cur, loss_sum_no_cur, weight_sum;
int i;
if (group->slowstart) {
return 0.0;
}
loss_sum_cur = 0;
loss_sum_no_cur = 0;
weight_sum = 0;
for (i = 0; i < 8; i++) {
log5(group->group_id, 0, "loss_events[%d].len=%d",
i, group->loss_events[i].len);
if (group->loss_events[i].len != 0) {
loss_sum_cur += group->loss_events[i].len * weights[i];
weight_sum += weights[i];
}
}
for (i = 1; i < 9; i++) {
if (group->loss_events[i].len == 0) break;
loss_sum_no_cur += group->loss_events[i].len * weights[i - 1];
}
log5(group->group_id, 0, "cur_sum=%f, cur_no_sum=%f, weight_sum=%f",
loss_sum_cur, loss_sum_no_cur, weight_sum);
// Return inverse of larger average
if (loss_sum_no_cur > loss_sum_cur) {
return weight_sum / loss_sum_no_cur;
} else {
return weight_sum / loss_sum_cur;
}
}
/**
* Returns the current congestion control rate in bytes / second.
* As specified in RFC 4654
*/
unsigned current_cc_rate(struct group_list_t *group)
{
double p, rtt;
int i, bytes, thresh;
if (group->rtt != 0.0) {
rtt = group->rtt;
} else {
rtt = group->grtt;
}
p = loss_event_rate(group);
if (p == 0.0) {
thresh = (int)(group->grtt * 1000000 * 4);
bytes = 0;
i = group->max_txseq;
while ((i != group->start_txseq) &&
(diff_usec(group->loss_history[group->max_txseq].t,
group->loss_history[i].t) < thresh)) {
bytes += group->loss_history[i--].size;
}
return (unsigned)(2.0 * bytes / (4.0 * group->grtt));
} else {
log5(group->group_id, 0, "getting cc rate, p=%f, rtt=%f", p, rtt);
return (unsigned)(group->datapacketsize /
(rtt * (sqrt(p * 2.0 / 3.0) +
(12 * sqrt(p * 3.0 / 8.0) * p * (1 + (32 * p * p))))));
}
}
uftp-4.1.5/client_config.c 0000644 0000764 0000764 00000043356 12250244021 014455 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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 "client.h"
#include "client_config.h"
/**
* Global command line values and sockets
*/
SOCKET listener;
char tempdir[MAXDIRNAME], destdir[MAXDIR][MAXDIRNAME];
char pidfile[MAXPATHNAME];
char keyfile[MAXLIST][MAXPATHNAME], keyinfo[MAXLIST][MAXPATHNAME];
char backupdir[MAXDIR][MAXDIRNAME];
int debug, encrypted_only, dscp, destdircnt, tempfile, keyinfo_count;
int interface_count, pub_multi_count, keyfile_count, rcvbuf, backupcnt;
char postreceive[MAXPATHNAME], portname[PORTNAME_LEN];
int port, move_individual;
uint32_t uid;
union sockaddr_u hb_hosts[MAXLIST];
struct iflist m_interface[MAX_INTERFACES];
union sockaddr_u 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, priority;
int hbhost_count, hb_interval;
union key_t privkey[MAXLIST];
int privkey_type[MAXLIST];
struct fp_list_t proxy_info;
union key_t proxy_pubkey, proxy_dhkey;
int proxy_pubkeytype;
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 *ip,
const char *fingerprint)
{
struct addrinfo ai_hints, *ai_rval;
uint32_t server_uid;
int rval;
server_uid = strtoul(server, NULL, 16);
if ((server_uid == 0xffffffff) || (server_uid == 0)) {
fprintf(stderr, "Invalid server UID %s\n", server);
exit(1);
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(ip, NULL, &ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid server name/address %s: %s\n",
ip, gai_strerror(rval));
exit(1);
}
server_keys[server_count].uid = htonl(server_uid);
memcpy(&server_keys[server_count].addr, ai_rval->ai_addr,
ai_rval->ai_addrlen);
server_keys[server_count].has_fingerprint =
parse_fingerprint(server_keys[server_count].fingerprint,
fingerprint);
server_count++;
freeaddrinfo(ai_rval);
}
/**
* Set defaults for all command line arguments
*/
void set_defaults(void)
{
debug = 0;
log_level = DEF_LOG_LEVEL;
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;
strncpy(portname, DEF_PORT, sizeof(portname)-1);
portname[sizeof(portname)-1] = '\x0';
port = atoi(portname);
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;
keyinfo_count = 0;
rcvbuf = 0;
server_count = 0;
has_proxy = 0;
sys_keys = 0;
memset(hb_hosts, 0, sizeof(hb_hosts));
hbhost_count = 0;
hb_interval = DEF_HB_INT;
priority = 0;
memset(postreceive, 0, sizeof(postreceive));
move_individual = 0;
max_log_size = 0;
max_log_count = DEF_MAX_LOG_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 addrinfo ai_hints, *ai_rval;
char line[1000], *servername, *ipstr, *fingerprint;
char *p, *p2, *hoststr, *portstr, pubname[INET6_ADDRSTRLEN];
FILE *serverfile;
const char opts[] = "dx:L:P:s:I:p:tT:D:A:M:B:Q:EU:S:R:k:K:mN:ig:n:h:H:";
set_defaults();
// read lettered arguments
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 'd':
debug = 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 's':
strncpy(postreceive, optarg, sizeof(postreceive)-1);
postreceive[sizeof(postreceive)-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;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(p, NULL,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid name/address %s: %s\n",
p, gai_strerror(rval));
exit(1);
}
if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
ifl, ifl_len)) == -1) {
fprintf(stderr, "Interface %s not found\n", p);
exit(1);
}
m_interface[interface_count++] = ifl[listidx];
freeaddrinfo(ai_rval);
p = strtok(NULL, ",");
}
break;
case 'p':
strncpy(portname, optarg, sizeof(portname)-1);
portname[sizeof(portname)-1] = '\x0';
port = atoi(portname);
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) {
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(p, NULL,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid multicast address %s: %s\n",
p, gai_strerror(rval));
exit(1);
}
memcpy(&pub_multi[pub_multi_count], ai_rval->ai_addr,
ai_rval->ai_addrlen);
pub_multi_count++;
freeaddrinfo(ai_rval);
p = strtok(NULL, ",");
}
break;
case 'B':
rcvbuf = atoi(optarg);
if ((rcvbuf < 65536) || (rcvbuf > 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':
errno = 0;
uid = strtoul(optarg, NULL, 16);
if (errno) {
perror("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';
}
if ((line[0] == '#') || (line[0] == '\x0')) {
continue;
}
servername = line;
ipstr = strchr(servername, '|');
if (ipstr) {
*ipstr = '\x0';
ipstr++;
fingerprint = strchr(ipstr, '|');
if (fingerprint) {
*fingerprint = '\x0';
fingerprint++;
}
} else {
fingerprint = NULL;
}
if (strlen(servername) >= DESTNAME_LEN) {
fprintf(stderr, "Server list: name too long\n");
exit(1);
}
add_server_by_name(servername, ipstr, 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, "/");
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(servername, NULL,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid proxy address %s: %s\n",
servername, gai_strerror(rval));
exit(1);
}
memcpy(&proxy_info.addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
proxy_info.has_fingerprint =
parse_fingerprint(proxy_info.fingerprint, fingerprint);
has_proxy = 1;
freeaddrinfo(ai_rval);
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':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(keyinfo[keyinfo_count], p, sizeof(keyinfo[0])-1);
keyinfo[keyinfo_count][sizeof(keyinfo[0])-1] = '\x0';
keyinfo_count++;
p = strtok(NULL, ",");
}
break;
case 'm':
sys_keys = 1;
break;
case 'N':
priority = atoi(optarg);
if (!valid_priority(priority)) {
fprintf(stderr, "Invalid priority value\n");
exit(1);
}
break;
case 'i':
move_individual = 1;
break;
case 'g':
max_log_size = atoi(optarg);
if ((max_log_size < 1) || (max_log_size > 1024)) {
fprintf(stderr, "Invalid max log size\n");
exit(1);
}
max_log_size *= 1000000;
break;
case 'n':
max_log_count = atoi(optarg);
if ((max_log_count < 1) || (max_log_count > 1000)) {
fprintf(stderr, "Invalid max log count\n");
exit(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;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(hoststr, portstr,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid heartbeat address %s: %s\n",
p, gai_strerror(rval));
exit(1);
}
memcpy(&hb_hosts[hbhost_count], ai_rval->ai_addr,
ai_rval->ai_addrlen);
freeaddrinfo(ai_rval);
if (portstr) {
free(hoststr);
} else {
if (hb_hosts[hbhost_count].ss.ss_family == AF_INET6) {
hb_hosts[hbhost_count].sin6.sin6_port =
htons(atoi(DEF_PORT));
} else {
hb_hosts[hbhost_count].sin.sin_port =
htons(atoi(DEF_PORT));
}
}
hbhost_count++;
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)) {
if ((rval = getnameinfo((struct sockaddr *)&pub_multi[i],
family_len(pub_multi[i]), pubname, sizeof(pubname),
NULL, 0, NI_NUMERICHOST)) != 0) {
fprintf(stderr,"getnameinfo failed: %s",gai_strerror(rval));
}
fprintf(stderr, "Invalid source specific "
"multicast address: %s\n", pubname);
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 ((keyfile_count != 0) && (keyinfo_count != 0) &&
(keyfile_count != keyinfo_count)) {
fprintf(stderr, "Must list same number of items for -k and -K\n");
exit(1);
}
if (has_proxy) {
if (proxy_info.addr.ss.ss_family == AF_INET6) {
proxy_info.addr.sin6.sin6_port = htons(port);
} else {
proxy_info.addr.sin.sin_port = htons(port);
}
}
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-4.1.5/uftpd.1 0000644 0000764 0000764 00000037163 12176066071 012727 0 ustar dbush dbush .TH uftpd 1 "30 July 2013" "UFTP 4.1"
.SH NAME
uftpd - Encrypted UDP based ftp with multicast - client daemon
.SH SYNOPSIS
uftpd [ -d ] [ -p port ] [ -B buf_size ]
[ -E ] [ -Q dscp ] [ -U UID ] [ -x log_level ] [ -t ]
[ -T temp_dir ] [ -D dest_dir[,dest_dir... ]]
[ -A backup_dir[,backup_dir... ]] [ -L logfile ]
[ -P pidfile ] [ -S serverlist_file ] [ -R proxy[/fp] ]
[ -k keyfile[,keyfile...] ]
[ -K rsa:key_len | ec:curve[,rsa:key_len | ec:curve...]]
[ -m ] [ -N priority ] [ -i ] [ -s postreceive_script ]
[ -g max_log_size ] [ -n max_log_count ]
[ -H hb_server[:port][,hb_server[:port]...] ]
[ -h hb_interval ] [ -I interface[,interface...] ]
[ -M pub_mcast_addr[,pub_mcast_addr...] ]
.SH DESCRIPTION
.P
uftpd is the client daemon of the UFTP suite. It listens on one or more multicast addresses to receive files from servers.
This version of the client supports servers running UFTP 4.x.
.SH OPTIONS
.P
The following options are supported:
.TP
.B \-d
Enable debug mode. The process will run in the foreground and all output will go to stderr. If specified, the -L option is ignored.
.TP
.B \-p port
The UDP port number to listen on. Default is 1044.
.TP
.B \-U UID
The unique ID for this client. Specified either as an 8 digit hexadecimal number (0xnnnnnnnn). The default value is based on the IP address of the first listed multicast capable interface on the system. If this address is IPv4, the UID is the address. If it is IPv6, the UID is the last 4 bytes of the address.
.TP
.B \-B buf_size
The size in bytes of the UDP receive buffer to use. Valid values are 65536-104857600 (64KB-100MB). Defaults to 262144.
.TP
.B \-E
Only allow incoming sessions if encryption is enabled. Default is to allow both encrypted and unencrypted sessions.
.TP
.B \-Q dscp
Specifies the Differentiated Services Code Point (DSCP), formerly Type of Service (TOS), in the IP header for all outgoing packets. Valid values are 0-63 and may be specified in either decimal or hexadecimal. Default is 0.
On Windows XP systems, the OS doesn\(aqt allow this parameter to be changed by default. To change this, add/modify the following DWORD registry value, set to 0, and reboot:
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\DisableUserTOSSetting
Not currently supported on Windows Vista or later.
.TP
.B \-x log_level
Specifies current logging level. Valid values are 0-5, with 0 being the least verbose and 5 being the most verbose. Default is 2, which is consistent with logging prior to version 3.5.
.TP
.B \-t
Receive each file into a temp file in the same directory as the destination file. The temp file will have an extension of .~uftp-{group-id}-{file-id}, where {group-id} and {file-id} are the group ID of the current session and file ID of the current file. If -A is also specified, the existing destination file is not moved to backup directory until after the file is fully received.
.TP
.B \-T temp_dir
Temp directory in which files are received, then moved to dest_dir when the session is complete. If omitted, files are received directly into dest_dir. Must reside on the same filesystem as the destination directory.
The -T option MUST be specified to allow the client to save the state of failed file transfers that can be resumed later.
Not compatible -A or -t. Not compatible with -D when multiple destination directories are specified. Also, no incoming files with an absolute path will be accepted if this option is specified.
IMPORTANT: When full directories are received, the entire directory is moved at once to the destination directory, removing any existing file/directory. This means that if an existing directory in dest_dir is the same name as a directory received into temp_dir, all files under the existing directory are deleted. The -i option prevents by moving all files individually.
.TP
.B \-D dest_dir[,dest_dir...]
Destination directories for all received files. When an incoming file specifies an absolute path, it must match one of the destination directories, otherwise the file will be rejected. Incoming files that don\(aqt specify an absolute path will be received into the first destination directory in the list. Default is /tmp for UNIX-like systems, C:\\temp for Windows.
.TP
.B \-A backup_dir[,backup_dir...]
Specifies backup directories. Each backup directory corresponds to a destination directory, so the number of each MUST be the same. Existing files that would be overwritten by incoming files are moved to the corresponding backup directory for the selected destination directory, first under timestamped directories, then under the full path of the existing file.
For example, if /full/path/to/file would be overwritten, it is moved to {backup_dir}/YYYYMMDD/HHMMSS/full/path/to/file. Under Windows, drive letters for local files are not part of the name, but host/share names for network files are. So C:\\path\\to\\file would be backed up to {backup_dir}\\YYYYMMDD\\HHMMSS\\path\\to\\file, and \\\\host\\share\\path\\to\\file would be backed up to {backup_dir}\\YYYYMMDD\\HHMMSS\\host\\share\\path\\to\\file.
Not compatible with -T.
.TP
.B \-L logfile
Specifies the log file. Default is /tmp/uftpd.log for UNIX-like systems systems, C:\\uftpd_log.txt for Windows.
.TP
.B \-P pidfile
The pidfile to write the daemon\(aqs pid to on startup. Default is no pidfile.
.TP
.B \-S serverlist_file
A file containing a list of servers the client will allow to send files to it. The file should contain the ID of the server, the IP address the client expects the server\(aqs request to come from, and optionally the server\(aqs public key fingerprint, with one entry for a server on each line. If a key fingerprint is given, the key specified by the server must match the fingerprint. If your system supports source specific multicast (SSM), the client will subscribe to all public and private multicast addresses using SSM for all servers listed.
When this option is specified, the public and private addresses specified by the server must be valid SSM addresses. Any ANNOUNCE that specifies a private IP that is not a valid SSM address will be rejected. Valid SSM addresses are in the 232/8 range for IPv4 and the ff30::/96 range for IPv6.
.nf
Example contents:
0x11112222|192.168.1.101|66:1E:C9:1D:FC:99:DB:60:B0:1A:F0:8F:CA:F4:28:27:A6:BE:94:BC
0x11113333|fe80::213:72ff:fed6:69ca
.fi
When expecting to receive from a server that is behind a proxy, the file should list the ID of the server along with the IP and fingerprint of the client proxy. The proxy can authenticate the server.
.TP
.B \-R proxy[/fingerprint]
Specifies the name/IP of the response proxy that all responses are forwarded to. If fingerprint is given, it specifies the proxy\(aqs public key fingerprint. Upon startup, the client will query the proxy for its public key, retrying every 5 seconds until it gets a successful response. The client cannot accept an encrypted file transfer from a server until it gets the proxy\(aqs key.
.TP
.B \-k keyfile[,keyfile...]
.TP
.B \-K rsa:key_length | ec:curve[,rsa:key_length | ec:curve...]
These two options are used to read and/or write the client\(aqs RSA/ECDSA private keys.
The -K option creates one or more RSA or ECDSA private keys. New keys are specified as either rsa:key_length, which creates an RSA private key key_length bits wide, or as ec:curve, which creates an EC key using the curve "curve".
The list of supported EC curves is as follows (availability may vary depending on system settings and crypto library used):
sect163k1 sect163r1 sect163r2 sect193r1 sect193r2 sect233k1 sect233r1 sect239k1 sect283k1 sect283r1 sect409k1 sect409r1 sect571k1 sect571r1 secp160k1 secp160r1 secp160r2 secp192k1 prime192v1 secp224k1 secp224r1 secp256k1 prime256v1 secp384r1 secp521r1
If only -K is specified, the keys created are not persisted.
If only -k is specified, this option reads RSA or ECDSA private keys from each keyfile.
If -k and -K are specified, the keys created by -K are written to the keyfiles listed by -k. In this case, -k and -K must give the same number of items.
If neither -k nor -K are specified, an RSA private key 512 bytes in length is generated and not persisted.
If -k is specified but not -K, the RSA or ECDSA private keys are read from each keyfile.
The definition of keyfile is dependent on the crypto library UFTP is compiled to use.
On Windows systems, UFTP can built to use either CNG, which is the new API supported by Windows Vista and Windows 7, or CryptoAPI, which is the legacy API and the only one available to Windows XP.
Under CryptoAPI, all RSA private keys must be stored in a key container (technically only keys used to sign data, but for UFTP\(aqs purposes this is the case). Key containers are internal to Windows, and each user (and the system) has its own set of key containers. In this case, key_file is actually the name of the key container. When -k is not specified, the generated key is not persisted. Elliptic Curve algorithms are not supported under CryptoAPI.
Under CNG, RSA and ECDSA private keys are also stored in key containers, and RSA keys created by CrypoAPI may be read by CNG. Like CryptoAPI, key_file also specifies the key container name, and the generated key is not persisted if -k is not specified. CNG only supports 3 named EC curves: prime256v1, secp384r1, and secp521r1.
All other systems use OpenSSL for the crypto library (although under Windows UFTP can be also be built to use it). In this case, key_file specifies a file name where the RSA private key is stored unencrypted in PEM format (the OS is expected to protect this file). When both -k and -K are specified, the file is only written to if it does not currently exist. If the file does exist, an error message will be returned and the server will exit. When -k is not specified, the generated key is not persisted. These PEM files may also be manipulated via the openssl(1) command line tool.
Keys can also be generated and viewed via the uftp_keymgt(1) utility.
.TP
.B \-m
For Windows systems using CryptoAPI or CNG, private keys are normally stored in the key container of the running user. Specifying this option stores keys in the system key container. Useful when running as a service. On non-Windows systems, this option has no effect.
.TP
.B \-N priority
Sets the process priority. On Windows systems, valid values are from -2 to 2, with a default of 0. These correspond to the following priorities:
.nf
-2 High
-1 Above Normal
0 Normal
1 Below Normal
2 Low
.fi
On all other systems, this is the "nice" value. Valid values are from -20 to 19, where -20 is the highest priority and 19 is the lowest priority. Default is 0.
.TP
.B -i
When -T is specified, directories are normally moved from the temp directory to the destionation directory at once, removing all existing files in the that subdirectory within the destionaion directory. This option causes directories to be traversed so that all received files are moved individually, preventing unwanted deletions. This also affects the operation of the -s option. If -T is not specified, this option has no effect.
.TP
.B -s postreceive_script
The full path to an external command or script to be called when files are received. The command will be called as follows:
postreceive_script -I session_id file [ file... ]
Where "session_id" is an 8 hexadecimal digit number identifiying the current session, and "file" is the full pathname to one or more received files/directories in the destination directory specified by -D.
The way this script is called depends on whether or not a temp directory is specified by -T, and if -i is specified. If a temp directory is not specified, or if both -T and -i are specified, the script gets called once for each file as soon as the file is received. If a temp directory is specified but -i is not, the script gets called once at the end of the session, and is passed all top level files/directories received. Here, "top level files/directories" refers to all entries in the temp directory for the session, but not subdirectories. So the script would be responsible for traversing any listed directories to find files contained within them.
.TP
.B \-g max_log_size
Specifies the maximum log file size in MB. Once the log file reaches this size, the file is renamed with a .1 extension and a new log file is opened. For example, if the log file is /tmp/uftpd.log, it will be renamed /tmp/uftpd.log.1 and a new /tmp/uftpd.log will be created. Ignored if -d is specified. Valid values are 1-1024. Default is no log rolling.
.TP
.B \-n max_log_count
Specifies the maximum number of archive log files to keep when log rolling is active. When the log file rolls, archive logs are renamed with an incrementing numerical extension until the max is reached. Archive log files beyond the maximum are deleted. Ignored if -g is not specified. Valid values are 1-1000. Default is 5.
.TP
.B -H hb_server[:port][,hb_server[:port]...]
Lists one or more proxies to send heartbeat messages to. When sending a signed heartbeat message, the first key listed under -k is used to sign the message. If port is not specified for a given proxy, the default port of 1044 is assumed.
.TP
.B -h hb_interval
The time in seconds between sending heartbeat messages. Ignored if -H is not specified.
.TP
.B \-I interface[,interface...]
Lists one or more interfaces to listen to multicast traffic on. Interfaces can be specified either by interface name, by hostname, or by IP. When receiving a closed group membership request, the client will participate if any of these interfaces matches an IP in the announcement. When receiving an open group membership request, the first interface listed is the one the client will report back to the server. This may not necessarily be the interface that the ANNOUNCE was received on. The default is to listen on all active non-loopback interfaces. NOTE: Since Windows doesn\(aqt have named interfaces (not in the sense that UNIX-like systems do), only hostnames or IP addresses are accepted on Windows. If specifying by hostname or IP, may be a mixture of IPv4 and IPv6 addresses, except on systems that don\(aqt support dual mode sockets such as Windows XP.
.TP
.B \-M pub_multicast_addr[,pub_multicast_addr...]
The list of public multicast addresses to listen on. May be a mixture of IPv4 and IPv6 addresses, except on systems that don\(aqt support dual mode sockets such as Windows XP. Default is 230.4.4.1
.SH EXAMPLES
.P
Starting with the default options:
.RS 5
uftpd
.RE
The client runs as a daemon and listens for announcements on UDP port 1044 on multicast address 230.4.4.1 on all non-loopback network interfaces. Incoming files are received directly into /tmp (C:\\temp on Windows). A 512-bit RSA key is generated to handle encrypted sessions.
Suppose you want an external process to handle incoming files in /tmp/dest. Since you don\(aqt want to pick up incomplete files, you might want them to be received into /tmp/receiving then moved to /tmp/dest when done. Then call the client like this:
.RS 5
uftpd -D /tmp/dest -T /tmp/receiving
.RE
If the client expects to receive from different servers, one sending on 230.4.4.1 and one sending on ff02:4:4:2:
.RS 5
uftpd -M 230.4.4.1,ff02:4:4:2
.RE
To handle incoming encrypted sessions with differing private keys:
.RS 5
uftpd -k file_for_rsa_1024_key,file_for_rsa_2048_key,file_for_ec_prime256v1_key
.RE
If incoming packets aren\(aqt being read quickly enough, and you want to increase the UDP receive buffer size to 2 MB:
.RS 5
uftpd -B 2097152
.RE
.SH SEE ALSO
uftp(1), uftpproxyd(1), uftp_keymgt(1)
.SH NOTES
The latest version of UFTP can be found at http://uftp-multicast.sourceforge.net. UFTP is covered by the GNU General Public License. Commercial licenses and support are available from Dennis Bush (bush@tcnj.edu).
uftp-4.1.5/encrypt_openssl.c 0000644 0000764 0000764 00000102710 12250244021 015067 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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, found;
char errstr[1000];
found = 0;
while ((err = ERR_get_error())) {
ERR_error_string(err, errstr);
log0(0, 0, "%s: %s", mes, errstr);
found = 1;
}
if (!found) {
log0(0, 0, "%s", mes);
}
}
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();
#ifdef EVP_CIPH_CCM_MODE
EVP_add_cipher(EVP_aes_128_ccm());
EVP_add_cipher(EVP_aes_256_ccm());
#endif
ERR_load_crypto_strings();
init_done = 1;
}
/**
* Performs all necessary steps to clean up the crypto library
*/
void crypto_cleanup(void)
{
if (init_done) {
ERR_free_strings();
EVP_cleanup();
}
}
#ifndef NO_EC
/**
* Gets the EC curve type associated with a given curve NID
*/
static uint8_t get_ec_curve_type(int curve)
{
switch (curve) {
case NID_sect163k1:
return CURVE_sect163k1;
case NID_sect163r1:
return CURVE_sect163r1;
case NID_sect163r2:
return CURVE_sect163r2;
case NID_sect193r1:
return CURVE_sect193r1;
case NID_sect193r2:
return CURVE_sect193r2;
case NID_sect233k1:
return CURVE_sect233k1;
case NID_sect233r1:
return CURVE_sect233r1;
case NID_sect239k1:
return CURVE_sect239k1;
case NID_sect283k1:
return CURVE_sect283k1;
case NID_sect283r1:
return CURVE_sect283r1;
case NID_sect409k1:
return CURVE_sect409k1;
case NID_sect409r1:
return CURVE_sect409r1;
case NID_sect571k1:
return CURVE_sect571k1;
case NID_sect571r1:
return CURVE_sect571r1;
case NID_secp160k1:
return CURVE_secp160k1;
case NID_secp160r1:
return CURVE_secp160r1;
case NID_secp160r2:
return CURVE_secp160r2;
case NID_secp192k1:
return CURVE_secp192k1;
case NID_X9_62_prime192v1:
return CURVE_secp192r1;
case NID_secp224k1:
return CURVE_secp224k1;
case NID_secp224r1:
return CURVE_secp224r1;
case NID_secp256k1:
return CURVE_secp256k1;
case NID_X9_62_prime256v1:
return CURVE_secp256r1;
case NID_secp384r1:
return CURVE_secp384r1;
case NID_secp521r1:
return CURVE_secp521r1;
default:
return 0;
}
}
/**
* Gets the EC curve NID associated with a given curve
*/
static int get_ec_curve_nid(uint8_t curve)
{
switch (curve) {
case CURVE_sect163k1:
return NID_sect163k1;
case CURVE_sect163r1:
return NID_sect163r1;
case CURVE_sect163r2:
return NID_sect163r2;
case CURVE_sect193r1:
return NID_sect193r1;
case CURVE_sect193r2:
return NID_sect193r2;
case CURVE_sect233k1:
return NID_sect233k1;
case CURVE_sect233r1:
return NID_sect233r1;
case CURVE_sect239k1:
return NID_sect239k1;
case CURVE_sect283k1:
return NID_sect283k1;
case CURVE_sect283r1:
return NID_sect283r1;
case CURVE_sect409k1:
return NID_sect409k1;
case CURVE_sect409r1:
return NID_sect409r1;
case CURVE_sect571k1:
return NID_sect571k1;
case CURVE_sect571r1:
return NID_sect571r1;
case CURVE_secp160k1:
return NID_secp160k1;
case CURVE_secp160r1:
return NID_secp160r1;
case CURVE_secp160r2:
return NID_secp160r2;
case CURVE_secp192k1:
return NID_secp192k1;
case CURVE_secp192r1:
return NID_X9_62_prime192v1;
case CURVE_secp224k1:
return NID_secp224k1;
case CURVE_secp224r1:
return NID_secp224r1;
case CURVE_secp256k1:
return NID_secp256k1;
case CURVE_secp256r1:
return NID_X9_62_prime256v1;
case CURVE_secp384r1:
return NID_secp384r1;
case CURVE_secp521r1:
return NID_secp521r1;
default:
return 0;
}
}
#endif
/**
* 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_CBC:
return EVP_get_cipherbyname("AES-128-CBC");
case KEY_AES256_CBC:
return EVP_get_cipherbyname("AES-256-CBC");
case KEY_AES128_GCM:
return EVP_get_cipherbyname("id-aes128-GCM");
case KEY_AES256_GCM:
return EVP_get_cipherbyname("id-aes256-GCM");
case KEY_AES128_CCM:
return EVP_get_cipherbyname("id-aes128-CCM");
case KEY_AES256_CCM:
return EVP_get_cipherbyname("id-aes256-CCM");
default:
log0(0, 0, "Unknown keytype: %d", keytype);
return NULL;
}
}
/**
* Gets the EVP_MD associated with a given hashtype
*/
static const EVP_MD *get_hash(int hashtype)
{
switch (hashtype) {
case HASH_SHA512:
return EVP_get_digestbyname("SHA512");
case HASH_SHA384:
return EVP_get_digestbyname("SHA384");
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:
log0(0, 0, "Unknown hashtype: %d", hashtype);
return NULL;
}
}
/**
* 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);
int mode;
if (cipher == NULL) {
*keylen = 0;
*ivlen = 0;
} else {
mode = EVP_CIPHER_mode(cipher);
*keylen = EVP_CIPHER_key_length(cipher);
#ifdef EVP_CIPH_GCM_MODE
if (mode == EVP_CIPH_GCM_MODE) {
*ivlen = GCM_IV_LEN;
} else if (mode == EVP_CIPH_CCM_MODE) {
*ivlen = CCM_IV_LEN;
} else {
*ivlen = EVP_CIPHER_iv_length(cipher);
}
#else
*ivlen = EVP_CIPHER_iv_length(cipher);
#endif
}
}
/**
* Gets the length of the given hash
*/
int get_hash_len(int hashtype)
{
const EVP_MD *hashptr = get_hash(hashtype);
if (hashptr == NULL) {
return 0;
} else {
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.
* For authenticated cipher modes, also takes additional authentication data.
* 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 *aad, unsigned int aadlen,
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 mode, len;
if (cipher == NULL) {
log0(0, 0, "Invalid keytype");
return 0;
}
mode = EVP_CIPHER_mode(cipher);
EVP_CIPHER_CTX_init(&ctx);
if (!EVP_EncryptInit_ex(&ctx, cipher, NULL, NULL, NULL)) {
log_ssl_err("EncryptInit for cipher failed");
return 0;
}
#ifdef EVP_CIPH_GCM_MODE
if (mode == EVP_CIPH_GCM_MODE) {
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_LEN, 0)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for IVLEN failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
} else if (mode == EVP_CIPH_CCM_MODE) {
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_IVLEN, CCM_IV_LEN, 0)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for IVLEN failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_TAG, CCM_TAG_LEN, 0)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for tag len failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
#endif
if (!EVP_EncryptInit_ex(&ctx, NULL, NULL, key, IV)) {
log_ssl_err("EncryptInit for key/IV failed");
return 0;
}
len = 0;
#ifdef EVP_CIPH_GCM_MODE
if ((mode == EVP_CIPH_GCM_MODE) || (mode == EVP_CIPH_CCM_MODE)) {
if (mode == EVP_CIPH_CCM_MODE) {
if (!EVP_EncryptUpdate(&ctx, NULL, &len, NULL, srclen)) {
log_ssl_err("EncryptUpdate for datalen failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
if ((aad != NULL) && (aadlen > 0)) {
if (!EVP_EncryptUpdate(&ctx, NULL, &len, aad, aadlen)) {
log_ssl_err("EncryptUpdate for authdata failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
}
#endif
if (!EVP_EncryptUpdate(&ctx, dest, &len, src, srclen)) {
log_ssl_err("EncryptUpdate for data failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
*destlen = len;
if (!EVP_EncryptFinal_ex(&ctx, dest + *destlen, &len)) {
log_ssl_err("EncryptFinal failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
#ifdef EVP_CIPH_GCM_MODE
if (mode == EVP_CIPH_GCM_MODE) {
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, GCM_TAG_LEN,
dest + *destlen)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for get tag failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
len += GCM_TAG_LEN;
} else if (mode == EVP_CIPH_CCM_MODE) {
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_GET_TAG, CCM_TAG_LEN,
dest + *destlen)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for get tag failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
len += CCM_TAG_LEN;
}
#endif
*destlen += len;
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
/**
* Takes a block of data encrypted with a symmetric cypher and decrypts it.
* For authenticated cipher modes, also takes additional authentication data.
* 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 *aad, unsigned int aadlen,
unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher = get_cipher(keytype);
int mode, len, l_srclen;
if (cipher == NULL) {
log0(0, 0, "Invalid keytype");
return 0;
}
mode = EVP_CIPHER_mode(cipher);
EVP_CIPHER_CTX_init(&ctx);
if (!EVP_DecryptInit_ex(&ctx, cipher, NULL, NULL, NULL)) {
log_ssl_err("DecryptInit for cipher failed");
return 0;
}
#ifdef EVP_CIPH_GCM_MODE
if (mode == EVP_CIPH_GCM_MODE) {
l_srclen = srclen - GCM_TAG_LEN;
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_LEN, 0)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for IVLEN failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, GCM_TAG_LEN,
(void *)(src + l_srclen))) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for set tag failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
} else if (mode == EVP_CIPH_CCM_MODE) {
l_srclen = srclen - CCM_TAG_LEN;
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_IVLEN, CCM_IV_LEN, 0)) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for IVLEN failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_TAG, CCM_TAG_LEN,
(void *)(src + l_srclen))) {
log_ssl_err("EVP_CIPHER_CTX_ctrl for set tag failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
} else {
l_srclen = srclen;
}
#else
l_srclen = srclen;
#endif
if (!EVP_DecryptInit_ex(&ctx, NULL, NULL, key, IV)) {
log_ssl_err("DecryptInit for key/IV failed");
return 0;
}
len = 0;
#ifdef EVP_CIPH_GCM_MODE
if ((mode == EVP_CIPH_GCM_MODE) || (mode == EVP_CIPH_CCM_MODE)) {
if (mode == EVP_CIPH_CCM_MODE) {
if (!EVP_DecryptUpdate(&ctx, NULL, &len, NULL, l_srclen)) {
log_ssl_err("DecryptUpdate for datalen failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
if ((aad != NULL) && (aadlen > 0)) {
if (!EVP_DecryptUpdate(&ctx, NULL, &len, aad, aadlen)) {
log_ssl_err("DecryptUpdate for authdata failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
}
#endif
if (!EVP_DecryptUpdate(&ctx, dest, &len, src, l_srclen)) {
log_ssl_err("DecryptUpdate for data failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
*destlen = len;
#ifdef EVP_CIPH_CCM_MODE
if (mode != EVP_CIPH_CCM_MODE) {
#endif
if (!EVP_DecryptFinal_ex(&ctx, dest + *destlen, &len)) {
log_ssl_err("DecryptFinal failed");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
*destlen += len;
#ifdef EVP_CIPH_CCM_MODE
}
#endif
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);
if (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
return (HMAC(hashptr, key, keylen, src, srclen, dest, destlen) != NULL);
}
/**
* 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);
if (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
EVP_MD_CTX_init(&hashctx);
if (!EVP_DigestInit_ex(&hashctx, hashptr, NULL)) {
log_ssl_err("DigestInit failed");
EVP_MD_CTX_cleanup(&hashctx);
return 0;
}
if (!EVP_DigestUpdate(&hashctx, src, srclen)) {
log_ssl_err("DigestUpdate failed");
EVP_MD_CTX_cleanup(&hashctx);
return 0;
}
if (!EVP_DigestFinal_ex(&hashctx, dest, (unsigned int *)destlen)) {
log_ssl_err("DigestUpdate failed");
EVP_MD_CTX_cleanup(&hashctx);
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);
}
/**
* Returns the length of an exported EC public key
* An exported key is built as follows:
* uint8_t xpoint[ceil(curve_bitlen/8)]
* uint8_t ypoint[ceil(curve_bitlen/8)]
* uint8_t padding[]
*/
int EC_keylen(const EC_key_t ec)
{
#ifndef NO_EC
int keylen, padding;
if ((keylen = i2o_ECPublicKey(ec, NULL)) == 0) {
log0(0, 0, "error getting size of EC key");
return 0;
}
// Don't count leading "4"
keylen--;
if ((keylen % 4) == 0) {
padding = 0;
} else {
padding = 4 - (keylen % 4);
}
return keylen + padding;
#else
return 0;
#endif
}
/**
* Returns the length in bytes of a signature created by the given ECDSA key
* ECDSA signatures consist of:
* uint16_t rlen
* uint16_t slen
* uint8_t rsig[ceil(curve_bitlen/8)]
* uint8_t ssig[ceil(curve_bitlen/8)]
* uint8_t padding[]
*/
int ECDSA_siglen(const EC_key_t ec)
{
#ifndef NO_EC
return sizeof(uint16_t) + sizeof(uint16_t) + EC_keylen(ec);
#else
return 0;
#endif
}
/**
* 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, unsigned int fromlen,
unsigned char *to, unsigned 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, unsigned int fromlen,
unsigned char *to, unsigned 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 (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
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 (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
if (!RSA_verify(EVP_MD_type(hashptr), meshash, meshashlen,
sig, siglen, rsa)) {
log_ssl_err("RSA_verify failed");
return 0;
} else {
return 1;
}
}
/**
* Hashes a block of data and signs it with a ECDSA private key.
* Output buffer must be at least ECDSA_siglen bytes
*/
int create_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
#ifndef NO_EC
unsigned char meshash[HMAC_LEN];
unsigned int meshashlen;
const EVP_MD *hashptr;
ECDSA_SIG *_sig;
uint16_t *rlen, *slen;
unsigned char *rval, *sval;
if (!hash(hashtype, mes, meslen, meshash, &meshashlen)) {
return 0;
}
hashptr = get_hash(hashtype);
if (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
if ((_sig = ECDSA_do_sign(meshash, meshashlen, ec)) == NULL) {
log_ssl_err("ECDSA_do_sign failed");
return 0;
}
rlen = (uint16_t *)sig;
slen = (uint16_t *)(sig + sizeof(uint16_t));
rval = (unsigned char *)slen + sizeof(uint16_t);
sval = rval + BN_num_bytes(_sig->r);
*siglen = ECDSA_siglen(ec);
memset(sig, 0, *siglen);
*rlen = htons(BN_num_bytes(_sig->r));
*slen = htons(BN_num_bytes(_sig->s));
BN_bn2bin(_sig->r, rval);
BN_bn2bin(_sig->s, sval);
ECDSA_SIG_free(_sig);
return 1;
#else
log0(0, 0, "ECDSA not supported");
return 0;
#endif
}
/**
* Hashes a block of data and verifies it against a ECDSA signature.
*/
int verify_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
const unsigned char *sig, unsigned int siglen)
{
#ifndef NO_EC
unsigned char meshash[HMAC_LEN];
unsigned int meshashlen;
const EVP_MD *hashptr;
ECDSA_SIG *_sig;
const uint16_t *rlen, *slen;
const unsigned char *rval, *sval;
if (!hash(hashtype, mes, meslen, meshash, &meshashlen)) {
return 0;
}
hashptr = get_hash(hashtype);
if (hashptr == NULL) {
log0(0, 0, "Invalid hashtype");
return 0;
}
rlen = (const uint16_t *)sig;
slen = (const uint16_t *)(sig + sizeof(uint16_t));
rval = (const unsigned char *)slen + sizeof(uint16_t);
sval = rval + ntohs(*rlen);
if (ntohs(*rlen) + ntohs(*slen) > siglen) {
log0(0, 0, "Invalid signature length");
return 0;
}
_sig = ECDSA_SIG_new();
if (BN_bin2bn(rval, ntohs(*rlen), _sig->r) == NULL) {
log_ssl_err("BN_bn2bin failed for r");
ECDSA_SIG_free(_sig);
return 0;
}
if (BN_bin2bn(sval, ntohs(*slen), _sig->s) == NULL) {
log_ssl_err("BN_bn2bin failed for r");
ECDSA_SIG_free(_sig);
return 0;
}
if (!ECDSA_do_verify(meshash, meshashlen, _sig, ec)) {
log_ssl_err("ECDSA_do_verify failed");
ECDSA_SIG_free(_sig);
return 0;
} else {
ECDSA_SIG_free(_sig);
return 1;
}
#else
log0(0, 0, "ECDSA not supported");
return 0;
#endif
}
#ifndef NO_EC
/**
* Key derivation function for ECDH.
* Takes the raw key and returns the SHA-1 hash of the key
*/
static void *KDF(const void *in, size_t inlen, void *out, size_t *outlen)
{
if (!hash(HASH_SHA1, in, inlen, out, outlen)) {
return NULL;
} else {
return out;
}
}
#endif
/**
* Creates an ECDH key based on two EC keys, one public and one private
*/
int get_ECDH_key(EC_key_t pubkey, EC_key_t privkey, unsigned char *key,
unsigned int *keylen)
{
#ifndef NO_EC
if (!ECDH_compute_key(key, 0, EC_KEY_get0_public_key(pubkey),
privkey, KDF)) {
log_ssl_err("couldn't compute shared key");
return 0;
}
*keylen = get_hash_len(HASH_SHA1);
return 1;
#else
log0(0, 0, "ECDH not supported");
return 0;
#endif
}
/**
* Creates an RSA public key with the given modulus and public exponent
*/
int import_RSA_key(RSA_key_t *rsa, const unsigned char *keyblob,
uint16_t bloblen)
{
const struct rsa_blob_t *rsablob;
const unsigned char *modulus;
rsablob = (const struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
if (sizeof(struct rsa_blob_t) + ntohs(rsablob->modlen) != bloblen) {
log0(0, 0, "Error importing RSA key: invalid length");
return 0;
}
*rsa = RSA_new();
if (((*rsa)->e = BN_bin2bn((const unsigned char *)&rsablob->exponent,
4, NULL)) == NULL) {
log_ssl_err("BN_bin2bn failed for e");
return 0;
}
if (((*rsa)->n = BN_bin2bn(modulus, ntohs(rsablob->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, unsigned char *keyblob,
uint16_t *bloblen)
{
struct rsa_blob_t *rsablob;
unsigned char *modulus;
unsigned char bin_exponent[4];
uint32_t exponent;
int explen, modlen, i;
rsablob = (struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
if (BN_num_bytes(rsa->e) > sizeof(bin_exponent)) {
log0(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) {
log0(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 ((modlen = BN_bn2bin(rsa->n, modulus)) <= 0) {
log_ssl_err("BN_bn2bin failed for n");
return 0;
}
rsablob->blobtype = KEYBLOB_RSA;
rsablob->reserved = 0;
rsablob->modlen = htons(modlen);
rsablob->exponent = htonl(exponent);
*bloblen = sizeof(struct rsa_blob_t) + modlen;
return 1;
}
/**
* Creates an EC public key with the curve and key value
*/
int import_EC_key(EC_key_t *ec, const unsigned char *keyblob, uint16_t bloblen,
int isdh)
{
#ifndef NO_EC
const struct ec_blob_t *ecblob;
const unsigned char *keyval, *tmp;
unsigned char *buf;
ecblob = (const struct ec_blob_t *)keyblob;
keyval = keyblob + sizeof(struct ec_blob_t);
if (sizeof(struct ec_blob_t) + ntohs(ecblob->keylen) > bloblen) {
log0(0, 0, "Error importing EC key: invalid length");
return 0;
}
if ((*ec = EC_KEY_new_by_curve_name(
get_ec_curve_nid(ecblob->curve))) == NULL) {
log_ssl_err("EC_KEY_new_by_curve_name failed");
return 0;
}
buf = safe_malloc(ntohs(ecblob->keylen) + 1);
buf[0] = 4;
memcpy(&buf[1], keyval, ntohs(ecblob->keylen));
tmp = buf;
if (!o2i_ECPublicKey(ec, &tmp, ntohs(ecblob->keylen) + 1)) {
log_ssl_err("o2i_ECPublicKey failed");
EC_KEY_free(*ec);
free(buf);
return 0;
}
free(buf);
return 1;
#else
log0(0, 0, "EC keys not supported");
return 0;
#endif
}
/**
* Extracts the key value from an EC public key
*/
int export_EC_key(const EC_key_t ec, unsigned char *keyblob, uint16_t *bloblen)
{
#ifndef NO_EC
struct ec_blob_t *ecblob;
unsigned char *keyval, *buf, *tmp;
int keylen;
ecblob = (struct ec_blob_t *)keyblob;
keyval = keyblob + sizeof(struct ec_blob_t);
if ((keylen = i2o_ECPublicKey(ec, NULL)) == 0) {
log0(0, 0, "error getting size of EC key");
return 0;
}
buf = safe_malloc(keylen);
tmp = buf;
// After this call, tmp points to (buf + keylen),
// but the exported key lives at buf
if (!i2o_ECPublicKey(ec, &tmp)) {
log_ssl_err("i2o_ECPublicKey failed");
free(buf);
return 0;
}
// Keyblob may contain trailing padding; ensure it's zero'ed out
*bloblen = sizeof(struct ec_blob_t) + EC_keylen(ec);
memset(keyblob, 0, *bloblen);
ecblob->blobtype = KEYBLOB_EC;
ecblob->curve = get_EC_curve(ec);
// Don't copy the leading "4"
keylen--;
memcpy(keyval, &buf[1], keylen);
ecblob->keylen = htons(keylen);
free(buf);
return 1;
#else
log0(0, 0, "EC keys not supported");
return 0;
#endif
}
/**
* 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 (filename && strcmp(filename, "")) {
if ((f = fopen(filename, "rb")) != NULL) {
log0(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;
}
/**
* Generates an EC private key with the given curve
* and writes it to the given file (if specified).
*/
EC_key_t gen_EC_key(uint8_t curve, int isdh, const char *filename)
{
#ifndef NO_EC
EC_key_t ec;
FILE *f;
if (curve == 0) {
curve = DEF_CURVE;
}
if ((ec = EC_KEY_new_by_curve_name(get_ec_curve_nid(curve))) == NULL) {
log_ssl_err("EC_KEY_new_by_curve_name failed");
return NULL;
}
if (!EC_KEY_generate_key(ec)) {
log_ssl_err("EC_KEY_generate_key failed");
return NULL;
}
// Needed to write the PEM with a named curve
EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
if (filename && strcmp(filename, "")) {
if ((f = fopen(filename, "rb")) != NULL) {
log0(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_ECPrivateKey(f, ec, NULL, NULL, 0, NULL, NULL)) {
log_ssl_err("couldn't write EC private key");
fclose(f);
return NULL;
}
fclose(f);
}
return ec;
#else
log0(0, 0, "EC keys not supported");
return NULL;
#endif
}
/**
* Reads an EC private key from the specified file
*/
EC_key_t read_EC_key(const char *filename)
{
#ifndef NO_EC
EC_key_t ec;
FILE *f;
if ((f = fopen(filename, "rb")) == NULL) {
syserror(0, 0, "failed to open key file");
return NULL;
}
if ((ec = PEM_read_ECPrivateKey(f, NULL, NULL, NULL)) == NULL) {
log_ssl_err("couldn't read EC private key");
return NULL;
}
fclose(f);
return ec;
#else
log0(0, 0, "EC keys not supported");
return NULL;
#endif
}
/**
* Reads a private key of uknown type
*/
union key_t read_private_key(const char *filename, int *keytype)
{
union key_t key;
FILE *f;
key.key = 0;
if ((f = fopen(filename, "rb")) == NULL) {
syserror(0, 0, "failed to open key file");
*keytype = 0;
return key;
}
if ((key.rsa = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL)) != NULL) {
fclose(f);
*keytype = KEYBLOB_RSA;
return key;
}
fclose(f);
#ifndef NO_EC
key.key = 0;
if ((f = fopen(filename, "rb")) == NULL) {
syserror(0, 0, "failed to open key file");
*keytype = 0;
return key;
}
if ((key.ec = PEM_read_ECPrivateKey(f, NULL, NULL, NULL)) != NULL) {
*keytype = KEYBLOB_EC;
} else {
log0(0, 0, "Failed to read key");
*keytype = 0;
}
fclose(f);
return key;
#else
log0(0, 0, "Failed to read key");
key.key = 0;
*keytype = 0;
return key;
#endif
}
/**
* Returns the EC curve type of the specified EC key
*/
uint8_t get_EC_curve(const EC_key_t ec)
{
#ifndef NO_EC
int nid;
nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
return get_ec_curve_type(nid);
#else
log0(0, 0, "EC keys not supported");
return 0;
#endif
}
void free_RSA_key(RSA_key_t rsa)
{
RSA_free(rsa);
}
void free_EC_key(EC_key_t ec)
{
#ifndef NO_EC
EC_KEY_free(ec);
#endif
}
const char *get_next_container()
{
return NULL;
}
void delete_container(const char *name)
{
}
void set_sys_keys(int set)
{
}
uftp-4.1.5/server_send.h 0000644 0000764 0000764 00000002651 12250244021 014167 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_SEND_H
#define _SERVER_SEND_H
int send_files(void);
#endif // _SERVER_SEND_H
uftp-4.1.5/client_transfer.c 0000644 0000764 0000764 00000112555 12250244021 015032 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
#include "client_transfer.h"
void move_files_individual(struct group_list_t *group, const char *local_temp,
char *local_dest);
/**
* Moves a file from the temp to the destination directory
*/
void move_file_individual(struct group_list_t *group, const char *local_temp,
const char *local_dest, const char *filename)
{
char temppath[MAXPATHNAME], destpath[MAXPATHNAME];
stat_struct temp_stat, dest_stat;
int len, found_dir;
len = snprintf(temppath, sizeof(temppath), "%s%c%s", local_temp,
PATH_SEP, filename);
if ((len >= sizeof(temppath)) || (len == -1)) {
log0(group->group_id, group->file_id,
"Max pathname length exceeded: %s%c%s", local_temp,
PATH_SEP, filename);
return;
}
len = snprintf(destpath, sizeof(destpath), "%s%c%s", local_dest,
PATH_SEP, filename);
if ((len >= sizeof(destpath)) || (len == -1)) {
log0(group->group_id, group->file_id,
"Max pathname length exceeded: %s%c%s", local_dest,
PATH_SEP, filename);
return;
}
if (lstat_func(temppath, &temp_stat) == -1) {
syserror(group->group_id, group->file_id,
"Error getting file status for %s", temppath);
return;
}
if (S_ISDIR(temp_stat.st_mode)) {
found_dir = 0;
if (lstat_func(destpath, &dest_stat) != -1) {
if (!S_ISDIR(dest_stat.st_mode)) {
clear_path(destpath, group);
} else {
found_dir = 1;
}
}
if (!found_dir) {
if (mkdir(destpath, 0755) == -1) {
syserror(group->group_id, group->file_id,
"Failed to create directory %s", destpath);
return;
}
}
move_files_individual(group, temppath, destpath);
} else {
clear_path(destpath, group);
#ifdef WINDOWS
if (!MoveFile(temppath, destpath)) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), 0, errbuf, sizeof(errbuf),NULL);
log0(group->group_id, group->file_id,
"error (%d): %s", GetLastError(), errbuf);
}
#else
if (rename(temppath, destpath) == -1) {
syserror(group->group_id,
group->file_id, "Couldn't move file");
}
#endif
run_postreceive(group, destpath);
}
}
/**
* Move all files from temp to destination directory if at end of group.
* Called recursively to move each file individually.
*/
void move_files_individual(struct group_list_t *group, const char *local_temp,
char *local_dest)
{
int emptydir;
{
#ifdef WINDOWS
intptr_t ffhandle;
struct _finddatai64_t finfo;
char dirglob[MAXPATHNAME];
snprintf(dirglob, sizeof(dirglob), "%s%c*", local_temp, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
syserror(group->group_id, group->file_id,
"Failed to open directory %s", dirglob);
return;
}
emptydir = 1;
do {
if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
emptydir = 0;
move_file_individual(group, local_temp, local_dest, finfo.name);
}
} while (_findnexti64(ffhandle, &finfo) == 0);
_findclose(ffhandle);
#else
DIR *dir;
struct dirent *de;
if ((dir = opendir(local_temp)) == NULL) {
syserror(group->group_id, group->file_id,
"Failed to open directory %s", local_temp);
return;
}
emptydir = 1;
// 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) {
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
emptydir = 0;
move_file_individual(group, local_temp, local_dest, de->d_name);
}
}
if (errno && (errno != ENOENT)) {
syserror(group->group_id, group->file_id,
"Failed to read directory %s", tempdir);
}
closedir(dir);
#endif
}
if (emptydir) {
run_postreceive(group, local_dest);
}
if (rmdir(local_temp) == -1) {
syserror(group->group_id, group->file_id,
"Failed remove temp directory %s", local_temp);
}
}
/**
* Move all files from temp to destination directory if at end of group
*/
void move_files(struct group_list_t *group)
{
char temppath[MAXPATHNAME], destpath[MAXPATHNAME];
char *filelist[10000]; // TODO: no magic number
int len, filecount, i;
if (!strcmp(tempdir, "") || (group->file_id != 0)) {
return;
}
if (move_individual) {
len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X",
tempdir, PATH_SEP, group->group_id);
if ((len >= sizeof(temppath)) || (len == -1)) {
log0(group->group_id, group->file_id,
"Max pathname length exceeded: %s%c_group_%08X",
tempdir, PATH_SEP, group->group_id);
} else {
move_files_individual(group, temppath, destdir[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->group_id, PATH_SEP);
if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
syserror(group->group_id, group->file_id,
"Failed to open directory %s", dirglob);
return;
}
filecount = 0;
do {
len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X%c%s",
tempdir, PATH_SEP, group->group_id,
PATH_SEP, finfo.name);
if ((len >= sizeof(temppath)) || (len == -1)) {
log0(group->group_id, group->file_id,
"Max pathname length exceeded: %s%c_group_%08X%c%s",
tempdir, PATH_SEP, group->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->group_id, group->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, group);
if (!MoveFile(temppath, destpath)) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
log0(group->group_id, group->file_id,
"error (%d): %s", GetLastError(), errbuf);
}
filelist[filecount] = strdup(destpath);
if (filelist[filecount] == NULL) {
syserror(0, 0, "strdup failed!");
exit(1);
}
filecount++;
}
} 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->group_id);
if ((dir = opendir(dirname)) == NULL) {
syserror(group->group_id, group->file_id,
"Failed to open directory %s", dirname);
return;
}
filecount = 0;
// 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->group_id, group->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->group_id, group->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, group);
if (rename(temppath, destpath) == -1) {
syserror(group->group_id,
group->file_id, "Couldn't move file");
}
filelist[filecount] = strdup(destpath);
if (filelist[filecount] == NULL) {
syserror(0, 0, "strdup failed!");
exit(1);
}
filecount++;
}
}
if (errno && (errno != ENOENT)) {
syserror(group->group_id, group->file_id,
"Failed to read directory %s", dirname);
}
closedir(dir);
#endif
}
run_postreceive_multi(group, filelist, filecount);
for (i = 0; i < filecount; i++) {
free(filelist[i]);
}
snprintf(temppath, sizeof(temppath), "%s%c_group_%08X", tempdir,
PATH_SEP, group->group_id);
if (rmdir(temppath) == -1) {
syserror(group->group_id, group->file_id,
"Failed remove temp directory %s", temppath);
}
}
/**
* Sets the fields in a EXT_TFMCC_ACK_INFO extension for transmission
*/
void set_tfmcc_ack_info(struct group_list_t *group,
struct tfmcc_ack_info_he *tfmcc)
{
struct timeval now, send_time;
unsigned ccrate;
tfmcc->exttype = EXT_TFMCC_ACK_INFO;
tfmcc->extlen = sizeof(struct tfmcc_ack_info_he) / 4;
tfmcc->cc_seq = htons(group->ccseq);
ccrate = current_cc_rate(group);
log4(group->group_id, 0, "ccrate=%d", ccrate);
tfmcc->cc_rate = htons(quantize_rate(ccrate));
//tfmcc->cc_rate = htons(quantize_rate(current_cc_rate(group)));
tfmcc->flags = 0;
if (group->slowstart) {
tfmcc->flags |= FLAG_CC_START;
}
if (group->rtt != 0.0) {
tfmcc->flags |= FLAG_CC_RTT;
}
tfmcc->client_id = uid;
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
send_time = group->last_server_ts;
} else {
send_time = add_timeval(group->last_server_ts,
diff_timeval(now, group->last_server_rx_ts));
}
tfmcc->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
tfmcc->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
}
/**
* Sends back a STATUS message with the given NAK list
*/
void send_status(struct group_list_t *group, 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 tfmcc_ack_info_he *tfmcc;
unsigned char *sent_naks;
int payloadlen, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
status = (struct status_h *)(buf + sizeof(struct uftp_h));
tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)status +
sizeof(struct status_h));
set_uftp_header(header, STATUS, group);
status->func = STATUS;
if (group->cc_type == CC_TFMCC) {
status->hlen =
(sizeof(struct status_h) + sizeof(struct tfmcc_ack_info_he)) / 4;
} else {
status->hlen = sizeof(struct status_h) / 4;
}
status->file_id = htons(group->file_id);
status->section = htons(section);
if (section >= group->fileinfo.big_sections) {
payloadlen = (group->fileinfo.secsize_small / 8) + 1;
} else {
payloadlen = (group->fileinfo.secsize_big / 8) + 1;
}
if (group->cc_type == CC_TFMCC) {
set_tfmcc_ack_info(group, tfmcc);
}
sent_naks = (unsigned char *)status + (status->hlen * 4);
memcpy(sent_naks, naks, payloadlen);
payloadlen += status->hlen * 4;
if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->client_privkey, group->client_privkeylen)) {
log0(group->group_id, group->file_id, "Error encrypting STATUS");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->replyaddr,
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending STATUS");
} else {
log2(group->group_id, group->file_id,
"Sent %d NAKs for section %d", nak_count, section);
}
free(buf);
free(encrypted);
}
/**
* Sets the fields in a EXT_FREESPACE_INFO extension for transmission
*/
void set_freespace_info(struct group_list_t *group,
struct freespace_info_he *freespace)
{
int64_t disk_space;
disk_space = free_space(group->fileinfo.filepath);
freespace->exttype = EXT_FREESPACE_INFO;
freespace->extlen = sizeof(struct freespace_info_he) / 4;
freespace->freespace_hi = htonl((uint32_t)(disk_space >> 32));
freespace->freespace_lo = htonl((uint32_t)(disk_space & 0xFFFFFFFF));
}
/**
* Sends back a COMPLETE message in response to a DONE or FILEINFO
*/
void send_complete(struct group_list_t *group, int set_freespace)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct complete_h *complete;
struct freespace_info_he *freespace;
int payloadlen, enclen;
struct timeval tv;
gettimeofday(&tv, NULL);
if ((group->phase == PHASE_COMPLETE) &&
(cmptimestamp(tv, group->expire_time) >= 0)) {
log0(group->group_id, group->file_id,
"Completion unconfirmed by server");
move_files(group);
file_cleanup(group, 0);
return;
}
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
complete = (struct complete_h *)(buf + sizeof(struct uftp_h));
freespace = (struct freespace_info_he *)((unsigned char *)complete +
sizeof(struct complete_h));
set_uftp_header(header, COMPLETE, group);
complete->func = COMPLETE;
if (set_freespace) {
complete->hlen = (sizeof(struct complete_h) +
sizeof(struct freespace_info_he)) / 4;
} else {
complete->hlen = sizeof(struct complete_h) / 4;
}
complete->status = group->fileinfo.comp_status;
complete->file_id = htons(group->file_id);
if (set_freespace) {
set_freespace_info(group, freespace);
}
payloadlen = complete->hlen * 4;
if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->client_privkey, group->client_privkeylen)) {
log0(group->group_id, group->file_id, "Error encrypting COMPLETE");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->replyaddr,
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending COMPLETE");
} else {
log2(group->group_id, group->file_id, "COMPLETE sent");
}
set_timeout(group, 0);
free(buf);
free(encrypted);
}
/**
* Sends back a CC_ACK message for congestion control feedback
*/
void send_cc_ack(struct group_list_t *group)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct cc_ack_h *cc_ack;
struct tfmcc_ack_info_he *tfmcc;
int payloadlen, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
cc_ack = (struct cc_ack_h *)(buf + sizeof(struct uftp_h));
tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)cc_ack +
sizeof(struct cc_ack_h));
set_uftp_header(header, CC_ACK, group);
cc_ack->func = CC_ACK;
cc_ack->hlen =
(sizeof(struct cc_ack_h) + sizeof(struct tfmcc_ack_info_he)) / 4;
set_tfmcc_ack_info(group, tfmcc);
payloadlen = cc_ack->hlen * 4;
if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->client_privkey, group->client_privkeylen)) {
log0(group->group_id, group->file_id, "Error encrypting CC_ACK");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&group->replyaddr,
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending CC_ACK");
} else {
log2(group->group_id, group->file_id, "CC_ACK sent");
}
set_timeout(group, 0);
group->cc_time.tv_sec = 0;
group->cc_time.tv_usec = 0;
free(buf);
}
/**
* Starts a new feedback round under TFMCC
*/
void init_tfmcc_fb_round(struct group_list_t *group, uint16_t new_ccseq)
{
double urand, backoff;
group->ccseq = new_ccseq;
urand = (double)(rand32() + 0.0) / 0xFFFFFFFF;
if (urand == 0.0) urand = 1.0;
backoff = 6 * group->grtt * (1 + log(urand) / log(group->gsize));
if (backoff < 0) backoff = 0.0;
gettimeofday(&group->cc_time, NULL);
add_timeval_d(&group->cc_time, (backoff > 0) ? backoff : 0);
group->initrate = current_cc_rate(group);
log3(group->group_id, group->file_id, "Starting feedback round %d: "
"backoff = %.3f, initrate = %d",
new_ccseq, backoff, group->initrate);
}
/**
* Reads a EXT_TFMCC_DATA_INFO extension in a FILESEG message
*/
void handle_tfmcc_data_info(struct group_list_t *group,
const struct tfmcc_data_info_he *tfmcc)
{
uint32_t ccrate;
uint16_t new_ccseq;
new_ccseq = ntohs(tfmcc->cc_seq);
ccrate = unquantize_rate(ntohs(tfmcc->cc_rate));
if (((int16_t)(new_ccseq - group->ccseq)) > 0) {
init_tfmcc_fb_round(group, new_ccseq);
} else if ((group->cc_time.tv_sec != 0) &&
((ccrate < current_cc_rate(group)) || (ccrate < group->initrate)) &&
(group->grtt > group->rtt)) {
log4(group->group_id, group->file_id, "Canceling feedback timer");
group->cc_time.tv_sec = 0;
group->cc_time.tv_usec = 0;
}
}
/**
* Reads an expected FILESEG and writes it to the proper place in the file
*/
void handle_fileseg(struct group_list_t *group, const unsigned char *message,
unsigned meslen, uint16_t txseq)
{
const struct fileseg_h *fileseg;
const struct tfmcc_data_info_he *tfmcc;
const unsigned char *data;
const uint8_t *he;
int datalen, section, seq, wrote_len;
unsigned extlen;
f_offset_t offset, seek_rval;
if (group->fileinfo.ftype != FTYPE_REG) {
log2(group->group_id, group->file_id,
"Rejecting FILESEG: not a regular file");
return;
}
fileseg = (const struct fileseg_h *)message;
data = message + (fileseg->hlen * 4);
datalen = meslen - (fileseg->hlen * 4);
if ((meslen < (fileseg->hlen * 4U)) ||
((fileseg->hlen * 4U) < sizeof(struct fileseg_h))) {
log2(group->group_id, group->file_id,
"Rejecting FILESEG: invalid message size");
return;
}
if (ntohs(fileseg->file_id) != group->file_id) {
log2(group->group_id, group->file_id,
"Rejecting FILESEG: got incorrect file_id %04X",
ntohs(fileseg->file_id));
return;
}
tfmcc = NULL;
if (fileseg->hlen * 4U > sizeof(struct fileseg_h)) {
he = (const uint8_t *)fileseg + sizeof(struct fileseg_h);
if (*he == EXT_TFMCC_DATA_INFO) {
tfmcc = (const struct tfmcc_data_info_he *)he;
extlen = tfmcc->extlen * 4U;
if ((extlen > (fileseg->hlen * 4U) - sizeof(struct fileseg_h)) ||
extlen < sizeof(struct tfmcc_data_info_he)) {
log2(group->group_id, group->file_id,
"Rejecting FILESEG: invalid extension size");
return;
}
}
}
section = ntohs(fileseg->section);
if (section >= group->fileinfo.big_sections) {
seq = (group->fileinfo.big_sections * group->fileinfo.secsize_big) +
((section - group->fileinfo.big_sections) *
group->fileinfo.secsize_small) + ntohs(fileseg->sec_block);
} else {
seq = (section * group->fileinfo.secsize_big) +
ntohs(fileseg->sec_block);
}
if ((datalen != group->blocksize) &&
(seq != group->fileinfo.blocks - 1)) {
log2(group->group_id, group->file_id,
"Rejecting FILESEG: invalid data size %d", datalen);
return;
}
if (log_level >= 5) {
log5(group->group_id, group->file_id, "Got packet %d", seq);
} else if (log_level == 4) {
if (seq != group->fileinfo.last_block + 1) {
log4(group->group_id, group->file_id, "Got packet %d, last was %d",
seq, group->fileinfo.last_block);
}
}
if ((group->cc_type == CC_TFMCC) && tfmcc) {
handle_tfmcc_data_info(group, tfmcc);
}
group->fileinfo.last_block = seq;
if (txseq == group->max_txseq) {
if ((section > group->fileinfo.last_section) &&
(group->fileinfo.nak_time.tv_sec == 0)) {
// Start timer to send NAKs
gettimeofday(&group->fileinfo.nak_time, NULL);
add_timeval_d(&group->fileinfo.nak_time, 1 * group->grtt);
group->fileinfo.nak_section_first = group->fileinfo.last_section;
group->fileinfo.nak_section_last = section;
group->fileinfo.got_done = 0;
log3(group->group_id, group->file_id, "New section, set NAK timer "
"for sections %d - %d", group->fileinfo.nak_section_first,
group->fileinfo.nak_section_last);
}
group->fileinfo.last_section = section;
}
if (group->fileinfo.naklist[seq]) {
offset = (f_offset_t) seq * group->blocksize;
if ((seek_rval = lseek_func(group->fileinfo.fd,
offset - group->fileinfo.curr_offset,
SEEK_CUR)) == -1) {
syserror(group->group_id, group->file_id, "lseek failed for file");
}
if (seek_rval != offset) {
log2(group->group_id, group->file_id,
"offset is %s", printll(seek_rval));
log2(group->group_id, group->file_id,
" should be %s", printll(offset));
if ((seek_rval = lseek_func(group->fileinfo.fd,
seek_rval - group->fileinfo.curr_offset,
SEEK_CUR)) == -1) {
syserror(group->group_id, group->file_id,
"lseek failed for file");
return;
}
}
if ((wrote_len = write(group->fileinfo.fd, data, datalen)) == -1) {
syserror(group->group_id, group->file_id,
"Write failed for segment %d", seq);
} else {
group->fileinfo.curr_offset = offset + wrote_len;
if (wrote_len != datalen) {
log0(group->group_id, group->file_id,
"Write failed for segment %d, only wrote %d bytes",
seq, wrote_len);
} else {
group->fileinfo.naklist[seq] = 0;
}
}
}
set_timeout(group, 0);
}
/**
* Returns 1 if a file has been completely received, 0 otherwise
*/
int file_done(struct group_list_t *group, int detail)
{
unsigned int section_offset, blocks_this_sec, section, block, nakidx;
if ((group->phase == PHASE_MIDGROUP) || (group->file_id == 0)) {
return 1;
}
for (section = 0; section < group->fileinfo.sections; section++) {
if (!group->fileinfo.section_done[section]) {
if (!detail) {
return 0;
}
if (section >= group->fileinfo.big_sections) {
section_offset = (group->fileinfo.big_sections *
group->fileinfo.secsize_big) +
((section - group->fileinfo.big_sections) *
group->fileinfo.secsize_small);
blocks_this_sec = group->fileinfo.secsize_small;
} else {
section_offset = section * group->fileinfo.secsize_big;
blocks_this_sec = group->fileinfo.secsize_big;
}
for (block = 0; block < blocks_this_sec; block++) {
nakidx = block + section_offset;
if (group->fileinfo.naklist[nakidx]) {
return 0;
}
}
group->fileinfo.section_done[section] = 1;
}
}
return 1;
}
/**
* Build the NAK list for a given section. Returns the NAK count.
*/
unsigned int get_naks(struct group_list_t *group,
unsigned int section, unsigned char **naks)
{
unsigned int section_offset, blocks_this_sec, i;
unsigned int nakidx, naklistidx, numnaks;
if ((group->phase == PHASE_MIDGROUP) || (group->file_id == 0) ||
(group->fileinfo.section_done[section])) {
*naks = NULL;
return 0;
}
if (section >= group->fileinfo.big_sections) {
section_offset = (group->fileinfo.big_sections *
group->fileinfo.secsize_big) +
((section - group->fileinfo.big_sections) *
group->fileinfo.secsize_small);
blocks_this_sec = group->fileinfo.secsize_small;
} else {
section_offset = section * group->fileinfo.secsize_big;
blocks_this_sec = group->fileinfo.secsize_big;
}
log3(group->group_id, group->file_id,
"getting naks: section: %d, offset: %d, blocks: %d",
section, section_offset, blocks_this_sec);
*naks = safe_calloc(group->blocksize, 1);
// Build NAK list
numnaks = 0;
for (i = 0; i < blocks_this_sec; i++) {
nakidx = i + section_offset;
naklistidx = i;
if (group->fileinfo.naklist[nakidx]) {
log4(group->group_id, group->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->blocksize; i++) {
sclog5("%02X ", (*naks)[i]);
if (i % 25 == 24) slog5("");
}
slog5("");
}
if (numnaks == 0) {
group->fileinfo.section_done[section] = 1;
}
return numnaks;
}
/**
* Processes an expected DONE message
*/
void handle_done(struct group_list_t *group, const unsigned char *message,
unsigned meslen)
{
const struct done_h *done;
const uint32_t *addrlist;
unsigned int section, listlen;
done = (const struct done_h *)message;
addrlist = (const uint32_t *)(message + (done->hlen * 4));
listlen = (meslen - (done->hlen * 4)) / 4;
if ((meslen < (done->hlen * 4U)) ||
((done->hlen * 4U) < sizeof(struct done_h))) {
log2(group->group_id, group->file_id,
"Rejecting DONE: invalid message size");
return;
}
section = ntohs(done->section);
if ((ntohs(done->file_id) != group->file_id) &&
(ntohs(done->file_id) != 0) && (group->phase != PHASE_MIDGROUP)) {
// Silently reject if not for this file and not end of group
return;
}
if (ntohs(done->file_id) == 0) {
// We're at end of group, so set local file_id=0 to flag this
group->file_id = 0;
}
if (group->file_id) {
log1(group->group_id, group->file_id,
"Got DONE message for section %d", section);
} else {
log1(group->group_id, group->file_id,
"Got DONE message for group");
}
if (uid_in_list(addrlist, listlen)) {
if (group->file_id == 0) {
log1(group->group_id, 0, "Group complete");
group->phase = PHASE_COMPLETE;
group->fileinfo.comp_status = COMP_STAT_NORMAL;
gettimeofday(&group->expire_time, NULL);
if (group->robust * group->grtt < 1.0) {
add_timeval_d(&group->expire_time, 1.0);
} else {
add_timeval_d(&group->expire_time, group->robust * group->grtt);
}
send_complete(group, 0);
} else {
if (file_done(group, 1)) {
log1(group->group_id, group->file_id, "File transfer complete");
group->fileinfo.nak_time.tv_sec = 0;
group->fileinfo.nak_time.tv_usec = 0;
send_complete(group, 0);
file_cleanup(group, 0);
} else if (group->fileinfo.nak_time.tv_sec == 0) {
log4(group->group_id, group->file_id,
"Setting nak_time to trigger in %.6f", group->grtt);
gettimeofday(&group->fileinfo.nak_time, NULL);
add_timeval_d(&group->fileinfo.nak_time, 1 * group->grtt);
group->fileinfo.nak_section_first=group->fileinfo.last_section;
group->fileinfo.nak_section_last = section + 1;
group->fileinfo.got_done = 1;
log3(group->group_id, group->file_id, "Got DONE for client, "
"set NAK timer for sections %d - %d",
group->fileinfo.nak_section_first,
group->fileinfo.nak_section_last);
}
group->fileinfo.last_section = section + 1;
}
} else if (group->phase != PHASE_MIDGROUP) {
if ((section + 1 > group->fileinfo.last_section) &&
(group->fileinfo.nak_time.tv_sec == 0)) {
// Start timer to send NAKs
gettimeofday(&group->fileinfo.nak_time, NULL);
add_timeval_d(&group->fileinfo.nak_time, 1 * group->grtt);
group->fileinfo.nak_section_first = group->fileinfo.last_section;
group->fileinfo.nak_section_last = section + 1;
group->fileinfo.got_done = 0;
log3(group->group_id, group->file_id, "Got DONE, set NAK timer "
"for sections %d - %d", group->fileinfo.nak_section_first,
group->fileinfo.nak_section_last);
}
group->fileinfo.last_section = section + 1;
}
set_timeout(group, 0);
}
/**
* Processes an expected DONE_CONF message
*/
void handle_done_conf(struct group_list_t *group, const unsigned char *message,
unsigned meslen)
{
const struct doneconf_h *doneconf;
const uint32_t *addrlist;
int listlen;
doneconf = (const struct doneconf_h *)message;
addrlist = (const uint32_t *)(message + sizeof(struct doneconf_h));
listlen = (meslen - (doneconf->hlen * 4)) / 4;
if ((meslen < (doneconf->hlen * 4U)) ||
((doneconf->hlen * 4U) < sizeof(struct doneconf_h))) {
log2(group->group_id, group->file_id,
"Rejecting DONE_CONF: invalid message size");
return;
}
if (uid_in_list(addrlist, listlen)) {
log0(group->group_id, group->file_id,
"Group file transfer confirmed");
move_files(group);
file_cleanup(group, 0);
}
}
/**
* Processes an expected CONG_CTRL message
*/
void handle_cong_ctrl(struct group_list_t *group, const unsigned char *message,
unsigned meslen, struct timeval rxtime)
{
const struct cong_ctrl_h *cong_ctrl;
const struct cc_item *cc_list;
int listlen, i, ccidx, clridx;
uint32_t ccrate;
uint16_t new_ccseq;
double new_rtt;
cong_ctrl = (const struct cong_ctrl_h *)message;
cc_list = (const struct cc_item *)(message + sizeof(struct cong_ctrl_h));
listlen = (meslen - (cong_ctrl->hlen * 4)) / sizeof(struct cc_item);
if ((meslen < (cong_ctrl->hlen * 4U)) ||
((cong_ctrl->hlen * 4U) < sizeof(struct cong_ctrl_h))) {
log2(group->group_id, group->file_id,
"Rejecting CONG_CTRL: invalid message size");
return;
}
if (group->cc_type != CC_TFMCC) {
log3(group->group_id, group->file_id, "Rejecting CONG_CTRL: "
"not allowed for given congestion control type");
return;
}
new_ccseq = ntohs(cong_ctrl->cc_seq);
ccrate = unquantize_rate(ntohs(cong_ctrl->cc_rate));
group->last_server_ts.tv_sec = ntohl(cong_ctrl->tstamp_sec);
group->last_server_ts.tv_usec = ntohl(cong_ctrl->tstamp_usec);
group->last_server_rx_ts = rxtime;
for (clridx = -1, ccidx = -1, i = 0;
(i < listlen) && ((clridx == -1) || (ccidx == -1)); i++) {
if (cc_list[i].dest_id == uid) {
ccidx = i;
}
if ((cc_list[i].flags & FLAG_CC_CLR) != 0) {
clridx = i;
}
}
if (ccidx != -1) {
new_rtt = unquantize_grtt(cc_list[ccidx].rtt);
if (ccidx == clridx) {
group->isclr = 1;
send_cc_ack(group);
} else {
group->isclr = 0;
}
if (group->isclr) {
group->rtt = (0.9 * group->rtt) + (0.1 * new_rtt);
} else {
group->rtt = (0.5 * group->rtt) + (0.5 * new_rtt);
}
} else {
group->isclr = 0;
}
if (((int16_t)(new_ccseq - group->ccseq)) > 0) {
init_tfmcc_fb_round(group, new_ccseq);
} else if ((group->cc_time.tv_sec != 0) &&
((ccrate < current_cc_rate(group)) || (ccrate < group->initrate)) &&
(group->grtt > group->rtt)) {
log4(group->group_id, group->file_id, "Canceling feedback timer");
group->cc_time.tv_sec = 0;
group->cc_time.tv_usec = 0;
}
}
uftp-4.1.5/server_phase.h 0000644 0000764 0000764 00000003057 12137013343 014344 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2012 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.
*/
#ifndef _SERVER_PHASE_H
#define _SERVER_PHASE_H
#include "server.h"
int announce_phase(struct finfo_t *finfo);
int transfer_phase(struct finfo_t *finfo);
int completion_phase(struct finfo_t *finfo);
#endif // _SERVER_PHASE_H
uftp-4.1.5/uftp.1 0000644 0000764 0000764 00000060750 12176075022 012555 0 ustar dbush dbush .TH uftp 1 "30 July 2013" "UFTP 4.1"
.SH NAME
uftp - Encrypted UDP based ftp with multicast - server
.SH SYNOPSIS
uftp [ -R txrate ] [ -L logfile ] [ -B udp_buf_size ]
[ -g max_log_size ] [ -n max_log_count ]
[ -Y keytype ] [ -h hashtype ] [ -w sigtype ]
[ -e keyextype[:curve] ] [ -c ] [ -m max_nak_count ]
[ -k key_file ] [ -K key_length | curve ] [ -l ] [ -T ]
[ -b block_size ] [ -t ttl ] [ -Q dscp ] [ -z | -Z ]
[ -I interface ] [ -p port ] [ -u source_port ]
[ -j proxylist_file ] [ -q ] [ -f ] [ -y ]
[ -x log_level ] [ -W txweight ]
[ -H host[,host...] | -H @hostlist_file
| -F restart_file ] [ -X exclude_file ]
[ -M pub_multicast_addr ] [ -P priv_multicast_addr ]
[ -N max_nak_pct ] [ -C cc_type ] [ -o ][ -D dest_name ]
[ -E base_dir[,base_dir... ] ] [ -S status_file ]
[ -r init_grtt[:min_grtt:max_grtt] ] [ -s robust ]
{ -i list_file | file [ file... ] }
.SH DESCRIPTION
.P
uftp is the server process of the UFTP suite. It sends one or more files to one or more receivers via multicast with optional encryption.
.SH OPTIONS
.P
The following options are supported:
.TP
.B \-R txrate
The transmission speed in Kbps. Specifying -1 for this value results in data being sent as fast as the network interface will allow. Using a value of -1 is recommended only if the network path between the server and all clients is as fast as the server\(aqs local interface, and works best in a gigabit environment. Default is 1000 Kbps. Ignored if -C is given any value other than "none".
.TP
.B \-L logfile
Specifies the log file. Default is to write to stderr.
.TP
.B \-B buf_size
The size in bytes of the UDP send buffer and receive buffer to use. Valid values are 65536-104857600 (64KB-100MB). Defaults to 262144.
.TP
.B \-g max_log_size
Specifies the maximum log file size in MB. Once the log file reaches this size, the file is renamed with a .1 extension and a new log file is opened. For example, if the log file is /tmp/uftp.log, it will be renamed /tmp/uftp.log.1 and a new /tmp/uftp.log will be created. Ignored if -L is not specified. Valid values are 1-1024. Default is no log rolling.
.TP
.B \-n max_log_count
Specifies the maximum number of archive log files to keep when log rolling is active. When the log file rolls, archive logs are renamed with an incrementing numerical extension until the max is reached. Archive log files beyond the maximum are deleted. Ignored if -L and -g are not specified. Valid values are 1-1000. Default is 5.
.TP
.B \-Y keytype
The symmetric encryption algorithm to use. Valid values are "des" for DES in CBC mode, "3des" for three key Triple DES in CBC mode, "aes128-cbc", "aes256-cbc", "aes128-gcm", "aes256-gcm", "aes128-ccm", "aes256-ccm", or "none" to not set up encryption at all. The GCM and CCM modes are authenticated encryption modes which applies a signatures at the same time as encryption. If one of these modes are specifies, the value of -w is ignored. Default is "none". Not all installations may support all of these algorithms.
.TP
.B \-h hashtype
The hashing algorithm to use for key derivation and HMAC signatures. Valid values are "sha1" for SHA-1, "sha256" for SHA-256, "sha384" for SHA-384, and "sha512" for SHA-512. Defaults to "sha1". Ignored if -Y is "none". Not all installations may support all of these algorithms.
.TP
.B \-w sigtype
Specifies the type of signature to be applied to encrypted messages. Valid values are "hmac" to apply an HMAC to the encrypted message, and "keyex" to apply either an RSA or ECDSA signature depending on the key exchange algorithm chosen via -e. HMAC signatures are based off the group master key and ensure the sender of a message is a valid member of the group, but does not guarantee that the message came from a specific group member. RSA and ECDSA signatures ensure that messages come from a particular member, but is much much slower to calculate than HMAC and creates a larger per-packet overhead. If the keytype specified by -Y is an authentication mode cipher (i.e. AES in GCM or CCM mode), this field is ignored and signatures will instead be generated at the same time data is encrypted. This also has the lowest size overhead and is the fastest. Default is "hmac". Ignored if -Y is "none".
.TP
.B \-e keyextype[:curve]
Specifies the key exchange algorithm to use. Valid values are "rsa" for an RSA key exchange, "ecdh_rsa" for an Elliptic Curve Diffie-Hellman (ECDH) key exchange with RSA signatures, and "ecdh_ecdsa" for an ECDH key exchange with ECDSA signatures. Using one of the ECDH schemes provides perfect forward security, while using just RSA is slightly more resilient to replay attacks. If ecdh_rsa or ecdh_ecdsa are chosen, the named EC curve that the ECDH key is based on may optionally be selected, with prime256v1 as the default (See -k and -K for the list of available EC curves). Default key exchange scheme is "rsa". Ignored if -Y is "none".
.TP
.B \-c
If specified, forces clients to authenticate by sending their RSA public key in a CLIENT_KEY message. Client key fingerprints and proxy key fingerprints specified by -H and -j respectively will NOT be checked unless -c is specified. Ignored if -Y is "none".
.TP
.B \-m max_nak_count
Specifies the number of times a client reports naks beyond the maximum percentage before getting dropped. Valid values are 1-10. Default is 1.
.TP
.B \-k key_file
.TP
.B \-K key_length | curve
These two options are used to read and/or write the server\(aqs RSA/ECDSA private key. Both are ignored if -Y is "none".
The type of private key read/written depend on the key exchange algorithm chosen via the -e option. If -e is "rsa" or "ecdh_rsa", -K specifies the key length in bits of an RSA public/private keypair to generate, and -k expects an RSA key. If -e is "ecdh_ecdsa", -K specifies a named EC curve on which an EC public/private keypair is generated, and -k expects an EC key.
The list of supported EC curves is as follows (availability may vary depending on system settings and crypto library used):
sect163k1 sect163r1 sect163r2 sect193r1 sect193r2 sect233k1 sect233r1 sect239k1 sect283k1 sect283r1 sect409k1 sect409r1 sect571k1 sect571r1 secp160k1 secp160r1 secp160r2 secp192k1 prime192v1 secp224k1 secp224r1 secp256k1 prime256v1 secp384r1 secp521r1
If neither -k nor -K are specified, either an RSA private key 512 bits in length or an EC private key on curve prime256p1 (depending on the value of -e) is generated but not persisted.
If -k is specified but not -K, the RSA or ECDSA private key is read from key_file.
If -k is not specified but -K is, an RSA or ECDSA private key is generated but not persisted.
If both -k and -K are specified, an RSA or ECDSA private key is generated and stored in key_file.
The definition of key_file is dependent on the crypto library UFTP is compiled to use.
On Windows systems, UFTP can built to use either CNG, which is the new API supported by Windows Vista and Windows 7, or CryptoAPI, which is the legacy API and the only one available to Windows XP.
Under CryptoAPI, all RSA private keys must be stored in a key container (technically only keys used to sign data, but for UFTP\(aqs purposes this is the case). Key containers are internal to Windows, and each user (and the system) has its own set of key containers. In this case, key_file is actually the name of the key container. When -k is not specified, the generated key is not persisted. Elliptic Curve algorithms are not supported under CryptoAPI.
Under CNG, RSA and ECDSA private keys are also stored in key containers, and RSA keys created by CrypoAPI may be read by CNG. Like CryptoAPI, key_file also specifies the key container name, and the generated key is not persisted if -k is not specified. CNG only supports 3 named EC curves: prime256v1, secp384r1, and secp521r1.
All other systems use OpenSSL for the crypto library (although under Windows UFTP can be also be built to use it). In this case, key_file specifies a file name where the RSA private key is stored unencrypted in PEM format (the OS is expected to protect this file). When both -k and -K are specified, the file is only written to if it does not currently exist. If the file does exist, an error message will be returned and the server will exit. When -k is not specified, the generated key is not persisted. These PEM files may also be manipulated via the openssl(1) command line tool.
Keys can also be generated and viewed via the uftp_keymgt(1) utility.
.TP
.B \-l
Follow symbolic links. By default, if the server encounters a symbolic link, it will send the link itself instead of the file it points to. Specifying this flag causes the server to send the file the link points to.
.TP
.B \-T
Print the timestamp on each line of output. If -L is specified, this option is implied.
.TP
.B \-b block_size
Specifies the size of a data block. This value should be around 100-200 bytes less that the path MTU to provide ample room for all headers and extensions, up to and including the IP and UDP headers. Prior to version 4.0, this option specified the MTU and calculated the block size based on that. Default is 1300.
.TP
.B \-t ttl
Specifies the time-to-live for multicast packets. Default is 1.
.TP
.B \-Q dscp
Specifies the Differentiated Services Code Point (DSCP), formerly Type of Service (TOS), in the IP header for all outgoing packets. Valid values are 0-63 and may be specified in either decimal or hexadecimal. Default is 0.
On Windows XP systems, the OS doesn\(aqt allow this parameter to be changed by default. To change this, add/modify the following DWORD registry value, set to 0, and reboot:
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\DisableUserTOSSetting
Not currently supported on Windows Vista or later.
.TP
.B \-z
Enables sync mode. Clients will check if an incoming file exists. If so, the client will decline the incoming file if it either older than the existing file or the same age and the same size as the existing file.
As of version 4.1, parsable output that was previously generated by this option is now enabled separately via the -S option.
.TP
.B \-Z
Sync preview mode. Works like sync mode, except no files are actually transmitted, and the RESULT and STATS lines reflect the status of each file had they actually been sent. The "time" and "speed" datapoints are approximated based on the transmission speed.
.TP
.B \-I interface
The interface to send the data from. Can be specified either by interface name, by hostname, or by IP. If not specified, the default system interface is used.
.TP
.B \-p port
The UDP port number to send to. Default is 1044.
.TP
.B \-u source_port
The UDP port number to send from. Default is 0, which uses a random port number.
.TP
.B \-j proxylist_file
A file containing a list of proxies the server is expecting to hear from. The file should contain the ID of a proxy optionally followed by the proxy\(aqs public key fingerprint, with one on each line. If a key fingerprint is given, the key specified by the proxy must match the fingerprint. This option should not be used without -H. If -H is specified, -j must also be specified if proxies are expected to respond, otherwise the server will reject the proxies.
.nf
Example contents:
0x00001111|66:1E:C9:1D:FC:99:DB:60:B0:1A:F0:8F:CA:F4:28:27:A6:BE:94:BC
0x00002222
.fi
.TP
.B \-q
Quit-on-error flag. Normally, the server will continue with a session as long as at least one client is still active. With this flag, the server will quit if any client aborts, drops out, or never responds. Most useful in conjunction with clients using the temp directory option (-T) so that clients that successfully receive at least one file before being told to abort don\(aqt have files from an aborted session in the destination directory.
.TP
.B \-f
Restartable flag. If specified, and at least one client fails to receive all files, the server will write a restart file named "_group_{group ID}_restart in the current directory to save the current state, which includes the group ID, list of files, and list of failed clients. This file can then be passed to -F to restart the failed transfer.
.TP
.B \-y
For Windows systems using CryptoAPI or CNG, private keys are normally stored in the key container of the running user. Specifying this option stores keys in the system key container. On non-Windows systems, this option has no effect.
.TP
.B \-x log_level
Specifies current logging level. Valid values are 0-5, with 0 being the least verbose and 5 being the most verbose. Default is 2, which is consistent with logging prior to version 3.5.
.TP
.B \-W txweight
Sets the maximum file transfer time, expressed as a percentage of the optimal time. Valid values are 110-10000. Ignored if congestion control is enabled. Default is no maximum time.
.TP
.B \-H { host[,host...] | @hostlist_file }
Specifies the clients for closed group membership. Can be specified as either a comma separated list of client IDs, or can be read from hostlist_file. This file is in the same format as proxylist_file. Note that key fingerprints cannot be specified using the comma separated syntax. Clients that are behind a proxy do not need key fingerprints specified, since the proxy\(aqs key fingerprint will be checked instead. If unspecified, open group membership is used, and any client may register.
.TP
.B \-F restart_file
Specifies the name of a restart file to use to resume a failed transfer. If specified, -H may not be specified and all files listed to send will be ignored, since the restart file contains both of these. All other command line options specified on the first attempt are not automatically applied, so you can alter then for the next attempt if need be.
.TP
.B \-X exclude_file
A file containing the names of files/paths to be excluded from the session, one per line. For example, if you send a directory called d1 containing subdirectories d2, d3, and d4, and you don\(aqt want to send the contents of d4, the exclude_file should contain a line reading "d1/d4".
.TP
.B \-M pub_multicast_addr
The public address to announce on. May be either a multicast address or a unicast address, and either IPv4 or IPv6. If a unicast address is specified, the -P option is ignored and all data moves over the specified unicast address. If a multicast IPv6 address is specified, -P must also be specified. Default is 230.4.4.1.
.TP
.B \-P priv_multicast_addr
The private multicast address that the data is transferred to. One or more parts of the IP address (other that the first) may be replaced with the letter \(aqx\(aq, resulting in a random number being chosen for that part, either 0-255 for IPv4 or 0-0xFFFF for IPv6. Default value is 230.5.5.x. If clients are using source specific multicast (SSM), this and -M must specify valid SSM addresses, which fall in the range 232.0.0.0/8 for IPv4 and ff3x::/32 for IPv6 (here x specifies the multicast scope). The values for -M and -P must both be the same IP version.
.TP
.B \-N max_nak_pct
Specifies the maximum percentage of NAKs that a client can report for a particular section. This option works with the -m option, which specifies the number of times a client may exceed this limit before getting dropped. This allows the server to keep a very slow client from stalling the session for others. Valid values are 0-100. Default is 100.
.TP
.B \-C cc_type
Specifies the congestion control mode to use. Currently supported values are "none" and "tfmcc". Specifiying "none" means data will be sent at a fixed rate as specified by the -R option. Specifying "tfmcc" will use the TCP Friendly Multicast Congestion Control scheme as specified in RFC 4654. Default value is "none".
The following is a description of how congestion control worked prior to version 4.0 and should be considered historical.
Specifies a congestion control config file. Normally, the server always transmits at the speed specified by -R. With this option, the speed can be adjusted each time the server makes a request for NAKs from the clients based on the percentage of NAKs received to data packets sent. The file consists of one or more of the following lines:
percentage;scaling_factor
Where "percentage" is a whole number from 0-100 specifying a percentage of NAKs, and scaling_factor is a positive decimal number that the current sending rate is multiplied by for the given percentage. Entries must be listed in ascending order by percentage. If there is no entry for "100", the scaling factor for the last entry becomes the scaling factor for "100".
When the server collects NAKs from the clients, it calculates the NAK percentage, then searches the congestion control entries in order for a percengage greater than or equal to the current NAK percentage, and adjusts the rate by the corresponding scaling factor.
There may also be a single line specifying the maximum transmission speed:
max;speed
Where "speed" is the transmission speed in Kbps. If this entry is not specified, the maximum speed is the initial speed specified by -R.
The congestion control config file is reread each time just before adjusting the rate. This allows environments which externally monitor the network to adjust the configuration on the fly. In the event of a failure to read the file, the last configuration successfully read is used.
Here is a sample cc_config file:
.nf
max;10000
0;1.3
5;1.1
10;0.9
25;0.7
50;0.5
100;0.4
.fi
.TP
.B \-o
.TP
.B \-D dest_name
These options specify the name given to the sent file(s) on the client side. If only one file/directory is specified to send and -o is not specified, the name spcified by -D is given to that file/directory, and the effects of -E are ignored. If more than one file/directory is specified to send, or if -o is specified, they are placed in a subdirectory with the name spcified by -D.
This option may also specify an absolute path name. If so, clients must be either all Windows or all UNIX-like, since they have differing filesystem structures, otherwise the behavior is undefined. The server, however, need not be the same OS as the clients. When specifying an absolute path name, the path must be contained in one of a client\(aqs destination directories, otherwise the client will reject the file. When sending to Windows clients, an absolute path may be either local (drive:\\path\\to\\file) or remote (\\\\host\\share\\path\\to\\file).
.TP
.B
-E base_dir[,base_dir...]
Specifies one or more "base" directories for files. Normally, for any file/directory specified, any leading path elements are stripped from the name before sending. If the specified file/directory name matches one of the base directories, only the path elements of the base directory are stripped, and the remainder is sent as the file name. Any specified file/directory that does not match a base directory is skipped.
For example, without -E, if you pass /path/to/file to send, the transmitted filename is file. If you pass in -E /path, the transmitted file name is to/file.
.TP
.B \-S status_file
Prints easily parsable status information to a file. This information was previously only available in sync mode (-z) and was mixed with the normal logging output.
The following is printed for each client after all have registered:
CONNECT;status;target
Where "status" is either "success" or "failed", and "target" is the name of the client.
The following is printed after each file:
RESULT;target;filename;size;status;speed
Where "target" is the name of the client, "file" is the name of the current file, "size" is the size of the file in kilobytes (i.e. 1234KB), "speed" is the transmission speed for that file in KB/s, and status is:
copy: The file was sent.
overwrite: The file was sent, and overwrote an existing file. Only generated in sync mode.
skipped: The file was declined by the client because it is older that the existing file. Only generated in sync mode.
rejected: The file was rejected, because the file was sent with an absolute pathname and either the client is using a temp directory or the filename doesn\(aqt match one of the client\(aqs destination directories.
The following is printed at the end of the session:
STATS;target;num_copy;num_overwrite;num_skip;total_size;time;speed
Where "target" is the name of the client, "num_copy" is the number of files sentwith "copy" status, "num_overwrite" is the number of files sent with "overwrite" status, "num_skip" is the number of files sent with "skipped" status, "total_size" is the total size of all files sent in kilobytes, "time" is the total transmission time for all files, and "speed" is the overall transmission speed for all files.
Also, the following line is printed verbatim prior to the STATS lines for ease of reading:
HSTATS;target;copy;overwrite;skip;totalKB;time;speedKB/s
.TP
.B \-r init_grtt[:min_grtt:max_grtt]
Specifies the initial value, and optionally the min and max values, of the Group Round Trip Time (GRTT) used in timing calculations. The GRTT changes dynamically based on the network conditions. This option is useful if the initial connection period is to short or long, if receivers are getting bogged down and cannot respond to the server quick enough before timing out, or if receivers are getting flagged with too high of an RTT and take too long to recover to a resonable value. Valid values are 0.001 to 1000. Defaults are 0.5 for init_grtt, 0.01 for min_grtt, and 15.0 for max_grtt.
.TP
.B \-s robust
Specifies the robustness factor for message retransmission. The server will resend particular messages up to robust times while waiting for client responses. Valid values are 10-50. Default is 20.
.TP
.B \-i list_file
Name of a file containing a list of files to send, one per line. Empty lines are ignored. Passing in \(aq-\(aq for list_file reads files from stdin. Other files specified on the command line are ignored if -i is given.
.TP
.B file [ file...]
The file(s) or directory(ies) to send. Any special files (block/character devices, pipes, sockets, etc.) are skipped. By default, any symbolic links are sent as links (see -l). Any Windows client will silently refuse to create them. If -F or -i is specified, any files listed will be ignored.
There are also special metafile names that can send commands to the clients. The @DELETE:{filename} metafile instructs the client to delete the given filename. The usual rules regarding which of the client\(aqs destination directories to use also applies here. The @FREESPACE metafile will cause the client to report back the amount of free disk space in the primary destination directory.
.SH EXAMPLES
.P
Starting with the default options:
.RS 5
uftp the_file
.RE
The server sends the_file with no encryption at 1000 Kbps, sending announcements over 230.4.4.1 and later messages over 230.5.5.x (x is randomly selected). Any client that responds to the announcement will be accepted. The payload portion of the packets will be 1300 bytes.
To send at 50 Mbps:
.RS 5
uftp -R 50000 the_file
.RE
Or to allow the transmission rate to be determined dynamically:
.RS 5
uftp -C tfmcc the_file
.RE
To send multiple files:
.RS 5
uftp file_1 file_2 file_3
.RE
or:
.RS 5
uftp dir_1 dir_2 file_3
.RE
To send multiple files that all land in a certain subdirectory on each client:
.RS 5
uftp -D dest_dir file_1 file_2
.RE
To send announcements over multicast address 224.1.2.3 and later messages over 224.4.5.6:
.RS 5
uftp -M 224.1.2.3 -P 224.4.5.6 file
.RE
Or for IPv6:
.RS 5
uftp -M ff02::1:2:3 -P ff02::4:5:6 file
.RE
Or in unicast mode:
.RS 5
uftp -M host_or_ip file
.RE
Where host_or_ip is the hostname or unicast IP address of the host to send to.
To send only to certain hosts:
.RS 5
uftp -H client_id_1,client_id_2,client_id_3 file_to_send
.RE
or:
.RS 5
uftp -H @file_containing_list_of_clients file_to_send
.RE
If you want to use jumbo ethernet frames of 9000 bytes (leaving 200 bytes of space for headers):
.RS 5
uftp -b 8800 file_to_send
.RE
To send /path/to/file1 and /path/to/file2, and have them appear on clients as /remote/dir/to/file1 and /remote/dir/to/file2:
.RS 5
uftp -E /path -D /remote/dir /path/to/file1 /path/to/file2
.RE
To send a file encrypted with AES-256-CBC and SHA-1 hashing, using an autogenerated 512-bit RSA key to negotiate the session:
.RS 5
uftp -Y aes256-cbc -h sha1 file_to_send
.RE
To do the above with a previously generated RSA key stored in key_file_or_container (under Windows, the name of an internal key container, otherwise the name of a file containing the key in PEM format):
.RS 5
uftp -Y aes256-cbc -h sha1 -k key_file_or_container file_to_send
.RE
.SH SEE ALSO
uftpd(1), uftpproxyd(1), uftp_keymgt(1)
.SH NOTES
.P
The latest version of UFTP can be found at http://uftp-multicast.sourceforge.net. UFTP is covered by the GNU General Public License. Commercial licenses and support are available from Dennis Bush (bush@tcnj.edu).
uftp-4.1.5/server_common.h 0000644 0000764 0000764 00000004664 12250244021 014534 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_COMMON_H
#define _SERVER_COMMON_H
#include "server.h"
void set_uftp_header(struct uftp_h *header, int func, uint32_t group_id,
uint8_t group_inst, double l_grtt, int l_gsize);
void send_abort(const struct finfo_t *finfo, const char *message,
const union sockaddr_u *destaddr,
uint32_t dest, int encrypt, int current);
int send_multiple(const struct finfo_t *finfo, unsigned char *packet,
int message, int attempt, uint32_t *idlist, int state,
int encrypt, const union sockaddr_u *destaddr, int regconf);
int validate_packet(const unsigned char *packet, int len,
const struct finfo_t *finfo);
int sign_announce(const struct finfo_t *finfo, unsigned char *packet, int len);
int find_client(uint32_t addr);
int client_error(int listidx);
void handle_abort(const unsigned char *message, int meslen, int idx,
struct finfo_t *finfo, uint32_t src);
int recalculate_grtt(const struct finfo_t *finfo, int grtt_set,
int clear_measured);
#endif // _SERVER_COMMON_H
uftp-4.1.5/proxy_config.c 0000644 0000764 0000764 00000045523 12250244021 014356 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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 pidfile[MAXPATHNAME];
char keyfile[MAXLIST][MAXPATHNAME], keyinfo[MAXLIST][MAXPATHNAME];
int proxy_type, debug, rcvbuf, dscp, keyfile_count, keyinfo_count;
int hb_interval, priority;
unsigned int ttl;
char portname[PORTNAME_LEN], out_portname[PORTNAME_LEN];
int port, out_port;
union sockaddr_u down_addr;
int have_down_fingerprint;
uint8_t down_fingerprint[HMAC_LEN];
uint32_t down_nonce, uid;
union sockaddr_u hb_hosts[MAXLIST];
union sockaddr_u 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 iflist out_if;
union key_t privkey[MAXLIST];
int privkey_type[MAXLIST];
union key_t dhkey;
uint8_t ecdh_curve;
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, int expect_ip)
{
char line[1000], *hostid, *ipstr, *fingerprint;
FILE *hostfile;
struct addrinfo ai_hints, *ai_rval;
uint32_t remote_uid;
int rval;
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';
}
if ((line[0] == '#') || (line[0] == '\x0')) {
continue;
}
hostid = line;
ipstr = strchr(hostid, '|');
if (ipstr) {
*ipstr = '\x0';
ipstr++;
if (expect_ip) {
fingerprint = strchr(ipstr, '|');
if (fingerprint) {
*fingerprint = '\x0';
fingerprint++;
}
} else {
fingerprint = ipstr;
ipstr = NULL;
}
} else {
fingerprint = NULL;
}
if (strlen(hostid) >= DESTNAME_LEN) {
fprintf(stderr, "Server/Client list %s: name too long\n", filename);
exit(1);
}
remote_uid = strtoul(hostid, NULL, 16);
if ((remote_uid == 0xffffffff) || (remote_uid == 0)) {
fprintf(stderr, "Invalid UID %s\n", hostid);
exit(1);
}
list[*list_count].uid = htonl(remote_uid);
if (expect_ip) {
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(ipstr, NULL, &ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid host name/address %s: %s\n",
ipstr, gai_strerror(rval));
exit(1);
}
memcpy(&list[*list_count].addr, ai_rval->ai_addr,
ai_rval->ai_addrlen);
freeaddrinfo(ai_rval);
}
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(void)
{
proxy_type = UNDEF_PROXY;
hbhost_count = 0;
memset(hb_hosts, 0, sizeof(hb_hosts));
hb_interval = DEF_HB_INT;
debug = 0;
log_level = DEF_LOG_LEVEL;
strncpy(portname, DEF_PORT, sizeof(portname)-1);
portname[sizeof(portname)-1] = '\x0';
port = atoi(portname);
memset(&out_if, 0, sizeof(out_if));
ttl = DEF_TTL;
dscp = DEF_DSCP;
strncpy(out_portname, DEF_PORT, sizeof(out_portname)-1);
out_portname[sizeof(out_portname)-1] = '\x0';
out_port = atoi(out_portname);
uid = 0;
rcvbuf = 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;
keyinfo_count = 0;
interface_count = 0;
pub_multi_count = 0;
sys_keys = 0;
priority = 0;
ecdh_curve = 0;
max_log_size = 0;
max_log_count = DEF_MAX_LOG_COUNT;
}
/**
* Set argument defaults, read and validate command line options
*/
void process_args(int argc, char *argv[])
{
struct addrinfo ai_hints, *ai_rval;
int c, i, listidx, rval;
long tmpval;
char *p, *p2, *hoststr, *portstr, pubname[INET6_ADDRSTRLEN];
const char opts[] = "s:crdx:p:t:Q:N:O:U:q:mh:H:g:n:B:L:P:C:S:e:k:K:I:M:";
set_defaults();
srand((unsigned int)time(NULL) ^ getpid());
// 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);
}
down_nonce = rand32();
} else {
have_down_fingerprint = 0;
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(optarg, NULL, &ai_hints,
&ai_rval)) != 0) {
fprintf(stderr, "Invalid host name: %s: %s\n",
optarg, gai_strerror(rval));
exit(1);
}
memcpy(&down_addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
freeaddrinfo(ai_rval);
}
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 'x':
log_level = atoi(optarg);
if (log_level < 0) {
fprintf(stderr, "Invalid log level\n");
exit(1);
}
break;
case 'p':
strncpy(portname, optarg, sizeof(portname)-1);
portname[sizeof(portname)-1] = '\x0';
port = atoi(portname);
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 'N':
priority = atoi(optarg);
if (!valid_priority(priority)) {
fprintf(stderr, "Invalid priority value\n");
exit(1);
}
break;
case 'O':
if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) {
out_if = ifl[listidx];
break;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid name/address %s: %s\n",
optarg, gai_strerror(rval));
exit(1);
}
// Just use the first addrinfo entry
if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
ifl, ifl_len)) == -1) {
fprintf(stderr, "Interface %s not found", optarg);
exit(1);
}
out_if = ifl[listidx];
freeaddrinfo(ai_rval);
break;
case 'U':
errno = 0;
uid = strtoul(optarg, NULL, 16);
if (errno) {
perror("Invalid UID\n");
exit(1);
}
uid = htonl(uid);
break;
case 'q':
strncpy(out_portname, optarg, sizeof(out_portname)-1);
out_portname[sizeof(out_portname)-1] = '\x0';
out_port = atoi(out_portname);
if (out_port == 0) {
fprintf(stderr, "Invalid outgoing port\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;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(hoststr, portstr ? portstr : DEF_PORT,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid name/address %s: %s\n",
hoststr, gai_strerror(rval));
exit(1);
}
memcpy(&hb_hosts[hbhost_count++], ai_rval->ai_addr,
ai_rval->ai_addrlen);
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 'g':
max_log_size = atoi(optarg);
if ((max_log_size < 1) || (max_log_size > 1024)) {
fprintf(stderr, "Invalid max log size\n");
exit(1);
}
max_log_size *= 1000000;
break;
case 'n':
max_log_count = atoi(optarg);
if ((max_log_count < 1) || (max_log_count > 1000)) {
fprintf(stderr, "Invalid max log count\n");
exit(1);
}
break;
case 'B':
rcvbuf = atoi(optarg);
if ((rcvbuf < 65536) || (rcvbuf > 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, 0);
break;
case 'S':
add_hosts_by_name(server_fp, &server_fp_count, optarg, 1);
break;
case 'e':
ecdh_curve = get_curve(optarg);
if (ecdh_curve == 0) {
fprintf(stderr, "Invalid curve\n");
exit(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':
p = strtok(optarg, ",");
while (p != NULL) {
strncpy(keyinfo[keyinfo_count], p, sizeof(keyinfo[0])-1);
keyinfo[keyinfo_count][sizeof(keyinfo[0])-1] = '\x0';
keyinfo_count++;
p = strtok(NULL, ",");
}
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;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(p, NULL,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid name/address %s: %s\n",
p, gai_strerror(rval));
exit(1);
}
if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
ifl, ifl_len)) == -1) {
fprintf(stderr, "Interface %s not found\n", p);
exit(1);
}
m_interface[interface_count++] = ifl[listidx];
freeaddrinfo(ai_rval);
p = strtok(NULL, ",");
}
break;
case 'M':
p = strtok(optarg, ",");
while (p != NULL) {
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(p, NULL,
&ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid multicast address %s: %s\n",
p, gai_strerror(rval));
exit(1);
}
memcpy(&pub_multi[pub_multi_count], ai_rval->ai_addr,
ai_rval->ai_addrlen);
pub_multi_count++;
freeaddrinfo(ai_rval);
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 == RESPONSE_PROXY) {
out_port = port;
}
if (proxy_type == SERVER_PROXY) {
if (down_addr.ss.ss_family == AF_INET6) {
down_addr.sin6.sin6_port = htons(out_port);
} else {
down_addr.sin.sin_port = htons(out_port);
}
}
if (proxy_type != CLIENT_PROXY) {
if (server_fp_count) {
for (i = 0; i < pub_multi_count; i++) {
if (!is_multicast(&pub_multi[i], 1)) {
if ((rval = getnameinfo((struct sockaddr *)&pub_multi[i],
family_len(pub_multi[i]), pubname, sizeof(pubname),
NULL, 0, NI_NUMERICHOST)) != 0) {
fprintf(stderr,"getnameinfo failed: %s",
gai_strerror(rval));
}
fprintf(stderr, "Invalid source specific "
"multicast address: %s\n", pubname);
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 ((keyfile_count != 0) && (keyinfo_count != 0) &&
(keyfile_count != keyinfo_count)) {
fprintf(stderr, "Must list same number of items for -k and -K\n");
exit(1);
}
for (i = 0; i < pub_multi_count; i++) {
if (pub_multi[i].ss.ss_family == AF_INET6) {
pub_multi[i].sin6.sin6_port = htons(out_port);
} else {
pub_multi[i].sin.sin_port = htons(out_port);
}
}
}
uftp-4.1.5/uftp_common.h 0000644 0000764 0000764 00000020527 12250244021 014200 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _UFTP_COMMON_H
#define _UFTP_COMMON_H
#include
#ifdef WINDOWS
#include
#include
#else
#include
#endif
#include "uftp.h"
#include "encryption.h"
#define DEF_LOG_LEVEL 2
#define DEF_MAX_LOG_COUNT 5
extern char logfile[MAXPATHNAME];
extern int showtime;
extern FILE *applog;
extern int log_level, init_log_mux, use_log_mux, max_log_count;
extern f_offset_t log_size, max_log_size;
extern mux_t log_mux;
void init_log(int _debug);
void close_log(void);
void roll_log(void);
void logfunc(uint32_t group_id, uint16_t file_id, int level, int _showtime,
int newline, int err, int sockerr, const char *str, ...);
#define clog0(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 0, 0, 0, __VA_ARGS__)
#define log0(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog0(...) \
logfunc(0, 0, 0, 0, 0, 0, 0, __VA_ARGS__)
#define slog0(...) \
logfunc(0, 0, 0, 0, 1, 0, 0, __VA_ARGS__)
#define clog1(group_id, file_id, ...) \
logfunc(group_id, file_id, 1, showtime, 0, 0, 0, __VA_ARGS__)
#define log1(group_id, file_id, ...) \
logfunc(group_id, file_id, 1, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog1(...) \
logfunc(0, 0, 1, 0, 0, 0, 0, __VA_ARGS__)
#define slog1(...) \
logfunc(0, 0, 1, 0, 1, 0, 0, __VA_ARGS__)
#define clog2(group_id, file_id, ...) \
logfunc(group_id, file_id, 2, showtime, 0, 0, 0, __VA_ARGS__)
#define log2(group_id, file_id, ...) \
logfunc(group_id, file_id, 2, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog2(...) \
logfunc(0, 0, 2, 0, 0, 0, 0, __VA_ARGS__)
#define slog2(...) \
logfunc(0, 0, 2, 0, 1, 0, 0, __VA_ARGS__)
#define clog3(group_id, file_id, ...) \
logfunc(group_id, file_id, 3, showtime, 0, 0, 0, __VA_ARGS__)
#define log3(group_id, file_id, ...) \
logfunc(group_id, file_id, 3, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog3(...) \
logfunc(0, 0, 3, 0, 0, 0, 0, __VA_ARGS__)
#define slog3(...) \
logfunc(0, 0, 3, 0, 1, 0, 0, __VA_ARGS__)
#define clog4(group_id, file_id, ...) \
logfunc(group_id, file_id, 4, showtime, 0, 0, 0, __VA_ARGS__)
#define log4(group_id, file_id, ...) \
logfunc(group_id, file_id, 4, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog4(...) \
logfunc(0, 0, 4, 0, 0, 0, 0, __VA_ARGS__)
#define slog4(...) \
logfunc(0, 0, 4, 0, 1, 0, 0, __VA_ARGS__)
#define clog5(group_id, file_id, ...) \
logfunc(group_id, file_id, 5, showtime, 0, 0, 0, __VA_ARGS__)
#define log5(group_id, file_id, ...) \
logfunc(group_id, file_id, 5, showtime, 1, 0, 0, __VA_ARGS__)
#define sclog5(...) \
logfunc(0, 0, 5, 0, 0, 0, 0, __VA_ARGS__)
#define slog5(...) \
logfunc(0, 0, 5, 0, 1, 0, 0, __VA_ARGS__)
#define syserror(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 1, errno, 0, __VA_ARGS__)
#define sockerror(group_id, file_id, ...) \
logfunc(group_id, file_id, 0, showtime, 1, errno, 1, __VA_ARGS__)
union sockaddr_u {
struct sockaddr_storage ss;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
struct iflist {
char name[IFNAME_LEN];
union sockaddr_u su;
int isloopback;
int ismulti;
int ifidx;
};
const char *func_name(int func);
const char *curve_name(int curve);
uint8_t get_curve(const char *name);
int32_t diff_sec(struct timeval t2, struct timeval t1);
int64_t diff_usec(struct timeval t2, struct timeval t1);
int cmptimestamp(struct timeval t1, struct timeval t2);
struct timeval add_timeval(struct timeval t2, struct timeval t1);
void add_timeval_d(struct timeval *t2, double t1);
struct timeval diff_timeval(struct timeval t2, struct timeval t1);
const char *printll(int64_t n);
void getiflist(struct iflist *list, int *len);
void split_path(const char *path, char **dir, char **file);
int parse_fingerprint(unsigned char *fingerprint, const char *fingerprint_str);
int is_multicast(const union sockaddr_u *addr, int ssm);
int addr_equal(const union sockaddr_u *addr1, const union sockaddr_u *addr2);
int addr_blank(const union sockaddr_u *addr);
uint64_t uftp_htonll(uint64_t val);
uint64_t uftp_ntohll(uint64_t val);
int family_len(union sockaddr_u addr);
int would_block_err(void);
int nb_sendto(SOCKET s, const void *msg, int len, int flags,
const struct sockaddr *to, int tolen);
int read_packet(SOCKET sock, union sockaddr_u *sa, unsigned char *buffer,
int *len, int bsize, const struct timeval *timeout);
void build_iv(uint8_t *iv, const uint8_t *salt, int ivlen, uint64_t ivctr,
uint32_t src_id);
void printhex(const char *name, const unsigned char *data, int len);
int is_auth_enc(int keytype);
int is_gcm_mode(int keytype);
int is_ccm_mode(int keytype);
int unauth_key(int keytype);
int encrypt_and_sign(const unsigned char *decpacket, unsigned char **encpacket,
int declen, int *enclen, int keytype, uint8_t *key,
const uint8_t *salt, uint64_t *ivctr, int ivlen,
int hashtype, uint8_t *hmackey, uint8_t hmaclen,
int sigtype, int keyextype, union key_t privkey,
int privkeylen);
int validate_and_decrypt(unsigned char *encpacket, unsigned int enclen,
unsigned char **decpacket, unsigned int *declen,
int keytype, const uint8_t *key,
const uint8_t *salt, int ivlen, int hashtype,
const uint8_t *hmackey, uint8_t hmaclen, int sigtype,
int keyextype, union key_t pubkey, int pubkeylen);
void PRF(int hashtype, 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);
const char *print_key_fingerprint(const union key_t key, int keytype);
/**
* Key fingerprint for an allowed server or client
*/
struct fp_list_t {
uint32_t uid;
union sockaddr_u addr;
int has_fingerprint;
uint8_t fingerprint[HMAC_LEN];
};
int multicast_join(SOCKET s, uint32_t group_id, const union sockaddr_u *multi,
const struct iflist *addrlist, int addrlen,
const struct fp_list_t *fplist, int fplist_len);
void multicast_leave(SOCKET s, uint32_t group_id, const union sockaddr_u *multi,
const struct iflist *addrlist, int addrlen,
const struct fp_list_t *fplist, int fplist_len);
int getifbyname(const char *name, const struct iflist *list, int len);
int getifbyaddr(union sockaddr_u *su, const struct iflist *list, int len);
int file_read(int fd, void *buf, int buflen, int allow_eof);
int file_write(int fd, const void *buf, int buflen);
int64_t free_space(const char *dir);
int valid_priority(int priority);
uint32_t rand32(void);
void *safe_malloc(size_t size);
void *safe_calloc(size_t num, size_t size);
uint8_t quantize_grtt(double rtt);
double unquantize_grtt(uint8_t rtt);
uint8_t quantize_gsize(int size);
int unquantize_gsize(uint8_t size);
uint16_t quantize_rate(uint32_t size);
uint32_t unquantize_rate(uint16_t size);
#endif // _UFTP_COMMON_H
uftp-4.1.5/proxy_loop.c 0000644 0000764 0000764 00000034246 12304524207 014071 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2014 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 // WINDOWS
#include
#include
#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(void)
{
static struct timeval tv = {0,0};
struct timeval current_timestamp, min_timestamp;
int i, j, found_timeout, recheck, pending;
struct pr_group_list_t *group;
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++) {
group = &group_list[i];
if (group->group_id != 0) {
if ((group->phase == PR_PHASE_REGISTERED) ||
(group->phase == PR_PHASE_READY)) {
if ((group->phase == PR_PHASE_READY) &&
(cmptimestamp(current_timestamp,
group->phase_timeout_time) >= 0)) {
send_keyinfo(group, NULL, 0);
recheck = 1;
}
if (cmptimestamp(current_timestamp,
group->phase_expire_time) >= 0) {
group->phase = PR_PHASE_RECEIVING;
check_unfinished_clients(group, 1);
}
}
if (cmptimestamp(current_timestamp, group->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.
log5(group->group_id, 0, "timeout, checking pending");
for (pending = 0, j = 0; (j < MAX_PEND) && !pending; j++) {
if (group->pending[j].msg != 0) {
log5(group->group_id, 0, "found pending %s",
func_name(group->pending[j].msg));
pending = 1;
}
}
if (pending) {
send_all_pending(group);
} else {
log1(group->group_id, 0, "Group timed out");
group_cleanup(group);
}
recheck = 1;
}
if (!recheck && ((!found_timeout) ||
(cmptimestamp(group->timeout_time,
min_timestamp) < 0))) {
min_timestamp = group->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, uid);
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(void)
{
struct uftp_h *header;
struct pr_group_list_t *group;
unsigned char *buf, *decrypted, *message;
char rxname[INET6_ADDRSTRLEN];
int packetlen, rval, hostidx, i;
unsigned int decryptlen, meslen;
uint8_t *func;
union sockaddr_u src;
struct timeval *tv;
double new_grtt;
log0(0, 0, "%s", VERSIONSTR);
for (i = 0; i < key_count; i++) {
if (privkey_type[i] == KEYBLOB_RSA) {
log1(0, 0, "Loaded %d bit RSA key with fingerprint %s",
RSA_keylen(privkey[i].rsa) * 8,
print_key_fingerprint(privkey[i], KEYBLOB_RSA));
} else {
log1(0, 0, "Loaded ECDSA key with curve %s and fingerprint %s",
curve_name(get_EC_curve(privkey[i].ec)),
print_key_fingerprint(privkey[i], KEYBLOB_EC));
}
}
buf = safe_calloc(MAXMTU, 1);
decrypted = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
while (1) {
tv = getrecenttimeout();
if (tv) {
log5(0, 0, "timeout: %d.%06d", tv->tv_sec, tv->tv_usec);
}
if (read_packet(listener, &src, buf, &packetlen,
MAXMTU, tv) <= 0) {
continue;
}
if ((rval = getnameinfo((struct sockaddr *)&src, family_len(src),
rxname, sizeof(rxname), NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
if (header->version != UFTP_VER_NUM) {
log2(0, 0, "Invalid message from %s: not uftp packet "
"or invalid version", rxname);
continue;
}
if (packetlen < sizeof(struct uftp_h) + 4) {
log1(0, 0, "Invalid packet size from %s: %d", rxname, packetlen);
continue;
}
if (addr_equal(&src, &out_if.su)) {
// Packet from self -- drop
continue;
}
if (header->func == HB_REQ) {
handle_hb_request(&src, buf, packetlen);
continue;
}
if (header->func == HB_RESP) {
handle_hb_response(listener, &src, buf + sizeof(struct uftp_h),
packetlen - sizeof(struct uftp_h), hb_hosts,
hbhost_count, privkey[0], privkey_type[0], uid);
continue;
}
if (header->func == KEY_REQ) {
handle_key_req(&src, buf, packetlen);
continue;
}
if (header->func == PROXY_KEY) {
// Only clients handle these, so drop
continue;
}
if ((proxy_type == SERVER_PROXY) && (addr_blank(&down_addr))) {
log2(0, 0, "Rejecting message from %s: downstream address "
"not established", rxname);
continue;
}
group = find_group(ntohl(header->group_id), header->group_inst);
if (header->func == ANNOUNCE) {
handle_announce(group, &src, buf, packetlen);
} else {
if (group == NULL) {
continue;
}
if (group->version != header->version) {
log1(group->group_id, 0, "Version mismatch");
continue;
}
if (proxy_type == SERVER_PROXY) {
// Server proxies don't do anything outside of an ANNOUNCE.
// Just send it on through.
if (!memcmp(&src, &group->up_addr, sizeof(src))) {
new_grtt = unquantize_grtt(header->grtt);
if (fabs(new_grtt - group->grtt) > 0.001) {
group->grtt = new_grtt;
set_timeout(group, 0, 1);
}
group->gsize = unquantize_gsize(header->gsize);
log4(group->group_id, 0, "grtt: %.3f", group->grtt);
}
forward_message(group, &src, buf, packetlen);
continue;
}
if (!memcmp(&src, &group->up_addr, sizeof(src))) {
// Downstream message
if (group->src_id != header->src_id) {
log1(group->group_id, 0, "Source ID mismatch");
continue;
}
new_grtt = unquantize_grtt(header->grtt);
if (fabs(new_grtt - group->grtt) > 0.001) {
group->grtt = new_grtt;
set_timeout(group, 0, 1);
}
group->gsize = unquantize_gsize(header->gsize);
log4(group->group_id, 0, "grtt: %.3f", group->grtt);
message = buf + sizeof(struct uftp_h);
meslen = packetlen - sizeof(struct uftp_h);
if (header->func == ABORT) {
handle_abort(group, &src, message, meslen, header->src_id);
} else if (header->func == KEYINFO) {
handle_keyinfo(group, message, meslen, header->src_id);
} else if ((header->func == REG_CONF) &&
(group->keytype != KEY_NONE)) {
handle_regconf(group, message, meslen);
} else {
// If we don't need to process the message, don't bother
// decrypting anything. Just forward it on.
forward_message(group, &src, buf, packetlen);
}
} else {
// Upstream message
// Decrypt first if necessary
hostidx = find_client(group, header->src_id);
if ((hostidx == -1) && (header->func != REGISTER) &&
(header->func != CLIENT_KEY)) {
log1(0, 0, "Host %08X not in host list",
ntohl(header->src_id));
continue;
}
if ((hostidx != -1) && (header->func == ENCRYPTED) &&
(group->keytype != KEY_NONE)) {
if (!validate_and_decrypt(buf, packetlen, &decrypted,
&decryptlen, group->keytype, group->groupkey,
group->groupsalt, group->ivlen, group->hashtype,
group->grouphmackey, group->hmaclen, group->sigtype,
group->keyextype, group->destinfo[hostidx].pubkey,
group->destinfo[hostidx].pubkeylen)) {
log2(ntohl(header->group_id), 0, "Rejecting message "
"from %s: decrypt/validate failed", rxname);
continue;
}
func = (uint8_t *)decrypted;
message = decrypted;
meslen = decryptlen;
} else {
if ((hostidx != -1) &&
(group->keytype != KEY_NONE) &&
((header->func == KEYINFO_ACK) ||
(header->func == FILEINFO_ACK) ||
(header->func == STATUS) ||
(header->func == COMPLETE))) {
log2(ntohl(header->group_id), 0, "Rejecting %s message "
"from %s: not encrypted",
func_name(header->func), rxname);
continue;
}
func = (uint8_t *)&header->func;
message = buf + sizeof(struct uftp_h);
meslen = packetlen - sizeof(struct uftp_h);
}
switch (*func) {
case REGISTER:
handle_register(group, hostidx, message, meslen,
header->src_id);
break;
case CLIENT_KEY:
handle_clientkey(group, hostidx, message, meslen,
header->src_id);
break;
case KEYINFO_ACK:
handle_keyinfo_ack(group, hostidx, message, meslen);
break;
case FILEINFO_ACK:
handle_fileinfo_ack(group, hostidx, message, meslen);
break;
case STATUS:
handle_status(group, hostidx, message, meslen);
break;
case COMPLETE:
handle_complete(group, hostidx, message, meslen);
break;
case ABORT:
handle_abort(group, &src, message, meslen, header->src_id);
break;
default:
forward_message(group, &src, buf, packetlen);
break;
}
}
}
}
}
uftp-4.1.5/server_announce.c 0000644 0000764 0000764 00000123244 12250244021 015041 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#endif
#include "server.h"
#include "server_common.h"
#include "server_announce.h"
/**
* Sets the fields in a EXT_ENC_INFO extension for transmission.
* Returns the number of bytes set, or 0 on error.
*/
int set_enc_info(const struct finfo_t *finfo, struct enc_info_he *encinfo)
{
unsigned char *keyblob;
uint16_t bloblen;
int extlen;
keyblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he));
encinfo->exttype = EXT_ENC_INFO;
encinfo->keyextype_sigtype = (keyextype & 0x0F) << 4;
encinfo->keyextype_sigtype |= (sigtype & 0x0F);
encinfo->keytype = keytype;
encinfo->hashtype = hashtype;
if (client_auth) {
encinfo->flags |= FLAG_CLIENT_AUTH;
}
memcpy(encinfo->rand1, rand1, sizeof(rand1));
if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(privkey.rsa, keyblob, &bloblen)) {
log0(finfo->group_id, finfo->file_id,
"Error exporting server public key");
return 0;
}
} else {
if (!export_EC_key(privkey.ec, keyblob, &bloblen)) {
log0(finfo->group_id, finfo->file_id,
"Error exporting server public key");
return 0;
}
}
encinfo->keylen = htons(bloblen);
if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) {
uint16_t dhlen;
uint8_t *dhblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he) +
ntohs(encinfo->keylen));
if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) {
log0(finfo->group_id, finfo->file_id,
"Error exporting server ECDH public key");
return 0;
}
encinfo->dhlen = htons(dhlen);
if (keyextype == KEYEX_ECDH_RSA) {
encinfo->siglen = htons(RSA_keylen(privkey.rsa));
} else {
encinfo->siglen = htons(ECDSA_siglen(privkey.ec));
}
} else {
encinfo->dhlen = 0;
encinfo->siglen = 0;
}
extlen = sizeof(struct enc_info_he) + ntohs(encinfo->keylen) +
ntohs(encinfo->dhlen) + ntohs(encinfo->siglen);
encinfo->extlen = extlen / 4;
return extlen;
}
/**
* 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, iplen, extlen;
unsigned char *buf;
struct uftp_h *header;
struct announce_h *announce;
unsigned char *publicaddr, *privateaddr;
struct enc_info_he *encinfo;
struct timeval tv;
uint32_t *idlist;
buf = safe_calloc(MAXMTU, 1);
if (listen_dest.ss.ss_family == AF_INET6) {
iplen = sizeof(struct in6_addr);
} else {
iplen = sizeof(struct in_addr);
}
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
publicaddr = (unsigned char *)announce + sizeof(struct announce_h);
privateaddr = publicaddr + iplen;
encinfo = (struct enc_info_he *)(privateaddr + iplen);
set_uftp_header(header, ANNOUNCE, finfo->group_id, finfo->group_inst,
grtt, destcount);
announce->func = ANNOUNCE;
if (sync_mode) {
announce->flags |= FLAG_SYNC_MODE;
if (sync_preview) {
announce->flags |= FLAG_SYNC_PREVIEW;
}
}
announce->robust = robust;
announce->cc_type = cc_type;
announce->blocksize = htons(blocksize);
gettimeofday(&tv, NULL);
announce->tstamp_sec = htonl(tv.tv_sec);
announce->tstamp_usec = htonl(tv.tv_usec);
if (!is_multicast(&listen_dest, 0)) {
memset(publicaddr, 0, iplen);
memset(privateaddr, 0, iplen);
} else if (listen_dest.ss.ss_family == AF_INET6) {
memcpy(publicaddr, &listen_dest.sin6.sin6_addr.s6_addr, iplen);
memcpy(privateaddr, &receive_dest.sin6.sin6_addr.s6_addr, iplen);
} else {
memcpy(publicaddr, &listen_dest.sin.sin_addr.s_addr, iplen);
memcpy(privateaddr, &receive_dest.sin.sin_addr.s_addr, iplen);
}
if (listen_dest.ss.ss_family == AF_INET6) {
announce->flags |= FLAG_IPV6;
}
if (keytype != KEY_NONE) {
extlen = set_enc_info(finfo, encinfo);
if (extlen == 0) {
log0(finfo->group_id, finfo->file_id,
"Error setting up EXT_ENC_INFO");
free(buf);
return 0;
}
announce->hlen = (sizeof(struct announce_h) +
iplen + iplen + extlen) / 4;
} else {
announce->hlen = (sizeof(struct announce_h) + iplen + iplen) / 4;
}
idlist = (uint32_t *)((uint8_t *)announce + (announce->hlen * 4));
if (open) {
header->seq = htons(send_seq++);
packetlen = sizeof(struct uftp_h) + (announce->hlen * 4);
if (!sign_announce(finfo, buf, packetlen)) {
log0(finfo->group_id, finfo->file_id, "Error signing ANNOUNCE");
free(buf);
return 0;
}
log2(finfo->group_id, finfo->file_id, "Sending ANNOUNCE %d", attempt);
if (nb_sendto(sock, buf, packetlen, 0, (struct sockaddr *)&listen_dest,
family_len(listen_dest)) == SOCKET_ERROR) {
sockerror(finfo->group_id,finfo->file_id, "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, idlist,
DEST_MUTE, 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 *idlist;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
regconf = (struct regconf_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, REG_CONF, finfo->group_id, finfo->group_inst,
grtt, destcount);
regconf->func = REG_CONF;
regconf->hlen = sizeof(struct regconf_h) / 4;
idlist = (uint32_t *)((uint8_t *)regconf + (regconf->hlen * 4));
rval = send_multiple(finfo, buf, REG_CONF, attempt, idlist, DEST_ACTIVE,
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;
unsigned int hsize, payloadlen, len;
int maxdest, packetcnt, dests, iv_init, i;
int unauth_keytype, unauth_keylen, unauth_ivlen;
// Don't use a cipher in an authentication mode to encrypt the group master
unauth_keytype = unauth_key(keytype);
get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);
buf = safe_calloc(MAXMTU, 1);
iv = safe_calloc(unauth_ivlen, 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->group_id, finfo->group_inst,
grtt, destcount);
keyinfo->func = KEYINFO;
keyinfo->hlen = sizeof(struct keyinfo_h) / 4;
keylist = (struct destkey *)((uint8_t *)keyinfo + (keyinfo->hlen * 4));
iv_init = 0;
hsize = sizeof(struct keyinfo_h);
maxdest = blocksize / sizeof(struct destkey);
packetcnt = 1;
for (i = 0, dests = 0; i < destcount; i++) {
if (destlist[i].status == DEST_REGISTERED) {
if (!iv_init) {
ivctr++;
keyinfo->iv_ctr_hi =htonl((ivctr & 0xFFFFFFFF00000000LL) >> 32);
keyinfo->iv_ctr_lo = htonl(ivctr & 0x00000000FFFFFFFFLL);
iv_init = 1;
}
keylist[dests].dest_id = destlist[i].id;
build_iv(iv, destlist[i].encinfo->salt, unauth_ivlen,
uftp_htonll(ivctr), header->src_id);
if (!encrypt_block(unauth_keytype, iv,destlist[i].encinfo->key,
NULL,0, &groupmaster[1], sizeof(groupmaster) - 1,
keylist[dests].groupmaster, &len)) {
log0(finfo->group_id, finfo->file_id,
"Error encrypting KEYINFO for %s", destlist[i].name);
free(buf);
free(iv);
return 0;
}
dests++;
}
if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) {
header->seq = htons(send_seq++);
payloadlen = hsize + (dests * sizeof(struct destkey));
log2(finfo->group_id, finfo->file_id,
"Sending KEYINFO %d.%d", attempt, packetcnt);
if (nb_sendto(sock, buf, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&receive_dest,
family_len(receive_dest)) == SOCKET_ERROR) {
sockerror(finfo->group_id, finfo->file_id,
"Error sending KEYINFO");
sleep(1);
free(buf);
free(iv);
return 0;
}
if (packet_wait) usleep(packet_wait);
memset(keylist, 0, maxdest * sizeof(struct destkey));
iv_init = 0;
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;
struct timeval tv;
uint32_t *idlist;
char *filename, *linkname;
if (strlen(finfo->destfname) > MAXPATHNAME) {
log0(finfo->group_id, finfo->file_id,
"File name too long: %s", finfo->destfname);
return 0;
}
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
fileinfo = (struct fileinfo_h *)(buf + sizeof(struct uftp_h));
filename = (char *)fileinfo + sizeof(struct fileinfo_h);
set_uftp_header(header, FILEINFO, finfo->group_id, finfo->group_inst,
grtt, destcount);
fileinfo->func = FILEINFO;
fileinfo->ftype = finfo->ftype;
fileinfo->file_id = htons(finfo->file_id);
fileinfo->namelen = (uint8_t)(0 + ceil(strlen(finfo->destfname) / 4.0));
fileinfo->lofsize = htonl((finfo->size & 0xFFFFFFFF));
fileinfo->hifsize = htons((uint16_t)(finfo->size >> 32));
if (sync_mode) {
fileinfo->ftstamp = htonl(finfo->tstamp);
} else {
fileinfo->ftstamp = 0;
}
gettimeofday(&tv, NULL);
fileinfo->tstamp_sec = htonl(tv.tv_sec);
fileinfo->tstamp_usec = htonl(tv.tv_usec);
strncpy(filename, finfo->destfname, MAXPATHNAME);
if (finfo->ftype == FTYPE_LINK) {
if (strlen(finfo->linkname) >
(unsigned)MAXPATHNAME - (fileinfo->namelen * 4)) {
log0(finfo->group_id, finfo->file_id,
"Link name too long: %s", finfo->linkname);
free(buf);
return 0;
}
linkname = filename + (fileinfo->namelen * 4);
strncpy(linkname, finfo->linkname,
MAXPATHNAME - (fileinfo->namelen * 4));
fileinfo->linklen = (uint8_t)(0 + ceil(strlen(finfo->linkname) / 4.0));
}
fileinfo->hlen = (sizeof(struct fileinfo_h) + (fileinfo->namelen * 4) +
(fileinfo->linklen * 4)) / 4;
idlist = (uint32_t *)((uint8_t *)fileinfo + (fileinfo->hlen * 4));
rval = send_multiple(finfo, buf, FILEINFO, attempt, idlist,
DEST_REGISTERED, (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 id, struct finfo_t *finfo,
int state, int proxyidx, int clientcnt)
{
snprintf(destlist[destcount].name, sizeof(destlist[destcount].name),
"0x%08X", ntohl(id));
destlist[destcount].id = id;
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 *idlist,
const union sockaddr_u *su, int clientcnt,
int proxyidx, int open, double rtt)
{
int startcnt, addcnt, 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;
}
if (destlist[proxyidx].clients == NULL) {
destlist[proxyidx].clients =
safe_calloc(MAXPROXYDEST, sizeof(*destlist[proxyidx].clients));
}
startcnt = destlist[proxyidx].clientcnt;
for (addcnt = 0, i = 0; i < clientcnt; i++) {
dupmsg = 0;
hostidx = find_client(idlist[i]);
if (hostidx == -1) {
if (open) {
if (destcount == MAXDEST) {
log1(finfo->group_id, finfo->file_id,
"Rejecting client %08X: max destinations exceeded",
ntohl(idlist[i]));
send_abort(finfo, "Max destinations exceeded",
su, idlist[i], 0, 0);
continue;
}
if (startcnt + addcnt == MAXPROXYDEST) {
log1(finfo->group_id, finfo->file_id,
"Rejecting client %08X: max destinations "
"exceeded for proxy", idlist[i]);
send_abort(finfo, "Max destinations exceeded for proxy",
su, idlist[i], 0, 0);
continue;
}
hostidx = add_dest_by_addr(idlist[i], finfo, DEST_ACTIVE,
proxyidx, -1);
} else {
log1(finfo->group_id, finfo->file_id,
"Host %08X not in host list", idlist[i]);
send_abort(finfo, "Not in host list", su, idlist[i], 0, 0);
continue;
}
} else {
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
destlist[hostidx].status = DEST_ACTIVE;
destlist[hostidx].proxyidx = proxyidx;
}
destlist[hostidx].rtt = rtt;
finfo->deststate[hostidx].conf_sent = 0;
log1(finfo->group_id, finfo->file_id,
" For client%s %s", dupmsg ? "+" : "", destlist[hostidx].name);
destlist[proxyidx].clients[addcnt + startcnt] = hostidx;
addcnt++;
}
destlist[proxyidx].clientcnt += addcnt;
}
/**
* 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;
uint8_t privatemcast[16];
uint32_t n_group_id;
int iplen;
if (listen_dest.ss.ss_family == AF_INET6) {
iplen = sizeof(struct in6_addr);
} else {
iplen = sizeof(struct in_addr);
}
if (!is_multicast(&listen_dest, 0)) {
memset(privatemcast, 0, iplen);
} else if (listen_dest.ss.ss_family == AF_INET6) {
memcpy(privatemcast, &receive_dest.sin6.sin6_addr.s6_addr, iplen);
} else {
memcpy(privatemcast, &receive_dest.sin.sin_addr.s_addr, iplen);
}
*verifylen = 0;
if (destlist[hostidx].status == DEST_MUTE) {
verifydata = safe_calloc(sizeof(finfo->group_id) + iplen +
sizeof(rand1) + sizeof(destlist[hostidx].encinfo->rand2) +
sizeof(destlist[hostidx].encinfo->premaster), 1);
} else {
verifydata = safe_calloc(sizeof(finfo->group_id) + iplen +
sizeof(rand1) + sizeof(destlist[hostidx].encinfo->rand2) +
sizeof(destlist[hostidx].encinfo->premaster) + PUBKEY_LEN +
sizeof(groupmaster), 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, &privatemcast, iplen);
*verifylen += iplen;
memcpy(verifydata + *verifylen, rand1, sizeof(rand1));
*verifylen += sizeof(rand1);
memcpy(verifydata + *verifylen, destlist[hostidx].encinfo->rand2,
sizeof(destlist[hostidx].encinfo->rand2));
*verifylen += sizeof(destlist[hostidx].encinfo->rand2);
memcpy(verifydata + *verifylen, destlist[hostidx].encinfo->premaster,
destlist[hostidx].encinfo->premaster_len);
*verifylen += destlist[hostidx].encinfo->premaster_len;
if (destlist[hostidx].status != DEST_MUTE) {
if (destlist[hostidx].encinfo->pubkey.key) {
uint16_t bloblen;
uint8_t *keyblob = verifydata + *verifylen;
if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(destlist[hostidx].encinfo->pubkey.rsa,
keyblob, &bloblen)) {
free(verifydata);
return NULL;
}
} else {
if (!export_EC_key(destlist[hostidx].encinfo->pubkey.ec,
keyblob, &bloblen)) {
log0(finfo->group_id, finfo->file_id,
"Error exporting server public key");
free(verifydata);
return NULL;
}
}
*verifylen += bloblen;
}
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 ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
if (!verify_RSA_sig(destlist[hostidx].encinfo->pubkey.rsa, hashtype,
verifydata, verifylen, destlist[hostidx].encinfo->verifydata,
destlist[hostidx].encinfo->verifylen)) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY "
"from %s: verify data mismatch", destlist[hostidx].name);
free(verifydata);
return 0;
}
} else {
if (!verify_ECDSA_sig(destlist[hostidx].encinfo->pubkey.ec, hashtype,
verifydata, verifylen, destlist[hostidx].encinfo->verifydata,
destlist[hostidx].encinfo->verifylen)) {
log1(finfo->group_id, finfo->file_id, "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 + SALT_LEN + hmaclen;
seedlen = sizeof(rand1) * 2;
seed = safe_calloc(seedlen, 1);
prf_buf = safe_calloc(MASTER_LEN + explen + hmaclen, 1);
memcpy(seed, rand1, sizeof(rand1));
memcpy(seed + sizeof(rand1), destlist[hostidx].encinfo->rand2,
sizeof(destlist[hostidx].encinfo->rand2));
PRF(hashtype, MASTER_LEN, destlist[hostidx].encinfo->premaster,
destlist[hostidx].encinfo->premaster_len,
"master secret", seed, seedlen, prf_buf, &len);
memcpy(destlist[hostidx].encinfo->master,prf_buf,
sizeof(destlist[hostidx].encinfo->master));
PRF(hashtype, explen, destlist[hostidx].encinfo->master,
sizeof(destlist[hostidx].encinfo->master), "key expansion",
seed, seedlen, prf_buf, &len);
memcpy(destlist[hostidx].encinfo->hmackey, prf_buf, hmaclen);
memcpy(destlist[hostidx].encinfo->key, prf_buf + hmaclen, keylen);
memcpy(destlist[hostidx].encinfo->salt, prf_buf + hmaclen + keylen,
SALT_LEN);
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 *keyinfo, struct finfo_t *finfo,
int hostidx)
{
unsigned char premaster[PUBKEY_LEN];
unsigned int len;
destlist[hostidx].encinfo = safe_calloc(1, sizeof(struct encinfo_t));
memcpy(destlist[hostidx].encinfo->rand2, reg->rand2,
sizeof(destlist[hostidx].encinfo->rand2));
if (keyextype == KEYEX_RSA) {
if (!RSA_decrypt(privkey.rsa, keyinfo, ntohs(reg->keyinfo_len),
premaster, &len)) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"failed to decrypt premaster secret", destlist[hostidx].name);
return 0;
}
if (len != MASTER_LEN) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"decrypted premaster secret wrong length",
destlist[hostidx].name);
return 0;
}
} else {
if (!import_EC_key(&destlist[hostidx].encinfo->dhkey.ec, keyinfo,
ntohs(reg->keyinfo_len), 1)) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"failed to import ECDH key", destlist[hostidx].name);
return 0;
}
if (get_EC_curve(destlist[hostidx].encinfo->dhkey.ec) != ecdh_curve) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"invalid curve for ECDH", destlist[hostidx].name);
return 0;
}
if (!get_ECDH_key(destlist[hostidx].encinfo->dhkey.ec, dhkey.ec,
premaster, &len)) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"failed to calculate premaster secret",
destlist[hostidx].name);
return 0;
}
}
memcpy(destlist[hostidx].encinfo->premaster, premaster, len);
destlist[hostidx].encinfo->premaster_len = len;
calculate_client_keys(hostidx);
if (destlist[hostidx].encinfo->pubkey.key) {
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, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
uint32_t src, int regconf)
{
const struct register_h *reg;
const uint32_t *idlist;
const unsigned char *enckey;
int clientcnt, hostidx;
struct timeval tv1, tv2;
reg = (const struct register_h *)message;
enckey = (const unsigned char *)reg + sizeof(struct register_h);
gettimeofday(&tv2, NULL);
if (destcount == MAXDEST) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %08X: "
"max destinations exceeded", ntohl(src));
send_abort(finfo, "Max destinations exceeded", su, src, 0, 0);
return;
}
if ((meslen < (reg->hlen * 4U)) || ((reg->hlen * 4U) <
sizeof(struct register_h) + ntohs(reg->keyinfo_len))) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %08X: "
"invalid message size", ntohl(src));
send_abort(finfo, "Invalid message size", su, src, 0, 0);
return;
}
clientcnt = (meslen - (reg->hlen * 4)) / 4;
hostidx = add_dest_by_addr(src, 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;
}
tv1.tv_sec = ntohl(reg->tstamp_sec);
tv1.tv_usec = ntohl(reg->tstamp_usec);
destlist[hostidx].rtt = diff_usec(tv2, tv1) / 1000000.0;
destlist[hostidx].rtt_measured = 1;
destlist[hostidx].registered = 1;
destlist[hostidx].status =
regconf ? DEST_ACTIVE : (client_auth ? DEST_MUTE : DEST_REGISTERED);
log1(finfo->group_id, finfo->file_id, "Received REGISTER from %s %s",
(clientcnt > 0) ? "proxy" : "client", destlist[hostidx].name);
if (clientcnt > 0) {
idlist = (const uint32_t *)(message + (reg->hlen * 4));
add_proxy_dests(finfo, idlist, su, clientcnt, hostidx, 1,
destlist[hostidx].rtt);
}
log3(finfo->group_id, finfo->file_id,
"send time = %d.%06d", tv1.tv_sec, tv1.tv_usec);
log3(finfo->group_id, finfo->file_id,
"rx time = %d.%06d", tv2.tv_sec, tv2.tv_usec);
log3(finfo->group_id,finfo->file_id, " rtt = %.6f", destlist[hostidx].rtt);
}
/**
* 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, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx, int regconf, int open)
{
const struct register_h *reg;
const uint32_t *idlist;
const unsigned char *enckey;
int clientcnt, dupmsg, isproxy;
struct timeval tv1, tv2;
reg = (const struct register_h *)message;
enckey = (const unsigned char *)reg + sizeof(struct register_h);
gettimeofday(&tv2, NULL);
if ((meslen < (reg->hlen * 4U)) || ((reg->hlen * 4U) <
sizeof(struct register_h) + ntohs(reg->keyinfo_len))) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"invalid message size", destlist[hostidx].name);
send_abort(finfo, "Invalid message size", su, destlist[hostidx].id,0,0);
return;
}
clientcnt = (meslen - (reg->hlen * 4)) / 4;
if ((clientcnt > 0) && (destlist[hostidx].clientcnt == -1) && (!open)) {
log1(finfo->group_id, finfo->file_id, "Rejecting REGISTER from %s: "
"specified multiple clients but not a proxy",
destlist[hostidx].name);
send_abort(finfo, "specified multiple clients but not a proxy", su,
destlist[hostidx].id, 0, 0);
destlist[hostidx].status = DEST_ABORT;
return;
}
if (finfo->file_id != 0) {
log1(finfo->group_id, finfo->file_id,
"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].encinfo->pubkey.key))
? DEST_MUTE : DEST_REGISTERED);
}
dupmsg = (destlist[hostidx].registered);
tv1.tv_sec = ntohl(reg->tstamp_sec);
tv1.tv_usec = ntohl(reg->tstamp_usec);
destlist[hostidx].rtt = diff_usec(tv2, tv1) / 1000000.0;
destlist[hostidx].rtt_measured = 1;
destlist[hostidx].registered = 1;
if (regconf) {
finfo->deststate[hostidx].conf_sent = 0;
}
isproxy = (destlist[hostidx].clientcnt != -1);
log1(finfo->group_id, finfo->file_id, "Received REGISTER%s from %s %s",
(dupmsg && !isproxy) ? "+" : "",
(isproxy) ? "proxy" : "client", destlist[hostidx].name);
if (clientcnt > 0) {
idlist = (const uint32_t *)(message + (reg->hlen * 4));
add_proxy_dests(finfo, idlist, su, clientcnt, hostidx, open,
destlist[hostidx].rtt);
}
log3(finfo->group_id, finfo->file_id,
"send time = %d.%06d", tv1.tv_sec, tv1.tv_usec);
log3(finfo->group_id, finfo->file_id,
"rx time = %d.%06d", tv2.tv_sec, tv2.tv_usec);
log3(finfo->group_id,finfo->file_id, " rtt = %.6f", destlist[hostidx].rtt);
}
/**
* Verifies a client's public key fingerprint
*/
int verify_client_fingerprint(const struct finfo_t *finfo,
const unsigned char *keyblob,
uint16_t bloblen, int hostidx)
{
unsigned char fingerprint[HMAC_LEN];
unsigned int fplen;
if (destlist[hostidx].verified) {
return 1;
}
if (keyblob[0] == KEYBLOB_RSA) {
if (!import_RSA_key(&destlist[hostidx].encinfo->pubkey.rsa,
keyblob, bloblen)) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY "
"from %s: failed to import key", destlist[hostidx].name);
return 0;
}
destlist[hostidx].encinfo->pubkeylen =
RSA_keylen(destlist[hostidx].encinfo->pubkey.rsa);
} else {
if (!import_EC_key(&destlist[hostidx].encinfo->pubkey.ec,
keyblob, bloblen, 0)) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY "
"from %s: failed to import key", destlist[hostidx].name);
return 0;
}
destlist[hostidx].encinfo->pubkeylen =
ECDSA_siglen(destlist[hostidx].encinfo->pubkey.ec);
}
if (destlist[hostidx].has_fingerprint) {
hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
if (memcmp(destlist[hostidx].keyfingerprint, fingerprint, fplen)) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY "
"from %s: key fingerprint mismatch", destlist[hostidx].name);
if (keyblob[0] == KEYBLOB_RSA) {
free_RSA_key(destlist[hostidx].encinfo->pubkey.rsa);
} else {
free_EC_key(destlist[hostidx].encinfo->pubkey.ec);
}
destlist[hostidx].encinfo->pubkey.key = 0;
destlist[hostidx].encinfo->pubkeylen = 0;
return 0;
}
}
destlist[hostidx].verified = 1;
return 1;
}
/**
* Process an expected CLIENT_KEY with open group membership
*/
void handle_open_clientkey(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
uint32_t src)
{
const struct client_key_h *clientkey;
const unsigned char *keyblob, *verify;
int hostidx;
clientkey = (const struct client_key_h *)message;
keyblob = (const unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keyblob + ntohs(clientkey->bloblen);
if (destcount == MAXDEST) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY from %08X: "
"max destinations exceeded", ntohl(src));
send_abort(finfo, "Max destinations exceeded", su, src, 0, 0);
return;
}
if ((meslen < (clientkey->hlen * 4U)) ||
((clientkey->hlen * 4U) < sizeof(struct client_key_h) +
ntohs(clientkey->bloblen) + ntohs(clientkey->siglen))) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY from %08X: "
"invalid message size", ntohl(src));
send_abort(finfo, "Invalid message size", su, src, 0, 0);
return;
}
if ((((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) &&
(keyblob[0] != KEYBLOB_RSA)) ||
((keyextype == KEYEX_ECDH_ECDSA) &&
(keyblob[0] != KEYBLOB_EC))) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY from %08X: "
"invalid keyblob type", ntohl(src));
return;
}
hostidx = add_dest_by_addr(src, finfo, DEST_MUTE, -1, -1);
if (!verify_client_fingerprint(finfo, keyblob, ntohs(clientkey->bloblen),
hostidx)) {
return;
}
memcpy(destlist[hostidx].encinfo->verifydata, verify,
ntohs(clientkey->siglen));
destlist[hostidx].encinfo->verifylen = ntohs(clientkey->siglen);
log2(finfo->group_id, finfo->file_id, "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, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx)
{
const struct client_key_h *clientkey;
const unsigned char *keyblob, *verify;
clientkey = (const struct client_key_h *)message;
keyblob = (const unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keyblob + ntohs(clientkey->bloblen);
if ((meslen < (clientkey->hlen * 4U)) ||
((clientkey->hlen * 4U) < sizeof(struct client_key_h) +
ntohs(clientkey->bloblen) + ntohs(clientkey->siglen))) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY from %s: "
"invalid message size", destlist[hostidx].name);
send_abort(finfo, "Invalid message size", su, destlist[hostidx].id,0,0);
return;
}
if ((((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) &&
(keyblob[0] != KEYBLOB_RSA)) ||
((keyextype == KEYEX_ECDH_ECDSA) &&
(keyblob[0] != KEYBLOB_EC))) {
log1(finfo->group_id, finfo->file_id, "Rejecting CLIENT_KEY from %s: "
"invalid keyblob type", destlist[hostidx].name);
return;
}
if (finfo->file_id != 0) {
log2(finfo->group_id, finfo->file_id,
"Received CLIENT_KEY+ from %s", destlist[hostidx].name);
return;
}
if (!verify_client_fingerprint(finfo, keyblob, ntohs(clientkey->bloblen),
hostidx)) {
return;
}
memcpy(destlist[hostidx].encinfo->verifydata, verify,
ntohs(clientkey->siglen));
destlist[hostidx].encinfo->verifylen = ntohs(clientkey->siglen);
if (destlist[hostidx].registered) {
if (!verify_client_key(finfo, hostidx)) {
return;
}
}
log2(finfo->group_id, finfo->file_id,
"Received CLIENT_KEY from %s", destlist[hostidx].name);
}
/**
* Process an expected KEYINFO_ACK and validate the verify_data field.
*/
void handle_keyinfo_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx)
{
const struct keyinfoack_h *keyinfoack;
unsigned char *verifydata, *verify_hash, *verify_test;
int verifylen, len, dupmsg;
unsigned int hashlen;
keyinfoack = (const struct keyinfoack_h *)message;
if ((meslen < (keyinfoack->hlen * 4U)) ||
((keyinfoack->hlen * 4U) < sizeof(struct keyinfoack_h))) {
log1(finfo->group_id, finfo->file_id, "Rejecting KEYINFO_ACK from %s: "
"invalid message size", destlist[hostidx].name);
send_abort(finfo, "Invalid message size", su, destlist[hostidx].id,0,0);
return;
}
if (keytype == KEY_NONE) {
log1(finfo->group_id, finfo->file_id, "Rejecting KEYINFO_ACK from %s: "
"encryption not enabled", destlist[hostidx].name);
send_abort(finfo, "Encryption not enabled", su, destlist[hostidx].id,
0, 0);
return;
}
if (!(verifydata = build_verify_data(finfo, hostidx, &verifylen))) {
log1(finfo->group_id, finfo->file_id, "Rejecting KEYINFO_ACK from %s: "
"error exporting client public key", destlist[hostidx].name);
return;
}
verify_hash = safe_calloc(hmaclen, 1);
verify_test = safe_calloc(VERIFY_LEN + hmaclen, 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(keyinfoack->verify_data, verify_test, VERIFY_LEN)) {
log1(finfo->group_id, finfo->file_id, "Rejecting KEYINFO_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);
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
log2(finfo->group_id, finfo->file_id, "Received KEYINFO_ACK%s from %s",
dupmsg ? "+" : "", destlist[hostidx].name);
destlist[hostidx].status = DEST_ACTIVE;
}
/**
* Process an expected FILEINFO_ACK.
*/
void handle_fileinfo_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx)
{
const struct fileinfoack_h *fileinfoack;
struct timeval tv1, tv2;
const uint32_t *addr;
int clientcnt, dupmsg, isproxy, clientidx, i;
fileinfoack = (const struct fileinfoack_h *)message;
gettimeofday(&tv2, NULL);
if ((meslen < (fileinfoack->hlen * 4U)) ||
((fileinfoack->hlen * 4U) < sizeof(struct fileinfoack_h))) {
log1(finfo->group_id, finfo->file_id, "Rejecting FILEINFO_ACK from %s: "
"invalid message size", destlist[hostidx].name);
return;
}
clientcnt = (meslen - (fileinfoack->hlen * 4)) / 4;
if ((clientcnt > 0) && (destlist[hostidx].clientcnt == -1)) {
log1(finfo->group_id, finfo->file_id, "Rejecting FILEINFO_ACK from %s: "
"specified multiple clients but not a proxy",
destlist[hostidx].name);
return;
}
if (ntohs(fileinfoack->file_id) != finfo->file_id) {
log1(finfo->group_id, finfo->file_id, "Rejecting FILEINFO_ACK from %s: "
"invalid file ID %04X, expected %04X ", destlist[hostidx].name,
ntohs(fileinfoack->file_id), finfo->file_id );
return;
}
finfo->partial = finfo->partial &&
((fileinfoack->flags & FLAG_PARTIAL) != 0);
tv1.tv_sec = ntohl(fileinfoack->tstamp_sec);
tv1.tv_usec = ntohl(fileinfoack->tstamp_usec);
destlist[hostidx].rtt = diff_usec(tv2, tv1) / 1000000.0;
destlist[hostidx].rtt_measured = 1;
destlist[hostidx].rtt_sent = 0;
isproxy = (destlist[hostidx].clientcnt != -1);
dupmsg = (destlist[hostidx].status == DEST_ACTIVE);
destlist[hostidx].status = DEST_ACTIVE;
log2(finfo->group_id, finfo->file_id, "Received FILEINFO_ACK%s from %s %s",
(dupmsg && !isproxy) ? "+" : "",
(isproxy) ? "proxy" : "client", destlist[hostidx].name);
if (clientcnt > 0) {
addr = (const uint32_t *)(message + (fileinfoack->hlen * 4));
for (i = 0; i < clientcnt; i++) {
dupmsg = 0;
clientidx = find_client(addr[i]);
if (clientidx == -1) {
log2(finfo->group_id, finfo->file_id,
"Host %08X not in host list", ntohl(addr[i]));
continue;
} else {
dupmsg = (destlist[clientidx].status == DEST_ACTIVE);
destlist[clientidx].status = DEST_ACTIVE;
destlist[clientidx].rtt = destlist[hostidx].rtt;
}
log2(finfo->group_id, finfo->file_id, " For client%s %s",
dupmsg ? "+" : "", destlist[clientidx].name);
}
}
log3(finfo->group_id, finfo->file_id,
"send time = %d.%06d", tv1.tv_sec, tv1.tv_usec);
log3(finfo->group_id, finfo->file_id,
"rx time = %d.%06d", tv2.tv_sec, tv2.tv_usec);
log3(finfo->group_id,finfo->file_id, " rtt = %.6f", destlist[hostidx].rtt);
return;
}
uftp-4.1.5/client_fileinfo.h 0000664 0000764 0000764 00000003155 12250244021 015003 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_FILEINFO_H
#define _CLIENT_FILEINFO_H
void handle_fileinfo(struct group_list_t *group, const unsigned char *message,
unsigned meslen, struct timeval rxtime);
void send_fileinfo_ack(struct group_list_t *group, int restart);
#endif // _CLIENT_FILEINFO_H
uftp-4.1.5/server_config.c 0000644 0000764 0000764 00000101372 12250244021 014476 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#endif
#include "server.h"
#include "server_config.h"
/**
* Global command line values and sockets
*/
SOCKET sock;
union sockaddr_u listen_dest, receive_dest;
int max_rate, rate, rcvbuf, packet_wait, txweight, max_nak_pct;
int client_auth, quit_on_error, dscp, follow_links, max_nak_cnt;
int save_fail, restart_groupid, restart_groupinst;
int sync_mode, sync_preview, dest_is_dir, cc_type, user_abort;
unsigned int ttl;
char port[PORTNAME_LEN], srcport[PORTNAME_LEN];
char pub_multi[INET6_ADDRSTRLEN], priv_multi[INET6_ADDRSTRLEN];
char keyfile[MAXPATHNAME], cc_config[MAXPATHNAME];
char filelist[MAXFILES][MAXPATHNAME], exclude[MAXEXCLUDE][MAXPATHNAME];
char basedir[MAXDIR][MAXDIRNAME], destfname[MAXPATHNAME];
char statusfilename[MAXPATHNAME];
FILE *status_file;
struct iflist ifl[MAX_INTERFACES];
int keytype, hashtype, sigtype, keyextype, newkeylen, sys_keys;
int blocksize, datapacketsize;
int ifl_len, destcount, filecount, excludecount, basedircount, cc_count;
struct cc_config_t cc_list[MAXCC];
struct iflist out_if;
struct destinfo_t destlist[MAXDEST];
int robust;
double grtt, min_grtt, max_grtt;
uint16_t send_seq;
uint32_t server_id;
/**
* Encryption variables
*/
union key_t privkey, dhkey;
unsigned char rand1[RAND_LEN], groupmaster[MASTER_LEN];
uint8_t groupsalt[MAXIV], groupkey[MAXKEY], grouphmackey[HMAC_LEN];
uint64_t ivctr;
int ivlen, keylen, hmaclen, privkeylen;
uint8_t ecdh_curve, ecdsa_curve;
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 addrinfo ai_hints, *ai_rval;
uint32_t uid;
int rval;
if (destcount == MAXDEST) {
fprintf(stderr,"Exceeded maximum destination count\n");
exit(1);
}
// Check if the client is specified by an IPv4 name/address
ai_hints.ai_family = AF_INET;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(destname, NULL, &ai_hints, &ai_rval)) != 0) {
uid = strtoul(destname, NULL, 16);
if ((uid == 0xffffffff) || (uid == 0)) {
fprintf(stderr, "Invalid UID %s\n", destname);
exit(1);
}
destlist[destcount].id = htonl(uid);
} else {
destlist[destcount].id =
((struct sockaddr_in *)ai_rval->ai_addr)->sin_addr.s_addr;
freeaddrinfo(ai_rval);
}
snprintf(destlist[destcount].name, sizeof(destlist[destcount].name),
"%s", destname);
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(void)
{
memset(destlist, 0, sizeof(destlist));
memset(filelist, 0, sizeof(filelist));
memset(exclude, 0, sizeof(exclude));
memset(destfname, 0, sizeof(destfname));
memset(cc_config, 0, sizeof(cc_config));
destcount = 0;
strncpy(port, DEF_PORT, sizeof(port)-1);
port[sizeof(port)-1] = '\x0';
strncpy(srcport, DEF_SRCPORT, sizeof(srcport)-1);
srcport[sizeof(srcport)-1] = '\x0';
rate = DEF_RATE;
max_rate = 0;
memset(&out_if, 0, sizeof(out_if));
ttl = DEF_TTL;
dscp = DEF_DSCP;
blocksize = DEF_BLOCKSIZE;
log_level = DEF_LOG_LEVEL;
rcvbuf = 0;
memset(keyfile, 0, sizeof(keyfile));
client_auth = 0;
quit_on_error = 0;
save_fail = 0;
restart_groupid = 0;
restart_groupinst = 0;
keytype = DEF_KEYTYPE;
hashtype = DEF_HASHTYPE;
sigtype = DEF_SIGTYPE;
keyextype = DEF_KEYEXTYPE;
ecdh_curve = DEF_CURVE;
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';
strncpy(statusfilename, "", sizeof(statusfilename)-1);
statusfilename[sizeof(statusfilename)-1] = '\x0';
status_file = NULL;
filecount = 0;
excludecount = 0;
basedircount = 0;
newkeylen = 0;
ecdh_curve = 0;
ecdsa_curve = 0;
follow_links = 0;
showtime = 0;
sys_keys = 0;
sync_mode = 0;
sync_preview = 0;
cc_count = 0;
dest_is_dir = 0;
grtt = DEF_GRTT;
min_grtt = DEF_MIN_GRTT;
max_grtt = DEF_MAX_GRTT;
robust = DEF_ROBUST;
send_seq = 0;
user_abort = 0;
txweight = DEF_TXWEIGHT;
max_nak_pct = DEF_MAX_NAK_PCT;
max_nak_cnt = DEF_MAX_NAK_CNT;
max_log_size = 0;
max_log_count = DEF_MAX_LOG_COUNT;
}
/**
* 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) {
log0(0, 0, "Failed to read header from restart file");
close(fd);
exit(1);
}
restart_groupid = header.group_id;
restart_groupinst = header.group_inst;
if (restart_groupinst == 0xff) {
log0(0, 0, "Maximum number of restarts reached");
close(fd);
exit(1);
}
if ((header.filecount > MAXFILES) || (header.filecount <= 0)) {
log0(0, 0, "Too many files listed in restart file");
close(fd);
exit(1);
}
for (i = 0; i < header.filecount; i++) {
if (file_read(fd, filelist[i], sizeof(filelist[i]), 0) == -1) {
log0(0, 0, "Failed to read filename from restart file");
close(fd);
exit(1);
}
}
filecount = header.filecount;
while ((rval = file_read(fd, &host, sizeof(host), 1)) != 0) {
if (rval == -1) {
log0(0, 0, "Failed to read host from restart file");
close(fd);
exit(1);
}
memcpy(destlist[destcount].name, host.name, sizeof(host.name));
destlist[destcount].id = host.id;
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++;
}
close(fd);
}
/**
* Reads in the congestion control config file.
* Each line contains a percentage (0-100) followed by a scaling factor
* or the string "max" followed by the maximum transmission rate in Kbps
* Sample:
* max;10000
* 0;1.3
* 5;1.1
* 10;0.9
* 25;0.7
* 50;0.4
*
* Returns 1 on success, 0 on fail.
*/
int read_cc_config(const char *filename)
{
FILE *cc_file;
char line[100], *p;
int last_percentage, percentage;
double scaling_factor;
struct cc_config_t tmp_cc_list[MAXCC];
int tmp_cc_count, tmp_max_rate;
if ((cc_file = fopen(filename, "rt")) == NULL) {
log0(0, 0, "Couldn't open congestion control file %s: %s\n",
cc_config, strerror(errno));
return 0;
}
tmp_cc_count = 0;
tmp_max_rate = 0;
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) {
log0(0, 0, "Error reading congestion control entry\n");
fclose(cc_file);
return 0;
}
if (!strcmp(p, "max")) {
if (tmp_max_rate) {
log0(0, 0, "Cannot have multiple entries for max rate\n");
fclose(cc_file);
return 0;
}
p = strtok(NULL, ";");
if (p == NULL) {
log0(0, 0, "Error reading congestion control entry\n");
fclose(cc_file);
return 0;
}
// Change from Kbps to B/S
tmp_max_rate = strtol(p, NULL, 10) * 1024 / 8;
if (tmp_max_rate <= 0) {
log0(0, 0, "Invalid max rate in congestion control\n");
fclose(cc_file);
return 0;
}
continue;
}
percentage = strtol(p, NULL, 10);
if ((percentage < 0) || percentage > 100) {
log0(0, 0, "Invalid percentage in congestion control entry\n");
fclose(cc_file);
return 0;
}
if (percentage < last_percentage) {
log0(0, 0, "Congestion control entries must be in ascending "
"order by percentage\n");
fclose(cc_file);
return 0;
}
p = strtok(NULL, ";");
if (p == NULL) {
log0(0, 0, "Error reading congestion control entry\n");
fclose(cc_file);
return 0;
}
errno = 0;
scaling_factor = strtod(p, NULL);
if (errno) {
log0(0, 0, "Error reading congestion control entry: %s\n",
strerror(errno));
fclose(cc_file);
return 0;
}
if (scaling_factor <= 0) {
log0(0, 0, "Invalid scaling factor in congestion control entry\n");
fclose(cc_file);
return 0;
}
last_percentage = percentage;
tmp_cc_list[tmp_cc_count].percentage = percentage;
tmp_cc_list[tmp_cc_count].scaling_factor = scaling_factor;
tmp_cc_count++;
}
fclose(cc_file);
// Make sure we have a top end entry, make scaling same as last specified
tmp_cc_list[tmp_cc_count].percentage = 100;
tmp_cc_list[tmp_cc_count].scaling_factor =
tmp_cc_list[tmp_cc_count - 1].scaling_factor;
tmp_cc_count++;
// Config successfully read in, copy to real list
for (cc_count = 0; cc_count < tmp_cc_count; cc_count++) {
cc_list[cc_count] = tmp_cc_list[cc_count];
}
if (tmp_max_rate) {
max_rate = tmp_max_rate;
} else if (!max_rate) {
max_rate = rate;
}
return 1;
}
/**
* Gets the symmetric cypher constant for the given cypher name
* Returns -1 if the name is invalid
*/
static int get_keytype(const char *name)
{
if (!strcmp(optarg, "none")) {
return KEY_NONE;
} else if (!strcmp(optarg, "des")) {
return KEY_DES;
} else if (!strcmp(optarg, "3des")) {
return KEY_DES_EDE3;
} else if (!strcmp(optarg, "aes128-cbc")) {
return KEY_AES128_CBC;
} else if (!strcmp(optarg, "aes256-cbc")) {
return KEY_AES256_CBC;
} else if (!strcmp(optarg, "aes128-gcm")) {
return KEY_AES128_GCM;
} else if (!strcmp(optarg, "aes256-gcm")) {
return KEY_AES256_GCM;
} else if (!strcmp(optarg, "aes128-ccm")) {
return KEY_AES128_CCM;
} else if (!strcmp(optarg, "aes256-ccm")) {
return KEY_AES256_CCM;
} else {
return -1;
}
}
/**
* Gets the hash constant for the given hash name
* Returns -1 if the name is invalid
*/
static int get_hashtype(const char *name)
{
if (!strcmp(optarg, "sha1")) {
return HASH_SHA1;
} else if (!strcmp(optarg, "sha256")) {
return HASH_SHA256;
} else if (!strcmp(optarg, "sha384")) {
return HASH_SHA384;
} else if (!strcmp(optarg, "sha512")) {
return HASH_SHA512;
} else {
return -1;
}
}
/**
* Set argument defaults, read and validate command line options
*/
void process_args(int argc, char *argv[])
{
int c, i, listidx, read_restart, rval;
long tmpval;
char line[1000], *dest, *destname, filename[MAXPATHNAME], *fingerprint, *p;
char keylenstr[50];
struct addrinfo ai_hints, *ai_rval;
FILE *destfile, *excludefile, *listfile;
const char opts[] = "x:R:L:B:g:n:m:Y:h:w:e:ck:K:lTb:t:Q:"
"zZI:p:u:j:qfyH:F:X:M:P:C:D:oE:S:r:s:i:W:N:";
set_defaults();
memset(keylenstr, 0, sizeof(keylenstr));
read_restart = 0;
// read lettered arguments
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 'x':
log_level = atoi(optarg);
if (log_level < 0) {
fprintf(stderr,"Invalid log level\n");
exit(1);
}
break;
case 'R':
// Expecting rate as Kbps, translate to B/s
rate = atoi(optarg);
if ((rate <= 0) && (rate != -1)) {
fprintf(stderr,"Invalid rate\n");
exit(1);
}
if (rate != -1) {
rate = rate * 1024 / 8;
}
if ((rate == -1) && (cc_count > 0)) {
fprintf(stderr,"Can't specify -R -1 with -C\n");
exit(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 'g':
max_log_size = atoi(optarg);
if ((max_log_size < 1) || (max_log_size > 1024)) {
fprintf(stderr, "Invalid max log size\n");
exit(1);
}
max_log_size *= 1000000;
break;
case 'n':
max_log_count = atoi(optarg);
if ((max_log_count < 1) || (max_log_count > 1000)) {
fprintf(stderr, "Invalid max log count\n");
exit(1);
}
break;
case 'm':
max_nak_cnt = atoi(optarg);
if ((max_nak_cnt < 1) || (max_nak_cnt > 1000)) {
fprintf(stderr, "Invalid max nak count\n");
exit(1);
}
break;
case 'Y':
if ((keytype = get_keytype(optarg)) == -1) {
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 ((hashtype = get_hashtype(optarg)) == -1) {
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, "keyex")) {
sigtype = SIG_KEYEX;
} else {
fprintf(stderr, "Invalid sigtype\n");
exit(1);
}
break;
case 'e':
p = strtok(optarg, ":");
if (!p) {
fprintf(stderr, "Error reading keyextype\n");
exit(1);
}
if (!strcmp(p, "rsa")) {
keyextype = KEYEX_RSA;
} else if (!strcmp(p, "ecdh_rsa")) {
keyextype = KEYEX_ECDH_RSA;
} else if (!strcmp(p, "ecdh_ecdsa")) {
keyextype = KEYEX_ECDH_ECDSA;
} else {
fprintf(stderr, "Invalid keyextype\n");
exit(1);
}
if ((keyextype == KEYEX_ECDH_RSA) ||
(keyextype == KEYEX_ECDH_ECDSA)) {
p = strtok(NULL, ":");
if (p) {
ecdh_curve = get_curve(p);
if (ecdh_curve == 0) {
fprintf(stderr, "Invalid curve\n");
exit(1);
}
} else {
ecdh_curve = DEF_CURVE;
}
}
break;
case 'c':
client_auth = 1;
break;
case 'k':
strncpy(keyfile, optarg, sizeof(keyfile)-1);
keyfile[sizeof(keyfile)-1] = '\x0';
break;
case 'K':
strncpy(keylenstr, optarg, sizeof(keylenstr)-1);
keylenstr[sizeof(keylenstr)-1] = '\x0';
break;
case 'l':
follow_links = 1;
break;
case 'T':
showtime = 1;
break;
case 'b':
blocksize = atoi(optarg);
if ((blocksize < 512) || (blocksize > (MAXMTU - 200))) {
fprintf(stderr, "Invalid blocksize\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_if = ifl[listidx];
break;
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) {
fprintf(stderr, "Invalid name/address %s: %s\n",
optarg, gai_strerror(rval));
exit(1);
}
// Just use the first addrinfo entry
if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
ifl, ifl_len)) == -1) {
fprintf(stderr, "Interface %s not found", optarg);
exit(1);
}
out_if = ifl[listidx];
freeaddrinfo(ai_rval);
break;
case 'z':
sync_mode = 1;
break;
case 'Z':
sync_preview = 1;
sync_mode = 1;
break;
case 'p':
strncpy(port, optarg, sizeof(port)-1);
port[sizeof(port)-1] = '\x0';
break;
case 'u':
strncpy(srcport, optarg, sizeof(srcport)-1);
srcport[sizeof(srcport)-1] = '\x0';
break;
case 'j':
if (read_restart) {
fprintf(stderr,"Can't specify both -j and -F\n");
exit(1);
}
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, "|");
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 (read_restart) {
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, "|");
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);
}
read_restart = 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 == MAXEXCLUDE) {
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':
p = strtok(optarg, ":");
if (!p) {
fprintf(stderr, "Error reading cc_type\n");
exit(1);
}
if (!strcmp(p, "none")) {
cc_type = CC_NONE;
} else if (!strcmp(p, "uftp3")) {
if (rate == -1) {
fprintf(stderr,"Can't specify -C uftp3 with -R -1\n");
exit(1);
}
cc_type = CC_UFTP3;
p = strtok(NULL, ":");
if (!p) {
fprintf(stderr, "Error reading CC config file\n");
exit(1);
}
strncpy(cc_config, p, sizeof(cc_config)-1);
cc_config[sizeof(cc_config)-1] = '\x0';
if (!read_cc_config(cc_config)) {
fprintf(stderr,"Error loading congestion control config\n");
exit(1);
}
} else if (!strcmp(p, "tfmcc")) {
cc_type = CC_TFMCC;
} else {
// PGMCC not currently supported
fprintf(stderr, "Invalid congestion control type\n");
exit(1);
}
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 'S':
strncpy(statusfilename, optarg, sizeof(statusfilename)-1);
statusfilename[sizeof(statusfilename)-1] = '\x0';
break;
case 'r':
p = strtok(optarg, ":");
if (!p) {
fprintf(stderr, "Error reading cc_type\n");
exit(1);
}
errno = 0;
grtt = atof(p);
if (errno) {
perror("Invalid grtt");
exit(1);
} else if ((grtt < CLIENT_RTT_MIN) || (grtt > 1000)) {
fprintf(stderr, "Invalid grtt\n");
exit(1);
}
p = strtok(NULL, ":");
if (p) {
errno = 0;
min_grtt = atof(p);
if (errno) {
perror("Invalid min_grtt");
exit(1);
} else if ((min_grtt < CLIENT_RTT_MIN) || (min_grtt > 1000)) {
fprintf(stderr, "Invalid min_grtt\n");
exit(1);
}
p = strtok(NULL, ":");
if (!p) {
fprintf(stderr, "Missing max_grtt\n");
exit(1);
}
errno = 0;
max_grtt = atof(p);
if (errno) {
perror("Invalid max_grtt");
exit(1);
} else if ((max_grtt < CLIENT_RTT_MIN) || (max_grtt > 1000)) {
fprintf(stderr, "Invalid max_grtt\n");
exit(1);
}
if (min_grtt > max_grtt) {
fprintf(stderr, "Invalid min_grtt/max_grtt\n");
exit(1);
} else if ((grtt > max_grtt) || (grtt < min_grtt)) {
fprintf(stderr, "Invalid grtt\n");
exit(1);
}
}
break;
case 's':
robust = atoi(optarg);
if ((robust < 10) || (robust > 50)) {
fprintf(stderr,"Invalid robustness factor\n");
exit(1);
}
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';
filecount++;
}
if (!feof(listfile) && ferror(listfile)) {
perror("Failed to read from file list");
exit(1);
}
fclose(listfile);
break;
case 'W':
txweight = atoi(optarg);
if ((txweight < 110) || (txweight > 10000)) {
fprintf(stderr, "Invalid txweight\n");
exit(1);
}
break;
case 'N':
max_nak_pct = atoi(optarg);
if ((max_nak_pct < 0) || (max_nak_pct > 100)) {
fprintf(stderr, "Invalid max_nak_pct\n");
exit(1);
}
break;
case '?':
fprintf(stderr, USAGE);
exit(1);
}
}
argc -= optind;
argv += optind;
if ((argc == 0) && (filecount == 0)) {
fprintf(stderr, USAGE);
exit(1);
}
if (save_fail && sync_mode) {
fprintf(stderr, "Error: Cannot use restart mode "
"and sync mode together\n");
exit(1);
}
if (keytype == KEY_NONE) {
hashtype = HASH_NONE;
sigtype = SIG_NONE;
keyextype = KEYEX_NONE;
}
if (is_auth_enc(keytype)) {
sigtype = SIG_AUTHENC;
}
if (strcmp(keylenstr, "")) {
if (keyextype == KEYEX_ECDH_ECDSA) {
ecdsa_curve = get_curve(keylenstr);
if (ecdsa_curve == 0) {
fprintf(stderr, "Invalid curve\n");
exit(1);
}
} else if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
newkeylen = atoi(keylenstr);
if ((newkeylen < 512) || (newkeylen > 2048)) {
fprintf(stderr, "Invalid new key length\n");
exit(1);
}
}
}
if (!max_rate) {
max_rate = rate;
}
if (filecount != 0) {
if (argc > 0) {
fprintf(stderr, "Warning: ignoring paths "
"specified on command line\n");
}
return;
}
// Read list of files.
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';
filecount++;
}
}
uftp-4.1.5/client_announce.c 0000644 0000764 0000764 00000116147 12250244021 015015 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#include "win_func.h"
#else // if WINDOWS
#include
#include
#include
#endif
#include "client.h"
#include "client_common.h"
#include "client_announce.h"
/**
* Finds next open slot in the global group list.
* Returns a pointer to the open slot, or NULL if none found.
*/
struct group_list_t *find_open_slot(void)
{
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 &group_list[i];
}
}
return NULL;
}
/**
* 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(struct group_list_t *group, int *verifylen)
{
uint8_t *verifydata;
uint32_t group_id;
int iplen;
iplen = (group->multi.ss.ss_family == AF_INET6) ?
sizeof(struct in6_addr) : sizeof(struct in_addr);
*verifylen = 0;
if (group->phase == PHASE_REGISTERED) {
verifydata = safe_calloc(sizeof(group->group_id) +
iplen + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster), 1);
} else {
verifydata = safe_calloc(sizeof(group->group_id) +
iplen + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster) +
PUBKEY_LEN + sizeof(group->groupmaster), 1);
}
group_id = htonl(group->group_id);
memcpy(verifydata, &group_id, sizeof(group_id));
*verifylen += sizeof(group_id);
if (group->multi.ss.ss_family == AF_INET6) {
memcpy(verifydata + *verifylen, &group->multi.sin6.sin6_addr.s6_addr,
iplen);
} else {
memcpy(verifydata + *verifylen, &group->multi.sin.sin_addr.s_addr,
iplen);
}
*verifylen += iplen;
memcpy(verifydata + *verifylen, group->rand1, sizeof(group->rand1));
*verifylen += sizeof(group->rand1);
memcpy(verifydata + *verifylen, group->rand2, sizeof(group->rand2));
*verifylen += sizeof(group->rand2);
memcpy(verifydata + *verifylen, group->premaster, group->premaster_len);
*verifylen += group->premaster_len;
if (group->phase != PHASE_REGISTERED) {
if (group->client_auth) {
uint16_t bloblen;
uint8_t *keyblob = verifydata + *verifylen;
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(group->client_privkey.rsa,
keyblob, &bloblen)) {
free(verifydata);
return NULL;
}
} else {
if (!export_EC_key(group->client_privkey.ec,
keyblob, &bloblen)) {
free(verifydata);
return NULL;
}
}
*verifylen += bloblen;
}
memcpy(verifydata + *verifylen, group->groupmaster,
sizeof(group->groupmaster));
*verifylen += sizeof(group->groupmaster);
}
return verifydata;
}
/**
* Sends a CLIENT_KEY message if the server requested it.
* Always sent right after a REGISTER.
*/
void send_client_key(struct group_list_t *group)
{
struct uftp_h *header;
struct client_key_h *client_key;
unsigned char *buf, *keyblob, *verify;
uint8_t *verifydata;
unsigned int siglen, meslen;
int verifylen;
uint16_t bloblen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h));
keyblob = (unsigned char *)client_key + sizeof(struct client_key_h);
verifydata = build_verify_data(group, &verifylen);
if (!verifydata) {
log0(group->group_id, group->file_id, "Error getting verify data");
send_abort(group, "Error getting verify data");
goto end;
}
set_uftp_header(header, CLIENT_KEY, group);
client_key->func = CLIENT_KEY;
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(group->client_privkey.rsa, keyblob, &bloblen)) {
log0(group->group_id, group->file_id, "Error exporting public key");
send_abort(group, "Error exporting public key");
goto end;
}
verify = keyblob + bloblen;
if (!create_RSA_sig(group->client_privkey.rsa, group->hashtype,
verifydata, verifylen, verify, &siglen)) {
log0(group->group_id, group->file_id, "Error signing verify data");
send_abort(group, "Error signing verify data");
goto end;
}
} else {
if (!export_EC_key(group->client_privkey.ec, keyblob, &bloblen)) {
log0(group->group_id, group->file_id, "Error exporting public key");
send_abort(group, "Error exporting public key");
goto end;
}
verify = keyblob + bloblen;
if (!create_ECDSA_sig(group->client_privkey.ec, group->hashtype,
verifydata, verifylen, verify, &siglen)) {
log0(group->group_id, group->file_id, "Error signing verify data");
send_abort(group, "Error signing verify data");
goto end;
}
}
client_key->bloblen = htons(bloblen);
client_key->siglen = htons(siglen);
client_key->hlen = (sizeof(struct client_key_h) + bloblen + siglen) / 4;
meslen = sizeof(struct uftp_h) + (client_key->hlen * 4);
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group->replyaddr),
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending CLIENT_KEY");
} else {
log2(group->group_id, group->file_id, "CLIENT_KEY sent");
}
end:
free(verifydata);
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(struct group_list_t *group)
{
struct uftp_h *header;
struct register_h *reg;
unsigned char *buf, *keydata;
struct timeval now, send_time;
unsigned int len, meslen;
union key_t key;
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->expire_time) >= 0) {
log1(group->group_id, group->file_id,
"Registration unconfirmed by server");
send_abort(group, "Registration unconfirmed");
return;
}
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
reg = (struct register_h *)(buf + sizeof(struct uftp_h));
keydata = (unsigned char *)reg + sizeof(struct register_h);
set_uftp_header(header, REGISTER, group);
reg->func = REGISTER;
if (group->keytype != KEY_NONE) {
memcpy(reg->rand2, group->rand2, RAND_LEN);
if (group->keyextype == KEYEX_RSA) {
if (has_proxy) {
key = proxy_pubkey;
} else {
key = group->server_pubkey;
}
if (!RSA_encrypt(key.rsa, group->premaster, group->premaster_len,
keydata, &len)) {
log0(group->group_id, group->file_id,
"Error encrypting premaster secret");
send_abort(group, "Error encrypting premaster secret");
free(buf);
return;
}
} else {
uint16_t keylen;
if (!export_EC_key(group->client_dhkey.ec, keydata, &keylen)) {
log0(group->group_id, group->file_id,
"Error exporting ECDH public key");
send_abort(group, "Error exporting ECDH public key");
free(buf);
return;
}
len = keylen;
}
reg->keyinfo_len = htons(len);
} else {
len = 0;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
send_time = group->last_server_ts;
} else {
send_time = add_timeval(group->last_server_ts,
diff_timeval(now, group->last_server_rx_ts));
}
reg->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
reg->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
reg->hlen = (sizeof(struct register_h) + len) / 4;
meslen = sizeof(struct uftp_h) + (reg->hlen * 4);
if (nb_sendto(listener, buf, meslen, 0,
(struct sockaddr *)&(group->replyaddr),
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending REGISTER");
} else {
log2(group->group_id, group->file_id, "REGISTER sent");
}
log3(group->group_id, 0, "send time: %d.%06d",
send_time.tv_sec, send_time.tv_usec);
set_timeout(group, 0);
if (group->client_auth) {
send_client_key(group);
}
free(buf);
}
/**
* Sends a KEYINFO_ACK in response to a KEYINFO
*/
void send_keyinfo_ack(struct group_list_t *group)
{
unsigned char *buf, *encrypted;
struct uftp_h *header;
struct keyinfoack_h *keyinfo_ack;
unsigned char *verifydata, *verify_hash, *verify_val;
unsigned int payloadlen, hashlen;
int verifylen, enclen, len;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
keyinfo_ack = (struct keyinfoack_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, KEYINFO_ACK, group);
keyinfo_ack->func = KEYINFO_ACK;
keyinfo_ack->hlen = sizeof(struct keyinfoack_h) / 4;
verifydata = build_verify_data(group, &verifylen);
if (!verifydata) {
log0(group->group_id, group->file_id,
"Error getting verify data");
send_abort(group, "Error getting verify data");
free(buf);
return;
}
verify_hash = safe_calloc(group->hmaclen, 1);
verify_val = safe_calloc(VERIFY_LEN + group->hmaclen, 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_val, &len);
memcpy(keyinfo_ack->verify_data, verify_val, VERIFY_LEN);
free(verifydata);
free(verify_hash);
free(verify_val);
payloadlen = sizeof(struct keyinfoack_h);
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype,
group->groupkey, group->groupsalt, &group->ivctr, group->ivlen,
group->hashtype, group->grouphmackey, group->hmaclen,group->sigtype,
group->keyextype, group->client_privkey,group->client_privkeylen)) {
log0(0, 0, "Error encrypting KEYINFO_ACK");
free(buf);
return;
}
payloadlen = enclen + sizeof(struct uftp_h);
if (nb_sendto(listener, encrypted, payloadlen, 0,
(struct sockaddr *)&(group->replyaddr),
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id, group->file_id, "Error sending KEYINFO_ACK");
} else {
log2(group->group_id, group->file_id, "KEYINFO_ACK sent");
}
free(encrypted);
free(buf);
}
/**
* Sends a FILEINFO_ACK in response to a FILEINFO
*/
void send_fileinfo_ack(struct group_list_t *group, int restart)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct fileinfoack_h *fileinfo_ack;
struct timeval now, send_time;
unsigned int payloadlen;
int enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
fileinfo_ack = (struct fileinfoack_h *)(buf + sizeof(struct uftp_h));
payloadlen = sizeof(struct fileinfoack_h);
set_uftp_header(header, FILEINFO_ACK, group);
fileinfo_ack->func = FILEINFO_ACK;
fileinfo_ack->hlen = sizeof(struct fileinfoack_h) / 4;
fileinfo_ack->file_id = htons(group->file_id);
if (restart) {
fileinfo_ack->flags |= FLAG_PARTIAL;
}
gettimeofday(&now, NULL);
if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
send_time = group->last_server_ts;
} else {
send_time = add_timeval(group->last_server_ts,
diff_timeval(now, group->last_server_rx_ts));
}
fileinfo_ack->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
fileinfo_ack->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
if (group->keytype != KEY_NONE) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
group->ivlen, group->hashtype, group->grouphmackey,
group->hmaclen, group->sigtype, group->keyextype,
group->client_privkey, group->client_privkeylen)) {
log0(0, 0, "Error encrypting FILEINFO_ACK");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
payloadlen += sizeof(struct uftp_h);
if (nb_sendto(listener, outpacket, payloadlen, 0,
(struct sockaddr *)&(group->replyaddr),
family_len(group->replyaddr)) == SOCKET_ERROR) {
sockerror(group->group_id,group->file_id, "Error sending FILEINFO_ACK");
} else {
log2(group->group_id, group->file_id, "FILEINFO_ACK sent");
}
log3(group->group_id, group->file_id, "send time: %d.%06d",
send_time.tv_sec, send_time.tv_usec);
free(encrypted);
free(buf);
}
/**
* Verifies a server's public key fingerprint
*/
int verify_server_fingerprint(const unsigned char *keyblob, int bloblen,
struct group_list_t *group)
{
unsigned char fingerprint[HMAC_LEN];
unsigned int fplen;
int found, keyidx;
if (server_count == 0) {
return 1;
}
for (keyidx = 0, found = 0; (keyidx < server_count) && !found; keyidx++) {
if (server_keys[keyidx].uid == group->src_id) {
keyidx--;
found = 1;
}
}
if (!found) {
return 0;
}
if (!server_keys[keyidx].has_fingerprint) {
return 1;
}
hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
if (memcmp(server_keys[keyidx].fingerprint, fingerprint, fplen)) {
return 0;
} else {
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(struct group_list_t *group,
const struct enc_info_he *encinfo)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
time_t t;
uint32_t t2;
memcpy(group->rand1, encinfo->rand1, sizeof(encinfo->rand1));
if (!get_random_bytes(group->rand2, sizeof(group->rand2))) {
log0(group->group_id, 0, "Failed to get random bytes for rand2");
send_abort(group, "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->rand2) = t2;
if (group->keyextype == KEYEX_RSA) {
if (!get_random_bytes(group->premaster, MASTER_LEN)) {
log0(group->group_id,0, "Failed to get random bytes for premaster");
send_abort(group, "Failed to get random bytes for premaster");
return 0;
}
group->premaster_len = MASTER_LEN;
} else {
EC_key_t pubecdh;
if (has_proxy) {
pubecdh = proxy_dhkey.ec;
} else {
pubecdh = group->server_dhkey.ec;
}
if (!get_ECDH_key(pubecdh, group->client_dhkey.ec,
group->premaster, &group->premaster_len)) {
log0(group->group_id,0, "Failed to calculate ECDH key");
send_abort(group, "Failed to calculate ECDH key");
return 0;
}
}
get_key_info(group->keytype, &group->keylen, &group->ivlen);
group->hmaclen = get_hash_len(group->hashtype);
explen = group->keylen + SALT_LEN + group->hmaclen;
seedlen = RAND_LEN * 2;
seed = safe_calloc(seedlen, 1);
prf_buf = safe_calloc(MASTER_LEN + explen + group->hmaclen, 1);
memcpy(seed, group->rand1, sizeof(group->rand1));
memcpy(seed + sizeof(group->rand1), group->rand2, sizeof(group->rand2));
PRF(group->hashtype, MASTER_LEN, group->premaster, group->premaster_len,
"master secret", seed, seedlen, prf_buf, &len);
memcpy(group->master,prf_buf, sizeof(group->master));
PRF(group->hashtype, explen, group->master, sizeof(group->master),
"key expansion", seed, seedlen, prf_buf, &len);
memcpy(group->hmackey, prf_buf, group->hmaclen);
memcpy(group->key, prf_buf + group->hmaclen, group->keylen);
memcpy(group->salt, prf_buf + group->hmaclen + group->keylen, SALT_LEN);
free(seed);
free(prf_buf);
return 1;
}
/**
* Read encryption related fields from an ANNOUNCE
*/
int read_announce_encryption(struct group_list_t *group,
struct enc_info_he *encinfo,
const unsigned char *packet, int packetlen)
{
int keyextype, sigtype, keytype, i;
unsigned char *keys;
keys = (unsigned char *)encinfo + sizeof(struct enc_info_he);
// Sanity check the selected encryption parameters
if (!cipher_supported(encinfo->keytype)) {
log0(group->group_id, 0, "Keytype invalid or not supported here");
send_abort(group, "Keytype invalid or not supported here");
return 0;
}
if (!hash_supported(encinfo->hashtype)) {
log0(group->group_id, 0, "Hashtype invalid or not supported here");
send_abort(group, "Hashtype invalid or not supported here");
return 0;
}
keyextype = (encinfo->keyextype_sigtype & 0xF0) >> 4;
sigtype = encinfo->keyextype_sigtype & 0x0F;
if (((sigtype != SIG_HMAC) && (sigtype != SIG_KEYEX) &&
(sigtype != SIG_AUTHENC)) ||
((sigtype == SIG_AUTHENC) && (!is_auth_enc(encinfo->keytype)))) {
log0(group->group_id, 0, "Invalid sigtype specified");
send_abort(group, "Invalid sigtype specified");
return 0;
}
if ((keyextype != KEYEX_RSA) && (keyextype != KEYEX_ECDH_RSA) &&
(keyextype != KEYEX_ECDH_ECDSA)) {
log0(group->group_id, 0, "Invalid keyextype specified");
send_abort(group, "Invalid keyextype specified");
return 0;
}
group->keyextype = keyextype;
group->keytype = encinfo->keytype;
group->hashtype = encinfo->hashtype;
group->sigtype = sigtype;
group->client_auth = ((encinfo->flags & FLAG_CLIENT_AUTH) != 0);
if (!verify_server_fingerprint(keys, ntohs(encinfo->keylen), group)) {
log0(group->group_id, 0, "Failed to verify server key fingerprint");
send_abort(group, "Failed to verify server key fingerprint");
return 0;
}
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
keytype = KEYBLOB_RSA;
} else {
keytype = KEYBLOB_EC;
}
// Load server key and select a matching client key
if (keytype == KEYBLOB_RSA) {
if (!import_RSA_key(&group->server_pubkey.rsa, keys,
ntohs(encinfo->keylen))) {
log0(group->group_id, 0, "Failed to load server public key");
send_abort(group, "Failed to load server public key");
return 0;
}
group->server_pubkeylen = RSA_keylen(group->server_pubkey.rsa);
for (i = 0; i < key_count; i++) {
if ((privkey_type[i] == KEYBLOB_RSA) &&
(group->server_pubkeylen == RSA_keylen(privkey[i].rsa))) {
group->client_privkey = privkey[i];
group->client_privkeylen = RSA_keylen(privkey[i].rsa);
break;
}
}
} else {
if (!import_EC_key(&group->server_pubkey.ec, keys,
ntohs(encinfo->keylen), 0)) {
log0(group->group_id, 0, "Failed to load server public key");
send_abort(group, "Failed to load server public key");
return 0;
}
group->server_pubkeylen = ECDSA_siglen(group->server_pubkey.ec);
for (i = 0; i < key_count; i++) {
if ((privkey_type[i] == KEYBLOB_EC) &&
(get_EC_curve(group->server_pubkey.ec) ==
get_EC_curve(privkey[i].ec))) {
group->client_privkey = privkey[i];
group->client_privkeylen = ECDSA_siglen(privkey[i].ec);
break;
}
}
}
if (!group->client_privkey.key) {
log0(group->group_id, 0, "No client key compatible with server key");
send_abort(group, "No client key compatible with server key");
return 0;
}
if (has_proxy) {
if (!proxy_pubkey.key) {
log0(group->group_id, 0,
"Response proxy set but haven't gotten key yet");
send_abort(group,"Response proxy set but haven't gotten key yet");
return 0;
}
if (!(((keytype == KEYBLOB_RSA) && (proxy_pubkeytype == KEYBLOB_RSA)) &&
(RSA_keylen(group->server_pubkey.rsa) ==
RSA_keylen(proxy_pubkey.rsa))) &&
!(((keytype == KEYBLOB_EC) && (proxy_pubkeytype==KEYBLOB_EC)) &&
(get_EC_curve(group->server_pubkey.ec) ==
get_EC_curve(proxy_pubkey.ec)))) {
log0(group->group_id, 0,
"Response proxy key not compatible with server key");
send_abort(group,
"Response proxy key not compatible with server key");
return 0;
}
}
if ((group->keyextype == KEYEX_ECDH_ECDSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
unsigned char *sigcopy;
int siglen;
unsigned char *dhblob = keys + ntohs(encinfo->keylen);
unsigned char *sig = dhblob + ntohs(encinfo->dhlen);
if (!import_EC_key(&group->server_dhkey.ec, dhblob,
ntohs(encinfo->dhlen), 1)) {
log0(group->group_id, 0, "Failed to load server public ECDH key");
send_abort(group, "Failed to load server public ECDH key");
return 0;
}
group->client_dhkey.ec =
gen_EC_key(get_EC_curve(group->server_dhkey.ec), 1, NULL);
if (!group->client_dhkey.key) {
log0(group->group_id, 0, "Failed to generate client ECDH key");
send_abort(group, "Failed to generate client ECDH key");
return 0;
}
if (has_proxy) {
// We already checked if the proxy key exists, so no need to repeat
if (get_EC_curve(group->server_dhkey.ec) !=
get_EC_curve(proxy_dhkey.ec)) {
log0(group->group_id, 0, "Response proxy ECDH key "
"not compatible with server ECDH key");
send_abort(group, "Response proxy ECDH key "
"not compatible with server ECDH key");
return 0;
}
}
siglen = ntohs(encinfo->siglen);
sigcopy = safe_calloc(siglen, 1);
memcpy(sigcopy, sig, siglen);
memset(sig, 0, siglen);
if (keytype == KEYBLOB_RSA) {
if (!verify_RSA_sig(group->server_pubkey.rsa, group->hashtype,
packet, packetlen, sigcopy, siglen)) {
log0(group->group_id, 0, "Signature verification failed");
send_abort(group, "Signature verification failed");
free(sigcopy);
return 0;
}
} else {
if (!verify_ECDSA_sig(group->server_pubkey.ec, group->hashtype,
packet, packetlen, sigcopy, siglen)) {
log0(group->group_id, 0, "Signature verification failed");
send_abort(group, "Signature verification failed");
free(sigcopy);
return 0;
}
}
free(sigcopy);
}
// Calculate keys
if (!calculate_server_keys(group, encinfo)) {
return 0;
}
return 1;
}
/**
* Read in the contents of an ANNOUNCE.
*/
int read_announce(struct group_list_t *group, unsigned char *packet,
union sockaddr_u *src, struct timeval rxtime, int packetlen)
{
struct uftp_h *header;
struct announce_h *announce;
struct enc_info_he *encinfo;
uint8_t *publicmcast, *privatemcast;
uint8_t *he;
unsigned int iplen, extlen;
header = (struct uftp_h *)packet;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
encinfo = NULL;
group->phase = PHASE_REGISTERED;
group->version = header->version;
group->group_id = ntohl(header->group_id);
group->group_inst = header->group_inst;
group->src_id = header->src_id;
if (has_proxy) {
group->replyaddr = proxy_info.addr;
} else {
group->replyaddr = *src;
}
group->grtt = unquantize_grtt(header->grtt);
group->rtt = 0;
group->robust = announce->robust;
group->cc_type = announce->cc_type;
group->gsize = unquantize_gsize(header->gsize);
group->blocksize = ntohs(announce->blocksize);
group->last_server_ts.tv_sec = ntohl(announce->tstamp_sec);
group->last_server_ts.tv_usec = ntohl(announce->tstamp_usec);
group->last_server_rx_ts = rxtime;
group->restart = ((group->group_inst != 0) && (strcmp(tempdir, "")));
group->sync_preview = ((announce->flags & FLAG_SYNC_PREVIEW) != 0);
group->sync_mode = group->sync_preview ||
((announce->flags & FLAG_SYNC_MODE) != 0);
iplen = ((announce->flags & FLAG_IPV6) != 0) ?
sizeof(struct in6_addr) : sizeof(struct in_addr);
publicmcast = ((uint8_t *)announce) + sizeof(struct announce_h);
privatemcast = publicmcast + iplen;
if ((announce->flags & FLAG_IPV6) != 0) {
group->multi.sin6.sin6_family = AF_INET6;
memcpy(&group->multi.sin6.sin6_addr.s6_addr, privatemcast, iplen);
} else {
group->multi.sin.sin_family = AF_INET;
memcpy(&group->multi.sin.sin_addr.s_addr, privatemcast, iplen);
}
group->fileinfo.fd = -1;
if ((announce->hlen * 4U) < sizeof(struct announce_h) + (2U * iplen)) {
log0(group->group_id, 0, "Rejecting ANNOUNCE from %08X: "
"invalid header size", ntohl(group->src_id));
send_abort(group, "Invalid header size");
return 0;
}
if ((announce->hlen * 4U) > sizeof(struct announce_h) + (2U * iplen)) {
he = (unsigned char *)announce + sizeof(struct announce_h) +
(2U * iplen);
if (*he == EXT_ENC_INFO) {
encinfo = (struct enc_info_he *)he;
extlen = encinfo->extlen * 4U;
if ((extlen > ((announce->hlen * 4U) -
sizeof(struct announce_h))) ||
(extlen < sizeof(struct enc_info_he)) ||
(extlen != (sizeof(struct enc_info_he) +
ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) +
ntohs(encinfo->siglen)))) {
log0(group->group_id, 0, "Rejecting ANNOUNCE from %08X: "
"invalid extension size", ntohl(group->src_id));
send_abort(group, "Invalid extension size");
return 0;
}
}
}
if (encinfo != NULL) {
if (!read_announce_encryption(group, encinfo, packet, packetlen)) {
return 0;
}
} else if (encrypted_only) {
log0(group->group_id, 0, "No unencrypted transfers allowed");
send_abort(group, "No unencrypted transfers allowed");
return 0;
} else {
group->keyextype = KEYEX_NONE;
group->keytype = KEY_NONE;
group->hashtype = HASH_NONE;
group->sigtype = SIG_NONE;
group->client_auth = 0;
}
gettimeofday(&group->expire_time, NULL);
if (group->robust * group->grtt < 1.0) {
add_timeval_d(&group->expire_time, 1.0);
} else {
add_timeval_d(&group->expire_time, group->robust * group->grtt);
}
group->fileinfo.nak_time.tv_sec = 0;
group->fileinfo.nak_time.tv_usec = 0;
// Size of data packet, used in transmission speed calculations
group->datapacketsize = group->blocksize + sizeof(struct fileseg_h);
if (group->cc_type == CC_TFMCC) {
group->datapacketsize += sizeof(struct tfmcc_data_info_he);
}
if (group->keytype != KEY_NONE) {
group->datapacketsize += ((group->sigtype == SIG_KEYEX) ?
group->server_pubkeylen : (group->sigtype == SIG_HMAC) ?
group->hmaclen : 0) + KEYBLSIZE + sizeof(struct encrypted_h);
}
// 8 = UDP size, 20 = IPv4 size, 40 = IPv6 size
if ((announce->flags & FLAG_IPV6) != 0) {
group->datapacketsize += sizeof(struct uftp_h) + 8 + 40;
} else {
group->datapacketsize += sizeof(struct uftp_h) + 8 + 20;
}
if (group->cc_type != CC_NONE) {
group->loss_history= safe_calloc(0x10000,sizeof(struct loss_history_t));
group->slowstart = 1;
group->seq_wrap = 0;
group->start_txseq = ntohs(header->seq);
group->max_txseq = group->start_txseq;
group->loss_history[group->start_txseq].found = 1;
group->loss_history[group->start_txseq].t = rxtime;
group->loss_history[group->start_txseq].size = packetlen;
}
return 1;
}
/**
* Processes a new incoming ANNOUNCE
*/
void handle_announce(union sockaddr_u *src, unsigned char *packet,
unsigned packetlen, struct timeval rxtime)
{
struct uftp_h *header;
struct announce_h *announce;
uint32_t *addrlist;
int addrlen, rval;
struct group_list_t *group;
time_t t;
struct tm *start_time;
char privname[INET6_ADDRSTRLEN];
header = (struct uftp_h *)packet;
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
addrlist = (uint32_t *)((unsigned char *)announce + (announce->hlen * 4));
addrlen = (packetlen - sizeof(struct uftp_h) - (announce->hlen * 4)) / 4;
if ((packetlen < sizeof(struct uftp_h) + (announce->hlen * 4U)) ||
((announce->hlen * 4U) < sizeof(struct announce_h))) {
log0(ntohl(header->group_id), 0, "Rejecting ANNOUNCE from %08X: "
"invalid message size", ntohl(header->src_id));
return;
}
if ((addrlen != 0) && (!uid_in_list(addrlist, addrlen))) {
log1(ntohl(header->group_id), 0, "Name not in host list");
return;
}
if ((group = find_open_slot()) == NULL ) {
log0(ntohl(header->group_id), 0,
"Error: maximum number of incoming files exceeded: %d\n", MAXLIST);
return;
}
t = time(NULL);
start_time = localtime(&t);
snprintf(group->start_date, sizeof(group->start_date), "%04d%02d%02d",
start_time->tm_year + 1900,
start_time->tm_mon + 1, start_time->tm_mday);
snprintf(group->start_time, sizeof(group->start_time), "%02d%02d%02d",
start_time->tm_hour, start_time->tm_min, start_time->tm_sec);
if (!read_announce(group, packet, src, rxtime, packetlen)) {
return;
}
if ((rval = getnameinfo((struct sockaddr *)&group->multi,
family_len(group->multi), privname, sizeof(privname),
NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log0(group->group_id,0, "Received request from %08X", ntohl(group->src_id));
log1(group->group_id, 0, "Using private multicast address %s", privname);
log3(group->group_id, 0, "grtt = %.6f", group->grtt);
log3(group->group_id, 0, "send time: %d.%06d",
group->last_server_ts.tv_sec, group->last_server_ts.tv_usec);
log3(group->group_id, 0, "receive time: %d.%06d",
group->last_server_rx_ts.tv_sec, group->last_server_rx_ts.tv_usec);
if (group->restart) {
if (group->sync_mode) {
log0(group->group_id, 0, "Sync mode and restart mode incompatable");
send_abort(group, "Sync mode and restart mode incompatable");
return;
}
}
if (!addr_blank(&group->multi)) {
if (server_count > 0) {
if (!is_multicast(&group->multi, 1)) {
log0(group->group_id, 0,
"Invalid source specific multicast address: %s",
privname);
send_abort(group, "Invalid source specific multicast address");
return;
}
if (!multicast_join(listener, group->group_id, &group->multi,
m_interface, interface_count, server_keys, server_count)) {
send_abort(group, "Error joining multicast group");
return;
}
if (has_proxy) {
if (!multicast_join(listener, group->group_id, &group->multi,
m_interface, interface_count, &proxy_info, 1)) {
send_abort(group, "Error joining multicast group");
return;
}
}
} else {
if (!is_multicast(&group->multi, 0)) {
log0(group->group_id, 0, "Invalid multicast address: %s",
privname);
send_abort(group, "Invalid multicast address");
return;
}
if (!multicast_join(listener, group->group_id,
&group->multi, m_interface, interface_count, NULL, 0)) {
send_abort(group, "Error joining multicast group");
return;
}
}
group->multi_join = 1;
}
send_register(group);
}
/**
* Processes an incoming REG_CONF message.
* Expected in response to a REGISTER when encryption is disabled.
*/
void handle_regconf(struct group_list_t *group, const unsigned char *message,
unsigned meslen)
{
const struct regconf_h *regconf;
const uint32_t *addrlist;
int addrcnt;
regconf = (const struct regconf_h *)message;
addrlist = (const uint32_t *)(message + (regconf->hlen * 4));
if ((meslen < (regconf->hlen * 4U)) ||
((regconf->hlen * 4U) < sizeof(struct regconf_h))) {
log1(group->group_id, 0,
"Rejecting REG_CONF from server: invalid message size");
return;
}
addrcnt = (meslen - (regconf->hlen * 4)) / 4;
if (uid_in_list(addrlist, addrcnt)) {
log2(group->group_id, 0, "Registration confirmed");
group->phase = PHASE_MIDGROUP;
set_timeout(group, 0);
}
if (group->restart) {
read_restart_file(group);
}
}
/**
* Process an incoming KEYINFO message.
* Expected in response to a REGISTER when encryption is enabled.
*/
void handle_keyinfo(struct group_list_t *group, unsigned char *message,
unsigned meslen, uint32_t src_id)
{
struct keyinfo_h *keyinfo_hdr;
struct destkey *keylist;
int i, keyidx, len, destkeycnt, unauth_keytype, unauth_keylen, unauth_ivlen;
unsigned explen, declen;
uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv;
uint64_t ivctr;
keyinfo_hdr = (struct keyinfo_h *)message;
keylist = (struct destkey *)(message + (keyinfo_hdr->hlen * 4));
if ((meslen < (keyinfo_hdr->hlen * 4U)) ||
((keyinfo_hdr->hlen * 4U) < sizeof(struct keyinfo_h))) {
log1(group->group_id, 0,
"Rejecting KEYINFO from server: invalid message size");
return;
}
destkeycnt = (meslen - (keyinfo_hdr->hlen * 4)) / sizeof(struct destkey);
// This duplicates uid_in_list, but here it's addressed in a struct array
for (i = 0, keyidx = -1; (i < destkeycnt) && (keyidx == -1); i++) {
if (uid == keylist[i].dest_id) {
keyidx = i;
}
}
// Don't use a cipher in an authentication mode to decrypt the group master
unauth_keytype = unauth_key(group->keytype);
get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);
if (keyidx != -1) {
log2(group->group_id, 0, "Received KEYINFO");
if (group->phase == PHASE_MIDGROUP) {
// We already got the KEYINFO, so no need to reprocess.
// Just resend the KEYINFO_ACK and reset the timeout
send_keyinfo_ack(group);
set_timeout(group, 0);
return;
}
iv = safe_calloc(unauth_ivlen, 1);
ivctr = ntohl(keyinfo_hdr->iv_ctr_lo);
ivctr |= (uint64_t)ntohl(keyinfo_hdr->iv_ctr_hi) << 32;
build_iv(iv, group->salt, unauth_ivlen, uftp_htonll(ivctr), src_id);
if (!decrypt_block(unauth_keytype, iv, group->key, NULL, 0,
keylist[keyidx].groupmaster, MASTER_LEN,
decgroupmaster, &declen) ||
(declen != MASTER_LEN - 1)) {
log0(group->group_id, 0, "Decrypt failed for group master");
send_abort(group, "Decrypt failed for group master");
free(iv);
return;
}
free(iv);
group->groupmaster[0] = group->version;
memcpy(&group->groupmaster[1], decgroupmaster, declen);
explen = group->keylen + SALT_LEN + group->hmaclen;
prf_buf = safe_calloc(explen + group->hmaclen, 1);
PRF(group->hashtype, explen, group->groupmaster,
sizeof(group->groupmaster), "key expansion",
group->rand1, sizeof(group->rand1), prf_buf, &len);
memcpy(group->grouphmackey, prf_buf, group->hmaclen);
memcpy(group->groupkey, prf_buf + group->hmaclen, group->keylen);
memcpy(group->groupsalt, prf_buf + group->hmaclen + group->keylen,
SALT_LEN);
free(prf_buf);
group->phase = PHASE_MIDGROUP;
send_keyinfo_ack(group);
set_timeout(group, 0);
if (group->restart) {
read_restart_file(group);
}
}
}
uftp-4.1.5/LICENSE.txt 0000644 0000764 0000764 00000104513 12137013343 013327 0 ustar dbush dbush 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-4.1.5/server_common.c 0000644 0000764 0000764 00000036467 12250244021 014535 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#endif
#include "server.h"
#include "server_common.h"
/**
* Initializes the uftp header of an outgoing packet
*/
void set_uftp_header(struct uftp_h *header, int func, uint32_t group_id,
uint8_t group_inst, double l_grtt, int l_gsize)
{
header->version = UFTP_VER_NUM;
header->func = func;
header->src_id = htonl(server_id);
header->group_id = htonl(group_id);
header->group_inst = group_inst;
header->grtt = quantize_grtt(l_grtt);
header->gsize = quantize_gsize(l_gsize);
}
/**
* Sends an ABORT message to one or more clients
*/
void send_abort(const struct finfo_t *finfo, const char *message,
const union sockaddr_u *destaddr,
uint32_t dest, int encrypt, int current)
{
unsigned char *buf, *encrypted, *outpacket;
struct uftp_h *header;
struct abort_h *abort_hdr;
int payloadlen, enclen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, finfo->group_id, finfo->group_inst,
grtt, destcount);
header->seq = htons(send_seq++);
abort_hdr->func = ABORT;
abort_hdr->hlen = sizeof(struct abort_h) / 4;
if (dest) {
abort_hdr->host = dest;
} else if (current) {
abort_hdr->flags |= FLAG_CURRENT_FILE;
}
strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1);
payloadlen = (abort_hdr->hlen * 4);
if (encrypt) {
encrypted = NULL;
if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, keytype,
groupkey, groupsalt, &ivctr, ivlen, hashtype, grouphmackey,
hmaclen, sigtype, keyextype, privkey, privkeylen)) {
log0(finfo->group_id, finfo->file_id, "Error encrypting ABORT");
free(buf);
return;
}
outpacket = encrypted;
payloadlen = enclen;
} else {
encrypted = NULL;
outpacket = buf;
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0,
(const struct sockaddr *)destaddr,
family_len(*destaddr)) == SOCKET_ERROR) {
sockerror(finfo->group_id, finfo->file_id, "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 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 *idlist, int state,
int encrypt, const union sockaddr_u *destaddr, int regconf)
{
struct uftp_h *header;
int hsize, payloadlen, enclen, rval;
int maxdest, packetcnt, dests, i;
unsigned char *mheader, *encpacket, *outpacket;
char out_addr[INET6_ADDRSTRLEN];
header = (struct uftp_h *)packet;
mheader = packet + sizeof(struct uftp_h);
hsize = (unsigned char *)idlist - mheader;
maxdest = blocksize / sizeof(uint32_t);
packetcnt = 1;
if (encrypt) {
encpacket = safe_calloc(MAXMTU + keylen, 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))) {
idlist[dests++] = destlist[i].id;
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)) {
idlist[dests++] = destlist[i].id;
finfo->deststate[i].conf_sent = 1;
}
} else if (destlist[i].status == state) {
idlist[dests++] = destlist[i].id;
}
if ((dests >= maxdest) ||
((i == destcount - 1) && ((dests > 0) || (message == DONE)))) {
header->seq = htons(send_seq++);
payloadlen = hsize + (dests * sizeof(uint32_t));
if (message == ANNOUNCE) {
outpacket = packet;
if (!sign_announce(finfo, outpacket, payloadlen)) {
log0(finfo->group_id, finfo->file_id,
"Error signing ANNOUNCE");
free(encpacket);
return 0;
}
} else if (encrypt) {
if (!encrypt_and_sign(packet, &encpacket, payloadlen, &enclen,
keytype, groupkey, groupsalt, &ivctr, ivlen, hashtype,
grouphmackey, hmaclen, sigtype, keyextype,
privkey, privkeylen)) {
log0(finfo->group_id, finfo->file_id,
"Error encrypting %s", func_name(message));
free(encpacket);
return 0;
}
outpacket = encpacket;
payloadlen = enclen;
} else {
outpacket = packet;
}
log2(finfo->group_id, finfo->file_id,
"Sending %s %d.%d", func_name(message),
attempt, packetcnt);
if (log_level >= 4) {
rval = getnameinfo((const struct sockaddr *)destaddr,
family_len(*destaddr), out_addr, sizeof(out_addr),
NULL, 0, NI_NUMERICHOST);
if (rval) {
log4(finfo->group_id, finfo->file_id,
"getnameinfo failed: %s", gai_strerror(rval));
}
log4(finfo->group_id,finfo->file_id, "Sending to %s", out_addr);
}
if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h),
0, (const struct sockaddr *)destaddr,
family_len(*destaddr)) == SOCKET_ERROR) {
sockerror(finfo->group_id, finfo->file_id,
"Error sending %s", func_name(message));
sleep(1);
free(encpacket);
return 0;
}
if (packet_wait) usleep(packet_wait);
memset(idlist, 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)
{
const struct uftp_h *header;
header = (const struct uftp_h *)packet;
if (header->version != UFTP_VER_NUM) {
log1(finfo->group_id, finfo->file_id,
"Invalid version %02X", header->version);
return 0;
}
if (header->func == ENCRYPTED) {
if (len < sizeof(struct uftp_h) + sizeof(struct encrypted_h)) {
log1(finfo->group_id,finfo->file_id, "Invalid packet size %d", len);
return 0;
}
} else {
if (len < sizeof(struct uftp_h) + 4) {
log1(finfo->group_id,finfo->file_id, "Invalid packet size %d", len);
return 0;
}
}
if (ntohl(header->group_id) != finfo->group_id) {
log1(finfo->group_id, finfo->file_id, "Invalid group ID %08X, "
"expected %08X", ntohl(header->group_id), finfo->group_id);
return 0;
}
if ((header->func == ENCRYPTED) && (keytype == KEY_NONE)) {
log1(finfo->group_id, finfo->file_id,
"Received encrypted packet with encryption disabled");
return 0;
}
return 1;
}
/**
* Apply a signature to an ANNOUCE if the key exchange scheme calls for it.
* On entry, the packet should be complete other that the signature.
* Returns 1 on success, 0 on fail.
*/
int sign_announce(const struct finfo_t *finfo, unsigned char *packet,
int packetlen)
{
struct announce_h *announce;
struct enc_info_he *encinfo;
unsigned char *sig, *sigcopy;
unsigned int iplen, siglen, _siglen;
if ((keyextype != KEYEX_ECDH_RSA) && (keyextype != KEYEX_ECDH_ECDSA)) {
return 1;
}
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
if (announce->flags & FLAG_IPV6) {
iplen = sizeof(struct in6_addr);
} else {
iplen = sizeof(struct in_addr);
}
encinfo = (struct enc_info_he *)(packet + sizeof(struct uftp_h) +
sizeof(struct announce_h) + iplen + iplen);
sig = (unsigned char *)encinfo + sizeof(struct enc_info_he) +
ntohs(encinfo->keylen) + ntohs(encinfo->dhlen);
siglen = ntohs(encinfo->siglen);
memset(sig, 0, siglen);
sigcopy = safe_calloc(siglen, 1);
if (keyextype == KEYEX_ECDH_ECDSA) {
if (!create_ECDSA_sig(privkey.ec, hashtype, packet, packetlen,
sigcopy, &_siglen)) {
// Called function should log
free(sigcopy);
return 0;
}
} else {
if (!create_RSA_sig(privkey.rsa, hashtype, packet, packetlen,
sigcopy, &_siglen)) {
// Called function should log
free(sigcopy);
return 0;
}
}
if (_siglen != siglen) {
log0(finfo->group_id, finfo->file_id,
"Signature length doesn't match expected length");
log1(finfo->group_id, finfo->file_id,
"expected %d, got %d", siglen, _siglen);
free(sigcopy);
return 0;
}
memcpy(sig, sigcopy, siglen);
free(sigcopy);
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 id)
{
int i;
// TODO: This can be a lot more efficient. Should probably sort by
// ID and keep an index, then do a binary search.
for (i = 0; i < destcount; i++) {
if (destlist[i].id == id) {
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, uint32_t src)
{
const struct abort_h *abort_hdr;
int i;
abort_hdr = (const struct abort_h *)message;
if (meslen < (abort_hdr->hlen * 4)) {
log1(finfo->group_id, finfo->file_id,
"Rejecting ABORT from %08X: invalid message size",
(idx == -1) ? ntohl(src) : destlist[idx].id);
return;
}
if (idx == -1) {
log1(finfo->group_id, finfo->file_id, "Transfer aborted by %08X: %s",
ntohl(src), abort_hdr->message);
return;
}
if (abort_hdr->host != 0) {
idx = find_client(abort_hdr->host);
}
if (idx == -1) {
log1(finfo->group_id, finfo->file_id, "Transfer aborted by %08X: %s",
ntohl(src), abort_hdr->message);
} else {
destlist[idx].status = DEST_ABORT;
log1(finfo->group_id, finfo->file_id, "Transfer aborted by %s: %s",
destlist[idx].name, abort_hdr->message);
}
if (quit_on_error) {
log0(finfo->group_id, finfo->file_id, "Aboring all clients");
send_abort(finfo, "A client aborted, aborting all",
&receive_dest, 0, 0, 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, 0, 1, 0);
}
for (i = 0; i < destcount; i++) {
if ((destlist[i].status == DEST_ACTIVE) ||
(destlist[i].status == DEST_REGISTERED)) {
destlist[i].status = DEST_ABORT;
}
}
}
}
/**
* Recalculate the GRTT based on the RTTs of all receivers
* Returns 1 if at least one active client is found, 0 if no active clients
*/
int recalculate_grtt(const struct finfo_t *finfo, int grtt_set,
int clear_measured)
{
double new_grtt;
int i, found;
for (new_grtt = 0, found = 0, i = 0; i < destcount; i++) {
if (!client_error(i) && destlist[i].rtt_measured) {
found = 1;
if (destlist[i].rtt > new_grtt) {
new_grtt = destlist[i].rtt;
}
}
if (clear_measured) {
destlist[i].rtt_measured = 0;
}
destlist[i].rtt_sent = 0;
}
if (found) {
if (new_grtt < min_grtt) {
new_grtt = min_grtt;
} else if (new_grtt > max_grtt) {
new_grtt = max_grtt;
}
if (grtt_set && (new_grtt < 0.9 * grtt)) {
grtt = 0.9 * grtt;
} else {
grtt = new_grtt;
}
log3(finfo->group_id, finfo->file_id, "grtt = %.6f", grtt);
return 1;
} else {
return 0;
}
}
uftp-4.1.5/client_common.h 0000644 0000764 0000764 00000005213 12250244021 014473 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_COMMON_H
#define _CLIENT_COMMON_H
struct group_list_t *find_group(uint32_t group_id, uint8_t group_inst);
int uid_in_list(const uint32_t *addrlist, int size);
void read_restart_file(struct group_list_t *group);
void run_postreceive_multi(struct group_list_t *group, char *const *files,
int count);
void run_postreceive(struct group_list_t *group, char *file);
void file_cleanup(struct group_list_t *group, int abort);
void set_uftp_header(struct uftp_h *header, int func,
struct group_list_t *group);
void set_timeout(struct group_list_t *group, int rescale);
void send_abort(struct group_list_t *group, const char *message);
void handle_abort(struct group_list_t *group, const unsigned char *message,
unsigned meslen);
void send_key_req(void);
void handle_proxy_key(const union sockaddr_u *src,
unsigned char *message, unsigned meslen);
void clear_path(const char *path, struct group_list_t *group);
void move_to_backup(struct group_list_t *group);
int create_path_to_file(struct group_list_t *group, const char *filename);
void update_loss_history(struct group_list_t *group, uint16_t txseq, int size);
double loss_event_rate(struct group_list_t *group);
unsigned current_cc_rate(struct group_list_t *group);
#endif // _CLIENT_COMMON_H
uftp-4.1.5/encrypt_none.c 0000644 0000764 0000764 00000013113 12250244021 014341 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#ifdef WINDOWS
typedef unsigned __int8 uint8_t;
typedef __int8 int8_t;
typedef unsigned __int16 uint16_t;
typedef __int16 int16_t;
typedef unsigned __int32 uint32_t;
typedef __int32 int32_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
#else
#include
#endif
#include "encryption.h"
void crypto_init(int set_sys_key)
{
}
void crypto_cleanup()
{
}
/**
* Returns whether a particular cipher is supported
*/
int cipher_supported(int keytype)
{
return 0;
}
/**
* Returns whether a particular hash is supported
*/
int hash_supported(int hashtype)
{
return 0;
}
void get_key_info(int keytype, int *keylen, int *ivlen)
{
}
int get_hash_len(int hashtype)
{
return 0;
}
int get_random_bytes(unsigned char *buf, int num)
{
return 0;
}
int encrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *aad, unsigned int aadlen,
const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
return 0;
}
int decrypt_block(int keytype, const unsigned char *IV,
const unsigned char *key,
const unsigned char *aad, unsigned int aadlen,
unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
return 0;
}
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)
{
return 0;
}
int hash(int hashtype, const unsigned char *src, unsigned int srclen,
unsigned char *dest, unsigned int *destlen)
{
return 0;
}
int RSA_keylen(const RSA_key_t rsa)
{
return 0;
}
int EC_keylen(const EC_key_t ec)
{
return 0;
}
int ECDSA_siglen(const EC_key_t ec)
{
return 0;
}
int RSA_encrypt(RSA_key_t rsa, const unsigned char *from, unsigned int fromlen,
unsigned char *to, unsigned int *tolen)
{
return 0;
}
int RSA_decrypt(RSA_key_t rsa, const unsigned char *from, unsigned int fromlen,
unsigned char *to, unsigned int *tolen)
{
return 0;
}
int create_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
return 0;
}
int verify_RSA_sig(RSA_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int siglen)
{
return 0;
}
int create_ECDSA_sig(EC_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
return 0;
}
int verify_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
const unsigned char *sig, unsigned int siglen)
{
return 0;
}
int get_ECDH_key(EC_key_t pubkey, EC_key_t privkey, unsigned char *key,
unsigned int *keylen)
{
return 0;
}
int import_RSA_key(RSA_key_t *rsa, const unsigned char *keyblob,
uint16_t bloblen)
{
return 0;
}
int export_RSA_key(const RSA_key_t rsa, unsigned char *keyblob,
uint16_t *bloblen)
{
return 0;
}
int import_EC_key(EC_key_t *ec, const unsigned char *keyblob, uint16_t bloblen,
int isdh)
{
return 0;
}
int export_EC_key(const EC_key_t ec, unsigned char *keyblob, uint16_t *bloblen)
{
return 0;
}
RSA_key_t gen_RSA_key(int bits, int exponent, const char *filename)
{
return NULL;
}
RSA_key_t read_RSA_key(const char *filename)
{
return NULL;
}
EC_key_t gen_EC_key(uint8_t curve, int isdh, const char *filename)
{
return NULL;
}
EC_key_t read_EC_key(const char *filename)
{
return NULL;
}
union key_t read_private_key(const char *filename, int *keytype)
{
union key_t key;
key.key = 0;
*keytype = 0;
return key;
}
uint8_t get_EC_curve(const EC_key_t ec)
{
return 0;
}
void free_RSA_key(RSA_key_t rsa)
{
}
void free_EC_key(EC_key_t ec)
{
}
const char *get_next_container()
{
return NULL;
}
void delete_container(const char *name)
{
}
void set_sys_keys(int set)
{
}
uftp-4.1.5/proxy_downstream.c 0000644 0000764 0000764 00000052333 12250244021 015271 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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 id, struct pr_group_list_t *group)
{
struct pr_destinfo_t *dest;
dest = &group->destinfo[group->destcount];
snprintf(dest->name, sizeof(dest->name), "0x%08X", ntohl(id));
dest->id = id;
dest->pending = -1;
return group->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(struct pr_group_list_t *group, int hostidx)
{
unsigned char *seed, *prf_buf;
int explen, len, seedlen;
struct pr_destinfo_t *dest;
dest = &group->destinfo[hostidx];
explen = group->keylen + group->ivlen +
group->hmaclen;
seedlen = sizeof(group->rand1) * 2;
seed = safe_calloc(seedlen, 1);
prf_buf = safe_calloc(MASTER_LEN + explen + group->hmaclen, 1);
memcpy(seed, group->rand1, sizeof(group->rand1));
memcpy(seed + sizeof(group->rand1), dest->rand2,
sizeof(dest->rand2));
PRF(group->hashtype, MASTER_LEN, dest->premaster, dest->premaster_len,
"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(struct pr_group_list_t *group, int hostidx)
{
uint8_t *verifydata;
int verifylen;
struct pr_destinfo_t *dest;
dest = &group->destinfo[hostidx];
// build_verify_data should never fail in this case
verifydata = build_verify_data(group, hostidx, &verifylen, 0);
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!verify_RSA_sig(dest->pubkey.rsa, group->hashtype, verifydata,
verifylen, dest->verifydata, dest->verifylen)) {
log2(group->group_id, 0, "Rejecting CLIENT_KEY from %s: "
"verify data mismatch", dest->name);
free(verifydata);
return 0;
}
} else {
if (!verify_ECDSA_sig(dest->pubkey.ec, group->hashtype, verifydata,
verifylen, dest->verifydata, dest->verifylen)) {
log2(group->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,
struct pr_group_list_t *group, int hostidx,
uint32_t src)
{
unsigned char premaster[PUBKEY_LEN];
unsigned int len;
struct pr_destinfo_t *dest;
dest = &group->destinfo[hostidx];
memcpy(dest->rand2, reg->rand2, sizeof(dest->rand2));
if (group->keyextype == KEYEX_RSA) {
if (!RSA_decrypt(group->proxy_privkey.rsa, enckey,
ntohs(reg->keyinfo_len), premaster, &len)) {
log2(group->group_id, 0, "Rejecting REGISTER from %s: "
"failed to decrypt premaster secret", dest->name);
return 0;
}
if (len != MASTER_LEN) {
log2(group->group_id, 0, "Rejecting REGISTER from %s: "
"premaster secret wrong length", dest->name);
return 0;
}
} else {
if (!import_EC_key(&dest->dhkey.ec, enckey,
ntohs(reg->keyinfo_len), 1)) {
log2(group->group_id, 0, "Rejecting REGISTER from %s: "
"failed to import ECDH key", dest->name);
return 0;
}
if (get_EC_curve(dest->dhkey.ec) !=
get_EC_curve(group->proxy_dhkey.ec)) {
log2(group->group_id, 0, "Rejecting REGISTER from %s: "
"invalid curve for ECDH", dest->name);
return 0;
}
if (!get_ECDH_key(dest->dhkey.ec, group->proxy_dhkey.ec,
premaster, &len)) {
log2(group->group_id, 0, "Rejecting REGISTER from %s: "
"failed to calculate premaster secret", dest->name);
return 0;
}
}
memcpy(dest->premaster, premaster, len);
dest->premaster_len = len;
calculate_client_keys(group, hostidx);
if (dest->pubkey.key) {
if (!verify_client_key(group, hostidx)) {
return 0;
}
}
return 1;
}
/**
* Handles an incoming REGSITER message from a client.
*/
void handle_register(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen,
uint32_t src)
{
const struct register_h *reg;
const unsigned char *enckey;
struct pr_destinfo_t *dest;
int dupmsg;
reg = (const struct register_h *)message;
enckey = (const unsigned char *)reg + sizeof(struct register_h);
if (group->destcount == MAXPROXYDEST) {
log2(group->group_id, 0, "Rejecting REGISTER from %08X: "
"max destinations exceeded", ntohl(src));
send_downstream_abort(group, src, "Max destinations exceeded", 0);
return;
}
if ((meslen < (reg->hlen * 4U)) || ((reg->hlen * 4U) <
sizeof(struct register_h) + ntohs(reg->keyinfo_len))) {
log2(group->group_id, 0, "Rejecting REGISTER from %08X: "
"invalid message size", ntohl(src));
send_downstream_abort(group, src, "Invalid message size", 0);
return;
}
if (hostidx == -1) {
hostidx = add_client(src, group);
}
dest = &group->destinfo[hostidx];
dupmsg = (dest->registered == 1);
dest->registered = 1;
dest->regtime.tv_sec = ntohl(reg->tstamp_sec);
dest->regtime.tv_usec = ntohl(reg->tstamp_usec);
if (dest->state != PR_CLIENT_REGISTERED) {
if (group->keytype != KEY_NONE) {
if (!handle_register_keys(reg, enckey, group, hostidx, src)) {
return;
}
}
if (!group->client_auth || dest->pubkey.key) {
dest->state = PR_CLIENT_REGISTERED;
}
}
log2(group->group_id, 0, "Received REGISTER%s from %s",
dupmsg ? "+" : "", dest->name);
if (dest->state == PR_CLIENT_REGISTERED) {
check_pending(group, hostidx, message);
}
}
/**
* Handles an incoming CLIENT_KEY message from a client.
*/
void handle_clientkey(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen,
uint32_t src)
{
const struct client_key_h *clientkey;
const unsigned char *keyblob, *verify;
struct pr_destinfo_t *dest;
int dupmsg;
clientkey = (const struct client_key_h *)message;
keyblob = (const unsigned char *)clientkey + sizeof(struct client_key_h);
verify = keyblob + ntohs(clientkey->bloblen);
if (group->destcount == MAXPROXYDEST) {
log2(group->group_id, 0, "Rejecting CLIENT_KEY from %08X: "
"max destinations exceeded", ntohl(src));
send_downstream_abort(group, src, "Max destinations exceeded", 0);
return;
}
if ((meslen < (clientkey->hlen * 4U)) ||
((clientkey->hlen * 4U) < sizeof(struct client_key_h) +
ntohs(clientkey->bloblen) + ntohs(clientkey->siglen))) {
log2(group->group_id, 0, "Rejecting CLIENT_KEY from %08X: "
"invalid message size", ntohl(src));
send_downstream_abort(group, src, "Invalid message size", 0);
return;
}
if ((((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) &&
(keyblob[0] != KEYBLOB_RSA)) ||
((group->keyextype == KEYEX_ECDH_ECDSA) &&
(keyblob[0] != KEYBLOB_EC))) {
log2(group->group_id, 0, "Rejecting CLIENT_KEY from %08X: "
"invalid keyblob type", ntohl(src));
send_downstream_abort(group, src, "Invalid keyblob type", 0);
return;
}
if (hostidx == -1) {
hostidx = add_client(src, group);
}
dest = &group->destinfo[hostidx];
dupmsg = (dest->pubkey.key != 0);
if (!dest->verified) {
if (keyblob[0] == KEYBLOB_RSA) {
if (!import_RSA_key(&dest->pubkey.rsa, keyblob,
ntohs(clientkey->bloblen))) {
log2(group->group_id, 0, "Failed to load client public key");
send_downstream_abort(group, src,
"Failed to load client public key", 0);
return;
}
dest->pubkeylen = RSA_keylen(dest->pubkey.rsa);
} else {
if (!import_EC_key(&dest->pubkey.ec, keyblob,
ntohs(clientkey->bloblen), 0)) {
log2(group->group_id, 0, "Failed to load client public key");
send_downstream_abort(group, src,
"Failed to load client public key", 0);
return;
}
dest->pubkeylen = ECDSA_siglen(dest->pubkey.ec);
}
if (!verify_fingerprint(client_fp, client_fp_count, keyblob,
ntohs(clientkey->bloblen), group, src)) {
log2(group->group_id, 0, "Failed to verify client key fingerprint");
send_downstream_abort(group, src,
"Failed to verify client key fingerprint", 0);
return;
}
dest->verified = 1;
}
memcpy(dest->verifydata, verify, ntohs(clientkey->siglen));
dest->verifylen = ntohs(clientkey->siglen);
if (dest->registered) {
if (!verify_client_key(group, hostidx)) {
return;
}
dest->state = PR_CLIENT_REGISTERED;
}
log2(group->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.
struct register_h reg;
reg.func = REGISTER;
check_pending(group, hostidx, (unsigned char *)®);
}
}
/**
* Handles an incoming KEYINFO_ACK message from a client
*/
void handle_keyinfo_ack(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen)
{
const struct keyinfoack_h *keyinfoack;
unsigned char *verifydata, *verify_hash, *verify_test;
int verifylen, len, dupmsg;
unsigned int hashlen;
struct pr_destinfo_t *dest;
keyinfoack = (const struct keyinfoack_h *)message;
dest = &group->destinfo[hostidx];
if ((meslen < (keyinfoack->hlen * 4U)) ||
((keyinfoack->hlen * 4U) < sizeof(struct keyinfoack_h))) {
log2(group->group_id, 0, "Rejecting KEYINFO_ACK from %s: "
"invalid message size", dest->name);
send_downstream_abort(group, dest->id, "Invalid message size", 0);
return;
}
if (!(verifydata = build_verify_data(group, hostidx, &verifylen,1))) {
log2(group->group_id, 0, "Rejecting KEYINFO_ACK from %s: "
"error exporting client public key", dest->name);
return;
}
verify_hash = safe_calloc(group->hmaclen, 1);
verify_test = safe_calloc(VERIFY_LEN + group->hmaclen, 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(keyinfoack->verify_data, verify_test, VERIFY_LEN)) {
log2(group->group_id, 0, "Rejecting KEYINFO_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);
log2(group->group_id, 0, "Received KEYINFO_ACK%s from %s",
dupmsg ? "+" : "", dest->name);
dest->state = PR_CLIENT_READY;
if (!check_unfinished_clients(group, 0)) {
group->phase = PR_PHASE_RECEIVING;
}
}
/**
* Handles an incoming FILEINFO_ACK message from a client
*/
void handle_fileinfo_ack(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen)
{
const struct fileinfoack_h *fileinfoack;
struct pr_destinfo_t *dest;
fileinfoack = (const struct fileinfoack_h *)message;
dest = &group->destinfo[hostidx];
if ((meslen < (fileinfoack->hlen * 4U)) ||
((fileinfoack->hlen * 4U) < sizeof(struct fileinfoack_h))) {
log2(group->group_id, ntohs(fileinfoack->file_id),
"Rejecting FILEINFO_ACK from %s: invalid message size",
dest->name);
return;
}
log2(group->group_id, ntohs(fileinfoack->file_id),
"Received FILEINFO_ACK from %s", dest->name);
check_pending(group, hostidx, message);
}
/**
* Sends a KEYINFO to each client that the server sent a REG_CONF for.
*/
void send_keyinfo(struct pr_group_list_t *group, const uint32_t *addrlist,
int addrlen)
{
unsigned char *buf, *iv;
struct uftp_h *header;
struct keyinfo_h *keyinfo_hdr;
struct destkey *keylist;
unsigned int payloadlen, len;
int maxdest, packetcnt, dests, iv_init, foundaddr, i, j;
int unauth_keytype, unauth_keylen, unauth_ivlen;
struct pr_destinfo_t *dest;
// Don't use a cipher in an authentication mode to encrypt the group master
unauth_keytype = unauth_key(group->keytype);
get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);
buf = safe_calloc(MAXMTU, 1);
iv = safe_calloc(unauth_ivlen, 1);
header = (struct uftp_h *)buf;
keyinfo_hdr = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
keylist= (struct destkey *)((char *)keyinfo_hdr + sizeof(struct keyinfo_h));
set_uftp_header(header, KEYINFO, group);
keyinfo_hdr->func = KEYINFO;
keyinfo_hdr->hlen = sizeof(struct keyinfo_h) / 4;
iv_init = 0;
maxdest = max_msg_dest(group, KEYINFO, keyinfo_hdr->hlen * 4);
packetcnt = 1;
for (i = 0, dests = 0; i < group->destcount; i++) {
dest = &group->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->id == addrlist[j]) {
foundaddr = 1;
}
}
} else {
foundaddr = 1;
}
if (foundaddr) {
if (!iv_init) {
group->ivctr++;
keyinfo_hdr->iv_ctr_hi =
htonl((group->ivctr & 0xFFFFFFFF00000000LL) >> 32);
keyinfo_hdr->iv_ctr_lo =
htonl(group->ivctr & 0x00000000FFFFFFFFLL);
iv_init = 1;
}
keylist[dests].dest_id = dest->id;
build_iv(iv, dest->salt, unauth_ivlen,
uftp_htonll(group->ivctr), group->src_id);
if (!encrypt_block(unauth_keytype, iv, dest->key,
NULL, 0, &group->groupmaster[1],
sizeof(group->groupmaster) - 1,
keylist[dests].groupmaster, &len)) {
log2(group->group_id, 0,
"Error encrypting KEYINFO for %s", dest->name);
free(buf);
free(iv);
return;
}
dests++;
}
}
if ((dests >= maxdest) ||
((i == group->destcount - 1) && (dests > 0))) {
payloadlen = sizeof(struct keyinfo_h) +
(dests * sizeof(struct destkey));
log2(group->group_id, 0, "Sending KEYINFO %d.%d",
group->keyinfo_cnt, packetcnt);
if (nb_sendto(listener, buf, payloadlen + sizeof(struct uftp_h), 0,
(struct sockaddr *)&group->privatemcast,
family_len(group->privatemcast)) == SOCKET_ERROR) {
sockerror(group->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));
iv_init = 0;
dests = 0;
packetcnt++;
}
}
group->keyinfo_cnt++;
set_timeout(group, 0, 0);
free(buf);
free(iv);
}
/**
* Handles an incoming STATUS message from a client
*/
void handle_status(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen)
{
const struct status_h *status;
int mes_section;
struct pr_destinfo_t *dest;
status = (const struct status_h *)message;
mes_section = ntohs(status->section);
dest = &group->destinfo[hostidx];
if ((meslen < (status->hlen * 4U)) ||
((status->hlen * 4U) < sizeof(struct status_h))) {
log2(group->group_id, ntohs(status->file_id),
"Rejecting STATUS from %s: invalid message size", dest->name);
return;
}
log2(group->group_id, ntohs(status->file_id),
"Got STATUS for section %d from %s", mes_section, dest->name);
check_pending(group, hostidx, message);
}
/**
* Handles an incoming COMPLETE message from a client
*/
void handle_complete(struct pr_group_list_t *group, int hostidx,
const unsigned char *message, unsigned meslen)
{
const struct complete_h *complete;
struct pr_destinfo_t *dest;
int alldone, i;
char status[20];
complete = (const struct complete_h *)message;
dest = &group->destinfo[hostidx];
if ((meslen < (complete->hlen * 4U)) ||
((complete->hlen * 4U) < sizeof(struct complete_h))) {
log2(group->group_id, ntohs(complete->file_id),
"Rejecting COMPLETE from %s: invalid message size", dest->name);
return;
}
switch (complete->status) {
case COMP_STAT_NORMAL:
strncpy(status, "", sizeof(status));
break;
case COMP_STAT_SKIPPED:
strncpy(status, "(skipped)", sizeof(status));
break;
case COMP_STAT_OVERWRITE:
strncpy(status, "(overwritten)", sizeof(status));
break;
case COMP_STAT_REJECTED:
strncpy(status, "(rejected)", sizeof(status));
break;
}
log2(group->group_id, ntohs(complete->file_id),
"Received COMPLETE%s from %s", status, dest->name);
if (ntohs(complete->file_id) == 0) {
dest->state = PR_CLIENT_DONE;
for (alldone = 1, i = 0;
(i < group->destcount) && alldone; i++) {
alldone = alldone && (group->destinfo[i].state == PR_CLIENT_DONE);
}
if (alldone) {
group->phase = PR_PHASE_DONE;
}
}
check_pending(group, hostidx, message);
}
uftp-4.1.5/server_init.c 0000644 0000764 0000764 00000043357 12250244021 014204 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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 // if WINDOWS
#include
#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;
closesocket(sock);
for (i = 0; i < destcount; i++) {
if (keytype != KEY_NONE) {
if (destlist[i].encinfo) {
if (destlist[i].encinfo->pubkey.key) {
if ((keyextype == KEYEX_RSA) ||
(keyextype == KEYEX_ECDH_RSA)) {
free_RSA_key(destlist[i].encinfo->pubkey.rsa);
} else {
free_EC_key(destlist[i].encinfo->pubkey.ec);
}
if ((keyextype == KEYEX_ECDH_RSA) ||
(keyextype == KEYEX_ECDH_ECDSA)) {
free_EC_key(destlist[i].encinfo->dhkey.ec);
}
}
free(destlist[i].encinfo);
}
}
if (destlist[i].clients) {
free(destlist[i].clients);
}
}
if (keytype != KEY_NONE) {
if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
free_RSA_key(privkey.rsa);
} else {
free_EC_key(privkey.ec);
}
if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) {
free_EC_key(dhkey.ec);
}
}
crypto_cleanup();
if (status_file) {
fclose(status_file);
}
#ifdef WINDOWS
WSACleanup();
#endif
}
/**
* Generic signal handler, sets user_abort flag
*/
void gotsig(int sig)
{
log0(0, 0, "Got signal %d, aborting", sig);
user_abort = 1;
}
#ifdef WINDOWS
/**
* Windows event handler, sets user_abort flag
*/
BOOL WINAPI winsig(DWORD event)
{
switch (event) {
case CTRL_C_EVENT:
log0(0, 0, "Got CTRL_C_EVENT");
break;
case CTRL_BREAK_EVENT:
log0(0, 0, "Got CTRL_BREAK_EVENT");
break;
case CTRL_CLOSE_EVENT:
log0(0, 0, "Got CTRL_CLOSE_EVENT");
break;
case CTRL_LOGOFF_EVENT:
log0(0, 0, "Got CTRL_LOGOFF_EVENT");
break;
case CTRL_SHUTDOWN_EVENT:
log0(0, 0, "Got CTRL_SHUTDOWN_EVENT");
break;
default:
log0(0, 0, "GOT unknown event", event);
break;
}
user_abort = 1;
return TRUE;
}
#endif
/**
* Do initial setup before parsing arguments, including getting interface list
*/
void pre_initialize(void)
{
#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(void)
{
struct addrinfo ai_hints, *ai_rval;
char *p, tmp_multi[INET6_ADDRSTRLEN];
int found_if, fdflag, bcast, rval, i;
// Set up global sockaddr_u structs for public and private addresses
// Perform octet substitution on private multicast address
// Make sure public, private, and interface addrs are the same IP version
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = AF_UNSPEC;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = 0;
if ((rval = getaddrinfo(pub_multi, port, &ai_hints, &ai_rval)) != 0) {
log0(0, 0, "Invalid public address or port: %s", gai_strerror(rval));
exit(1);
}
memcpy(&listen_dest, ai_rval->ai_addr, ai_rval->ai_addrlen);
freeaddrinfo(ai_rval);
if (!is_multicast(&listen_dest, 0)) {
receive_dest = listen_dest;
} else {
if (listen_dest.ss.ss_family == AF_INET6) {
while ((p = strchr(priv_multi,'x')) != NULL) {
memset(tmp_multi, 0, sizeof(tmp_multi));
snprintf(tmp_multi, sizeof(tmp_multi), "%.*s%x%s",
(int)(p - priv_multi), priv_multi, rand() & 0xFFFF, p+1);
strncpy(priv_multi, tmp_multi, sizeof(priv_multi));
priv_multi[sizeof(priv_multi)-1] = '\x0';
}
} else {
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);
strncpy(priv_multi, tmp_multi, sizeof(priv_multi));
priv_multi[sizeof(priv_multi)-1] = '\x0';
}
}
if ((rval = getaddrinfo(priv_multi, port, &ai_hints, &ai_rval)) != 0) {
log0(0, 0, "Invalid private address: %s", gai_strerror(rval));
exit(1);
}
memcpy(&receive_dest, ai_rval->ai_addr, ai_rval->ai_addrlen);
freeaddrinfo(ai_rval);
}
if (!strcmp(out_if.name, "")) {
for (i = 0, found_if = 0; (i < ifl_len) && !found_if; i++) {
if ((ifl[i].su.ss.ss_family == listen_dest.ss.ss_family) &&
(!ifl[i].isloopback)) {
found_if = 1;
out_if = ifl[i];
}
}
if (!found_if) {
for (i = 0, found_if = 0; (i < ifl_len) && !found_if; i++) {
if (ifl[i].su.ss.ss_family == listen_dest.ss.ss_family) {
found_if = 1;
out_if = ifl[i];
}
}
}
if (!found_if) {
log0(0, 0, "ERROR: no network interface found for family");
exit(1);
}
}
if (out_if.su.ss.ss_family == AF_INET6) {
server_id = out_if.su.sin6.sin6_addr.s6_addr[12] << 24;
server_id |= out_if.su.sin6.sin6_addr.s6_addr[13] << 16;
server_id |= out_if.su.sin6.sin6_addr.s6_addr[14] << 8;
server_id |= out_if.su.sin6.sin6_addr.s6_addr[15];
} else {
server_id = ntohl(out_if.su.sin.sin_addr.s_addr);
}
if (listen_dest.ss.ss_family != receive_dest.ss.ss_family) {
log0(0, 0, "IP version mismatch between public and private addresses");
exit(1);
}
if (listen_dest.ss.ss_family != out_if.su.ss.ss_family) {
log0(0,0, "IP version mismatch between public and interface addresses");
exit(1);
}
// Create and bind socket
if ((sock = socket(listen_dest.ss.ss_family, SOCK_DGRAM, 0)) ==
INVALID_SOCKET) {
sockerror(0, 0, "Error creating socket");
exit(1);
}
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = listen_dest.ss.ss_family;
ai_hints.ai_socktype = SOCK_DGRAM;
ai_hints.ai_protocol = 0;
ai_hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
if ((rval = getaddrinfo(NULL, srcport, &ai_hints, &ai_rval)) != 0) {
log0(0, 0, "Error getting bind address: %s", gai_strerror(rval));
exit(1);
}
if (bind(sock, ai_rval->ai_addr, ai_rval->ai_addrlen) == SOCKET_ERROR) {
sockerror(0, 0, "Error binding socket");
exit(1);
}
freeaddrinfo(ai_rval);
// 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);
}
}
}
bcast = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&bcast,
sizeof(bcast)) == SOCKET_ERROR) {
sockerror(0, 0, "Error enabling broadcast");
closesocket(sock);
exit(1);
}
if (listen_dest.ss.ss_family == AF_INET6) {
#ifdef IPV6_MTU_DISCOVER
{
int mtuflag = IP_PMTUDISC_DONT;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
(char *)&mtuflag, sizeof(mtuflag)) == SOCKET_ERROR) {
sockerror(0, 0, "Error disabling MTU discovery");
closesocket(sock);
exit(1);
}
}
#endif
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl,
sizeof(ttl)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting ttl");
closesocket(sock);
exit(1);
}
#if defined IPV6_TCLASS && !defined WINDOWS
if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (char *)&dscp,
sizeof(dscp)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting dscp");
closesocket(sock);
exit(1);
}
#endif
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
(char *)&out_if.ifidx, sizeof(int)) == SOCKET_ERROR) {
sockerror(0, 0, "Error setting outgoing interface");
closesocket(sock);
exit(1);
}
} else {
char l_ttl = ttl & 0xFF;
#ifdef IP_MTU_DISCOVER
{
int mtuflag = IP_PMTUDISC_DONT;
if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&mtuflag,
sizeof(mtuflag)) == SOCKET_ERROR) {
sockerror(0, 0, "Error disabling MTU discovery");
closesocket(sock);
exit(1);
}
}
#endif
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &l_ttl,
sizeof(l_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 (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&out_if.su.sin.sin_addr,
sizeof(out_if.su.sin.sin_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
}
/**
* Initialize crypto library, generate keys
*/
void key_init(void)
{
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 + SALT_LEN;
prf_buf = safe_calloc(explen + hmaclen, 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, SALT_LEN);
ivctr = 0;
free(prf_buf);
if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
if ((!strcmp(keyfile, "")) || (newkeylen != 0)) {
privkey.rsa = gen_RSA_key(newkeylen, RSA_EXP, keyfile);
} else {
privkey.rsa = read_RSA_key(keyfile);
}
if (!privkey.key) {
log0(0, 0, "Failed to read/generate private key");
exit(1);
}
privkeylen = RSA_keylen(privkey.rsa);
} else {
if ((!strcmp(keyfile, "")) || (ecdsa_curve != 0)) {
privkey.ec = gen_EC_key(ecdsa_curve, 0, keyfile);
} else {
privkey.ec = read_EC_key(keyfile);
}
if (!privkey.key) {
log0(0, 0, "Failed to read/generate private key");
exit(1);
}
privkeylen = ECDSA_siglen(privkey.ec);
}
if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) {
dhkey.ec = gen_EC_key(ecdh_curve, 1, NULL);
if (!dhkey.key) {
log0(0, 0, "Failed to generate DH key");
exit(1);
}
}
}
/**
* Initialization based on command line args
*/
void initialize(void)
{
atexit(cleanup);
init_log_mux = 1;
init_log(0);
#ifdef WINDOWS
SetConsoleCtrlHandler(winsig, TRUE);
#else
{
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);
sigaction(SIGPIPE, &act, NULL);
act.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &act, NULL);
}
#endif
if (strcmp(statusfilename, "")) {
if ((status_file = fopen(statusfilename, "at")) == NULL) {
perror("Can't open status file");
exit(1);
}
}
key_init();
create_sockets();
// Size of data packet, used in transmission speed calculations
datapacketsize = blocksize + sizeof(struct fileseg_h);
if (cc_type == CC_TFMCC) {
datapacketsize += sizeof(struct tfmcc_data_info_he);
}
if (keytype != KEY_NONE) {
datapacketsize += ((sigtype == SIG_KEYEX) ? privkeylen :
(sigtype == SIG_HMAC) ? hmaclen : 0) +
KEYBLSIZE + sizeof(struct encrypted_h);
}
// 8 = UDP size, 20 = IPv4 size, 40 = IPv6 size
if (listen_dest.ss.ss_family == AF_INET6) {
datapacketsize += sizeof(struct uftp_h) + 8 + 40;
} else {
datapacketsize += sizeof(struct uftp_h) + 8 + 20;
}
// Never ask for a client key with no encryption,
// and always ask with RSA/ECDSA signatures
if (keytype == KEY_NONE) {
client_auth = 0;
} else if (sigtype == SIG_KEYEX) {
client_auth = 1;
}
if (cc_type == CC_NONE || cc_type == CC_UFTP3) {
if (rate == -1) {
packet_wait = 0;
} else {
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
}
} else if (cc_type == CC_TFMCC) {
// Initialize the rate to the default rate for control message timing
packet_wait = (int32_t)(1000000.0 * datapacketsize / rate);
}
}
uftp-4.1.5/proxy_common.h 0000644 0000764 0000764 00000007055 12250244021 014404 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _PROXY_COMMON_H
#define _PROXY_COMMON_H
struct pr_group_list_t *find_group(uint32_t group_id, uint8_t group_inst);
int find_client(struct pr_group_list_t *group, uint32_t addr);
void group_cleanup(struct pr_group_list_t *group);
void set_uftp_header(struct uftp_h *header, int func,
struct pr_group_list_t *group);
void set_timeout(struct pr_group_list_t *group, int pending_reset, int rescale);
int max_msg_dest(struct pr_group_list_t *group, int func, int hlen);
void send_all_pending(struct pr_group_list_t *group);
void check_pending(struct pr_group_list_t *group, int hostdix,
const unsigned char *message);
int load_pending(struct pr_group_list_t *group, int pendidx, int func,
uint32_t *addrlist, int listlen);
int check_unfinished_clients(struct pr_group_list_t *group, int abort);
void forward_message(struct pr_group_list_t *group,
const union sockaddr_u *src,
unsigned char *packet, int packetlen);
void handle_hb_request(const union sockaddr_u *src,
unsigned char *packet, unsigned packetlen);
void handle_key_req(const union sockaddr_u *src,
const unsigned char *packet, unsigned packetlen);
void handle_abort(struct pr_group_list_t *group, const union sockaddr_u *src,
const unsigned char *message, unsigned meslen,
uint32_t src_id);
void send_upstream_abort(struct pr_group_list_t *group, uint32_t dest_id,
const char *message);
void send_downstream_abort(struct pr_group_list_t *group, uint32_t dest_id,
const char *message, int current);
void send_hb_response(const union sockaddr_u *src, int response);
void send_proxy_key(void);
int verify_fingerprint(const struct fp_list_t *fplist, int listlen,
const unsigned char *keyblob, uint16_t bloblen,
struct pr_group_list_t *group, uint32_t id);
uint8_t *build_verify_data(struct pr_group_list_t *group, int hostidx,
int *verifylen, int full);
void add_naks_to_pending(struct pr_group_list_t *group, int pendidx,
const unsigned char *message);
#endif // _PROXY_COMMON_H
uftp-4.1.5/uftp_keymgt.1 0000644 0000764 0000764 00000007605 12176066071 014141 0 ustar dbush dbush .TH uftp_keymgt 1 "30 July 2013" "UFTP 4.1"
.SH NAME
uftp_keymgt - Encrypted UDP based ftp with multicast - key management utility
.SH SYNOPSIS
uftp_keymgt [ -m ] [ key_file [ key_file ...] ]
uftp_keymgt [ -m ] -g { rsa:key_length | ec:curve } key_file
uftp_keymgt [ -m ] -d key_file
.SH DESCRIPTION
.P
uftp_keymgt is a utility for creating, viewing, and deleting RSA and EC private keys used by the UFTP suite. Although keys can be generated on the fly by uftp(1), uftpd(1), and uftpproxyd(1), this utility gives a more straightforward way of doing so without having to kick off a dummy process just to create/view a key.
The definition of key_file is dependent on the crypto library UFTP is compiled to use.
On Windows systems, UFTP can built to use either CNG, which is the new API supported by Windows Vista and Windows 7, or CryptoAPI, which is the legacy API and the only one available to Windows XP.
Under CryptoAPI, all RSA private keys must be stored in a key container (technically only keys used to sign data, but for UFTP\(aqs purposes this is the case). Key containers are internal to Windows, and each user (and the system) has its own set of key containers. In this case, key_file is actually the name of the key container. Elliptic Curve algorithms are not supported under CryptoAPI.
Under CNG, RSA and ECDSA private keys are also stored in key containers, and RSA keys created by CrypoAPI may be read by CNG. Like CryptoAPI, key_file also specifies the key container name. CNG only supports 3 named EC curves: prime256v1, secp384r1, and secp521r1.
All other systems use OpenSSL for the crypto library (although under Windows UFTP can be also be built to use it). In this case, key_file specifies a file name where the RSA or ECDSA private key is stored unencrypted in PEM format (the OS is expected to protect this file).
The -g option is used to generate a key of a given type and store it in the given key_file. The key size and key fingerprint are then printed. Under OpenSSL, a key can actually be generated by the openssl(1) command line utility, although you\(aqll still need to run uftp_keymgt on it to see the fingerprint.
The -d option is available only on Windows systems using CryptoAPI or CNG. It deletes the key container specified by key_file.
When neither -g or -d are specified, the type and fingerprint of all keys listed are printed. If no keys are specified under Windows with CryptoAPI or CNG, the private key for all key containers for the current user are printed. Note that some key containers may exist that an application other than UFTP is using, and some of those may not have an RSA or EC private key.
.SH OPTIONS
.P
The following options are supported:
.TP
.B \-g { rsa:key_length | ec:curve } key_file
Specifies the type of new key and the key_file to store it in. New keys are specified as either rsa:key_length, which creates an RSA private key key_length bits wide, or as ec:curve, which creates an EC key using the curve "curve".
The list of supported EC curves is as follows (availability may vary depending on system settings and crypto library used):
sect163k1 sect163r1 sect163r2 sect193r1 sect193r2 sect233k1 sect233r1 sect239k1 sect283k1 sect283r1 sect409k1 sect409r1 sect571k1 sect571r1 secp160k1 secp160r1 secp160r2 secp192k1 prime192v1 secp224k1 secp224r1 secp256k1 prime256v1 secp384r1 secp521r1
.TP
.B \-d key_file
Specifies the Windows key container to delete.
.TP
.B \-m
For Windows systems using CryptoAPI or CNG, private keys are normally stored in the key container of the running user. Specifying this option stores keys in the system key container. On non-Windows systems, this option has no effect.
.SH SEE ALSO
uftp(1), uftpd(1), uftpproxyd(1)
.SH NOTES
.P
The latest version of UFTP can be found at http://uftp-multicast.sourceforge.net. UFTP is covered by the GNU General Public License. Commercial licenses and support are available from Dennis Bush (bush@tcnj.edu).
uftp-4.1.5/client_config.h 0000644 0000764 0000764 00000005112 12176066071 014464 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_CONFIG_H
#define _CLIENT_CONFIG_H
#define DEF_PORT "1044"
#define DEF_PUB_MULTI "230.4.4.1"
#define DEF_RCVBUF 262144
#define DEF_BSD_RCVBUF 233016
#define DEF_DSCP 0
#define DEF_HB_INT 20
#ifdef WINDOWS
#define DEF_LOGFILE "C:\\uftpd_log.txt"
#define DEF_DESTDIR "C:\\temp"
#elif defined VMS
#define DEF_LOGFILE "SYS$SCRATCH:uftpd_log.txt"
#define DEF_DESTDIR "SYS$SCRATCH"
#else
#define DEF_LOGFILE "/tmp/uftpd.log"
#define DEF_DESTDIR "/tmp"
#endif
#define DEF_TEMPDIR ""
#define USAGE "uftpd [ -d ] [ -p port ] [ -B buf_size ]\n\
[ -E ] [ -Q dscp ] [ -U UID ] [ -x log_level ] [ -t ] [ -T temp_dir ]\n\
[ -D dest_dir [ dest_dir... ]] [ -A backup_dir [ backup_dir... ]]\n\
[ -s postreceive_script] [ -L logfile ] [] [ -P pidfile ]\n\
[ -S serverlist_file ] [ -R proxy[/fp] ] [ -k keyfile[,keyfile...] ]\n\
[ -K rsa:key_length | ec:curve[,rsa:key_length | ec:curve...]]\n\
[ -m ] [ -N priority ] [ -i ] [ -g max_log_size ] [ -n max_log_count ]\n\
[ -H hb_server[:port][,hb_server[:port]...] ] [ -h hb_interval ]\n\
[ -I interface[,interface...] ] [ -M pub_mcast_addr[,pub_mcast_addr...] ]\n"
void process_args(int argc, char *argv[]);
#endif // _CLIENT_CONFIG_H
uftp-4.1.5/server_transfer.h 0000644 0000764 0000764 00000004576 12250244021 015072 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_TRANSFER_H
#define _SERVER_TRANSFER_H
#include "server.h"
int send_doneconf(const struct finfo_t *finfo, int attempt);
int send_done(const struct finfo_t *finfo, int attempt, int section,
double l_grtt);
void create_cc_list(unsigned char **body, int *len);
void send_cong_ctrl(const struct finfo_t *finfo, double l_grtt,
uint16_t l_cc_seq, uint32_t l_cc_rate,
unsigned char *body, int len);
void handle_complete(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx);
void handle_status(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx, int *got_naks);
void handle_cc_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx);
int send_data(const struct finfo_t *finfo, unsigned char *packet, int datalen,
unsigned char *encpacket);
void print_status(const struct finfo_t *finfo, struct timeval start_time);
#endif // _SERVER_TRANSFER_H
uftp-4.1.5/client_transfer.h 0000644 0000764 0000764 00000004472 12250244021 015035 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_TRANSFER_H
#define _CLIENT_TRANSFER_H
void handle_fileseg(struct group_list_t *group, const unsigned char *message,
unsigned meslen, uint16_t txseq);
int file_done(struct group_list_t *group, int detail);
unsigned int get_naks(struct group_list_t *group,
unsigned int section, unsigned char **naks);
void handle_done(struct group_list_t *group, const unsigned char *message,
unsigned meslen);
void handle_done_conf(struct group_list_t *group, const unsigned char *message,
unsigned meslen);
void handle_cong_ctrl(struct group_list_t *group, const unsigned char *message,
unsigned meslen, struct timeval rxtime);
void send_status(struct group_list_t *group, unsigned int section,
const unsigned char *naks, unsigned int nak_count);
void send_complete(struct group_list_t *group, int freespace);
void send_cc_ack(struct group_list_t *group);
#endif // _CLIENT_TRANSFER_H
uftp-4.1.5/encrypt_cryptoapi.c 0000644 0000764 0000764 00000072634 12250244021 015431 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
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);
clog0(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(void)
{
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(void)
{
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 = safe_malloc(mlen);
}
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;
case KEY_DES_EDE3:
return CALG_3DES;
case KEY_AES128_CBC:
#ifdef CALG_AES_128
return CALG_AES_128;
#else
return 0;
#endif
case KEY_AES256_CBC:
#ifdef CALG_AES_256
return CALG_AES_256;
#else
return 0;
#endif
case KEY_AES128_GCM:
case KEY_AES256_GCM:
case KEY_AES128_CCM:
case KEY_AES256_CCM:
// Not supported by CryptoAPI
return 0;
default:
log0(0, 0, "Unknown keytype: %d", keytype);
return 0;
}
}
/**
* 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
case HASH_SHA1:
return CALG_SHA1;
case HASH_MD5:
return CALG_MD5;
default:
log0(0, 0, "Unknown hashtype: %d", hashtype);
return 0;
}
}
/**
* 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;
}
}
*keylen = 0;
*ivlen = 0;
}
/**
* 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;
}
}
return 0;
}
/**
* 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 *aad, unsigned int aadlen,
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);
if (alg == 0) {
log0(0, 0, "Invalid keytype");
return 0;
}
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 *aad, unsigned int aadlen,
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);
if (alg == 0) {
log0(0, 0, "Invalid keytype");
return 0;
}
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);
if (alg == 0) {
log0(0, 0, "Invalid hashtype");
return 0;
}
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 (alg == 0) {
log0(0, 0, "Invalid hashtype");
return 0;
}
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;
}
int EC_keylen(const EC_key_t ec)
{
log0(0, 0, "EC not supported");
return 0;
}
int ECDSA_siglen(const EC_key_t ec)
{
log0(0, 0, "ECDSA not supported");
return 0;
}
/**
* 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, unsigned int fromlen,
unsigned char *to, unsigned 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 = safe_calloc(RSA_keylen(rsa), 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, unsigned int fromlen,
unsigned char *to, unsigned int *tolen)
{
DWORD _tolen;
int flags;
unsigned 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) {
log0(0, 0, "Couldn't find provider for RSA key");
return 0;
}
idx--;
hashlen = get_hash_len(hashtype);
alg = get_hash(hashtype);
if (alg == 0) {
log0(0, 0, "Invalid hashtype");
return 0;
}
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 = safe_calloc(_siglen, 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 (alg == 0) {
log0(0, 0, "Invalid hashtype");
return 0;
}
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 = safe_calloc(siglen, 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;
}
int create_ECDSA_sig(EC_key_t rsa, int hashtype,
const unsigned char *mes, unsigned int meslen,
unsigned char *sig, unsigned int *siglen)
{
log0(0, 0, "ECDSA not supported");
return 0;
}
int verify_ECDSA_sig(EC_key_t ec, int hashtype,
const unsigned char *mes, unsigned int meslen,
const unsigned char *sig, unsigned int siglen)
{
log0(0, 0, "ECDSA not supported");
return 0;
}
int get_ECDH_key(EC_key_t pubkey, EC_key_t privkey, unsigned char *key,
unsigned int *keylen)
{
log0(0, 0, "ECDH not supported");
return 0;
}
/**
* Creates an RSA public key with the given modulus and public exponent
*/
int import_RSA_key(RSA_key_t *rsa, const unsigned char *keyblob,
uint16_t bloblen)
{
struct rsa_blob_t *rsablob;
const unsigned char *modulus;
char ms_keyblob[BLOBLEN];
BLOBHEADER *bheader;
RSAPUBKEY *pubkeyheader;
BYTE *ms_blob_modulus;
int ms_bloblen, modlen, i;
rsablob = (struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
modlen = ntohs(rsablob->modlen);
if (sizeof(struct rsa_blob_t) + modlen != bloblen) {
log0(0, 0, "Error importing RSA key: invalid length");
return 0;
}
bheader = (BLOBHEADER *)ms_keyblob;
pubkeyheader = (RSAPUBKEY *)(ms_keyblob + sizeof(BLOBHEADER));
ms_blob_modulus = (BYTE *)pubkeyheader + sizeof(RSAPUBKEY);
memset(ms_keyblob, 0, sizeof(ms_keyblob));
bheader->bType = PUBLICKEYBLOB;
bheader->bVersion = CUR_BLOB_VERSION;
bheader->aiKeyAlg = CALG_RSA_KEYX;
pubkeyheader->magic = 0x31415352;
pubkeyheader->bitlen = modlen * 8;
pubkeyheader->pubexp = ntohl(rsablob->exponent);
// CrypoAPI expects the modulus in little endian, so reverse the bytes
for (i = 0; i < modlen; i++) {
ms_blob_modulus[i] = modulus[modlen - i - 1];
}
ms_bloblen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + modlen;
if (!CryptImportKey(base_prov, ms_keyblob, ms_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, unsigned char *keyblob,
uint16_t *bloblen)
{
struct rsa_blob_t *rsablob;
unsigned char *modulus;
char ms_keyblob[BLOBLEN];
BLOBHEADER *bheader;
RSAPUBKEY *pubkeyheader;
BYTE *ms_blob_modulus;
int ms_bloblen, i;
uint16_t modlen;
rsablob = (struct rsa_blob_t *)keyblob;
modulus = keyblob + sizeof(struct rsa_blob_t);
ms_bloblen = sizeof(ms_keyblob);
if (!CryptExportKey(rsa, 0, PUBLICKEYBLOB, 0, ms_keyblob, &ms_bloblen)) {
mserror("CryptExportKey failed");
return 0;
}
bheader = (BLOBHEADER *)ms_keyblob;
pubkeyheader = (RSAPUBKEY *)(ms_keyblob + sizeof(BLOBHEADER));
ms_blob_modulus = (BYTE *)pubkeyheader + sizeof(RSAPUBKEY);
modlen = (pubkeyheader->bitlen / 8) & 0xFFFF;
rsablob->blobtype = KEYBLOB_RSA;
rsablob->reserved = 0;
rsablob->modlen = htons(modlen);
rsablob->exponent = htonl(pubkeyheader->pubexp);
// CrypoAPI exports the modulus in little endian, so reverse the bytes
for (i = 0; i < modlen; i++) {
modulus[i] = ms_blob_modulus[modlen - i - 1];
}
*bloblen = sizeof(struct rsa_blob_t) + modlen;
return 1;
}
int import_EC_key(EC_key_t *ec, const unsigned char *keyblob, uint16_t bloblen,
int isdh)
{
log0(0, 0, "EC keys not supported");
return 0;
}
int export_EC_key(const EC_key_t ec, unsigned char *keyblob, uint16_t *bloblen)
{
log0(0, 0, "EC keys not supported");
return 0;
}
/**
* 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, flags;
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) {
log0(0, 0, "Couldn't find empty key slot for private key");
return 0;
}
idx--;
if (!container || !strcmp(container, "")) {
_container = NULL;
flags = machine_keyset | CRYPT_VERIFYCONTEXT;
} else {
_container = container;
flags = machine_keyset;
}
if (!CryptAcquireContext(&private_key_list[idx].provider, _container,
NULL, prov_type, flags)) {
if (!CryptAcquireContext(&private_key_list[idx].provider, _container,
NULL, prov_type, CRYPT_NEWKEYSET | flags)) {
mserror("CryptAcquireContext failed");
return 0;
}
}
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 0;
}
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;
// 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) {
log0(0, 0, "Couldn't find empty key slot for private key");
return 0;
}
idx--;
if (!CryptAcquireContext(&private_key_list[idx].provider, container,
NULL, prov_type, machine_keyset)) {
mserror("CryptAcquireContext failed");
return 0;
}
if (!CryptGetUserKey(private_key_list[idx].provider, AT_KEYEXCHANGE,
&private_key_list[idx].key)) {
mserror("CryptGetUserKey failed");
return 0;
}
return private_key_list[idx].key;
}
EC_key_t gen_EC_key(uint8_t curve, int isdh, const char *filename)
{
log0(0, 0, "EC keys not supported");
return NULL;
}
EC_key_t read_EC_key(const char *filename)
{
log0(0, 0, "EC keys not supported");
return NULL;
}
union key_t read_private_key(const char *filename, int *keytype)
{
union key_t key;
key.rsa = read_RSA_key(filename);
if (key.rsa == 0) {
*keytype = 0;
} else {
*keytype = KEYBLOB_RSA;
}
return key;
}
uint8_t get_EC_curve(const EC_key_t ec)
{
log0(0, 0, "EC keys not supported");
return 0;
}
void free_RSA_key(RSA_key_t rsa)
{
if (!CryptDestroyKey(rsa)) {
mserror("CryptDestroyKey failed");
}
}
void free_EC_key(EC_key_t ec)
{
log0(0, 0, "EC keys not supported");
}
void set_sys_keys(int set_sys_key)
{
if (set_sys_key) {
machine_keyset = CRYPT_MACHINE_KEYSET;
} else {
machine_keyset = 0;
}
}
uftp-4.1.5/protocol.txt 0000644 0000764 0000764 00000252153 12176075261 014124 0 ustar dbush dbush
UFTP - Encrypted UDP based FTP with multicast
1. Introduction
A UFTP session consists of 3 main phases: The Announce/Register phase, the
File Transfer phase, and the Completion/Confirmation phase. The File Transfer
phase additionally consists of the File Info phase and the Data Transfer phase
for each file sent.
The Announce/Register phase sets up the multicast file transfer session and
negotiates all encryption parameters. The server sends out an announcement
over a public multicast address which the clients are expected to be listening
on. All subsequent messages from the server go over a private multicast
address specified in the announcement. Allowed clients send a registration to
respond to the announcement. The server will then send either a confirmation
message if encryption is disabled, or the encryption keys for the
session if encryption is enabled. If the client receives the encryption keys,
it sends an acknowledgment back to the server.
The File Transfer phase starts with the File Info phase for the first file to
send. The server sends a message describing the file in question. Besides
the name and size of the file, this message describes how the file will be
broken down. A file is divided into a number of blocks, and these blocks are
grouped into sections. A block is a piece of the file that is sent in a
single packet, and a section is a grouping of blocks. The total number of
blocks and sections is included in this message.
Continuing the File Transfer phase is the Data Transfer phase for the first
file. Data packets, each of which is a block, are sent by the server at a
rate specified by the user. Because UDP does not guarantee that packets will
arrive in order, each block is numbered so the client can properly reassemble
the file. When the server has finished sending all data packets, it sends a
message to the clients indicating this.
When a client detects the end of a section or receives an end of file message
from the server, and the client has detected one or more missing blocks, the
client will send back a message containing a list of NAKs (negative
acknowledgments). When the server receives NAKs from one or more clients, it
goes back and retransmits any blocks that were NAKed, then continues on
sending any untransmitted blocks. When a client has received the entire file,
it sends a completion message in response to the server's end of file message.
This continues until all clients have either send a completion message or
have timed out after the server sent its end of file message.
The File Info phase and the Data Transfer phase are then repeated for each
file to be sent during the session.
The Completion/Confirmation phase shuts down the session between the server
and clients. It starts with a message from the server indication the end of
the session. The clients then respond with a completion message, and the
server responds to each completion with a confirmation message.
2. Definitions
Server: A process run on demand that sends one or more files.
Client: Daemon process that receives files.
Public multicast address: A multicast address that a client or proxy expects
to receive announcements from a server on.
Private multicast address: Specified by the server when a session is
initiated. All traffic from the server other than announcements are
sent to this address.
Proxy: Daemon process that relays UFTP traffic between servers and clients.
Server proxy: A proxy, typically local to a server, that forms the upstream
end of a multicast tunnel. It listens on the public multicast address
(and private multicast address when specified) and forwards downstream
packets to a specific address downstream. Upstream packets are forwarded
back where the announcement originated from.
Client proxy: A proxy, typically local to one or more clients, that forms the
downstream end of a multicast tunnel. It receives unicast data from one
or more server proxies and forwards downstream traffic to the multicast
address specified in the packet header. Upstream traffic from clients
is gathered and forwarded back where the announcement came from as an
aggregated response.
Response proxy: A proxy that functions as a response aggregator in situations
where the server has direct multicast accessibility to clients but the
number of clients are to high for the server to handle itself. It
listens on the public multicast address (and private multicast address
when specified), but does not forward packets from the server since those
packets reach clients directly. It does however send some messages
directly to clients in the process of establishing encryption keys.
Upstream traffic from clients is gathered and forwarded back where the
announcement came from as an aggregated response. Clients in this
environment are configured to send all responses to a specific response
proxy. Messages sent directly from response proxies to clients use
multicast (either the primary public address, or the private address,
depending on the message).
Block: A piece of a file to be sent in a single packet.
Section: A grouping of blocks. A section of blocks is the most the server can
send before requesting status from a client. A client can send back a
single status message to represent the NAKs for an entire section.
3. Timing
3.1 Round Trip Timing
The server measures the round trip time (RTT) to each of the clients in order
to determine the proper retransmission timing.
In an ANNOUNCE, the server includes a timestamp. This timestamp is echoed
back in the REGISTER sent by each client, adjusted by the time between when
the client received the ANNOUNCE and when it sent the REGISTER. The server
then takes the difference between the time in the REGISTER and the current
time as the RTT to that client.
The server keeps track of the group round trip time (GRTT) which is advertised
to the clients in each message sent by the server. On the initial announce,
this is set to a resonable default. Once RTTs for the clients have been
measured, the largest of the individual RTTs becomes the GRTT.
After the GRTT has been set once based on the RTTs of one or more clients,
any subsequent adjustments have a floor of 0.9 times the prior value (RFC 5401).
3.2 Message Timing
The GRTT is used to determine the timing of message retransmissions in both
directions.
A message robustness factor (ROBUST) is used as part of the timing
calculations. This value is also sent to the clients in an ANNOUCE so that
both server and clients will use a common value.
The server will wait 3 * GRTT between each group of ANNOUNCE, REGCONF,
KEYINFO, and FILEINFO messages sent before sending the next set. Clients
should normally respond in the (1 * GRTT -- 2 * GRTT) time window. An extra
GRTT is added to this to allow for a proxy to aggregate client messages and
pass them on. Once all clients have responded during a given phase, the
server will wait 1 * GRTT for any late responses before ending the phase.
During the Announce/Register and File Info phases, the server will send the
relevant messages a maximum of ROBUST times, after which clients that have
not responsed will be marked as dropped.
When a client detects that a new section has started, or upon receiving a
DONE, it start a timer lasting 1 * GRTT. When this timer triggers, the client
will send a STATUS for each section containing a NAK up to the last section
completed, or up to the section specified in a DONE.
When the server receives a STATUS message from a client, it first checks if
the section listed in the STATUS is prior to the current transmission section.
If not, it is ignored, otherwise the NAKs are recorded for retransmission.
The server will then (at the end of the current pass of data, and if it hasn't
done so already) start a timer lasting 3 * GRTT. When this timer expires,
the server will rewind its transmission position to the earliest recorded NAK.
When the server reaches the end of data transmission, it will send DONE
messages every 3 * GRTT up to a maximum of ROBUST times to prompt clients to
send a COMPLETE or STATUS. This continues until all clients respond with a
COMPLETE, at least one client responds with a STATUS in which case it will
set the rewind timer as above, or ROBUST DONEs are sent in which case any
client that didn't respond with a DONE is marked as lost.
TODO: Enter a holdoff period like NORM and add later NAKs?
After a client sends a REGISTER, it will be retransmitted every 4 * GRTT with
a maximum time of ROBUST * GRTT, after which the client drops the session.
During the Completion/Confirmation stage, the client will retransmit a
COMPLETE every 4 * GRTT with a maximum of ROBUST * GRTT, after which the
client considers the session complete and does the normal cleanup. During the
Data Transfer phase, if there are no packets received in ROBUST * GRTT, the
client drops the session. In all cases, the maximum time is no less than one
second.
When a proxy receives a REGISTER, FILEINFO_ACK, STATUS, or COMPLETE from a
client, it will wait for 1 * GRTT for any related messages before sending an
aggregated response to the server. The proxy can also send a KEYINFO to one
or more clients. It will wait 2 * GRTT for responses before resending. When
a proxy has no pending aggregated messages to send, it will time out the
session if no packets have been received in ROBUST * GRTT.
When sending an aggregated REGISTER or FILEINFO_ACK message, a proxy will note
which client reported the longest RTT based on the echoed timestamp and will
include that client's timestamp in the reponse. That client will also be
listed first in the body of the message so the server may determine which
individual client has the longest RTT.
TODO: Figure out if we want the server to send one message per cycle or one
group of messages per cycle. The former will help manage message implosion
but will take longer, while the later is quicker but more prone to message
implosion, and is the way version 3.x does it.
3.3 Congestion Control
A congestion control scheme based on TFMCC (RFC 4654) will be employed. To
comply with this, each data packet includes a sequence number (cc_seq) and the
current congestion control rate (cc_rate). A separate CONG_CTRL message will
also be sent once per GRTT. This message also contains cc_seq and cc_rate
along with a timestamp for RTT measurements and a list of receivers along with
the RTT and a set of flags for each. Receivers send back CC_ACK messages
periodically in accordance with RFC 4654. The TFMCC specific info in this
message is included as a header extension. This extension is also included in
STATUS messages.
When a proxy prepares to send an aggregated STATUS message, it determines the
current limiting receiver (CLR) among the clients that contributed to the
aggregated message and forwards that client's congestion control info along
with the STATUS message. Proxies will not aggregate CC_ACK messages and will
forward them on immediately, adjusting the echoed timestamp as necessary.
4. Encryption Setup
The encryption protocol borrows from TLS (RFC5246) and DTLS (RFC4347). Due to
the large amount of traffic that may be generated while communicating with a
large number of clients, the required key exchange messages are made as small
as possible and integrated into the UFTP messaging structure to minimize the
amount of traffic in the setup phase.
Valid block encryption algorithms are DES and 3 key Triple DES in CBC mode,
and AES-128 and AES-256 in either CBC, GCM or CCM mode. While AES is more
secure and therefore preferred, DES and Triple DES are supported for
environments where AES is not available. GCM and CCM modes are for
authenticated encryption, so a separate signature is not required.
For the block encryption algorithm chosen, there are:
encryption_key_length
IV_length
For each message to be encrypted, an IV must be generated as follows:
For a 128-bit IV (for AES in CBC mode):
IV = S + UID + CTR
For a 96-bit IV (for AES in GCM or CCM mode):
IV = (S XOR UID) + CTR
For a 64-bit IV (for DES or 3 key Triple DES):
IV = (S + UID) XOR CTR
where CTR is a monotinically increasing 64-bit counter, UID is the unique ID
of the message sender, and S is a 32-bit salt, derived below.
Valid hash algorithms are SHA-1, SHA-256, SHA-384, and SHA-512. While SHA-256
or more is preferred, SHA-1 is supported for environments where SHA-256 is
not available.
For the hash algorithm chosen, there is:
hash_length
The server has the option to use a symmetric key HMAC, an asymmetric key
signature (RSA or ECDSA), or a cipher in GCM or CCM mode for authentication of
encrypted messages. Using a GCM or CCM cipher allows encryption and signature
generation to be done in a single step and is fastest (particularly GCM).
HMAC is slower that GCM/CCM but faster than asymmetric keys. While asymmetric
key signatures are larger and slower, they do protect against attacks
from within the group. When using asymmetric key signatures, all clients
MUST use a key of the same type and size (for RSA) or curve (for ECDSA) as
the server. Clients may maintain multiple private keys of differing types
and sizes to accommodate different servers.
During the initial setup, there is an exchange of symmetric keys between the
server and each client. This can be done with either RSA encryption or with
an Elliptic Curve Diffie-Hellman (ECDH) key exchange. With ECDH, the key
exchange parameters are validated with either RSA signatures or Elliptic Curve
DSA (ECDSA) signatures.
In the ANNOUNCE message, the server sends a random number along with certain
key exchange keys. The random number is 32 bytes, with the first 4 being the
current time in the standard UNIX time format, and the remaining 28 generated
by a secure random number generator. If RSA is chosen for key exchange, the
ANNOUNCE includes the server's public RSA key. If ECDH is chosen for key
exchange, it contains an ephemeral EC public key to use for the ECDH exchange
along with either its RSA public key or its ECDSA public key, plus a signature
over the whole UDP packet using the RSA/ECDSA key.
Each client then sends in the REGISTER its own random number, constructed
identically to the server's, along with it's part of the key exchange. In the
case of RSA key exchange, it includes a 48 byte premaster secret key encrypted
with the server's public key. The premaster secret consists of 1 byte for the
client's version and the remaining 47 chosen by a secure random number
generator. In the case of an ECDH key exchange, it includes an ephemeral EC
public key, using the same EC curve as the server's ECDH key, for the ECDH key
exchange. The server and client will then perform an ECDH key derivation,
with the result hashed with SHA-1, and the resulting value becomes the
premaster secret.
The client may also send a CLIENT_KEY message along with a REGISTER. This
message is required if the server requests either RSA/ECDSA signatures or
simply to authenticate the client. This messages also contains a signature
(using the client's RSA or ECDSA key) of the hash of:
group ID + private address + server random + client random + premaster secret
The server and each client calculates a 48 byte master secret as follows:
master_secret = PRF(pre_master_secret, "master secret",
server_random + client_random)[0-47]
When SHA-1 is the selected hash, the pseudo-random function (PRF) is as defined
in TLS 1.1 (RFC4346) which uses a SHA1/MD5 combination. Otherwise, the
pseudo-random function (PRF) is as defined in TLS 1.2 (RFC5246) using the
server selected hash function.
Using the master secret, a key block is generated:
key_block = PRF(master_secret, "key expansion",
server_random +
client_random);
From the key block, the first hash_length bytes comprise the HMAC key,
the next encryption_key_length bytes comprise the session key, and the
next 4 bytes comprise the salt from which session IVs are derived.
Currently, the HMAC key based on the master secret between the server and a
particular client is not used, but the key material is generated anyway to be
consistent with key expansion of the group key and as a hedge in case it could
be used in a future release.
After receiving the REGISTER the server sends a KEYINFO message containing
the group master key encrypted with the session key for the client, along with
a 64-bit counter used to generate the IV. If an authenticated cipher mode was
chosen (GCM/CCM), the encryption is instead performed in CBC mode as a
signature is not requried at this stage. A KEYINFO can contain the group key
encrypted multiple times, with each one encrypted with a different client's
session key and grouped with the unique ID of the client. The group master key
is chosen by the server. It is 48 bytes, with the first byte being the server
version and the remaining 47 bytes chosen by a secure random number generator.
To save bandwidth, only the 47 random bytes are encrypted. Given the block
size of the supported cyphers of 8 or 16, this results in the encrypted key
being 48 bytes (instead of 64, if 48 bytes were encrypted). The other byte
can be derived from the version of the KEYINFO message.
From the group master key, a key block is generated as follows:
key_block = PRF(group_master, "key expansion", server_random);
From the key block, the first hash_length bytes comprise the group HMAC key,
the next encryption_key_length bytes comprise the group session key, and the
next 4 bytes comprise the salt from which group session IVs are
derived.
Upon receiving the KEYINFO message, the client responds with an KEYINFO_ACK
message. This message contains a 12 byte verify_data field constructed as
follows:
PRF(group_master, "client finished", Hash(handshake_values))[0-11]
Where Hash is the server selected hash function and handshake_values is:
group ID + private address + server random + client random +
premaster secret + [ client_key_blob ] + group_master
where client_key_blob is an exported keyblob of the client's public key, if
a CLIENT_KEY message was sent. See section 5 for details of this keyblob.
All subsequent messages after a KEYINFO from either client or server are
encrypted using the group session key, and the whole UDP packet is signed with
either the group HMAC key, the sender's RSA/ECDSA key, or the GCM/CCM mode
cipher, depending on what the server selects. Each of these messages also
contains a 64-bit counter used to generate the IV for that message. The
unique ID used to generate the IV should be the same one placed in the Source
ID field. Clients receiving a KEYINFO message also uses the unique ID from
Source ID.
A server proxy may choose to dynamically determine its destination client
proxy. It does this by means of authenticated heartbeat messages. The server
proxy specifies the public key fingerprint of the client proxy it wishes to
send to. This is useful when the client proxy is NATed and its IP address
cannot be determined ahead of time, and may in fact change on the fly.
When a server proxy in this mode receives an HB_REQ message, it responds with
an HB_RESP message containing a flag indicating that authentication is
required along with a randomly chosen nonce value. The client proxy is then
expected to sign the nonce with its public key and sends back the nonce, the
signed nonce, and its public key in another HB_REQ message. When the server
proxy receives this message, it first checks the plaintext nonce to see if it
matches the last nonce it sent out. If so, it verifies the client proxy's
public key fingerprint then verifies the signature. If these checks all pass,
the server uses the incoming address of the HB_REQ as its downstream address,
and sends back an HB_RESP indicating successful authentication. At this time
the server will randomly select a new nonce so the old one is not reused. If
any of the checks fail, the server proxy sends back an HB_RESP message
indicating failed authentication, and the current destination remains
unchanged. Any future HB_REQ messages the server proxy gets from the
established downstream client proxy is automatically accepted. Only when the
IP/port does not match will the server proxy issue a challenge.
5. Messages
5.1 Message Flow
While it would save one round trip to send a KEYINFO and FILEINFO together
with encryption enabled and only one file to send, keeping them separate
prevents eavesdroppers from determining information about the encrypted
stream, such as the number of files being sent. See section 3.2 for more
details.
5.1.1 Announce/Register phase with encryption:
5.1.1.1 Without proxies
Server Client
------ ------
ANNOUNCE --->
<--- REGISTER
<--- CLIENT_KEY (optional)
KEYINFO --->
<--- KEYINFO_ACK
5.1.1.2 With server/client proxies
Server Server Proxy Client Proxy Client
------ ------------ ------------ ------
ANNOUNCE ---> ---> --->
<--- REGISTER
<--- CLIENT_KEY (optional)
<--- <--- REGISTER
<--- <--- CLIENT_KEY (optional)
KEYINFO ---> --->
<--- <--- KEYINFO_ACK
REG_CONF ---> --->
KEYINFO --->
<--- KEYINFO_ACK
5.1.1.3 With response proxy
Server Response Proxy Client
------ -------------- ------
ANNOUNCE --->
------------------------>
<--- REGISTER
<--- CLIENT_KEY (optional)
<--- REGISTER
<--- CLIENT_KEY (optional)
KEYINFO --->
<--- KEYINFO_ACK
REG_CONF --->
KEYINFO --->
<--- KEYINFO_ACK
5.1.2 Announce/Register phase without encryption
5.1.2.1 Without proxies
Server Client
------ ------
ANNOUNCE --->
<--- REGISTER
REG_CONF --->
5.1.2.2 With server/client proxies
Server Server Proxy Client Proxy Client
------ ------------ ------------ ------
ANNOUNCE ---> ---> --->
<--- REGISTER
<--- <--- REGISTER
REG_CONF ---> ---> --->
5.1.2.3 With response proxy
Server Response Proxy Client
------ -------------- ------
ANNOUNCE --->
------------------------>
<--- REGISTER
<--- REGISTER
REG_CONF --->
------------------------>
5.1.3 File Transfer phase:
Server Client
------ ------
FILEINFO --->
(either)
<--- FILEINFO_ACK
(or)
<--- COMPLETE
FILESEG --->
...
CONG_CTRL --->
<--- CC_ACK
...
DONE --->
(either)
<--- STATUS
(or)
<--- COMPLETE
...
5.1.4 Completion/Confirmation phase:
Server Client
------ ------
DONE --->
<--- COMPLETE
DONE_CONF --->
5.2 Message Details
All messages start with the UFTP header, followed by a message
specific header. With the exception of an ENCRYPTED message, each message
header starts with an 8 bit function number. This allows for determining
the message type of a decrypted message, so the message type is not
revealed in the UFTP header.
Messages may contain optional header extensions. Some are defined here,
although others may be added without breaking backward compatibility. Any
unexpected header extension should be ignored.
5.2.1 UFTP header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UFTP ID | Function | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Group Inst. | GRTT | Group Size | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UFTP ID: 8 bits
Defines the version number of the protocol. Currently 0x30.
Function: 8 bits
The message number of the contained message. If the message is
encrypted, this always specifies the message number for ENCRYPTED.
Sequence Number: 16 bits
A monotonically increasing sequence number used for congestion control
and replay attack purposes.
Source ID: 32 bits
The unique ID of the sender.
Group ID: 32 bits
A unique identifier for the current session.
Group Instance: 8 bits
A sequence number that increases each time the session identified by the
Group ID is retried. This allows for differentiating retried instances
of the same session.
GRTT: 8 bits
For messages originating from a server, a quantized version of the current
Group Round Trip Time. See RFC 5401 section 3.7.4 for details of how
this field is encoded.
Group Size: 8 bits
For messages originating from a server, a quantized version of the total
number of clients in the current session. This field consists of a 5 bit
mantissa and a 3 bit exponent. The mantissa is scaled so that a value of
0.0 is represented as 0 and a value of 10.0 is represented as 32.
Reserved: 8 bits
Reserved for future use and should be set to 0.
5.2.2 ENCRYPTED
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IV Counter (high bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IV Counter (low bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Signature length | Payload Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Signature |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Encrypted Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Specifies an encrypted message. Contains a signature followed by the
encrypted message payload (which is one of the messages below). The signature
is either an HMAC using the group key or an RSA/ECDSA signature using the
sender's RSA/ECDSA key over the whole UFTP message, depending on the signature
type chosen by the server. Alternately, if an authenticated cipher was
chosen, the signature field will be empty since the signature is embedded in
the encrypted data.
IV Counter: 64 bits
A monotonically increasing counter used as part of the symmetric key IV
for the encrypted payload. This field should be implemented as two
32-bits fields for purposes of alignment.
Signature length: 16 bits
The length of the signature in bytes (must be a multiple of 4).
Payload length: 16 bits
The length of the encrypted payload in bytes (must be a multiple of 4).
Signature: varies
The signature for this message. It applies to the entire UFTP packet,
including the UFTP header. May be either an HMAC using the group hmac
key or an RSA/ECDSA signature using the sender's RSA/ECDSA private key.
Encrypted Payload: varies
The encrypted message, encrypted with the group symmetric key.
5.2.3 ANNOUNCE
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Flags | Robust Factor |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CC Type | Reserved | Block Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Multicast Address |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Private Multicast Address |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server to initiate a file transfer. Contains basic info needed by
clients to initiate a session. For closed group membership, the list of
allowed clients is specified in the body. Multiple messages may be sent to
accommodate the full list of clients. This message goes over the public
multicast address. All subsequent server messages go over the private
multicast address. If the server needs to resend this message under closed
group membership, only clients that did not respond are listed.
Function: 8 bits
The message number for this message. Always 1.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Flags: 8 bits
0x01 - SYNC_MODE
If set, this indicates that the session is operating in sync mode.
Files received by clients are checked against existing files to see
whether or not the file should be accepted.
0x02 - SYNC_PREVIEW
Indicates sync preview mode for the session. This is like sync mode,
except client don't actually receive any files.
0x04 - IPV6
Indicates that the public multicast address and private multicast
address are IPv6 addresses when set.
All other bits should be set to 0.
Robust: 8 bits
Sets the robustness factor for the session. This value dictates how many
times certain messages may be retransmitted.
CC Type: 8 bits
Specifies the congestion control algorithm in use. See section 5.3 for
a list of valid values.
Reserved: 8 bits
Reserved for future use and should be set to 0.
Block Size: 16 bits
The size of the body for UFTP messages. This value should be somewhat
less that the path MTU to allow room for any UFTP header or extension
as well as the UDP and IP headers and extensions.
Timestamp_seconds: 32 bits
The current time expressed as seconds since the UNIX epoch of
1/1/1970 00:00:00 UTC.
Timestamp_microseconds: 32 bits
The microsecond portion of the current time. This field and the above
field are used in round trip time measurements.
Public Mulicast Address: varies
The public multicast address used by this session. May be an IPv4 address
of 4 bytes or an IPv6 address of 16 bytes.
Private Mulicast Address: varies
The private multicast address used by this session. May be an IPv4 address
of 4 bytes or an IPv6 address of 16 bytes. The public and private
addresses must be the same IP version.
Header Extensions: varies
Contains any header extensions that may be present
Client ID: 32 bits each
The body of the message. Contains the unique ID of one or more clients
that are allowed to join under closed group membership.
5.2.3.1 EXT_ENC_INFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension | Extension | Flags | Keyex | Sig |
| Type | Length | | Type | Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Type | Hash Type | Key Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Diffie-Hellman Key Length | Signature Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ +
| |
+ +
| Server Random Number |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Blob |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Diffie-Hellman Key Blob |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Public Key Signature |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
A header extension appended to an ANNOUNCE message indicating encryption
parameters for the session.
Extention Type: 8 bits
The extension number for this extension. Always 1.
Extension Length: 8 bits
The total length of the extension in 4 byte words.
Flags: 8 bits
0x01 - CLIENT_AUTH
Specifies that the client should send a CLIENT_KEY message in
additionto a REGISTER when responding when set.
All other bits should be set to 0.
Key Exchange Type: 4 bits
Specifies the key exchange algorithm to use. See section 5.3 for the
list of valid values.
Signature Type: 4 bits
Specifies the signature type number of the signature to use on encrypted
messages. See section 5.3 for the list of valid values.
Key Type: 8 bits
Specifies the key type number of the symmetric encryption algorithm to
use. See section 5.3 for the list of valid values.
Hash Type: 8 bits
Specifies the hash type number of the hashing algorithm to use for HMAC
signatures and key derivation. See section 5.3 for the list of valid
values.
Public Key Length: 16 bits
The length in bytes of the server's public key blob.
Diffie-Hellman Key Length: 16 bits
The length in bytes of the server's Diffie-Hellman key blob. Must be 0
if the key exchange algorithm is RSA.
Signature Length: 16 bits
The length in bytes of the server's public key signature. Must be 0
if the key exchange algorithm is RSA.
Server Random Number: 256 bits
A 32-byte random number chosen by the server used to derive the master
secret key between the server and each client.
Public Key Blob: varies
A key blob containing the server's RSA or ECDSA public key.
Diffie-Hellman Key Blob: varies
A key blob containing the server's ephemeral ECDH public key.
Public Key Signature: varies
The signature for this message. It applies to the entire UFTP packet,
including the UFTP header. Signed using the sender's RSA/ECDSA private key.
5.2.3.2 RSA key blob
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Blob Type | Reserved | Modulus Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Exponent |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Modulus |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A keyblob containing an RSA public key
Blob Type: 8 bits
Specifies the keyblob type. Always 1.
Reserved: 8 bits
MUST be set to 0.
Modulus Length: 16 bits
The length in bytes of the RSA public key modulus
Public Key Exponent: 32 bits
The public key exponent of the RSA public key.
Public Key Modulus: varies
The public key modulus of the RSA public key.
5.2.3.3 EC key blob
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Blob Type | Curve | Key Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| EC Public Key |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A keyblob containing an Elliptic Curve public key, which may be used for
either ECDH or ECDSA.
Blob Type: 8 bits
Specifies the keyblob type. Always 2.
Curve: 8 bits
Specifies the named curve used by this public key. See section 5.3 for
the list of valid values.
Key Length: 16 bits
The Length in bytes of the the EC public key.
EC Public Key: varies
The EC public key value, specified as the X coordinate followed by the Y
coordinate.
5.2.4 REGISTER
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Key Info Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ +
| |
+ +
| Client Random Number |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Info |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the client to acknowledge receipt of an ANNOUNCE. If encryption is
requested, also contains a random number and a premaster secret key encrypted
with the server's public RSA key.
Function: 8 bits
The message number for this message. Always 2.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Key Info Length: 16 bits
The length in bytes of the key info field.
Timestamp_seconds: 32 bits
The seconds part of the last server timestamp sent, adjusted for the
time between receiving the last server message and sending this message.
Timestamp_microseconds: 32 bits
The microseconds part of the last server timestamp sent, adjusted as above.
Client Random Number: 256 bits
A 32-byte random number chosen by the client used to derive the master
secret key between the server and each client.
Key Info: varies
Either the premaster secret key selected by the client, encrypted with the
server's RSA public key, or the client's ephemeral ECDH public key.
Client ID: 32 bits each
The body of the message. Contains the unique ID of one or more clients
that a proxy received a REGSITER from and is now relaying to the server.
5.2.5 CLIENT_KEY
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Length | Signed Verify Data Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Blob |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Signed Verify Data |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by the client if the server requests client authentication, or if the
server requests RSA/ECDSA signatures instead of HMAC.
Function: 8 bits
The message number for this message. Always 3.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Public Key Length: 16 bits
The length in bytes of the client's public key blob.
Signed Verify Data Length: 16 bits
The length in bytes of the signed verify data field.
Public Key Blob: varies
A keyblob containing client's RSA or ECDSA public key.
Signed Verify Data: varies
The signature from the client's RSA/ECDSA private key of verification data
based on the hash of the cryptographic parameters exchanged to this point.
5.2.6 REG_CONF
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server in response to a REGISTER if there is no encryption.
Contains a list of clients that the server received a REGISTER for, and
multiple messages may be sent to accommodate the full list of clients. Also
sent if the REGISTER came from a proxy. This allows proxies to confirm
registrations when encryption is enabled. The server will not resend this
message for a given client unless it receives an extra REGSITER from that
client.
Function: 8 bits
The message number for this message. Always 4.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Client ID: 32 bits each
The body of the message. Contains the unique ID of one or more clients
that the server received a REGISTER for.
5.2.7 KEYINFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IV Counter (high bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IV Counter (low bits) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
encrypted | Client ID |
key 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| Encrypted Group Master |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+ +
| |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
encrypted | Client ID |
key 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server in response to a REGISTER if encryption is enabled.
Contains the list of clients that the server received a REGISTER for, and
multiple message may be sent to accommodate the full list of clients. If the
server needs to resend this message, only clients that did not respond are
listed. Also, for each listed client, it contains the group master key
encrypted with the master secret key negotiated with that client. If a
REGISTER is received from a proxy, KEYINFO gets sent directly to the proxy,
not the clients it serves.
Function: 8 bits
The message number for this message. Always 5.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
IV Counter: 64 bits
A monotonically increasing counter used as part of the symmetric key IV
for the encrypted group master key for each listed client. This field
should be implemented as two 32-bits fields for purposes of alignment.
The message body contains a list of the following pair of fields:
Client ID: 32 bits each
The unique ID of a client that the server received a REGISTER for.
Encrypted Group Master: 384 bits each
The last 47 bytes of the 48 byte group master secret, encrypted using the
master symmetric key for the assiciated client.
5.2.8 KEYINFO_ACK
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| Verify Data |
+ +
| |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the client in response to a KEYINFO. Contains verification data based
on the hash of the cryptographic parameters exchanged to this point. This
message is always encrypted and embedded within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 6.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Verify Data: 96 bits
Contains a hash of all cryptographic parameters exchanged for the session
5.2.9 FILEINFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Type | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Name Length | Link Length | File Size (high bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Size (low bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Name |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Link Name |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server to give information on a particular file being sent.
Contains the list of currently active clients, and multiple message may be
sent to accommodate the full list of clients. If the server needs to resend
this message, only clients that did not respond are listed. If encryption
is enabled, this message is encrypted and embedded within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 7.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file. Chosen by the server sequentially
starting at 1.
File Type: 8 bits
Specifies the type of file being sent (regular file, directory, sybolic
link, delete request, or free dist space request). See section 4.3 for
the list of valid values.
Reserved: 24 bits
Reserved for future use and should be set to 0.
Name Length: 8 bits
The length of the file name in 4 byte words. If the file name is not a
multiple of 4, this field is null padded at the end.
Link Length: 8 bits
The length of the link name in 4 byte words. If the link name is not a
multiple of 4, this field is null padded at the end. May be 0.
File Size: 48 bits
The size of the file to send in bytes.
File Timestamp: 32 bits
Specifies the file's timestamp as seconds since the UNIX epoch of
1/1/1970 00:00:00 UTC.
Timestamp_seconds: 32 bits
The current time expressed as seconds since the UNIX epoch of
1/1/1970 00:00:00 UTC.
Timestamp_microseconds: 32 bits
The microsecond portion of the current time. This field and the above
field are used in round trip time measurements.
File Name: varies
The path name of the file to send. A slash (/) is used as a directory
separator. The file gets created with this path in the client's
destination directory.
Link Name: varies
If the File Type field specifies that the file is a symbolic link, this
field specifies the link destination.
Client ID: 32 bits each
The body of the message. Contains the ID of one or more active clients.
5.2.10 FILEINFO_ACK
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Flags | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the client in response to a FILEINFO. Contains the file ID . If encryption is enabled, this message is encrypted
and embedded within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 8.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file.
Flags: 8 bits
0x01 - PARTIAL
If set, this indicates that the client partially received the
indicated file on a prior run. Valid only in response to a FILEINFO.
All other bits should be set to 0.
Reserved: 24 bits
Reserved for future use and should be set to 0.
Timestamp_seconds: 32 bits
The seconds part of the last server timestamp sent, adjusted for the
time between receiving the last server message and sending this message.
Timestamp_microseconds: 32 bits
The microseconds part of the last server timestamp sent, adjusted as above.
Client ID: 32 bits each
The body of the message. Contains the ID of one or more active clients.
5.2.11 FILESEG
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Section | Block Number in Section |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server, and contains a block of data following this header. If
encryption is enabled, this message is encrypted and embedded within an
ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 9.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file.
Section: 16 bits
The section number for this block.
Block Number in Section: 16 bits
The number of this block within the current section.
Data: varies
The body of the message. Contains a block of the current file.
5.2.11.1 EXT_TFMCC_DATA_INFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension | Extension | Send Rate |
| Type | Length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CC Sequence | CC Rate |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
This extension, added to a FILESEG message, contains information necessary to
support TFMCC.
Extention Type: 8 bits
The extension number for this extension. Always 2.
Extension Length: 8 bits
The total length of the extension in 4 byte words.
Send Rate: 16 bits
The current transmission rate of the server.
CC Sequence: 16 bits
A sequence number which increases by 1 at the start of a new feedback round.
CC Rate: 16 bits
The current congestion control suppression rate. Only clients with a rate
less than this may send congestion control feedback in a CC_ACK message.
5.2.12 DONE
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Section | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the server at the end of a section to request NAKs. Contains the list
of clients that the server needs status for, and multiple message may be
sent to accommodate the full list of clients. If the server needs to resend
this message, only clients that did not respond are listed. If encryption
is enabled, this message is encrypted and embedded within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 10.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file. If zero, indicates the beginning of
the Completion/Confirmation phase to end the session.
Section: 16 bits
The section number the server is requesting status for.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Client ID: 32 bits each
The body of the message. Contains the ID of one or more clients the
server needs status for.
5.2.13 STATUS
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Section | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| NAK Content |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the client either in response to a DONE message or at the end of a
section if loss is detected. If encryption is enabled, this message is
encrypted and embedded within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 11.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file.
Section: 16 bits
The section number this STATUS is requesting retransmissions for.
Reserved: 16 bits
Reserved for future use and should be set to 0.
NAK Contest: varies
The body of the message. Contains a bitmap of the blocks in the section,
where a set bit designates a NAK for the given block.
5.2.13.1 EXT_TFMCC_ACK_INFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension | Extension | Flags | Reserved |
| Type | Length | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CC Sequence | CC Rate |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Client ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
This extension, added to a STATUS or CC_ACK message, contains information
necessary to support TFMCC.
Extention Type: 8 bits
The extension number for this extension. Always 3.
Extension Length: 8 bits
The total length of the extension in 4 byte words.
Flags: 8 bits
0x01 - CC_CLR
If set, specifies this client is the CLR. Not currently used.
0x02 - CC_RTT
If set, specifies that the client has received an RTT measurement from
the server.
0x04 - CC_START
If set, specifies that the client is in slow start mode.
0x08 - CC_LEAVE
If set, specifies that the client is preparing to leave the session.
All other bits should be set to 0.
Reserved: 8 bits
Reserved for future use and should be set to 0.
CC Sequence: 16 bits
The most recent CC sequence number received from the server.
CC Rate: 16 bits
The client's congestion control rate. If CC_START is set,
Client ID:
The ID of the client that created this extension. When a proxy forwards
or aggregates client responses, this allow the server to know who this
extension belongs to.
Timestamp_seconds: 32 bits
The seconds part of the last server timestamp sent, adjusted for the
time between receiving the last server message and sending this message.
Timestamp_microseconds: 32 bits
The microseconds part of the last server timestamp sent, adjusted as above.
5.2.14 COMPLETE
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | File ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Status | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client ID |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by the client in response to a DONE message when the client has received
the whole file. May also be sent in a response to a FILEINFO if the session
is a restart session and the client received the whole file on a prior
attempt. If encryption is enabled, this message is encrypted and
embedded within an ENCRYPTED message. If the file ID is 0, indicating the end
of the session, all files and directories sent during the session are moved
from the the client's temp directory to the destination directory, if a temp
directory was specified. Files within directories are moved as part of the
containing directory.
Function: 8 bits
The message number for this message. Always 12.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
File ID: 16 bits
The identifier of the current file.
Status: 8 bits
Specifies the status of the COMPLETE message. When in sync mode, a status
of COMP_STAT_NORMAL specifies that the file was a new file copied over,
a status of COMP_STAT_SKIPPED specifies that the file was skipped because
the incoming file was older, and a status of COMP_STAT_OVERWRITE specifies
that the file overwrote an existing file. when not in sync mode, the
status is set to COMP_STAT_NORMAL if the file was sent successfully. If
the client rejects the file due to a pathname or filename issue,
regardless of sync mode, the status is set to COMP_STAT_REJECTED.
Reserved: 24 bits
Reserved for future use and should be set to 0.
Client ID: 32 bits each
The body of the message. Contains the ID of one or more clients that a
proxy received a COMPLETE from and is now relaying to the server.
5.2.14.1 EXT_FREESPACE_INFO
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension | Extension | Reserved |
| Type | Length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Free Space (high bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Free Space (low bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
This extension, added to a COMPLETE message, contains the amount of free
disk space in bytes in the client's primary destination directory.
Extention Type: 8 bits
The extension number for this extension. Always 7.
Extension Length: 8 bits
The total length of the extension in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Free Space: 64 bits
The amount of free disk space in bytes.
5.2.15 DONE_CONF
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Client IP Address |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Send by the server in response to a COMPLETE message at the end of a session.
Contains the list of clients that have completed. Multiple messages may be
sent to accommodate the full list of clients. The server will not resend this
message for a given client unless it receives an extra COMPLETE from that
client. If encryption is enabled, this message is encrypted and embedded
within an ENCRYPTED message.
Function: 8 bits
The message number for this message. Always 13.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Client ID: 32 bits each
The body of the message. Contains the ID of one or more clients that a
server received a COMPLETE from.
5.2.16 ABORT
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Flags | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Host |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by either a client or server when an error condition occurs. This
message may or may not be encrypted, depending on whether or not the group
master key has been negotiated.
Function: 8 bits
The message number for this message. Always 19.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Flags: 8 bits
0x01 - CURRENT_FILE
Applies only if sent to a client, and only if the Host field is zero.
If set, specifies that all clients not actively working on the
current file must abort. Clients that finished the current file do
NOT abort and may receive the next file in the session.
All other bits should be set to 0.
Reserved: 8 bits
Reserved for future use and should be set to 0.
Host: 32 bits
If sent by the server, specifies the client the server wishes to abort, or
zero to specify that all clients must abort. If sent by a client, this
is set to zero. If sent by a proxy on behalf of a client, it is set to
the IP of the client that is aborting. If sent by a proxy on its own
behalf, it is set to zero.
Message: 300 bytes
Descriptive text stating the reason for the abort.
5.2.17 HB_REQ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Length | Signed Nonce Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nonce |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Blob |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Signed Nonce |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by a proxy (usually a client proxy) to an upstream proxy for the purpose
of opening up a hole in a firewall that the upstream proxy can send through,
and revealing the proxy's NATed IP to the upstream proxy so the upstream
proxy knows where to send other requests.
Function: 8 bits
The message number for this message. Always 14.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
Public Key Length: 16 bits
The length in bytes of the proxy's public key blob.
Signed Nonce Length: 16 bits
The length in bytes of the signed nonce field.
Nonce: 32 bits
The value received from a previous HB_RESP that is to be signed.
Public Key Blob: varies
A keyblob containing the proxy's RSA or ECDSA public key.
Signed Nonce: varies
The signature from the proxy's RSA/ECDSA private key of the specified nonce.
5.2.18 HB_RESP
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Authenticated | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nonce |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by a proxy in response to an HB_REQ message.
Function: 8 bits
The message number for this message. Always 15.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Authenticated: 8 bits
Specifies the status of the HB_REQ this message is responding to. A value
of HB_AUTH_OK means authentication succeeded or was not required. A value
of HB_AUTH_CHALLENGE means expected authentication info was not specified.
A value of HB_AUTH_FAILED means the given authentication info was invalid.
Reserved: 8 bits
Reserved for future use and should be set to 0.
Nonce: 32 bits
When Authenticated = HB_AUTH_CHALLENGE, the nonce value that is expected
to be signed in an authenticated HB_REQ.
5.2.19 KEY_REQ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by a client to a response proxy to solicit a PROXY_KEY message. The
client will send this message once every 5 seconds until it gets a valid
response.
Function: 8 bits
The message number for this message. Always 16.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
5.2.20 PROXY_KEY
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Public Key Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Diffie-Hellman Key Length | Signed Nonce Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Nonce |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Public Key Blob |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Diffie-Hellman Key Blob |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Signed Nonce |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Sent by a response proxy to clients for the purpose suppying its public
RSA/ECDSA key and public ECDH key. When a client gets an ANNOUNCE directly
from a server, it contains the server's RSA/ECDSA public key and perhaps an
ECDH key. The client can then use the proxy's keys to perform the key exchange
instead of the server's key. This message gets sent out on the first specified
public multicast address so all downstream clients be read it. To avoid a
denial of service attack, the proxy will not send this message more than once
every 5 seconds.
Function: 8 bits
The message number for this message. Always 17.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Public Key Length: 16 bits
The length in bytes of the proxy's public key blob.
Diffie-Hellman Key Length: 16 bits
The length in bytes of the proxy's public ECDH key blob.
Signed Nonce Length: 16 bits
The length in bytes of the signed nonce field.
Nonce: 32 bits
A randomly chosen value that will be signed by the proxy's RSA public key.
Public Key Blob: varies
A keyblob containing the proxy's RSA or ECDSA public key.
Diffie-Hellman Key Blob: varies
A keyblob containing the proxy's long-lived ECDH public key.
Signed Nonce: varies
The signature from the proxy's RSA/ECDSA private key of the specified nonce.
5.2.21 CONG_CTRL
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CC Sequence | CC Rate |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_seconds |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp_microseconds |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| CC Item |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
: .... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sent by a server as part of congestion control and round trip time calculations.
This message supplies clients with an up to date timestamp to adjust and
respond to for round trip time as well as a list of round trip times for one or
more clients.
Function: 8 bits
The message number for this message. Always 20.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
CC Sequence: 16 bits
A sequence number which increases by 1 at the start of a new feedback round.
CC Rate: 16 bits
The current congestion control suppression rate. Only clients with a rate
less than this may send congestion control feedback in a CC_ACK message.
Timestamp_seconds: 32 bits
The current time expressed as seconds since the UNIX epoch of
1/1/1970 00:00:00 UTC.
Timestamp_microseconds: 32 bits
The microsecond portion of the current time. This field and the above
field are used in round trip time measurements.
CC Item: 64 bits each
The body of the message. Contains a list of congestion control data items.
Defined as follows.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Client ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Flags | RTT | Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Client ID: 32 bits
The unique ID of the client this item represents.
Flags: 8 bits
0x01 - CC_CLR
If set, specifies this client is the CLR.
0x02 - CC_RTT
If set, specifies that the server calculated the RTT to this client.
0x04 - CC_START
If set, specifies that the server is in slow start mode.
0x08 - CC_LEAVE
If set, specifies that the client is preparing to leave the session.
Not currently used in this message.
All other bits should be set to 0.
RTT: 8 bits
The round trip time to this client as calculated by the server.
Reserved: 16 bits
Reserved for future use and should be set to 0.
5.2.22 CC_ACK
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Function | Header Length | Reserved |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| Header Extensions (if applicable) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
This message contains a congestion control feedback response from a client.
There should normaly be at least one header extension present such as
EXT_TFMCC_ACK_INFO to supply the specific congestion control data for a
particular scheme.
Function: 8 bits
The message number for this message. Always 21.
Header Length: 8 bits
The total length of the header, including extensions, in 4 byte words.
Reserved: 16 bits
Reserved for future use and should be set to 0.
5.3 Message Constants
5.3.1 Message type numbers
ANNOUNCE 1
REGISTER 2
CLIENT_KEY 3
REG_CONF 4
KEYINFO 5
KEYINFO_ACK 6
FILEINFO 7
FILEINFO_ACK 8
FILESEG 9
DONE 10
STATUS 11
COMPLETE 12
DONE_CONF 13
HB_REQ 14
HB_RESP 15
KEY_REQ 16
PROXY_KEY 17
ENCRYPTED 18
ABORT 19
CONG_CTRL 20
CC_ACK 21
5.3.2 Key type numbers
None 0
DES 1
Triple DES 2
AES 128 CBC 3
AES 256 CBC 4
AES 128 GCM 5
AES 256 GCM 6
AES 128 CCM 7
AES 256 CCM 8
5.3.3 Hash type numbers
None 0
MD5 1
SHA-1 2
SHA-256 3
SHA-384 4
SHA-512 5
5.3.4 Signature type numbers
None 0
HMAC 1
KEYEX 2
AUTHENC 3
5.3.5 Key Exchange type numbers
None 0
RSA 1
ECDH_RSA 2
ECDH_ECDSA 3
5.3.6 Key Blob type numbers
None 0
RSA 1
EC 2
5.3.7 Heartbeat authentication codes
HB_AUTH_FAILED 0
HB_AUTH_OK 1
HB_AUTH_CHALLENGE 2
5.3.8 File types
Regular file 0
Directory 1
Symbolic Link 2
Delete file 3
Get free space 4
5.3.9 Completion status
COMP_STAT_NORMAL 0
COMP_STAT_SKIPPED 1
COMP_STAT_OVERWRITE 2
COMP_STAT_REJECTED 3
5.3.10 Extension types
EXT_ENC_INFO 1
EXT_TFMCC_DATA_INFO 2
EXT_TFMCC_ACK_INFO 3
EXT_PGMCC_DATA_INFO 4
EXT_PGMCC_NAK_INFO 5
EXT_PGMCC_ACK_INFO 6
EXT_FREESPACE_INFO 7
5.3.11 Congestion control types
CC_NONE 0
CC_UFTP3 1
CC_TFMCC 2
CC_PGMCC 3
5.3.12 Elliptic Curve named curves
sect163k1 1
sect163r1 2
sect163r2 3
sect193r1 4
sect193r2 5
sect233k1 6
sect233r1 7
sect239k1 8
sect283k1 9
sect283r1 10
sect409k1 11
sect409r1 12
sect571k1 13
sect571r1 14
secp160k1 15
secp160r1 16
secp160r2 17
secp192k1 18
secp192r1 19 // also prime192v1
secp224k1 20
secp224r1 21
secp256k1 22
secp256r1 23 // also prime256v1
secp384r1 24
secp521r1 25
uftp-4.1.5/server_init.h 0000644 0000764 0000764 00000002707 12250244021 014203 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _SERVER_INIT_H
#define _SERVER_INIT_H
void pre_initialize(void);
void initialize(void);
#endif // _SERVER_INIT_H
uftp-4.1.5/client.h 0000644 0000764 0000764 00000022601 12176066071 013141 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_H
#define _CLIENT_H
#include "uftp_common.h"
#include "encryption.h"
#define MAXLIST 100
#define MAXMISORDER 5
#define KEY_REQ_INT 5
/**
* Current state of client for a given group
*/
enum client_phase {
PHASE_REGISTERED = 1, /// Registered and awaiting KEYINFO or REG_CONF
PHASE_RECEIVING = 2, /// Currently receiving a file
PHASE_COMPLETE = 3, /// Completed group and awaiting DONE_CONF
PHASE_MIDGROUP = 4 /// Registered awaiting next file or group end
};
/**
* Info pertaining to current file
*/
struct file_t {
uint32_t blocks; /// Total blocks
uint16_t sections; /// Total sections
uint16_t big_sections; /// Number of larger sized sections
uint32_t secsize_small, secsize_big; /// Size of sections
int ftype; /// File type (regular, directory, symlink)
f_offset_t size; /// Size in bytes
int32_t tstamp; /// File timestamp
char filepath[MAXPATHNAME]; /// Local path to file
char temppath[MAXPATHNAME]; /// Local path to temp file
char name[MAXPATHNAME]; /// Path name
char linkname[MAXPATHNAME]; /// Link name (symlinks only)
uint8_t *naklist; /// NAK list
uint8_t *section_done; /// Array of done flags for each section
int fd; /// File descriptor for file
uint32_t last_block; /// Block number of last block received
uint16_t last_section; /// Section number of last block received
struct timeval nak_time; /// Time to send out NAKs
uint16_t nak_section_first; /// First section number to send NAKs for
uint16_t nak_section_last; /// Last section number to send NAKs for
int got_done; /// A DONE was received for this client
f_offset_t curr_offset; /// Current file pointer offset in fd
int restart; /// True if restarting a prior session
int comp_status; /// Value for status field of COMPLETE
int destdiridx; /// Index of dest dir file is received in
};
/**
* Header of client save state file.
* Followed in the file by the NAK list and section_done list.
* The naklist and section_done fields are left blank when the struct is
* written to a file. When read back in, memory is allocated and the
* NAK list and section_done list are written to them.
*/
struct client_restart_t {
uint32_t blocks; /// Total blocks
uint32_t sections; /// Total sections
f_offset_t size; /// Size in bytes
char name[MAXPATHNAME]; /// Path name
uint8_t *naklist; /// NAK list
uint8_t *section_done; /// Array of done flags for each section
};
/**
* Loss history item.
* These are part of an array where the array index is the sequence number.
*/
struct loss_history_t {
int found; /// True if this packet was received
struct timeval t; /// Time received, either actual or inferred
int size; /// Size of received packet, including UDP/IP
};
/**
* Loss event item.
*/
struct loss_event_t {
uint32_t start_seq; /// Seq num of event start, including wraparound
int len; /// Size of loss interval
struct timeval t; /// Timestamp of event start
};
/**
* Info for a particular group
*/
struct group_list_t {
uint32_t group_id; /// Group ID
uint8_t group_inst; /// Group instance ID (restart number)
uint16_t file_id; /// File ID of current file
uint8_t version; /// Protocol version number of server
union sockaddr_u multi; /// Private multicast address
int multi_join; /// True if we're listening on private addr
char start_date[10]; /// Date initial ANNOUNCE was received
char start_time[10]; /// Time initial ANNOUNCE was received
uint16_t send_seq; /// Outgoing seq. number for loss detection
uint32_t src_id; /// ID of server (network byte order)
union sockaddr_u replyaddr;
int phase; /// Current client_phase of the group
int client_auth, restart, sync_mode, sync_preview; /// Flags from ANNOUNCE
struct client_restart_t *restartinfo; /// Restart file header
unsigned int blocksize; /// Size of packet payload
unsigned int datapacketsize; /// Max size of UFTP packet
struct timeval timeout_time, start_timeout_time, expire_time;
double rtt, grtt; /// Client's RTT and server's GRTT
uint16_t start_txseq, max_txseq; /// Server's starting, max sequence #
struct loss_history_t *loss_history; /// Loss history
struct loss_event_t loss_events[9]; /// Loss event history
int seq_wrap; /// Number of times server seq wrapped
int ccseq; /// Current congestion control sequence #
uint32_t initrate; /// Cong. control rate at start of fb round
int isclr; /// True if this client is the CLR
int slowstart; /// True if we're in slowstart mode
uint8_t robust, cc_type; /// Robust factor, congestion control type
uint32_t gsize; /// Group size estimate
struct timeval cc_time; /// Timer for sending CC_ACK
struct timeval last_server_ts, last_server_rx_ts;
int keytype, hashtype, sigtype, keyextype; /// Encryption parameters
union key_t server_pubkey; /// Server's public key
union key_t client_privkey; /// Client's private key for this group
union key_t server_dhkey; /// Server ECDH public key for this group
union key_t client_dhkey; /// Client ECDH public key for this group
unsigned int server_pubkeylen; /// Length in bytes of server key
unsigned int client_privkeylen; /// Length in bytes of client key
uint8_t rand1[RAND_LEN]; /// Server's random number
uint8_t rand2[RAND_LEN]; /// Client's random number
uint8_t premaster[MASTER_LEN]; /// Premaster secret sent by client
unsigned int premaster_len; /// Length of premaster secret
uint8_t master[MASTER_LEN]; /// Master key for client
uint8_t hmackey[HMAC_LEN]; /// HMAC key for client
uint8_t key[MAXKEY]; /// Symmetric encryption key for client
uint8_t salt[MAXIV]; /// Salt for block cypher IV for client
uint8_t groupmaster[MASTER_LEN];/// Master key for server
uint8_t grouphmackey[HMAC_LEN]; /// HMAC key for server
uint8_t groupkey[MAXKEY]; /// Symmetric encryption key for server
uint8_t groupsalt[MAXIV]; /// Salt for block cypher IV for server
uint64_t ivctr; /// Counter portion of the IV
int ivlen, keylen, hmaclen; /// Length of HMAC key, symmetric key and iv
struct file_t fileinfo; /// Info pertaining to current file
};
/**
* Global command line values and sockets
*/
extern SOCKET listener;
extern char tempdir[MAXDIRNAME], destdir[MAXDIR][MAXDIRNAME];
extern char pidfile[MAXPATHNAME];
extern char keyfile[MAXLIST][MAXPATHNAME], keyinfo[MAXLIST][MAXPATHNAME];
extern char backupdir[MAXDIR][MAXDIRNAME];
extern int debug, encrypted_only, dscp, destdircnt, tempfile, keyinfo_count;
extern int interface_count, pub_multi_count, keyfile_count, rcvbuf, backupcnt;
extern char postreceive[MAXPATHNAME], portname[PORTNAME_LEN];
extern int port, move_individual;
extern uint32_t uid;
extern union sockaddr_u hb_hosts[MAXLIST];
extern struct iflist m_interface[MAX_INTERFACES];
extern union sockaddr_u pub_multi[MAX_INTERFACES];
extern struct group_list_t group_list[MAXLIST];
extern struct fp_list_t server_keys[MAXLIST];
extern struct iflist ifl[MAX_INTERFACES];
extern struct timeval next_keyreq_time, next_hb_time;
extern int ifl_len, server_count, key_count, has_proxy, sys_keys, priority;
extern int hbhost_count, hb_interval;
extern union key_t privkey[MAXLIST];
extern int privkey_type[MAXLIST];
extern struct fp_list_t proxy_info;
extern union key_t proxy_pubkey, proxy_dhkey;
extern int proxy_pubkeytype;
#endif // _CLIENT_H
uftp-4.1.5/makefile 0000664 0000764 0000764 00000016763 12250244021 013212 0 ustar dbush dbush #
# UFTP - UDP based FTP with multicast
#
# Copyright (C) 2001-2013 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.
#
UNAME_S:=$(shell uname -s)
ifdef NO_ENCRYPTION
ENC_OPTS=-DNO_ENCRYPTION
CRYPT_LIB=
else ifdef NO_EC
ENC_OPTS=-DNO_EC
CRYPT_LIB=-lcrypto
else
ENC_OPTS=
CRYPT_LIB=-lcrypto
endif
# defaults
CC = gcc
OPTIONS=-g -Wall $(ENC_OPTS)
LDLIBS=-lc -lm $(CRYPT_LIB)
CFLAGS=
MTFLAGS=
# FreeBSD
ifeq ("FreeBSD", "$(UNAME_S)")
OPTIONS=-g -Wall -DHAS_GETIFADDRS -DNO_DUAL -DNO_MCAST_JOIN $(ENC_OPTS)
endif
# OSX, aka Darwin
ifeq ("Darwin", "$(UNAME_S)")
OPTIONS=-g -Wall -DHAS_GETIFADDRS $(ENC_OPTS)
endif
# Sun
ifeq ("SunOS", "$(UNAME_S)")
CC = cc
OPTIONS=-g -DBSD_COMP -DNO_DUAL $(ENC_OPTS)
LDLIBS=-lnsl -lsocket -lm $(CRYPT_LIB)
CFLAGS=`getconf LFS_CFLAGS`
OPENSSL=/usr/sfw
MTFLAGS=-mt
endif
# Linux
ifeq ("Linux", "$(UNAME_S)")
OPTIONS=-g -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Wformat=2 -Wwrite-strings -Wpointer-arith -Wcast-qual -Wshadow -Wno-missing-field-initializers -Wstrict-prototypes -Winline -Wbad-function-cast -DHAS_GETIFADDRS $(ENC_OPTS)
LDLIBS=-lm $(CRYPT_LIB)
CFLAGS=`getconf LFS_CFLAGS`
endif
ifdef OPENSSL
INCLUDE=-I $(OPENSSL)/include
LIB=-L $(OPENSSL)/lib
endif
all: uftp uftpd uftpproxyd uftp_keymgt
test: testclient_multi
testclient_multi.o: testclient_multi.c uftp.h uftp_common.h encryption.h
testclient_multi: testclient_multi.o encrypt_openssl.o uftp_common.o
$(CC) $(OPTIONS) $(LIB) -o $@ $^ $(LDLIBS)
clean:
rm -f testclient_multi uftp uftpd uftpproxyd uftp_keymgt *.o
client_announce.o: client_announce.c client.h uftp_common.h uftp.h \
encryption.h client_common.h client_announce.h
client_common.o: client_common.c client.h uftp_common.h uftp.h \
encryption.h client_common.h
client_config.o: client_config.c client.h uftp_common.h uftp.h \
encryption.h client_config.h
client_fileinfo.o: client_fileinfo.c client.h uftp_common.h uftp.h \
encryption.h client_common.h client_fileinfo.h client_transfer.h
client_init.o: client_init.c client.h uftp_common.h uftp.h encryption.h \
client_init.h client_config.h client_common.h
client_loop.o: client_loop.c client.h uftp_common.h uftp.h encryption.h \
client_common.h client_loop.h client_announce.h client_fileinfo.h \
client_transfer.h heartbeat_send.h
client_main.o: client_main.c client_config.h client_init.h client_loop.h
client_transfer.o: client_transfer.c client.h uftp_common.h uftp.h \
encryption.h client_common.h client_transfer.h
server_announce.o: server_announce.c server.h uftp_common.h uftp.h \
encryption.h server_common.h server_announce.h
server_common.o: server_common.c server.h uftp_common.h uftp.h \
encryption.h server_common.h
server_config.o: server_config.c server.h uftp_common.h uftp.h \
encryption.h server_config.h
server_init.o: server_init.c server.h uftp_common.h uftp.h encryption.h \
server_config.h server_init.h
server_main.o: server_main.c server_config.h server_init.h server_send.h
server_phase.o: server_phase.c server.h uftp_common.h uftp.h encryption.h \
server_config.h server_common.h server_announce.h server_transfer.h
server_send.o: server_send.c server.h uftp_common.h uftp.h encryption.h \
server_send.h server_phase.h server_common.h
server_transfer.o: server_transfer.c server.h uftp_common.h uftp.h \
encryption.h server_common.h server_transfer.h
proxy_common.o: proxy_common.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_common.h proxy_upstream.h
proxy_config.o: proxy_config.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_config.h
proxy_downstream.o: proxy_downstream.c proxy.h uftp_common.h uftp.h \
encryption.h proxy_common.h proxy_downstream.h
proxy_init.o: proxy_init.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_init.h proxy_config.h proxy_common.h
proxy_loop.o: proxy_loop.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_common.h proxy_loop.h proxy_upstream.h proxy_downstream.h \
heartbeat_send.h
proxy_main.o: proxy_main.c proxy_config.h proxy_init.h proxy_loop.h
proxy_upstream.o: proxy_upstream.c proxy.h uftp_common.h uftp.h \
encryption.h proxy_common.h proxy_upstream.h proxy_downstream.h
uftp_keymgt.o: uftp_keymgt.c uftp_common.h uftp.h encryption.h
encrypt_openssl.o: encrypt_openssl.c uftp_common.h uftp.h encryption.h
encrypt_none.o: encrypt_none.c encryption.h
uftp_common.o: uftp_common.c uftp.h uftp_common.h encryption.h
heartbeat_send.o: heartbeat_send.c uftp_common.h uftp.h encryption.h \
heartbeat_send.h
ifdef NO_ENCRYPTION
UFTP_OBJS=uftp_common.o encrypt_none.o \
server_announce.o server_transfer.o server_send.o server_phase.o \
server_common.o server_config.o server_init.o server_main.o
UFTPD_OBJS=uftp_common.o encrypt_none.o \
client_loop.o client_announce.o client_fileinfo.o client_transfer.o \
client_common.o client_config.o client_init.o client_main.o heartbeat_send.o
UFTPPROXYD_OBJS=uftp_common.o encrypt_none.o \
proxy_loop.o proxy_upstream.o proxy_downstream.o \
proxy_common.o proxy_config.o proxy_init.o proxy_main.o heartbeat_send.o
UFTP_KEYMGT_OBJS=uftp_keymgt.o uftp_common.o encrypt_none.o
else
UFTP_OBJS=uftp_common.o encrypt_openssl.o \
server_announce.o server_transfer.o server_send.o server_phase.o \
server_common.o server_config.o server_init.o server_main.o
UFTPD_OBJS=uftp_common.o encrypt_openssl.o \
client_loop.o client_announce.o client_fileinfo.o client_transfer.o \
client_common.o client_config.o client_init.o client_main.o heartbeat_send.o
UFTPPROXYD_OBJS=uftp_common.o encrypt_openssl.o \
proxy_loop.o proxy_upstream.o proxy_downstream.o \
proxy_common.o proxy_config.o proxy_init.o proxy_main.o heartbeat_send.o
UFTP_KEYMGT_OBJS=uftp_keymgt.o uftp_common.o encrypt_openssl.o
endif
uftp: $(UFTP_OBJS)
$(CC) $(OPTIONS) $(LIB) -o $@ $^ $(LDLIBS) $(MTFLAGS) -lpthread
uftpd: $(UFTPD_OBJS)
$(CC) $(OPTIONS) $(LIB) -o $@ $^ $(LDLIBS)
uftpproxyd: $(UFTPPROXYD_OBJS)
$(CC) $(OPTIONS) $(LIB) -o $@ $^ $(LDLIBS)
uftp_keymgt: $(UFTP_KEYMGT_OBJS)
$(CC) $(OPTIONS) $(LIB) -o $@ $^ $(LDLIBS)
%.o: %.c
$(CC) $(OPTIONS) $(INCLUDE) $(CFLAGS) $(MTFLAGS) -c $<
install: all
install -m 755 -d $(DESTDIR)/bin
install -m 755 -d $(DESTDIR)/usr/sbin
install -m 755 -d $(DESTDIR)/usr/share/man/man1
/bin/cp -p uftp $(DESTDIR)/bin
/bin/cp -p uftpd $(DESTDIR)/usr/sbin
/bin/cp -p uftpproxyd $(DESTDIR)/usr/sbin
/bin/cp -p uftp_keymgt $(DESTDIR)/bin
/bin/cp -p uftp.1 uftpd.1 uftpproxyd.1 uftp_keymgt.1 $(DESTDIR)/usr/share/man/man1
uftp-4.1.5/server_announce.h 0000644 0000764 0000764 00000005406 12137013343 015052 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2012 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.
*/
#ifndef _SERVER_ANNOUNCE_H
#define _SERVER_ANNOUNCE_H
#include "server.h"
int send_announce(const struct finfo_t *finfo, int attempt, int open);
int send_regconf(const struct finfo_t *finfo, int attempt, int do_regconf);
int send_keyinfo(const struct finfo_t *finfo, int attempt);
int send_fileinfo(const struct finfo_t *finfo, int attempt);
void handle_open_register(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
uint32_t src, int regconf);
void handle_register(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx, int regconf, int open);
void handle_open_clientkey(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
uint32_t src);
void handle_clientkey(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx);
void handle_keyinfo_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, const union sockaddr_u *su,
int hostidx);
void handle_fileinfo_ack(const unsigned char *message, unsigned meslen,
struct finfo_t *finfo, int hostidx);
#endif // _SERVER_ANNOUNCE_H
uftp-4.1.5/makefile.mak 0000644 0000764 0000764 00000021231 12137013343 013746 0 ustar dbush dbush #
# UFTP - UDP based FTP with multicast
#
# Copyright (C) 2001-2013 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.
#
CFLAGS=-Zi -W3 -nologo -Od /D WINDOWS
!IFDEF NO_ENCRYPTION
CFLAGS=$(CFLAGS) /D NO_ENCRYPTION
LDLIBS = ws2_32.lib iphlpapi.lib
!ELSE IFDEF OPENSSL
CFLAGS=$(CFLAGS) /D OPENSSL /MD
LDLIBS = libeay32.lib ssleay32.lib ws2_32.lib iphlpapi.lib
INCLUDE = $(INCLUDE);C:\OpenSSL\include
LIB = $(LIB);C:\OpenSSL\lib
!ELSE IFDEF WINXP
LDLIBS = advapi32.lib ws2_32.lib iphlpapi.lib
!ELSE
LDLIBS = bcrypt.lib ncrypt.lib ws2_32.lib iphlpapi.lib
!ENDIF
!IFDEF WINXP
CFLAGS=$(CFLAGS) /D _WIN32_WINNT=_WIN32_WINNT_WINXP
!ELSE
CFLAGS=$(CFLAGS) /D _WIN32_WINNT=_WIN32_WINNT_WIN7
!ENDIF
LDFLAGS=-debug -nologo -subsystem:console
CC = cl
LINK = link
all: uftp.exe uftpd.exe uftpproxyd.exe uftp_keymgt.exe
clean:
-del /f uftp.exe uftpd.exe uftpproxyd.exe uftp_keymgt.exe *.obj *.pdb *.ilk
test: testclient_multi.exe
testclient_multi.obj: testclient_multi.c uftp.h uftp_common.h encryption.h
!IFDEF OPENSSL
testclient_multi.exe: testclient_multi.obj encrypt_openssl.obj uftp_common.obj win_func.obj
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
!ELSE
testclient_multi.exe: testclient_multi.obj encrypt_cryptoapi.obj uftp_common.obj win_func.obj
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
!ENDIF
client_announce.obj: client_announce.c client.h uftp_common.h uftp.h \
encryption.h client_common.h client_announce.h
client_common.obj: client_common.c client.h uftp_common.h uftp.h \
encryption.h client_common.h
client_config.obj: client_config.c client.h uftp_common.h uftp.h \
encryption.h client_config.h
client_init.obj: client_init.c client.h uftp_common.h uftp.h encryption.h \
client_init.h client_config.h client_common.h
client_loop.obj: client_loop.c client.h uftp_common.h uftp.h encryption.h \
client_common.h client_loop.h client_announce.h \
client_transfer.h heartbeat_send.h
client_main.obj: client_main.c client_config.h client_init.h client_loop.h
client_transfer.obj: client_transfer.c client.h uftp_common.h uftp.h \
encryption.h client_common.h client_transfer.h
server_announce.obj: server_announce.c server.h uftp_common.h uftp.h \
encryption.h server_common.h server_announce.h
server_common.obj: server_common.c server.h uftp_common.h uftp.h \
encryption.h server_common.h
server_config.obj: server_config.c server.h uftp_common.h uftp.h \
encryption.h server_config.h
server_init.obj: server_init.c server.h uftp_common.h uftp.h encryption.h \
server_config.h server_init.h
server_main.obj: server_main.c server_config.h uftp.h server_init.h \
server_send.h
server_phase.obj: server_phase.c server.h uftp_common.h uftp.h encryption.h \
server_common.h server_announce.h server_transfer.h
server_send.obj: server_send.c server.h uftp_common.h uftp.h encryption.h \
server_send.h server_phase.h
server_transfer.obj: server_transfer.c server.h uftp_common.h uftp.h \
encryption.h server_common.h server_transfer.h
proxy_common.obj: proxy_common.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_common.h proxy_upstream.h
proxy_config.obj: proxy_config.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_config.h
proxy_downstream.obj: proxy_downstream.c proxy.h uftp_common.h uftp.h \
encryption.h proxy_common.h proxy_downstream.h
proxy_init.obj: proxy_init.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_init.h proxy_config.h proxy_common.h
proxy_loop.obj: proxy_loop.c proxy.h uftp_common.h uftp.h encryption.h \
proxy_common.h proxy_loop.h proxy_upstream.h proxy_downstream.h \
heartbeat_send.h
proxy_main.obj: proxy_main.c proxy_config.h proxy_init.h proxy_loop.h
proxy_upstream.obj: proxy_upstream.c proxy.h uftp_common.h uftp.h \
encryption.h proxy_common.h proxy_upstream.h proxy_downstream.h
uftp_keymgt.obj: uftp_keymgt.c uftp_common.h uftp.h encryption.h
encrypt_cng.obj: encrypt_cng.c uftp_common.h uftp.h encryption.h
encrypt_cryptoapi.obj: encrypt_cryptoapi.c uftp_common.h uftp.h encryption.h
encrypt_openssl.obj: encrypt_openssl.c uftp_common.h uftp.h encryption.h
uftp_common.obj: uftp_common.c uftp.h uftp_common.h encryption.h
heartbeat_send.o: heartbeat_send.c uftp_common.h uftp.h encryption.h \
heartbeat_send.h
win_func.obj: win_func.c win_func.h
!IFDEF NO_ENCRYPTION
UFTP_OBJS=win_func.obj uftp_common.obj encrypt_none.obj \
server_announce.obj server_transfer.obj \
server_send.obj server_phase.obj \
server_common.obj server_config.obj server_init.obj server_main.obj
UFTPD_OBJS=win_func.obj uftp_common.obj encrypt_none.obj \
client_loop.obj client_announce.obj client_transfer.obj \
client_common.obj client_config.obj client_init.obj client_main.obj \
heartbeat_send.obj
UFTPPROXYD_OBJS=win_func.obj uftp_common.obj encrypt_none.obj \
proxy_loop.obj proxy_upstream.obj proxy_downstream.obj \
proxy_common.obj proxy_config.obj proxy_init.obj proxy_main.obj \
heartbeat_send.obj
UFTP_KEYMGT_OBJS=uftp_keymgt.obj win_func.obj uftp_common.obj encrypt_none.obj
!ELSE IFDEF OPENSSL
UFTP_OBJS=win_func.obj uftp_common.obj encrypt_openssl.obj \
server_announce.obj server_transfer.obj \
server_send.obj server_phase.obj \
server_common.obj server_config.obj server_init.obj server_main.obj
UFTPD_OBJS=win_func.obj uftp_common.obj encrypt_openssl.obj \
client_loop.obj client_announce.obj client_transfer.obj \
client_common.obj client_config.obj client_init.obj client_main.obj \
heartbeat_send.obj
UFTPPROXYD_OBJS=win_func.obj uftp_common.obj encrypt_openssl.obj \
proxy_loop.obj proxy_upstream.obj proxy_downstream.obj \
proxy_common.obj proxy_config.obj proxy_init.obj proxy_main.obj \
heartbeat_send.obj
UFTP_KEYMGT_OBJS=uftp_keymgt.obj win_func.obj uftp_common.obj encrypt_openssl.obj
!ELSE IFDEF WINXP
UFTP_OBJS=win_func.obj uftp_common.obj encrypt_cryptoapi.obj \
server_announce.obj server_transfer.obj \
server_send.obj server_phase.obj \
server_common.obj server_config.obj server_init.obj server_main.obj \
UFTPD_OBJS=win_func.obj uftp_common.obj encrypt_cryptoapi.obj \
client_loop.obj client_announce.obj client_transfer.obj \
client_common.obj client_config.obj client_init.obj client_main.obj \
heartbeat_send.obj
UFTPPROXYD_OBJS=win_func.obj uftp_common.obj encrypt_cryptoapi.obj \
proxy_loop.obj proxy_upstream.obj proxy_downstream.obj \
proxy_common.obj proxy_config.obj proxy_init.obj proxy_main.obj \
heartbeat_send.obj
UFTP_KEYMGT_OBJS=uftp_keymgt.obj win_func.obj uftp_common.obj encrypt_cryptoapi.obj
!ELSE
UFTP_OBJS=win_func.obj uftp_common.obj encrypt_cng.obj \
server_announce.obj server_transfer.obj \
server_send.obj server_phase.obj \
server_common.obj server_config.obj server_init.obj server_main.obj \
UFTPD_OBJS=win_func.obj uftp_common.obj encrypt_cng.obj \
client_loop.obj client_announce.obj client_transfer.obj \
client_common.obj client_config.obj client_init.obj client_main.obj \
heartbeat_send.obj
UFTPPROXYD_OBJS=win_func.obj uftp_common.obj encrypt_cng.obj \
proxy_loop.obj proxy_upstream.obj proxy_downstream.obj \
proxy_common.obj proxy_config.obj proxy_init.obj proxy_main.obj \
heartbeat_send.obj
UFTP_KEYMGT_OBJS=uftp_keymgt.obj win_func.obj uftp_common.obj encrypt_cng.obj
!ENDIF
uftp.exe: $(UFTP_OBJS)
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
uftpd.exe: $(UFTPD_OBJS)
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
uftpproxyd.exe: $(UFTPPROXYD_OBJS)
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
uftp_keymgt.exe: $(UFTP_KEYMGT_OBJS)
$(LINK) $(LDFLAGS) -out:$@ $** $(LDLIBS)
.c.obj:
$(CC) -c $(CFLAGS) $<
uftp: uftp.exe
uftpd: uftpd.exe
uftpproxyd: uftpproxyd.exe
uftp_keymgt: uftp_keymgt.exe
testclient_multi: testclient_multi.exe
uftp-4.1.5/uftp_keymgt.c 0000644 0000764 0000764 00000012237 12176066071 014220 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#endif
#include "uftp_common.h"
#include "encryption.h"
void print_key(const char *name)
{
union key_t key;
int keytype;
key = read_private_key(name, &keytype);
if (keytype == KEYBLOB_RSA) {
fprintf(stderr, "%s: RSA, %d bits, fingerprint: %s\n", name,
RSA_keylen(key.rsa) * 8,
print_key_fingerprint(key, KEYBLOB_RSA));
free_RSA_key(key.rsa);
} else if (keytype == KEYBLOB_EC) {
fprintf(stderr, "%s: ECDSA, curve %s, fingerprint: %s\n", name,
curve_name(get_EC_curve(key.ec)),
print_key_fingerprint(key, KEYBLOB_EC));
free_EC_key(key.ec);
} else {
fprintf(stderr, "%s: no such key\n", name);
}
}
int main(int argc, char *argv[])
{
union key_t key;
int i, c;
int gen_key_len, del_key, sys_key;
uint8_t gen_key_curve;
const char opts[] = "g:dm";
log_level = 2;
max_log_size = 0;
init_log_mux = 0;
applog = stderr;
gen_key_len = 0;
gen_key_curve = 0;
del_key = 0;
sys_key = 0;
while ((c = getopt(argc, argv, opts)) != EOF) {
switch (c) {
case 'g':
if (!strncmp("ec:", optarg, 3)) {
gen_key_curve = get_curve(&optarg[3]);
if (gen_key_curve == 0) {
fprintf(stderr, "Invalid curve");
exit(1);
}
} else if (!strncmp("rsa:", optarg, 4)) {
gen_key_len = atoi(&optarg[4]);
if ((gen_key_len < 512) || (gen_key_len > 2048)) {
fprintf(stderr, "Invalid key size\n");
exit(1);
}
} else {
fprintf(stderr, "Invalid key specification\n");
exit(1);
}
break;
case 'd':
del_key = 1;
break;
case 'm':
sys_key = 1;
break;
}
}
argc -= optind;
argv += optind;
if (((gen_key_len != 0) || gen_key_curve != 0) && (del_key != 0)) {
fprintf(stderr, "Can't specify both -g and -d\n");
exit(1);
}
crypto_init(sys_key);
key.key = 0;
if (gen_key_len) {
if (argc < 1) {
fprintf(stderr, "No keyfile specified\n");
exit(1);
}
key.rsa = gen_RSA_key(gen_key_len, RSA_EXP, argv[0]);
if (key.key) {
fprintf(stderr, "%s: RSA, %d bits, fingerprint: %s\n", argv[0],
RSA_keylen(key.rsa) * 8,
print_key_fingerprint(key, KEYBLOB_RSA));
free_RSA_key(key.rsa);
} else {
fprintf(stderr, "Error generating/storing key\n");
}
} else if (gen_key_curve) {
if (argc < 1) {
fprintf(stderr, "No keyfile specified\n");
exit(1);
}
key.ec = gen_EC_key(gen_key_curve, 0, argv[0]);
if (key.key) {
fprintf(stderr, "%s: ECDSA, curve %s, fingerprint: %s\n", argv[0],
curve_name(get_EC_curve(key.ec)),
print_key_fingerprint(key, KEYBLOB_EC));
free_EC_key(key.ec);
} else {
fprintf(stderr, "Error generating/storing key\n");
}
} else if (del_key) {
if (argc < 1) {
fprintf(stderr, "No keyfile specified\n");
exit(1);
}
delete_container(argv[0]);
} else {
for (i = 0; i < argc; i++) {
print_key(argv[i]);
}
#if defined WINDOWS && !defined OPENSSL
if (argc == 0) {
const char *name;
while ((name = get_next_container()) != NULL) {
print_key(name);
}
}
#endif
}
crypto_cleanup();
return 0;
}
uftp-4.1.5/proxy_common.c 0000644 0000764 0000764 00000112730 12250244021 014374 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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
#include
#endif
#include "proxy.h"
#include "proxy_common.h"
#include "proxy_upstream.h"
/**
* Look for a given group in the global group list
* Returns a pointer to the group in the list, or NULL if not found
*/
struct pr_group_list_t *find_group(uint32_t group_id, uint8_t group_inst)
{
int i;
for (i = 0; i < MAXLIST; i++) {
if ((group_list[i].group_id == group_id) &&
(group_list[i].group_inst == group_inst)) {
return &group_list[i];
}
}
return NULL;
}
/**
* 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(struct pr_group_list_t *group, uint32_t id)
{
int i;
for (i = 0; i < group->destcount; i++) {
if (group->destinfo[i].id == id) {
return i;
}
}
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(struct pr_group_list_t *group)
{
int i;
for (i = 0; i < pub_multi_count; i++) {
if (addr_equal(&group->privatemcast, &pub_multi[i])) {
return 1;
}
}
for (i = 0; i < MAXLIST; i++) {
if ((&group_list[i] != group) && (group_list[i].group_id != 0) &&
(addr_equal(&group->privatemcast,
&group_list[i].privatemcast))) {
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(struct pr_group_list_t *group)
{
int i;
for (i = 0; i < MAX_PEND; i++) {
free(group->pending[i].naklist);
}
if (!addr_blank(&group->privatemcast) && (proxy_type != CLIENT_PROXY) &&
!other_mcast_users(group) && group->multi_join) {
multicast_leave(listener, group->group_id, &group->privatemcast,
m_interface, interface_count, server_fp, server_fp_count);
}
if (group->server_pubkey.key) {
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
free_RSA_key(group->server_pubkey.rsa);
} else {
free_EC_key(group->server_pubkey.ec);
}
if ((group->keyextype == KEYEX_ECDH_RSA) ||
(group->keyextype == KEYEX_ECDH_ECDSA)) {
free_EC_key(group->server_dhkey.ec);
free_EC_key(group->proxy_dhkey.ec);
}
}
for (i = 0; i < group->destcount; i++) {
if (group->destinfo[i].pubkey.key) {
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
free_RSA_key(group->destinfo[i].pubkey.rsa);
} else {
free_EC_key(group->destinfo[i].pubkey.ec);
}
if ((group->keyextype == KEYEX_ECDH_RSA) ||
(group->keyextype == KEYEX_ECDH_ECDSA)) {
free_EC_key(group->destinfo[i].dhkey.ec);
}
}
}
memset(group, 0, sizeof(struct pr_group_list_t));
}
/**
* Initializes the uftp header of an outgoing packet
*/
void set_uftp_header(struct uftp_h *header, int func,
struct pr_group_list_t *group)
{
header->version = group->version;
header->func = func;
header->group_id = htonl(group->group_id);
header->group_inst = group->group_inst;
header->src_id = uid;
switch (func) {
case REGISTER:
case KEYINFO_ACK:
case FILEINFO_ACK:
case STATUS:
case COMPLETE:
header->seq = htons(group->send_seq_up++);
break;
case KEYINFO:
header->seq = 0;
header->src_id = group->src_id;
header->grtt = quantize_grtt(group->grtt);
header->gsize = quantize_gsize(group->gsize);
break;
}
// ABORTs will set seq and src_id themselves depending on the direction
}
/**
* Sets the timeout time for a given group list member
*/
void set_timeout(struct pr_group_list_t *group, int pending_reset, int rescale)
{
int pending, i;
if (group->phase == PR_PHASE_READY) {
if (!rescale) {
gettimeofday(&group->start_phase_timeout_time, NULL);
}
group->phase_timeout_time = group->start_phase_timeout_time;
add_timeval_d(&group->phase_timeout_time, 2 * group->grtt);
}
log5(group->group_id, 0, "set timeout: pending_reset=%d", pending_reset);
for (pending = 0, i = 0; (i < MAX_PEND) && !pending; i++) {
if (group->pending[i].msg != 0) {
log5(group->group_id, 0, "set timeout: found pending %s",
func_name(group->pending[i].msg));
pending = group->pending[i].msg;
}
}
if (pending) {
if (pending_reset) {
if (!rescale) {
gettimeofday(&group->start_timeout_time, NULL);
}
group->timeout_time = group->start_timeout_time;
add_timeval_d(&group->timeout_time, 1 * group->grtt);
}
} else {
if (!rescale) {
gettimeofday(&group->start_timeout_time, NULL);
}
group->timeout_time = group->start_timeout_time;
if (group->robust * group->grtt < 1.0) {
add_timeval_d(&group->timeout_time, 1.0);
} else {
add_timeval_d(&group->timeout_time, group->robust * group->grtt);
}
}
}
/**
* Returns the maximum number of clients that can be listed in a given message
*/
int max_msg_dest(struct pr_group_list_t *group, int func, int hlen)
{
switch (func) {
case REGISTER:
return (group->blocksize / sizeof(uint32_t));
case KEYINFO:
return (group->blocksize / sizeof(struct destkey));
case FILEINFO_ACK:
return (group->blocksize / sizeof(uint32_t));
case COMPLETE:
return (group->blocksize / sizeof(uint32_t));
default:
return 0;
}
}
/**
* Sends a pending aggregate message for a given group and message
*/
void send_pending(struct pr_group_list_t *group, int pendidx)
{
switch (group->pending[pendidx].msg) {
case REGISTER:
send_register(group, pendidx);
break;
case FILEINFO_ACK:
send_fileinfo_ack(group, pendidx);
break;
case STATUS:
send_status(group, pendidx);
break;
case COMPLETE:
send_complete(group, pendidx);
break;
default:
log2(group->group_id, 0, "Tried to send pending on invalid type %s",
func_name(group->pending[pendidx].msg));
return;
}
if ((group->pending[pendidx].count <= 0) ||
(group->pending[pendidx].msg == STATUS)) {
// Finish the cleanup we started in load_pending
// Always do this for a STATUS, since we don't have a pending list
free(group->pending[pendidx].naklist);
memset(&group->pending[pendidx], 0, sizeof(struct pr_pending_info_t));
}
}
/**
* Sends all pending aggregate message for a given group
*/
void send_all_pending(struct pr_group_list_t *group)
{
int i;
for (i = 0; i < MAX_PEND; i++) {
if (group->pending[i].msg != 0) {
send_pending(group, i);
}
}
}
/**
* Add the NAKs in the given STATUS message to the list of pending NAKs
*/
void add_naks_to_pending(struct pr_group_list_t *group, int pendidx,
const unsigned char *message)
{
const unsigned char *naks;
unsigned i;
naks = message + sizeof(struct status_h);
for (i = 0; i < group->blocksize; i++) {
group->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(struct pr_group_list_t *group, int hostidx,
const unsigned char *message)
{
const struct fileinfoack_h *fileinfoack;
const struct status_h *status;
const struct complete_h *complete;
const uint8_t *func;
struct pr_pending_info_t *pending;
int match, pendidx, hlen;
func = message;
fileinfoack = (const struct fileinfoack_h *)message;
status = (const struct status_h *)message;
complete = (const struct complete_h *)message;
log3(group->group_id, 0, "check_timeout: looking for pending %s",
func_name(*func));
for (pendidx = 0; pendidx < MAX_PEND; pendidx++) {
pending = &group->pending[pendidx];
if (group->pending[pendidx].msg == 0) {
log3(group->group_id, 0, "check_timeout: found empty slot %d",
pendidx);
match = 1;
break;
}
match = (*func == pending->msg);
switch (*func) {
case REGISTER:
// REGISTER always matches itself
break;
case FILEINFO_ACK:
match = match && (ntohs(fileinfoack->file_id) == pending->file_id);
break;
case STATUS:
match = match && ((ntohs(status->file_id) == pending->file_id) &&
(ntohs(status->section) == pending->section));
break;
case COMPLETE:
match = match && ((ntohs(complete->file_id) == pending->file_id) &&
(complete->status == pending->comp_status));
break;
default:
log2(group->group_id, 0, "Tried to check pending "
"on invalid type %s", func_name(*func));
return;
}
if (match) {
break;
}
}
if (!match) {
send_all_pending(group);
pendidx = 0;
pending = &group->pending[pendidx];
}
log3(group->group_id, 0, "check_timeout: found match at slot %d", pendidx);
pending->msg = *func;
if (group->destinfo[hostidx].pending != pendidx) {
group->destinfo[hostidx].pending = pendidx;
pending->count++;
}
switch (*func) {
case REGISTER:
hlen = sizeof(struct register_h);
if (pending->count == 1) {
gettimeofday(&pending->rx_tstamp, NULL);
pending->tstamp = group->destinfo[hostidx].regtime;
log3(group->group_id, 0, "send time = %d.%06d",
pending->tstamp.tv_sec, pending->tstamp.tv_usec);
log3(group->group_id, 0, "rx time = %d.%06d",
pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
}
break;
case FILEINFO_ACK:
hlen = sizeof(struct fileinfoack_h);
if (pending->count == 1) {
pending->partial = 1;
gettimeofday(&pending->rx_tstamp, NULL);
pending->tstamp.tv_sec = ntohl(fileinfoack->tstamp_sec);
pending->tstamp.tv_usec = ntohl(fileinfoack->tstamp_usec);
log3(group->group_id, 0, "send time = %d.%06d",
pending->tstamp.tv_sec, pending->tstamp.tv_usec);
log3(group->group_id, 0, "rx time = %d.%06d",
pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
}
pending->file_id = ntohs(fileinfoack->file_id);
pending->partial = pending->partial &&
((fileinfoack->flags & FLAG_PARTIAL) != 0);
break;
case STATUS:
hlen = sizeof(struct status_h);
pending->file_id = ntohs(status->file_id);
pending->section = ntohs(status->section);
if (!pending->naklist) {
pending->naklist = safe_calloc(group->blocksize, 1);
}
add_naks_to_pending(group, pendidx, message);
break;
case COMPLETE:
hlen = sizeof(struct complete_h);
pending->file_id = ntohs(complete->file_id);
pending->comp_status = complete->status;
break;
}
if ((*func != STATUS) &&
(pending->count == max_msg_dest(group, *func, hlen))) {
send_pending(group, pendidx);
} else {
int total_pending, i;
log3(group->group_id, 0, "check_timeout: getting pending count for %s",
func_name(*func));
for (total_pending = 0, i = 0; i < MAX_PEND; i++) {
log3(group->group_id, 0, "check_timeout: adding %d pending for %d",
group->pending[i].count, i);
total_pending += group->pending[i].count;
}
if (total_pending == 1) {
set_timeout(group, 1, 0);
}
}
}
/**
* 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(struct pr_group_list_t *group, int abort_session)
{
int hostidx, found;
struct pr_destinfo_t *dest;
if (group->keytype == KEY_NONE) {
return 0;
}
found = 0;
for (hostidx = 0; hostidx < group->destcount; hostidx++) {
dest = &group->destinfo[hostidx];
if ((group->group_id != 0) &&
(dest->state != PR_CLIENT_READY)) {
if (abort_session) {
send_downstream_abort(group, dest->id,
"Client not fully registered at proxy", 0);
send_upstream_abort(group, dest->id,
"Client not fully registered at proxy");
}
found = 1;
}
}
return found;
}
/**
* Load a message body with the list of pending clients
*/
int load_pending(struct pr_group_list_t *group, int pendidx, int func,
uint32_t *addrlist, int listlen)
{
int hostidx, cnt;
struct pr_destinfo_t *dest;
for (cnt = 0, hostidx = 0;
(hostidx < group->destcount) && (cnt < listlen); hostidx++) {
dest = &group->destinfo[hostidx];
if (dest->pending == pendidx) {
addrlist[cnt++] = dest->id;
dest->pending = -1;
group->pending[pendidx].count--;
}
}
if (group->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->pending[pendidx].count = 0;
group->pending[pendidx].msg = 0;
}
return cnt;
}
/**
* Forward a message unmodified to the next hop, resigning if necessary.
*/
void forward_message(struct pr_group_list_t *group,
const union sockaddr_u *src,
unsigned char *packet, int packetlen)
{
struct uftp_h *header;
struct encrypted_h *encrypted;
struct announce_h *announce;
struct enc_info_he *encinfo;
union sockaddr_u dest;
unsigned int meslen, siglen;
int hostidx, rval, iplen, resign;
char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
uint8_t *sig, *sigcopy;
union key_t key;
header = (struct uftp_h *)packet;
meslen = (unsigned int)packetlen;
memset(&dest, 0, sizeof(dest));
if (!memcmp(src, &group->up_addr, sizeof(*src))) {
if (proxy_type == RESPONSE_PROXY) {
// Response proxy, no downstream fowarding
set_timeout(group, 0, 0);
return;
} else if (proxy_type == SERVER_PROXY) {
dest = down_addr;
} else {
if (header->func == ANNOUNCE) {
dest = group->publicmcast;
} else {
dest = group->privatemcast;
}
key = group->server_pubkey;
}
} else {
dest = group->up_addr;
if (proxy_type != SERVER_PROXY) {
hostidx = find_client(group, header->src_id);
if (hostidx == -1) {
log2(group->group_id, 0, "Couldn't find receiver in list");
return;
}
key = group->destinfo[hostidx].pubkey;
}
}
// If we're using KEYEX signatures, or sending an ANNOUNCE with ECDH,
// verify the signature and resign
resign = 0;
if ((proxy_type != SERVER_PROXY) && (header->func == ENCRYPTED) &&
(group->sigtype == SIG_KEYEX)) {
encrypted = (struct encrypted_h *)(packet + sizeof(struct uftp_h));
sig = (uint8_t *)encrypted + sizeof(struct encrypted_h);
siglen = ntohs(encrypted->sig_len);
resign = 1;
} else if ((proxy_type != SERVER_PROXY) && (header->func == ANNOUNCE) &&
((group->keyextype == KEYEX_ECDH_RSA) ||
(group->keyextype == KEYEX_ECDH_ECDSA))) {
announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
iplen = ((announce->flags & FLAG_IPV6) != 0) ? 16 : 4;
encinfo = (struct enc_info_he *) ((uint8_t *)announce +
sizeof(struct announce_h) + iplen + iplen);
sig = (uint8_t *)encinfo + sizeof(struct enc_info_he) +
ntohs(encinfo->keylen) + ntohs(encinfo->dhlen);
siglen = ntohs(encinfo->siglen);
resign = 1;
}
if (resign) {
sigcopy = safe_calloc(siglen, 1);
memcpy(sigcopy, sig, siglen);
memset(sig, 0, siglen);
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (header->func == ENCRYPTED) {
if (!verify_RSA_sig(key.rsa, group->hashtype, packet,
meslen, sigcopy, siglen)) {
log2(group->group_id, 0, "Signature verification failed");
free(sigcopy);
return;
}
}
if (!create_RSA_sig(group->proxy_privkey.rsa, group->hashtype,
packet, meslen, sigcopy, &siglen)) {
log2(group->group_id, 0, "Signature creation failed");
free(sigcopy);
return;
}
} else {
if (header->func == ENCRYPTED) {
if (!verify_ECDSA_sig(key.ec, group->hashtype, packet,
meslen, sigcopy, siglen)) {
log2(group->group_id, 0, "Signature verification failed");
free(sigcopy);
return;
}
}
if (!create_ECDSA_sig(group->proxy_privkey.ec, group->hashtype,
packet, meslen, sigcopy, &siglen)) {
log2(group->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,
family_len(dest)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error forwarding message");
if ((rval = getnameinfo((struct sockaddr *)&dest, family_len(dest),
destname, sizeof(destname), destport, sizeof(destport),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(group->group_id, 0, "Dest: %s:%s", destname, destport);
}
set_timeout(group, 0, 0);
}
/**
* Process an HB_REQ message
*/
void handle_hb_request(const union sockaddr_u *src,
unsigned char *packet, unsigned packetlen)
{
struct hb_req_h *hbreq;
unsigned char *keyblob, *sig;
union key_t key;
unsigned char fingerprint[HMAC_LEN];
unsigned int fplen, bloblen, siglen;
char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
int resp, rval;
hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h));
if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src),
destname, sizeof(destname), destport, sizeof(destport),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
if ((packetlen < sizeof(struct uftp_h) + (hbreq->hlen * 4)) ||
((hbreq->hlen * 4) < sizeof(struct hb_req_h))) {
log1(0, 0, "Rejecting HB_REQ from %s: invalid message size", destname);
return;
}
log2(0, 0, "Received HB_REQ from %s", destname);
if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) {
if (addr_equal(&down_addr, src)) {
resp = HB_AUTH_OK;
} else if (down_nonce != ntohl(hbreq->nonce)) {
resp = HB_AUTH_CHALLENGE;
} else {
keyblob = (unsigned char *)hbreq + sizeof(struct hb_req_h);
bloblen = ntohs(hbreq->bloblen);
sig = keyblob + bloblen;
siglen = ntohs(hbreq->siglen);
// First check key fingerprint, then check signature
if (keyblob[0] == KEYBLOB_RSA) {
if (!import_RSA_key(&key.rsa, keyblob, bloblen)) {
log2(0, 0, "Failed to import public key from HB_REQ");
resp = HB_AUTH_FAILED;
goto end;
}
hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
if (memcmp(down_fingerprint, fingerprint, fplen)) {
log2(0, 0, "Failed to verify HB_REQ fingerprint");
resp = HB_AUTH_FAILED;
goto end;
}
if (!verify_RSA_sig(key.rsa, HASH_SHA1,
(unsigned char *)&hbreq->nonce,
sizeof(hbreq->nonce), sig, siglen)) {
log2(0, 0, "Failed to verify HB_REQ signature");
resp = HB_AUTH_FAILED;
goto end;
}
} else {
if (!import_EC_key(&key.ec, keyblob, bloblen, 0)) {
log2(0, 0, "Failed to import public key from HB_REQ");
resp = HB_AUTH_FAILED;
goto end;
}
hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
if (memcmp(down_fingerprint, fingerprint, fplen)) {
log2(0, 0, "Failed to verify HB_REQ fingerprint");
resp = HB_AUTH_FAILED;
goto end;
}
if (!verify_ECDSA_sig(key.ec, HASH_SHA1,
(unsigned char *)&hbreq->nonce,
sizeof(hbreq->nonce), sig, siglen)) {
log2(0, 0, "Failed to verify HB_REQ signature");
resp = HB_AUTH_FAILED;
goto end;
}
}
down_addr = *src;
log2(0, 0, "Using %s:%s as downstream address:port",
destname, destport);
down_nonce = rand32();
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 union sockaddr_u *src,
const unsigned char *packet, unsigned packetlen)
{
const struct key_req_h *keyreq;
struct timeval current_timestamp;
char destname[INET6_ADDRSTRLEN];
int rval;
keyreq = (const struct key_req_h *)(packet + sizeof(struct uftp_h));
if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src),
destname, sizeof(destname), NULL, 0, NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
if ((packetlen < sizeof(struct uftp_h) + (keyreq->hlen * 4U)) ||
((keyreq->hlen * 4U) < sizeof(struct key_req_h))) {
log1(0, 0, "Rejecting KEY_REQ from %s: invalid message size", destname);
return;
}
log2(0, 0, "Received KEY_REQ from %s", destname);
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 union sockaddr_u *src, int response)
{
unsigned char *packet;
struct uftp_h *header;
struct hb_resp_h *hbresp;
char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
int meslen, rval;
packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_resp_h), 1);
header = (struct uftp_h *)packet;
hbresp = (struct hb_resp_h *)(packet + sizeof(struct uftp_h));
header->version = UFTP_VER_NUM;
header->func = HB_RESP;
header->src_id = uid;
hbresp->func = HB_RESP;
hbresp->hlen = sizeof(struct hb_resp_h) / 4;
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, (const struct sockaddr *)src,
family_len(*src)) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending HB_RESP");
} else {
if ((rval = getnameinfo((const struct sockaddr *)src,
family_len(*src), destname, sizeof(destname), destport,
sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Sent HB_RESP to %s:%s", destname, destport);
}
free(packet);
}
/**
* Sends a PROXY_KEY message to the first listed public multicast address.
*/
void send_proxy_key()
{
unsigned char *packet, *keyblob, *dhblob, *sig;
struct uftp_h *header;
struct proxy_key_h *proxykey;
uint32_t nonce;
unsigned int meslen, siglen;
uint16_t bloblen, dhlen;
char pubname[INET6_ADDRSTRLEN];
int rval;
packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h) +
(PUBKEY_LEN * 3) , 1);
header = (struct uftp_h *)packet;
proxykey = (struct proxy_key_h *)(packet + sizeof(struct uftp_h));
keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
header->version = UFTP_VER_NUM;
header->func = PROXY_KEY;
header->src_id = uid;
proxykey->func = PROXY_KEY;
nonce = htonl(rand32());
proxykey->nonce = nonce;
if (privkey_type[0] == KEYBLOB_RSA) {
if (!export_RSA_key(privkey[0].rsa, keyblob, &bloblen)) {
log2(0, 0, "Error exporting public key");
free(packet);
return;
}
} else {
if (!export_EC_key(privkey[0].ec, keyblob, &bloblen)) {
log2(0, 0, "Error exporting public key");
free(packet);
return;
}
}
dhblob = keyblob + bloblen;
if (dhkey.key) {
if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) {
log2(0, 0, "Error exporting public key");
free(packet);
return;
}
} else {
dhlen = 0;
}
sig = dhblob + dhlen;
if (privkey_type[0] == KEYBLOB_RSA) {
if (!create_RSA_sig(privkey[0].rsa, HASH_SHA1, (unsigned char *)&nonce,
sizeof(nonce), sig, &siglen)) {
log2(0, 0, "Error signing nonce");
free(packet);
return;
}
} else {
if (!create_ECDSA_sig(privkey[0].ec, HASH_SHA1, (unsigned char *)&nonce,
sizeof(nonce), sig, &siglen)) {
log2(0, 0, "Error signing nonce");
free(packet);
return;
}
}
proxykey->bloblen = htons(bloblen);
proxykey->dhlen = htons(dhlen);
proxykey->siglen = htons(siglen);
proxykey->hlen = (sizeof(struct proxy_key_h) + bloblen + dhlen + siglen)/4;
meslen = sizeof(struct uftp_h) + (proxykey->hlen * 4);
if (nb_sendto(listener, packet, meslen, 0,
(struct sockaddr *)&pub_multi[0],
family_len(pub_multi[0])) == SOCKET_ERROR) {
sockerror(0, 0, "Error sending PROXY_KEY");
} else {
if ((rval = getnameinfo((struct sockaddr *)&pub_multi[0],
family_len(pub_multi[0]), pubname, sizeof(pubname), NULL, 0,
NI_NUMERICHOST)) != 0) {
log1(0, 0, "getnameinfo failed: %s", gai_strerror(rval));
}
log2(0, 0, "Sent PROXY_KEY to %s", pubname);
}
free(packet);
}
/**
* Sends an ABORT message upstream to a server
*/
void send_upstream_abort(struct pr_group_list_t *group, uint32_t addr,
const char *message)
{
unsigned char *buf;
struct uftp_h *header;
struct abort_h *abort_hdr;
int payloadlen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, group);
header->seq = group->send_seq_up++;
header->src_id = uid;
abort_hdr->func = ABORT;
abort_hdr->hlen = sizeof(struct abort_h) / 4;
abort_hdr->flags = 0;
abort_hdr->host = addr;
strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1);
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->up_addr,
family_len(group->up_addr)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error sending ABORT");
}
if (addr == 0) {
group_cleanup(group);
}
free(buf);
}
/**
* Sends an ABORT message downstream to clients
*/
void send_downstream_abort(struct pr_group_list_t *group, uint32_t dest_id,
const char *message, int current)
{
unsigned char *buf;
struct uftp_h *header;
struct abort_h *abort_hdr;
int payloadlen;
buf = safe_calloc(MAXMTU, 1);
header = (struct uftp_h *)buf;
abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h));
set_uftp_header(header, ABORT, group);
header->seq = group->send_seq_down++;
header->src_id = uid;
abort_hdr->func = ABORT;
abort_hdr->hlen = sizeof(struct abort_h) / 4;
if ((dest_id == 0) && current) {
abort_hdr->flags |= FLAG_CURRENT_FILE;
}
abort_hdr->host = dest_id;
strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1);
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->privatemcast,
family_len(group->privatemcast)) == SOCKET_ERROR) {
sockerror(group->group_id, 0, "Error sending ABORT");
}
free(buf);
}
/**
* Handles an ABORT message from a client or server
* and forwards if necessary.
*/
void handle_abort(struct pr_group_list_t *group, const union sockaddr_u *src,
const unsigned char *message, unsigned meslen,
uint32_t src_id)
{
const struct abort_h *abort_hdr;
int upstream, hostidx, current;
abort_hdr = (const struct abort_h *)message;
upstream = (addr_equal(&group->up_addr, src));
if (meslen < (abort_hdr->hlen * 4U) ||
((abort_hdr->hlen * 4U) < sizeof(struct abort_h))) {
log2(group->group_id,0, "Rejecting ABORT from %s: invalid message size",
upstream ? "server" : "client");
}
if (upstream) {
if ((abort_hdr->host == 0) || abort_hdr->host == uid ) {
log2(group->group_id, 0,
"Transfer aborted by server: %s", abort_hdr->message);
current = ((abort_hdr->flags & FLAG_CURRENT_FILE) != 0);
if (proxy_type != RESPONSE_PROXY) {
send_downstream_abort(group, 0, abort_hdr->message, current);
}
if (!current) {
group_cleanup(group);
}
} else {
if (proxy_type != RESPONSE_PROXY) {
send_downstream_abort(group, abort_hdr->host,
abort_hdr->message, 0);
}
}
} else {
if ((hostidx = find_client(group, src_id)) != -1) {
log2(group->group_id, 0, "Transfer aborted by %s: %s",
group->destinfo[hostidx].name, abort_hdr->message);
} else {
log2(group->group_id, 0, "Transfer aborted by %08X: %s",
ntohl(src_id), abort_hdr->message);
}
send_upstream_abort(group, src_id, abort_hdr->message);
}
}
/**
* Verifies a server's or client's public key fingerprint
* Returns 1 on success, 0 on failure
*/
int verify_fingerprint(const struct fp_list_t *fplist, int listlen,
const unsigned char *keyblob, uint16_t bloblen,
struct pr_group_list_t *group, uint32_t id)
{
unsigned char fingerprint[HMAC_LEN];
unsigned int fplen;
int found, keyidx;
if (listlen == 0) {
return 1;
}
for (keyidx = 0, found = 0; (keyidx < listlen) && !found; keyidx++) {
if (fplist[keyidx].uid == id) {
keyidx--;
found = 1;
}
}
if (!found) {
return 0;
}
if (!fplist[keyidx].has_fingerprint) {
return 1;
}
hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
if (memcmp(fplist[keyidx].fingerprint, fingerprint, fplen)) {
return 0;
} else {
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(struct pr_group_list_t *group, int hostidx,
int *verifylen, int full)
{
uint8_t *verifydata, *keyblob;
uint32_t group_id;
struct pr_destinfo_t *dest;
union key_t key;
int iplen;
uint16_t bloblen;
iplen = (group->privatemcast.ss.ss_family == AF_INET6) ?
sizeof(struct in6_addr) : sizeof(struct in_addr);
if (hostidx != -1) {
dest = &group->destinfo[hostidx];
}
*verifylen = 0;
if (!full) {
verifydata = safe_calloc(sizeof(group->group_id) +
iplen + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster), 1);
} else {
verifydata = safe_calloc(sizeof(group->group_id) +
iplen + sizeof(group->rand1) +
sizeof(group->rand2) + sizeof(group->premaster) +
PUBKEY_LEN + sizeof(group->groupmaster), 1);
}
group_id = htonl(group->group_id);
memcpy(verifydata, &group_id, sizeof(group_id));
*verifylen += sizeof(group_id);
if (group->privatemcast.ss.ss_family == AF_INET6) {
memcpy(verifydata + *verifylen,
&group->privatemcast.sin6.sin6_addr.s6_addr, iplen);
} else {
memcpy(verifydata + *verifylen,
&group->privatemcast.sin.sin_addr.s_addr, iplen);
}
*verifylen += iplen;
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, group->premaster_len);
*verifylen += group->premaster_len;
} else {
memcpy(verifydata + *verifylen, dest->rand2, sizeof(dest->rand2));
*verifylen += sizeof(dest->rand2);
memcpy(verifydata + *verifylen, dest->premaster, dest->premaster_len);
*verifylen += dest->premaster_len;
}
if (full) {
if (group->client_auth) {
if (hostidx == -1) {
key = group->proxy_privkey;
} else {
key = dest->pubkey;
}
keyblob = verifydata + *verifylen;
if ((group->keyextype == KEYEX_RSA) ||
(group->keyextype == KEYEX_ECDH_RSA)) {
if (!export_RSA_key(key.rsa, keyblob, &bloblen)) {
free(verifydata);
return NULL;
}
} else {
if (!export_EC_key(key.ec, keyblob, &bloblen)) {
free(verifydata);
return NULL;
}
}
*verifylen += bloblen;
}
memcpy(verifydata + *verifylen, group->groupmaster,
sizeof(group->groupmaster));
*verifylen += sizeof(group->groupmaster);
}
return verifydata;
}
uftp-4.1.5/proxy_loop.h 0000644 0000764 0000764 00000002646 12250244021 014066 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _PROXY_LOOP_H
#define _PROXY_LOOP_H
void mainloop(void);
#endif // _PROXY_LOOP_H
uftp-4.1.5/client_announce.h 0000644 0000764 0000764 00000003571 12250244021 015016 0 ustar dbush dbush /*
* UFTP - UDP based FTP with multicast
*
* Copyright (C) 2001-2013 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.
*/
#ifndef _CLIENT_ANNOUNCE_H
#define _CLIENT_ANNOUNCE_H
void handle_announce(union sockaddr_u *src, unsigned char *packet,
unsigned packetlen, struct timeval rxtime);
void handle_keyinfo(struct group_list_t *group, unsigned char *message,
unsigned meslen, uint32_t src_id);
void handle_regconf(struct group_list_t *group, const unsigned char *message,
unsigned meslen);
void send_register(struct group_list_t *group);
void send_keyinfo_ack(struct group_list_t *group);
#endif // _CLIENT_ANNOUNCE_H
uftp-4.1.5/testclient_multi.c 0000644 0000764 0000764 00000071176 12137013343 015250 0 ustar dbush dbush #include
#include
#include
#include
#ifdef WINDOWS
#include
#include
#include "win_func.h"
#else
#include
#include
#include
#include
#include
#include
#endif
#include "uftp.h"
#include "uftp_common.h"
#include "encryption.h"
#define BLOCKSIZE 1500
SOCKET sock;
uint8_t groupmaster[MASTER_LEN];
uint8_t groupkey[MAXKEY], groupiv[MAXIV], hmackey[HMAC_LEN];
uint32_t myaddr;
int groupid, fileid, fsize, block_count, section_count;
int blocksize, payloadsize, encpayloadsize;
int hashtype, keytype, keylen, ivlen, hashlen;
RSA_key_t rsakey;
int phase, foundgroup;
int destcount;
uint8_t rand1[RAND_LEN];
struct destinfo_t {
struct in_addr addr;
uint8_t rand2[RAND_LEN];
uint8_t premaster[MASTER_LEN];
uint8_t premaster_enc[300];
int premaster_enc_len;
uint8_t master[MASTER_LEN];
uint8_t key[MAXKEY];
uint8_t iv[MAXIV];
uint8_t hmac[HMAC_LEN];
uint8_t verify[1000];
int verifylen;
int gotkeyinfo;
int gotfileinfo;
} *destlist;
void send_register(struct sockaddr_in sin)
{
unsigned char buf[BLOCKSIZE], seed[64], prf_buf[200];
struct uftp_h *header;
struct register_h *reg;
unsigned char *reg_premaster;
unsigned int explen;
int prf_len, i;
header = (struct uftp_h *)buf;
reg = (struct register_h *)(buf + sizeof(struct uftp_h));
reg_premaster = buf + sizeof(struct uftp_h) + sizeof(struct register_h);
for (i=0;iuftp_id = UFTP_VER_NUM;
header->func = REGISTER;
header->blsize = htons(sizeof(struct register_h) +
destlist[i].premaster_enc_len);
header->group_id = htonl(groupid);
header->srcaddr = destlist[i].addr.s_addr;
header->destaddr = sin.sin_addr.s_addr;
reg->func = REGISTER;
reg->destcount = 0;
memcpy(reg->rand2,destlist[i].rand2,sizeof(destlist[i].rand2));
memcpy(reg_premaster,
destlist[i].premaster_enc,sizeof(destlist[i].premaster_enc));
reg->premaster_len = htons(destlist[i].premaster_enc_len);
if (sendto(sock, buf, sizeof(struct uftp_h) + ntohs(header->blsize),
0, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sockerror(0,0,"sendto failed for REGISTER");
exit(1);
}
// calculate session keys
memcpy(seed,rand1,sizeof(rand1));
memcpy(seed+sizeof(rand1),
destlist[i].rand2,sizeof(destlist[i].rand2));
PRF(hashtype, MASTER_LEN,destlist[i].premaster,
sizeof(destlist[i].premaster), "master secret",
seed, sizeof(seed), prf_buf,&prf_len);
memcpy(destlist[i].master,prf_buf, sizeof(destlist[i].master));
//printhex("rand1",destlist[i].rand1,sizeof(destlist[i].rand1));
//printhex("rand2",destlist[i].rand2,sizeof(destlist[i].rand2));
//printhex("premaster",destlist[i].premaster,sizeof(destlist[i].premaster));
//printhex("master",destlist[i].master,sizeof(destlist[i].master));
explen = hashlen + keylen + ivlen;
PRF(hashtype, explen, destlist[i].master, sizeof(destlist[i].master),
"key expansion", seed, sizeof(seed), prf_buf,&prf_len);
memcpy(destlist[i].hmac, prf_buf, hashlen);
memcpy(destlist[i].key, prf_buf + hashlen, keylen);
memcpy(destlist[i].iv, prf_buf + hashlen + keylen, ivlen);
//printhex("local hmac", destlist[i].hmac, hashlen);
//printhex("local key", destlist[i].key, keylen);
//printhex("local iv", destlist[i].iv, ivlen);
}
}
void handle_announce(unsigned char *buf, struct sockaddr_in sin)
{
struct uftp_h *header;
struct announce_h *announce;
unsigned char *keymod;
uint32_t keyexp, n_groupid;
uint16_t modlen;
struct ip_mreq multi;
int i;
header = (struct uftp_h *)buf;
announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
keymod = buf + sizeof(struct uftp_h) + sizeof(struct announce_h);
fprintf(stderr,"Received ANNOUNCE from %s\n", inet_ntoa(sin.sin_addr));
memcpy(rand1,announce->rand1,sizeof(announce->rand1));
keyexp = ntohl(announce->keyexp);
modlen = ntohs(announce->keylen);
if (!import_RSA_key(&rsakey, keyexp, keymod, modlen)) {
fprintf(stderr, "Failed to import public key");
exit(0);
}
groupid = ntohl(header->group_id);
keytype = announce->keytype;
hashtype = announce->hashtype;
get_key_info(keytype, &keylen, &ivlen);
hashlen = get_hash_len(hashtype);
payloadsize = ntohs(announce->mtu) - 28 - sizeof(struct uftp_h);
encpayloadsize = payloadsize - sizeof(struct encrypted_h) - 16 - hashlen;
blocksize = encpayloadsize - sizeof(struct fileseg_h);
fprintf(stderr,"bsize=%d, epsize=%d, psize=%d\n",
blocksize, encpayloadsize, payloadsize);
multi.imr_multiaddr.s_addr=announce->privatemcast;
multi.imr_interface.s_addr=htonl(INADDR_ANY);
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&multi,sizeof(multi))==-1) {
perror("Error joining multicast group");
closesocket(sock);
exit(1);
}
// Assume open group membership, and respond for all
// Assume encryption enabled with HMAC signatures
for (i=0;iprivatemcast,
sizeof(announce->privatemcast));
destlist[i].verifylen += sizeof(announce->privatemcast);
memcpy(destlist[i].verify + destlist[i].verifylen,rand1,sizeof(rand1));
destlist[i].verifylen += sizeof(rand1);
memcpy(destlist[i].verify + destlist[i].verifylen, destlist[i].rand2,
sizeof(destlist[i].rand2));
destlist[i].verifylen += sizeof(destlist[i].rand2);
memcpy(destlist[i].verify + destlist[i].verifylen,destlist[i].premaster,
sizeof(destlist[i].premaster));
destlist[i].verifylen += sizeof(destlist[i].premaster);
if (!RSA_encrypt(rsakey, destlist[i].premaster,
sizeof(destlist[i].premaster), destlist[i].premaster_enc,
&destlist[i].premaster_enc_len)) {
fprintf(stderr, "couldn't encrypt");
exit(1);
}
}
foundgroup = 0;
phase = KEYINFO;
send_register(sin);
}
void send_infoack(struct sockaddr_in sin, int dest, int request)
{
unsigned char buf[BLOCKSIZE], data[BLOCKSIZE];
unsigned char iv[MAXIV], prf_buf[200], hmac[HMAC_LEN];
struct uftp_h *header;
struct encrypted_h *encrypted;
struct infoack_h *infoack;
unsigned char *enc_sig, *enc_payload;
int prf_len;
unsigned int len;
struct timeval tv;
header = (struct uftp_h *)buf;
encrypted=(struct encrypted_h *)(buf + sizeof(struct uftp_h));
enc_sig=(unsigned char *)encrypted + sizeof(struct encrypted_h);
enc_payload=enc_sig + hashlen;
infoack = (struct infoack_h *)data;
memset(buf, 0, sizeof(buf));
memset(data, 0, sizeof(data));
gettimeofday(&tv, NULL);
infoack->func = INFO_ACK;
infoack->destcount = 0;
if (request == KEYINFO) {
infoack->file_id = 0;
hash(hashtype, destlist[dest].verify, destlist[dest].verifylen,
hmac, &len);
PRF(hashtype, VERIFY_LEN,groupmaster, sizeof(groupmaster),
"client finished", hmac, len, prf_buf, &prf_len);
memcpy(infoack->verify_data,prf_buf,sizeof(infoack->verify_data));
} else if (request == FILEINFO) {
infoack->file_id = ntohs(fileid);
} else {
fprintf(stderr, "wrong request for INFO_ACK: %s\n",
func_name(request));
exit(1);
}
//printhex("encrypted INFO_ACK", infoack, sizeof(struct infoack_h));
build_iv(iv, groupiv, ivlen, htonl(groupid),
destlist[dest].addr.s_addr, htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(keytype, iv, groupkey, data,
sizeof(struct infoack_h), enc_payload, &len)) {
fprintf(stderr, "encrypt failed for INFO_ACK");
exit(1);
}
header->uftp_id = UFTP_VER_NUM;
header->func = ENCRYPTED;
header->blsize = htons(sizeof(struct encrypted_h) + hashlen + len);
header->group_id = htonl(groupid);
header->srcaddr = destlist[dest].addr.s_addr;
header->destaddr = sin.sin_addr.s_addr;
encrypted->tstamp_sec = htonl(tv.tv_sec);
encrypted->tstamp_usec = htonl(tv.tv_usec);
encrypted->sig_len = htons(hashlen);
encrypted->payload_len = htons(len);
create_hmac(hashtype, hmackey, hashlen, buf,
sizeof(struct uftp_h) + ntohs(header->blsize), hmac, &len);
if (len!=hashlen) {
fprintf(stderr,"invalid hmac len: %d\n",len);
exit(1);
}
memcpy(enc_sig, hmac, len);
if (sendto(sock, buf, sizeof(struct uftp_h) + ntohs(header->blsize),
0, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sockerror(0,0,"sendto failed for INFO_ACK");
exit(1);
}
}
void handle_keyinfo(unsigned char *buf, struct sockaddr_in sin)
{
unsigned char dec_groupmaster[MASTER_LEN], prf_buf[200], iv[MAXIV];
struct uftp_h *header;
struct keyinfo_h *keyinfo;
struct destkey *dkey;
int i, j, found;
unsigned int len, explen;
int prf_len, gotall;
header = (struct uftp_h *)buf;
keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
dkey = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));
fprintf(stderr,"Received KEYINFO from %s\n", inet_ntoa(sin.sin_addr));
for (j=0;jdestcount;j++) {
for (i=0,found=0;(igroupmaster_len));
/* decrypt group key */
build_iv(iv, destlist[i].iv, ivlen, htonl(groupid), header->srcaddr,
keyinfo->tstamp_sec, keyinfo->tstamp_usec);
if (!decrypt_block(keytype, iv, destlist[i].key, dkey[j].groupmaster,
keyinfo->groupmaster_len, dec_groupmaster, &len)) {
fprintf(stderr, "decrypt failed for group master");
exit(1);
}
if (!foundgroup) {
groupmaster[0]=header->uftp_id;
memcpy(groupmaster+1,dec_groupmaster,len);
printhex("group master",groupmaster, sizeof(groupmaster));
foundgroup=1;
explen = hashlen + keylen + ivlen;
PRF(hashtype, explen, groupmaster, sizeof(groupmaster),
"key expansion", rand1, sizeof(rand1), prf_buf, &prf_len);
memcpy(hmackey, prf_buf, hashlen);
memcpy(groupkey, prf_buf + hashlen, keylen);
memcpy(groupiv, prf_buf + hashlen + keylen, ivlen);
printhex("hmackey",hmackey,hashlen);
printhex("groupkey",groupkey,keylen);
printhex("groupiv",groupiv,ivlen);
} else {
if (memcmp(groupmaster+1, dec_groupmaster, len)) {
fprintf(stderr, "decrypted group master "
"doesn't match prior value");
exit(1);
}
}
if (!destlist[i].gotkeyinfo) {
memcpy(destlist[i].verify+destlist[i].verifylen,
groupmaster, sizeof(groupmaster));
destlist[i].verifylen+=sizeof(groupmaster);
destlist[i].gotkeyinfo=1;
}
send_infoack(sin, i, KEYINFO);
}
for (i=0,gotall=1;(ifile_id);
// Assuming file is < 4GB
fsize = ntohl(fileinfo->lofsize);
block_count = ntohl(fileinfo->block_total);
section_count = ntohs(fileinfo->section_total);
for (j=0;jdestcount);j++) {
for (i=0,found=0;(iuftp_id!=UFTP_VER_NUM) {
fprintf(stderr,"Invalid version number\n");
exit(1);
}
if (recv_lenblsize)) {
fprintf(stderr,"Invalid packet size: %d\n", recv_len);
exit(1);
}
if (header->func==ENCRYPTED) {
encrypted=(struct encrypted_h *)(buf + sizeof(struct uftp_h));
enc_sig=(unsigned char *)encrypted + sizeof(struct encrypted_h);
enc_payload=enc_sig + hashlen;
if (phase != FILEINFO) {
fprintf(stderr, "not expecting encrypted message\n");
exit(1);
}
// verify HMAC (assume sig is not RSA for now)
memcpy(hmacsav,enc_sig,ntohs(encrypted->sig_len));
memset(enc_sig, 0, ntohs(encrypted->sig_len));
create_hmac(hashtype, hmackey, hashlen, buf, recv_len, hmac, &len);
if (len!=hashlen) {
fprintf(stderr,"invalid hmac len: %d\n",len);
exit(1);
}
if (!memcmp(hmacsav,hmac,hashlen)) {
fprintf(stderr,"hmac matches!\n");
} else {
fprintf(stderr,"hmac mismatch\n");
printhex("calculated value",hmac,len);
printhex("expected value",hmacsav,sizeof(hmacsav));
exit(1);
}
// decrypt message
build_iv(iv, groupiv, ivlen, htonl(groupid), header->srcaddr,
encrypted->tstamp_sec, encrypted->tstamp_usec);
if (!decrypt_block(keytype, iv, groupkey, enc_payload,
ntohs(encrypted->payload_len), data, &len)) {
fprintf(stderr, "decrypt failed\n");
exit(1);
}
func = (char *)data;
if (*func != FILEINFO) {
fprintf(stderr, "Decrypted message not FILEINFO\n");
exit(1);
}
} else {
func = (char *)buf + sizeof(struct uftp_h);
}
switch (*func) {
case ANNOUNCE:
if (phase == ANNOUNCE) {
handle_announce(buf, sin);
}
break;
case KEYINFO:
if (phase == KEYINFO || phase == FILEINFO) {
handle_keyinfo(buf, sin);
}
break;
case FILEINFO:
if (phase == FILEINFO) {
handle_fileinfo(data, sin);
}
break;
default:
fprintf(stderr,"Invalid function: %d\n",header->func);
exit(1);
}
} while (phase != FILESEG);
}
void send_status(struct sockaddr_in sin, int dest, int last_file_id,
int pass, int section, unsigned char *naks, int nak_count)
{
unsigned char buf[BLOCKSIZE], data[BLOCKSIZE];
unsigned char iv[MAXIV], hmac[HMAC_LEN];
struct uftp_h *header;
struct encrypted_h *encrypted;
struct status_h *status;
unsigned char *enc_sig, *enc_payload, *naklist;
unsigned int len, dlen;
struct timeval tv;
header = (struct uftp_h *)buf;
encrypted=(struct encrypted_h *)(buf + sizeof(struct uftp_h));
enc_sig=(unsigned char *)encrypted + sizeof(struct encrypted_h);
enc_payload=enc_sig + hashlen;
status = (struct status_h *)data;
naklist = data + sizeof(struct status_h);
memset(buf, 0, sizeof(buf));
memset(data, 0, sizeof(data));
gettimeofday(&tv, NULL);
status->func = STATUS;
status->file_id = ntohs(last_file_id);
status->pass = pass;
status->section = ntohs(section);
status->nak_count = ntohl(nak_count);
if (nak_count) {
memcpy(naklist, naks, blocksize);
dlen = sizeof(struct status_h) + blocksize;
} else {
dlen = sizeof(struct status_h);
}
build_iv(iv, groupiv, ivlen, htonl(groupid),
destlist[dest].addr.s_addr, htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(keytype, iv, groupkey, data, dlen, enc_payload, &len)) {
fprintf(stderr, "encrypt failed for STATUS");
exit(1);
}
header->uftp_id = UFTP_VER_NUM;
header->func = ENCRYPTED;
header->blsize = htons(sizeof(struct encrypted_h) + hashlen + len);
header->group_id = htonl(groupid);
header->srcaddr = destlist[dest].addr.s_addr;
header->destaddr = sin.sin_addr.s_addr;
encrypted->tstamp_sec = htonl(tv.tv_sec);
encrypted->tstamp_usec = htonl(tv.tv_usec);
encrypted->sig_len = htons(hashlen);
encrypted->payload_len = htons(len);
create_hmac(hashtype, hmackey, hashlen, buf,
sizeof(struct uftp_h) + ntohs(header->blsize), hmac, &len);
if (len!=hashlen) {
fprintf(stderr,"invalid hmac len: %d\n",len);
exit(1);
}
memcpy(enc_sig, hmac, len);
if (sendto(sock, buf, sizeof(struct uftp_h) + ntohs(header->blsize),
0, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sockerror(0,0,"sendto failed for STATUS");
exit(1);
}
}
void send_complete(struct sockaddr_in sin, int dest, int last_file_id)
{
unsigned char buf[BLOCKSIZE], data[BLOCKSIZE];
unsigned char iv[MAXIV], hmac[HMAC_LEN];
struct uftp_h *header;
struct encrypted_h *encrypted;
struct complete_h *complete;
unsigned char *enc_sig, *enc_payload;
unsigned int len;
struct timeval tv;
header = (struct uftp_h *)buf;
encrypted=(struct encrypted_h *)(buf + sizeof(struct uftp_h));
enc_sig=(unsigned char *)encrypted + sizeof(struct encrypted_h);
enc_payload=enc_sig + hashlen;
complete = (struct complete_h *)data;
memset(buf, 0, sizeof(buf));
memset(data, 0, sizeof(data));
gettimeofday(&tv, NULL);
complete->func = COMPLETE;
complete->file_id = ntohs(last_file_id);
build_iv(iv, groupiv, ivlen, htonl(groupid),
destlist[dest].addr.s_addr, htonl(tv.tv_sec), htonl(tv.tv_usec));
if (!encrypt_block(keytype, iv, groupkey, data,
sizeof(struct complete_h), enc_payload, &len)) {
fprintf(stderr, "encrypt failed for COMPLETE");
exit(1);
}
header->uftp_id = UFTP_VER_NUM;
header->func = ENCRYPTED;
header->blsize = htons(sizeof(struct encrypted_h) + hashlen + len);
header->group_id = htonl(groupid);
header->srcaddr = destlist[dest].addr.s_addr;
header->destaddr = sin.sin_addr.s_addr;
encrypted->tstamp_sec = htonl(tv.tv_sec);
encrypted->tstamp_usec = htonl(tv.tv_usec);
encrypted->sig_len = htons(hashlen);
encrypted->payload_len = htons(len);
create_hmac(hashtype, hmackey, hashlen, buf,
sizeof(struct uftp_h) + ntohs(header->blsize), hmac, &len);
if (len!=hashlen) {
fprintf(stderr,"invalid hmac len: %d\n",len);
exit(1);
}
memcpy(enc_sig, hmac, len);
if (sendto(sock, buf, sizeof(struct uftp_h) + ntohs(header->blsize),
0, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
sockerror(0,0,"sendto failed for COMPLETE");
exit(1);
}
}
void recv_data()
{
uint8_t buf[BLOCKSIZE], hmac[HMAC_LEN], hmacsav[HMAC_LEN], data[BLOCKSIZE];
struct uftp_h *header;
struct encrypted_h *encrypted;
struct fileseg_h *fileseg;
struct done_h *done;
struct doneconf_h *doneconf;
unsigned char *enc_sig, *enc_payload;
unsigned char *decdata, iv[MAXIV], naklist[BLOCKSIZE];
int i, j, found, cnt, addr_len, recv_len;
unsigned int hlen, decodelen;
struct sockaddr_in sin;
struct timeval t_start, t_end;
double t_diff;
char *naks, *func;
uint32_t *addrlist;
int section_offset, blocks_this_sec, nakidx, naklistidx;
header = (struct uftp_h *)buf;
encrypted = (struct encrypted_h *)(buf + sizeof(struct uftp_h));
enc_sig = (unsigned char *)encrypted + sizeof(struct encrypted_h);
enc_payload = enc_sig + hashlen;
func = (char *)data;
fileseg = (struct fileseg_h *)data;
done = (struct done_h *)data;
doneconf = (struct doneconf_h *)data;
decdata = data + sizeof (struct fileseg_h);
naks = malloc(block_count);
for (i=0;iuftp_id!=UFTP_VER_NUM) {
fprintf(stderr,"Invalid version number\n");
exit(1);
}
if (recv_len!=sizeof(struct uftp_h)+htons(header->blsize)) {
fprintf(stderr,"Invalid packet size: %d\n", recv_len);
exit(1);
}
if (header->func!=ENCRYPTED) {
fprintf(stderr,"Invalid function: %d\n",header->func);
exit(1);
}
// verify HMAC (assume sig is not RSA for now)
memcpy(hmacsav,enc_sig,ntohs(encrypted->sig_len));
memset(enc_sig, 0, ntohs(encrypted->sig_len));
create_hmac(hashtype, hmackey, hashlen, buf, recv_len, hmac, &hlen);
if (hlen!=hashlen) {
fprintf(stderr,"invalid hmac len: %d\n",hlen);
exit(1);
}
if (memcmp(hmacsav,hmac,hashlen)) {
fprintf(stderr,"hmac mismatch\n");
printhex("calculated value",hmac,hlen);
printhex("expected value",hmacsav,sizeof(hmacsav));
exit(1);
}
/* decrypt pakcet */
build_iv(iv, groupiv, ivlen, htonl(groupid), header->srcaddr,
encrypted->tstamp_sec, encrypted->tstamp_usec);
if (!decrypt_block(keytype, iv, groupkey, enc_payload,
ntohs(encrypted->payload_len), data, &decodelen)) {
fprintf(stderr, "decrypt failed\n");
exit(1);
}
if (*func == FILESEG) {
int seq = ntohl(fileseg->seq_num);
if (seq > block_count) {
fprintf(stderr,"invalid seq_num: %d\n", seq);
} else {
naks[seq] = 0;
}
} else if (*func == DONE) {
int pass, section, last_file_id;
pass = done->pass;
section = ntohs(done->section);
last_file_id = ntohs(done->file_id);
addrlist = (uint32_t *)((char *)done + sizeof(struct done_h));
//////
section_offset = (blocksize * 8) * (section - 1);
blocks_this_sec = ((section < section_count) ?
(blocksize * 8) : (block_count % (blocksize * 8)));
if (section_count && !blocks_this_sec) {
blocks_this_sec = blocksize * 8;
}
memset(naklist, 0, sizeof(naklist));
for (i = 0; i < blocks_this_sec; i++) {
nakidx = i + section_offset;
naklistidx = i;
if (naks[nakidx]) {
log(0, 0, "NAK for %d", nakidx);
naklist[naklistidx >> 3] |= (1 << (naklistidx & 7));
}
}
//////
for (j=0;jdestcount);j++) {
for (i=0,found=0;(i