/*-
* Copyright (c) 2000-2005 MAEKAWA Masahide <maekawa@cvsync.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include "compat_stdbool.h"
#include "compat_stdint.h"
#include "compat_inttypes.h"
#include "compat_limits.h"
#include "basedef.h"
#include "attribute.h"
#include "collection.h"
#include "cvsync.h"
#include "hash.h"
#include "logmsg.h"
#include "mux.h"
#include "network.h"
#include "version.h"
#include "defs.h"
bool collection_exchange_list(int, struct collection *);
bool collection_exchange_rcs(int, struct collection *);
bool
protocol_exchange(int sock, struct config *cf)
{
uint8_t cmd[CVSYNC_MAXCMDLEN], mn;
size_t len;
SetWord(cmd, 2);
cmd[2] = CVSYNC_PROTO_MAJOR;
cmd[3] = CVSYNC_PROTO_MINOR;
if (!sock_send(sock, cmd, 4)) {
logmsg_err("Send: protocol version");
return (false);
}
if (!sock_recv(sock, cmd, 2)) {
logmsg_err("Recv: protocol version length");
return (false);
}
if ((len = GetWord(cmd)) != 2) {
logmsg_err("Recv: protocol version: invalid length: %u", len);
return (false);
}
if (!sock_recv(sock, cmd, len)) {
logmsg_err("Recv: protocol version");
return (false);
}
if (cmd[0] == CVSYNC_PROTO_ERROR) {
switch (cmd[1]) {
case CVSYNC_ERROR_DENIED:
logmsg_err("Access denied");
break;
case CVSYNC_ERROR_LIMITED:
logmsg_err("Access limited");
break;
case CVSYNC_ERROR_UNAVAIL:
logmsg_err("Service unavailable");
break;
case CVSYNC_ERROR_UNSPEC:
default:
logmsg_err("Your software seems to be old.\n"
"Please upgrade to the newer version.\n"
"URL: %s", CVSYNC_URL);
break;
}
SetWord(cmd, 2);
cmd[2] = CVSYNC_PROTO_ERROR;
cmd[3] = CVSYNC_ERROR_UNSPEC;
sock_send(sock, cmd, 4);
return (false);
}
if (cmd[0] != CVSYNC_PROTO_MAJOR) {
logmsg_err("The server(%u.%u) seems to be too old.", cmd[0],
cmd[1]);
SetWord(cmd, 2);
cmd[2] = CVSYNC_PROTO_ERROR;
cmd[3] = CVSYNC_ERROR_UNSPEC;
sock_send(sock, cmd, 4);
return (false);
}
if ((cmd[0] == 0) && (cmd[1] < 20)) {
logmsg_err("The server (%u.%u) seems to be old.", cmd[0],
cmd[1]);
SetWord(cmd, 2);
cmd[2] = CVSYNC_PROTO_ERROR;
cmd[3] = CVSYNC_ERROR_UNSPEC;
sock_send(sock, cmd, 4);
return (false);
}
if ((cmd[0] == 0) && (cmd[1] < CVSYNC_PROTO_MINOR))
mn = cmd[1];
else
mn = CVSYNC_PROTO_MINOR;
SetWord(cmd, 2);
cmd[2] = CVSYNC_PROTO_MAJOR;
cmd[3] = mn;
if (!sock_send(sock, cmd, 4)) {
logmsg_err("Send: protocol version: %u.%u", CVSYNC_PROTO_MAJOR,
mn);
return (false);
}
cf->cf_proto = CVSYNC_PROTO(CVSYNC_PROTO_MAJOR, mn);
logmsg_verbose("Protocol: %u.%u", cmd[2], cmd[3]);
return (true);
}
bool
hash_exchange(int sock, struct config *cf)
{
uint8_t cmd[CVSYNC_MAXCMDLEN];
char name[CVSYNC_NAME_MAX + 1];
size_t len;
if ((len = hash_ntop(cf->cf_hash, name, sizeof(name))) == 0)
return (false);
SetWord(cmd, len);
if (!sock_send(sock, cmd, 2)) {
logmsg_err("Send: hash type");
return (false);
}
if (!sock_send(sock, name, len)) {
logmsg_err("Send: hash type");
return (false);
}
if (!sock_recv(sock, cmd, 2)) {
logmsg_err("Recv: hash type length");
return (false);
}
len = GetWord(cmd);
if ((len == 0) || (len >= sizeof(name))) {
logmsg_err("Recv: hash type: invaild length: %u", len);
return (false);
}
if (!sock_recv(sock, name, len)) {
logmsg_err("Recv: hash type");
return (false);
}
if ((cf->cf_hash = hash_pton(name, len)) == HASH_UNSPEC) {
logmsg_err("Unknwon hash type: %.*s", len, name);
return (false);
}
logmsg_verbose("Hash: %.*s", len, name);
return (true);
}
bool
collectionlist_exchange(int sock, struct config *cf)
{
struct collection *cl;
uint8_t cmd[CVSYNC_MAXCMDLEN];
size_t len;
int enabled;
logmsg_verbose("Exchanging collection list...");
enabled = 0;
for (cl = cf->cf_collections ; cl != NULL ; cl = cl->cl_next) {
switch (cvsync_release_pton(cl->cl_release)) {
case CVSYNC_RELEASE_LIST:
if (!collection_exchange_list(sock, cl))
return (false);
break;
case CVSYNC_RELEASE_RCS:
if (!collection_exchange_rcs(sock, cl))
return (false);
break;
default:
return (false);
}
if (!(cl->cl_flags & CLFLAGS_DISABLE))
enabled++;
}
SetWord(cmd, 4);
cmd[2] = 1;
cmd[3] = 1;
cmd[4] = '.';
cmd[5] = '.';
if (!sock_send(sock, cmd, 6))
return (false);
if (!sock_recv(sock, cmd, 2))
return (false);
if ((len = GetWord(cmd)) != 4)
return (false);
if (!sock_recv(sock, cmd, len))
return (false);
if ((cmd[0] != 1) || (cmd[1] != 1) ||
(cmd[2] != '.') || (cmd[3] != '.')) {
return (false);
}
if (!collection_resolv_prefix(cf->cf_collections))
return (false);
if (enabled == 0) {
logmsg_err("No collections is available");
return (false);
}
return (true);
}
bool
collection_exchange_list(int sock, struct collection *cl)
{
uint8_t cmd[CVSYNC_MAXCMDLEN];
size_t namelen, relnamelen, len;
if ((namelen = strlen(cl->cl_name)) >= sizeof(cl->cl_name))
return (false);
if ((relnamelen = strlen(cl->cl_release)) >= sizeof(cl->cl_release))
return (false);
if ((len = namelen + relnamelen + 4) >= sizeof(cmd))
return (false);
SetWord(cmd, len - 2);
cmd[2] = namelen;
cmd[3] = relnamelen;
if (!sock_send(sock, cmd, 4))
return (false);
if (!sock_send(sock, cl->cl_name, namelen))
return (false);
if (!sock_send(sock, cl->cl_release, relnamelen))
return (false);
if (!sock_recv(sock, cmd, 2))
return (false);
if ((len = GetWord(cmd)) > sizeof(cmd) - 2)
return (false);
if (len == 0) {
logmsg("Not found such a collection %s/%s", cl->cl_name,
cl->cl_release);
cl->cl_flags |= CLFLAGS_DISABLE;
return (true);
}
if (len != namelen + relnamelen + 2)
return (false);
if (!sock_recv(sock, cmd, len))
return (false);
if ((cmd[0] != namelen) || (cmd[1] != relnamelen)) {
logmsg_err("Not match name/release length");
return (false);
}
if ((memcmp(&cmd[2], cl->cl_name, namelen) != 0) ||
(memcmp(&cmd[namelen + 2], cl->cl_release, relnamelen) != 0)) {
logmsg_err("Not match collection name/release");
return (false);
}
logmsg_verbose(" collection name \"%s\" release \"%s\"",
cl->cl_name, cl->cl_release);
return (true);
}
bool
collection_exchange_rcs(int sock, struct collection *cl)
{
uint8_t cmd[CVSYNC_MAXCMDLEN];
size_t namelen, relnamelen, len;
if ((namelen = strlen(cl->cl_name)) >= sizeof(cl->cl_name))
return (false);
if ((relnamelen = strlen(cl->cl_release)) >= sizeof(cl->cl_release))
return (false);
if ((len = namelen + relnamelen + 6) >= sizeof(cmd))
return (false);
SetWord(cmd, len - 2);
cmd[2] = namelen;
cmd[3] = relnamelen;
if (!sock_send(sock, cmd, 4))
return (false);
if (!sock_send(sock, cl->cl_name, namelen))
return (false);
if (!sock_send(sock, cl->cl_release, relnamelen))
return (false);
SetWord(cmd, cl->cl_umask);
if (!sock_send(sock, cmd, 2))
return (false);
if (!sock_recv(sock, cmd, 2))
return (false);
if ((len = GetWord(cmd)) > sizeof(cmd) - 2)
return (false);
if (len == 0) {
logmsg("Not found such a collection %s/%s", cl->cl_name,
cl->cl_release);
cl->cl_flags |= CLFLAGS_DISABLE;
return (true);
}
if (len < namelen + relnamelen + 4)
return (false);
if (!sock_recv(sock, cmd, len))
return (false);
if ((cmd[0] != namelen) || (cmd[1] != relnamelen)) {
logmsg_err("Not match name/release length");
return (false);
}
if ((memcmp(&cmd[2], cl->cl_name, namelen) != 0) ||
(memcmp(&cmd[namelen + 2], cl->cl_release, relnamelen) != 0)) {
logmsg_err("Not match collection name/release");
return (false);
}
cl->cl_umask = GetWord(&cmd[namelen + relnamelen + 2]);
if (cl->cl_umask & ~CVSYNC_ALLPERMS)
return (false);
cl->cl_rprefixlen = len - namelen - relnamelen - 4;
if (cl->cl_rprefixlen > sizeof(cl->cl_rprefix))
return (false);
(void)memcpy(cl->cl_rprefix, &cmd[namelen + relnamelen + 4],
cl->cl_rprefixlen);
cl->cl_rprefix[cl->cl_rprefixlen] = '/';
logmsg_verbose(" collection name \"%s\" release \"%s\" umask %03o",
cl->cl_name, cl->cl_release, cl->cl_umask);
return (true);
}
bool
compress_exchange(int sock, struct config *cf)
{
const char *name;
uint8_t cmd[CVSYNC_MAXCMDLEN];
size_t len;
if (cf->cf_proto < CVSYNC_PROTO(0, 22)) {
cf->cf_compress = CVSYNC_COMPRESS_NO;
cf->cf_mss = MUX_MAX_MSS;
return (true);
}
if (cf->cf_proto == CVSYNC_PROTO(0, 22))
cf->cf_compress = CVSYNC_COMPRESS_NO;
name = cvsync_compress_ntop(cf->cf_compress);
len = strlen(name);
SetWord(cmd, len);
if (!sock_send(sock, cmd, 2))
return (false);
if (!sock_send(sock, name, len))
return (false);
if (!sock_recv(sock, cmd, 2))
return (false);
len = GetWord(cmd);
if ((len == 0) || (len >= sizeof(cmd)))
return (false);
if (!sock_recv(sock, cmd, len))
return (false);
cmd[len] = '\0';
cf->cf_compress = cvsync_compress_pton((const char *)cmd);
if (cf->cf_compress == CVSYNC_COMPRESS_UNSPEC) {
logmsg_err("Unsupported compression type: %s", name);
return (false);
}
name = cvsync_compress_ntop(cf->cf_compress);
len = strlen(name);
SetWord(cmd, len);
if (!sock_send(sock, cmd, 2))
return (false);
if (!sock_send(sock, name, len))
return (false);
if ((cf->cf_proto > CVSYNC_PROTO(0, 22)) &&
(cf->cf_compress != CVSYNC_COMPRESS_NO)) {
cf->cf_mss = MUX_MAX_MSS_ZLIB;
} else {
cf->cf_mss = MUX_DEFAULT_MSS;
}
logmsg_verbose("Compression: %s", name);
return (true);
}
struct mux *
channel_establish(int sock, struct config *cf)
{
struct mux *mx;
struct muxbuf *mxb;
uint8_t cmd[CVSYNC_MAXCMDLEN];
size_t len;
int i;
logmsg_verbose("Trying to establish the multiplexed channel...");
if ((mx = mux_init(sock, cf->cf_mss, cf->cf_compress,
CVSYNC_COMPRESS_LEVEL_BEST)) == NULL) {
return (NULL);
}
for (i = 0 ; i < MUX_MAXCHANNELS ; i++) {
mxb = &mx->mx_buffer[MUX_IN][i];
SetWord(cmd, 7);
cmd[2] = i;
SetWord(&cmd[3], mxb->mxb_mss);
SetDWord(&cmd[5], mxb->mxb_bufsize);
if (!sock_send(sock, cmd, 9)) {
mux_destroy(mx);
return (NULL);
}
if (!sock_recv(sock, cmd, 2)) {
mux_destroy(mx);
return (NULL);
}
if ((len = GetWord(cmd)) != 7) {
mux_destroy(mx);
return (NULL);
}
if (!sock_recv(sock, cmd, len)) {
mux_destroy(mx);
return (NULL);
}
if (cmd[0] != i) {
mux_destroy(mx);
return (NULL);
}
mxb = &mx->mx_buffer[MUX_OUT][i];
if (!muxbuf_init(mxb, GetWord(&cmd[1]), GetDWord(&cmd[3]),
cf->cf_compress)) {
mux_destroy(mx);
return (NULL);
}
}
return (mx);
}
syntax highlighted by Code2HTML, v. 0.9.1