mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-13 05:38:57 -04:00
* Add IDN support rsync can now connect to IDN (internationalized domain name) hosts, and IDN names are recognized in a daemon's hosts allow/deny. * idn: convert host names label by label The IDNA mapping folds some non-ASCII characters onto ASCII ones, so running a whole hosts allow/deny token through idn2_to_ascii_8z() could hand back a pattern the admin never wrote: a "*" (U+FF0A FULLWIDTH ASTERISK) entry came back as "*" and let every host in. Convert label by label instead, keeping an ASCII label byte for byte and using a converted label only when it comes back as a bare A-label. An ASCII-only config now behaves as it did before there was IDN support, and a token that cannot be converted is left alone and so matches nothing. The client side shares the same helper, and neither side truncates a name at its 1024-byte buffer any more. strlower() folds only ASCII now, since its one caller is the hosts allow/deny list, which can hold UTF-8. Adds testsuite/daemon-access-idn and extends testsuite/idn to cover Unicode, punycode, mixed-case and invalid input on both sides. * idn: hand libidn2 the same flags on both sides The client path called idn2_lookup_ul() without IDN2_NFC_INPUT while the daemon path passed it to idn2_to_ascii_8z(). Both normalize either way -- idn2_lookup_ul() ors the flag in itself, and TR46 normalizes as it maps -- but there is no reason for the two calls to read differently, so pass one set of flags from one place. The flag asks libidn2 to normalize the label rather than promising that it already is: it gates the u32_normalize() call, and without it a decomposed label comes back IDN2_NOT_NFC. Adds composed/decomposed cases to testsuite/idn, which sees the exact host name rsync hands out, and a decomposed hosts allow token to testsuite/daemon-access-idn.
396 lines
9.8 KiB
C
396 lines
9.8 KiB
C
/*
|
|
* Some usage & version related functions.
|
|
*
|
|
* Copyright (C) 2002-2022 Wayne Davison
|
|
*
|
|
* 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, visit the http://fsf.org website.
|
|
*/
|
|
|
|
#include "rsync.h"
|
|
#include "version.h"
|
|
#include "latest-year.h"
|
|
#include "git-version.h"
|
|
#include "default-cvsignore.h"
|
|
#include "itypes.h"
|
|
#include "lib/acl.h"
|
|
|
|
extern struct name_num_obj valid_checksums, valid_compressions, valid_auth_checksums;
|
|
|
|
static char *istring(const char *fmt, int val)
|
|
{
|
|
char *str;
|
|
if (asprintf(&str, fmt, val) < 0)
|
|
out_of_memory("istring");
|
|
return str;
|
|
}
|
|
|
|
static void print_info_flags(enum logcode f)
|
|
{
|
|
STRUCT_STAT *dumstat;
|
|
BOOL as_json = f == FNONE ? 1 : 0; /* We use 1 == first attribute, 2 == need closing array */
|
|
char line_buf[75], item_buf[32];
|
|
int line_len, j;
|
|
char *info_flags[] = {
|
|
|
|
"*Capabilities",
|
|
|
|
istring("%d-bit files", (int)(sizeof (OFF_T) * 8)),
|
|
istring("%d-bit inums", (int)(sizeof dumstat->st_ino * 8)), /* Don't check ino_t! */
|
|
istring("%d-bit timestamps", (int)(sizeof (time_t) * 8)),
|
|
istring("%d-bit long ints", (int)(sizeof (int64) * 8)),
|
|
|
|
#ifndef HAVE_SOCKETPAIR
|
|
"no "
|
|
#endif
|
|
"socketpairs",
|
|
|
|
#ifndef SUPPORT_LINKS
|
|
"no "
|
|
#endif
|
|
"symlinks",
|
|
|
|
#ifndef CAN_SET_SYMLINK_TIMES
|
|
"no "
|
|
#endif
|
|
"symtimes",
|
|
|
|
#ifndef SUPPORT_HARD_LINKS
|
|
"no "
|
|
#endif
|
|
"hardlinks",
|
|
|
|
#ifndef CAN_HARDLINK_SPECIAL
|
|
"no "
|
|
#endif
|
|
"hardlink-specials",
|
|
|
|
#ifndef CAN_HARDLINK_SYMLINK
|
|
"no "
|
|
#endif
|
|
"hardlink-symlinks",
|
|
|
|
#ifndef INET6
|
|
"no "
|
|
#endif
|
|
"IPv6",
|
|
|
|
#ifndef SUPPORT_ATIMES
|
|
"no "
|
|
#endif
|
|
"atimes",
|
|
|
|
"batchfiles",
|
|
|
|
#ifndef HAVE_FTRUNCATE
|
|
"no "
|
|
#endif
|
|
"inplace",
|
|
|
|
#ifndef HAVE_FTRUNCATE
|
|
"no "
|
|
#endif
|
|
"append",
|
|
|
|
#ifndef SUPPORT_ACLS
|
|
"no "
|
|
#endif
|
|
"ACLs",
|
|
|
|
#ifdef SUPPORT_ACL_FD
|
|
#ifdef HAVE_LIBACL_AT
|
|
/* The patched libacl *_at calls apply received ACLs race-safely
|
|
* on every Linux kernel (compat layer below 6.13). Emit ACL-at (true, so
|
|
* the JSON "ACL_at": true lets acl-symlink-race assert) plus a
|
|
* distinguishing libacl token. */
|
|
"ACL-at",
|
|
"ACL-libacl-at",
|
|
#else
|
|
/* Runtime: are the *xattrat syscalls usable, so a received ACL is
|
|
* applied race-safely (dirfd+leaf, AT_SYMLINK_NOFOLLOW) even when the
|
|
* leaf can't be pinned? False => ACLs are applied via the path on an
|
|
* un-pinnable leaf, which carries the parent-symlink-race exposure. */
|
|
xacl_at_available() ? "ACL-at" : "no ACL-at",
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef SUPPORT_XATTRS
|
|
"no "
|
|
#endif
|
|
"xattrs",
|
|
|
|
#ifdef RSYNC_USE_SECLUDED_ARGS
|
|
"default "
|
|
#else
|
|
"optional "
|
|
#endif
|
|
"secluded-args",
|
|
|
|
#ifndef ICONV_OPTION
|
|
"no "
|
|
#endif
|
|
"iconv",
|
|
|
|
#ifndef SUPPORT_PREALLOCATION
|
|
"no "
|
|
#endif
|
|
"prealloc",
|
|
|
|
#ifndef HAVE_MKTIME
|
|
"no "
|
|
#endif
|
|
"stop-at",
|
|
|
|
#ifndef SUPPORT_CRTIMES
|
|
"no "
|
|
#endif
|
|
"crtimes",
|
|
|
|
#ifndef SUPPORT_IDN
|
|
"no "
|
|
#endif
|
|
"IDN",
|
|
|
|
"*Optimizations",
|
|
|
|
#ifndef USE_ROLL_SIMD
|
|
"no "
|
|
#endif
|
|
"SIMD-roll",
|
|
|
|
#ifndef USE_ROLL_ASM
|
|
"no "
|
|
#endif
|
|
"asm-roll",
|
|
|
|
#ifndef USE_OPENSSL
|
|
"no "
|
|
#endif
|
|
"openssl-crypto",
|
|
|
|
#ifndef USE_MD5_ASM
|
|
"no "
|
|
#endif
|
|
"asm-MD5",
|
|
|
|
NULL
|
|
};
|
|
|
|
for (line_len = 0, j = 0; ; j++) {
|
|
char *str = info_flags[j], *next_nfo = str ? info_flags[j+1] : NULL;
|
|
int need_comma = next_nfo && *next_nfo != '*' ? 1 : 0;
|
|
int item_len;
|
|
if (!str || *str == '*')
|
|
item_len = 1000;
|
|
else if (as_json) {
|
|
char *space = strchr(str, ' ');
|
|
int is_no = space && strncmp(str, "no ", 3) == 0;
|
|
int is_bits = space && isDigit(str);
|
|
char *quot = space && !is_no && !is_bits ? "\"" : "";
|
|
char *item = space ? space + 1 : str;
|
|
char *val = !space ? "true" : is_no ? "false" : str;
|
|
int val_len = !space ? 4 : is_no ? 5 : space - str;
|
|
if (is_bits && (space = strchr(val, '-')) != NULL)
|
|
val_len = space - str;
|
|
item_len = snprintf(item_buf, sizeof item_buf,
|
|
" \"%s%s\": %s%.*s%s%s", item, is_bits ? "bits" : "",
|
|
quot, val_len, val, quot, need_comma ? "," : "");
|
|
if (is_bits)
|
|
item_buf[strlen(item)+2-1] = '_'; /* Turn the 's' into a '_' */
|
|
for (space = item; (space = strpbrk(space, " -")) != NULL; space++)
|
|
item_buf[space - item + 2] = '_';
|
|
} else
|
|
item_len = snprintf(item_buf, sizeof item_buf, " %s%s", str, need_comma ? "," : "");
|
|
if (line_len && line_len + item_len >= (int)sizeof line_buf) {
|
|
if (as_json)
|
|
printf(" %s\n", line_buf);
|
|
else
|
|
rprintf(f, " %s\n", line_buf);
|
|
line_len = 0;
|
|
}
|
|
if (!str)
|
|
break;
|
|
if (*str == '*') {
|
|
if (as_json) {
|
|
if (as_json == 2)
|
|
printf(" }");
|
|
else
|
|
as_json = 2;
|
|
printf(",\n \"%c%s\": {\n", toLower(str+1), str+2);
|
|
} else
|
|
rprintf(f, "%s:\n", str+1);
|
|
} else {
|
|
strlcpy(line_buf + line_len, item_buf, sizeof line_buf - line_len);
|
|
line_len += item_len;
|
|
}
|
|
}
|
|
if (as_json == 2)
|
|
printf(" }");
|
|
}
|
|
|
|
static void output_nno_list(enum logcode f, const char *name, struct name_num_obj *nno)
|
|
{
|
|
char namebuf[64], tmpbuf[256];
|
|
char *tok, *next_tok, *comma = ",";
|
|
char *cp;
|
|
|
|
/* Using '(' ensures that we get a trailing "none" but also includes aliases. */
|
|
get_default_nno_list(nno, tmpbuf, sizeof tmpbuf - 1, '(');
|
|
if (f != FNONE) {
|
|
rprintf(f, "%s:\n", name);
|
|
rprintf(f, " %s\n", tmpbuf);
|
|
return;
|
|
}
|
|
|
|
strlcpy(namebuf, name, sizeof namebuf);
|
|
for (cp = namebuf; *cp; cp++) {
|
|
if (*cp == ' ')
|
|
*cp = '_';
|
|
else if (isUpper(cp))
|
|
*cp = toLower(cp);
|
|
}
|
|
|
|
printf(",\n \"%s\": [\n ", namebuf);
|
|
|
|
for (tok = strtok(tmpbuf, " "); tok; tok = next_tok) {
|
|
next_tok = strtok(NULL, " ");
|
|
if (*tok != '(') /* Ignore the alises in the JSON output */
|
|
printf(" \"%s\"%s", tok, comma + (next_tok ? 0 : 1));
|
|
}
|
|
|
|
printf("\n ]");
|
|
}
|
|
|
|
/* A request of f == FNONE wants json on stdout. */
|
|
void print_rsync_version(enum logcode f)
|
|
{
|
|
char copyright[] = "(C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.";
|
|
char url[] = "https://rsync.samba.org/";
|
|
BOOL first_line = 1;
|
|
|
|
#define json_line(name, value) \
|
|
do { \
|
|
printf("%c\n \"%s\": \"%s\"", first_line ? '{' : ',', name, value); \
|
|
first_line = 0; \
|
|
} while (0)
|
|
|
|
if (f == FNONE) {
|
|
char verbuf[32];
|
|
json_line("program", RSYNC_NAME);
|
|
json_line("version", rsync_version());
|
|
(void)snprintf(verbuf, sizeof verbuf, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
|
|
json_line("protocol", verbuf);
|
|
json_line("copyright", copyright);
|
|
json_line("url", url);
|
|
} else {
|
|
#if SUBPROTOCOL_VERSION != 0
|
|
char *subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
|
|
#else
|
|
char *subprotocol = "";
|
|
#endif
|
|
rprintf(f, "%s version %s protocol version %d%s\n",
|
|
RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
|
|
rprintf(f, "Copyright %s\n", copyright);
|
|
rprintf(f, "Web site: %s\n", url);
|
|
}
|
|
|
|
print_info_flags(f);
|
|
|
|
init_checksum_choices();
|
|
|
|
output_nno_list(f, "Checksum list", &valid_checksums);
|
|
output_nno_list(f, "Compress list", &valid_compressions);
|
|
output_nno_list(f, "Daemon auth list", &valid_auth_checksums);
|
|
|
|
if (f == FNONE) {
|
|
json_line("license", "GPLv3");
|
|
json_line("caveat", "rsync comes with ABSOLUTELY NO WARRANTY");
|
|
printf("\n}\n");
|
|
fflush(stdout);
|
|
return;
|
|
}
|
|
|
|
#ifdef MAINTAINER_MODE
|
|
rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
|
|
#endif
|
|
|
|
#if SIZEOF_INT64 < 8
|
|
rprintf(f, "WARNING: no 64-bit integers on this platform!\n");
|
|
#endif
|
|
if (sizeof (int64) != SIZEOF_INT64) {
|
|
rprintf(f,
|
|
"WARNING: size mismatch in SIZEOF_INT64 define (%d != %d)\n",
|
|
(int) SIZEOF_INT64, (int) sizeof (int64));
|
|
}
|
|
|
|
rprintf(f,"\n");
|
|
rprintf(f,"rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n");
|
|
rprintf(f,"are welcome to redistribute it under certain conditions. See the GNU\n");
|
|
rprintf(f,"General Public Licence for details.\n");
|
|
}
|
|
|
|
void usage(enum logcode F)
|
|
{
|
|
print_rsync_version(F);
|
|
|
|
rprintf(F,"\n");
|
|
rprintf(F,"rsync is a file transfer program capable of efficient remote update\n");
|
|
rprintf(F,"via a fast differencing algorithm.\n");
|
|
|
|
rprintf(F,"\n");
|
|
rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n");
|
|
rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n");
|
|
rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n");
|
|
rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n");
|
|
rprintf(F," or rsync [OPTION]... [USER@]HOST:SRC [DEST]\n");
|
|
rprintf(F," or rsync [OPTION]... [USER@]HOST::SRC [DEST]\n");
|
|
rprintf(F," or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n");
|
|
rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n");
|
|
rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n");
|
|
rprintf(F,"\n");
|
|
rprintf(F,"Options\n");
|
|
#include "help-rsync.h"
|
|
rprintf(F,"\n");
|
|
rprintf(F,"Use \"rsync --daemon --help\" to see the daemon-mode command-line options.\n");
|
|
rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) manpages for full documentation.\n");
|
|
rprintf(F,"See https://rsync.samba.org/ for updates, bug reports, and answers\n");
|
|
}
|
|
|
|
void daemon_usage(enum logcode F)
|
|
{
|
|
print_rsync_version(F);
|
|
|
|
rprintf(F,"\n");
|
|
rprintf(F,"Usage: rsync --daemon [OPTION]...\n");
|
|
#include "help-rsyncd.h"
|
|
rprintf(F,"\n");
|
|
rprintf(F,"If you were not trying to invoke rsync as a daemon, avoid using any of the\n");
|
|
rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) manpage.\n");
|
|
}
|
|
|
|
const char *rsync_version(void)
|
|
{
|
|
char *ver;
|
|
#ifdef RSYNC_GITVER
|
|
ver = RSYNC_GITVER;
|
|
#else
|
|
ver = RSYNC_VERSION;
|
|
#endif
|
|
return *ver == 'v' ? ver+1 : ver;
|
|
}
|
|
|
|
const char *default_cvsignore(void)
|
|
{
|
|
return DEFAULT_CVSIGNORE;
|
|
}
|