⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 clientserver.c

📁 Rsync 3.0.5 source code
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * The socket based protocol for setting up a connection with rsyncd. * * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org> * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org> * Copyright (C) 2002-2008 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 "ifuncs.h"extern int quiet;extern int verbose;extern int dry_run;extern int output_motd;extern int list_only;extern int am_sender;extern int am_server;extern int am_daemon;extern int am_root;extern int rsync_port;extern int protect_args;extern int ignore_errors;extern int preserve_xattrs;extern int kluge_around_eof;extern int daemon_over_rsh;extern int sanitize_paths;extern int numeric_ids;extern int filesfrom_fd;extern int remote_protocol;extern int protocol_version;extern int io_timeout;extern int no_detach;extern int write_batch;extern int default_af_hint;extern int logfile_format_has_i;extern int logfile_format_has_o_or_i;extern mode_t orig_umask;extern char *bind_address;extern char *sockopts;extern char *config_file;extern char *logfile_format;extern char *files_from;extern char *tmpdir;extern struct chmod_mode_struct *chmod_modes;extern struct filter_list_struct daemon_filter_list;extern char curr_dir[];#ifdef ICONV_OPTIONextern char *iconv_opt;extern iconv_t ic_send, ic_recv;#endifchar *auth_user;int read_only = 0;int module_id = -1;int munge_symlinks = 0;struct chmod_mode_struct *daemon_chmod_modes;/* module_dirlen is the length of the module_dir string when in daemon * mode and module_dir is not "/"; otherwise 0.  (Note that a chroot- * enabled module can have a non-"/" module_dir these days.) */char *module_dir = NULL;unsigned int module_dirlen = 0;char *full_module_path;static int rl_nulls = 0;#ifdef HAVE_SIGACTIONstatic struct sigaction sigact;#endif/** * Run a client connected to an rsyncd.  The alternative to this * function for remote-shell connections is do_cmd(). * * After negotiating which module to use and reading the server's * motd, this hands over to client_run().  Telling the server the * module will cause it to chroot/setuid/etc. * * Instead of doing a transfer, the client may at this stage instead * get a listing of remote modules and exit. * * @return -1 for error in startup, or the result of client_run(). * Either way, it eventually gets passed to exit_cleanup(). **/int start_socket_client(char *host, int remote_argc, char *remote_argv[],			int argc, char *argv[]){	int fd, ret;	char *p, *user = NULL;	/* This is redundant with code in start_inband_exchange(), but this	 * short-circuits a problem in the client before we open a socket,	 * and the extra check won't hurt. */	if (**remote_argv == '/') {		rprintf(FERROR,			"ERROR: The remote path must start with a module name not a /\n");		return -1;	}	if ((p = strrchr(host, '@')) != NULL) {		user = host;		host = p+1;		*p = '\0';	}	fd = open_socket_out_wrapped(host, rsync_port, bind_address,				     default_af_hint);	if (fd == -1)		exit_cleanup(RERR_SOCKETIO);#ifdef ICONV_CONST	setup_iconv();#endif	ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);	return ret ? ret : client_run(fd, fd, -1, argc, argv);}static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int am_client){	int remote_sub = -1;#if SUBPROTOCOL_VERSION != 0	int our_sub = protocol_version < PROTOCOL_VERSION ? 0 : SUBPROTOCOL_VERSION;#else	int our_sub = 0;#endif	char *motd;	io_printf(f_out, "@RSYNCD: %d.%d\n", protocol_version, our_sub);	if (!am_client) {		motd = lp_motd_file();		if (motd && *motd) {			FILE *f = fopen(motd,"r");			while (f && !feof(f)) {				int len = fread(buf, 1, bufsiz - 1, f);				if (len > 0)					write_buf(f_out, buf, len);			}			if (f)				fclose(f);			write_sbuf(f_out, "\n");		}	}	/* This strips the \n. */	if (!read_line_old(f_in, buf, bufsiz)) {		if (am_client)			rprintf(FERROR, "rsync: did not see server greeting\n");		return -1;	}	if (sscanf(buf, "@RSYNCD: %d.%d", &remote_protocol, &remote_sub) < 1) {		if (am_client)			rprintf(FERROR, "rsync: server sent \"%s\" rather than greeting\n", buf);		else			io_printf(f_out, "@ERROR: protocol startup error\n");		return -1;	}	if (remote_sub < 0) {		if (remote_protocol == 30) {			if (am_client)				rprintf(FERROR, "rsync: server is speaking an incompatible beta of protocol 30\n");			else				io_printf(f_out, "@ERROR: your client is speaking an incompatible beta of protocol 30\n");			return -1;		}		remote_sub = 0;	}	if (protocol_version > remote_protocol) {		protocol_version = remote_protocol;		if (remote_sub)			protocol_version--;	} else if (protocol_version == remote_protocol) {		if (remote_sub != our_sub)			protocol_version--;	}#if SUBPROTOCOL_VERSION != 0	else if (protocol_version < remote_protocol) {		if (our_sub)			protocol_version--;	}#endif	if (protocol_version >= 30)		rl_nulls = 1;	return 0;}int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[]){	int i, modlen;	char line[BIGPATHBUFLEN];	char *sargs[MAX_ARGS];	int sargc = 0;	char *p, *modname;	assert(argc > 0 && *argv != NULL);	if (**argv == '/') {		rprintf(FERROR,			"ERROR: The remote path must start with a module name\n");		return -1;	}	if (!(p = strchr(*argv, '/')))		modlen = strlen(*argv);	else		modlen = p - *argv;	if (!(modname = new_array(char, modlen+1+1))) /* room for '/' & '\0' */		out_of_memory("start_inband_exchange");	strlcpy(modname, *argv, modlen + 1);	modname[modlen] = '/';	modname[modlen+1] = '\0';	if (!user)		user = getenv("USER");	if (!user)		user = getenv("LOGNAME");	if (exchange_protocols(f_in, f_out, line, sizeof line, 1) < 0)		return -1;	/* set daemon_over_rsh to false since we need to build the	 * true set of args passed through the rsh/ssh connection;	 * this is a no-op for direct-socket-connection mode */	daemon_over_rsh = 0;	server_options(sargs, &sargc);	if (sargc >= MAX_ARGS - 2)		goto arg_overflow;	sargs[sargc++] = ".";	while (argc > 0) {		if (sargc >= MAX_ARGS - 1) {		  arg_overflow:			rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");			exit_cleanup(RERR_SYNTAX);		}		if (strncmp(*argv, modname, modlen) == 0		 && argv[0][modlen] == '\0')			sargs[sargc++] = modname; /* we send "modname/" */		else			sargs[sargc++] = *argv;		argv++;		argc--;	}	sargs[sargc] = NULL;	if (verbose > 1)		print_child_argv("sending daemon args:", sargs);	io_printf(f_out, "%.*s\n", modlen, modname);	/* Old servers may just drop the connection here,	 rather than sending a proper EXIT command.  Yuck. */	kluge_around_eof = list_only && protocol_version < 25 ? 1 : 0;	while (1) {		if (!read_line_old(f_in, line, sizeof line)) {			rprintf(FERROR, "rsync: didn't get server startup line\n");			return -1;		}		if (strncmp(line,"@RSYNCD: AUTHREQD ",18) == 0) {			auth_client(f_out, user, line+18);			continue;		}		if (strcmp(line,"@RSYNCD: OK") == 0)			break;		if (strcmp(line,"@RSYNCD: EXIT") == 0) {			/* This is sent by recent versions of the			 * server to terminate the listing of modules.			 * We don't want to go on and transfer			 * anything; just exit. */			exit(0);		}		if (strncmp(line, "@ERROR", 6) == 0) {			rprintf(FERROR, "%s\n", line);			/* This is always fatal; the server will now			 * close the socket. */			return -1;		}		/* This might be a MOTD line or a module listing, but there is		 * no way to differentiate it.  The manpage mentions this. */		if (output_motd)			rprintf(FINFO, "%s\n", line);	}	kluge_around_eof = 0;	if (rl_nulls) {		for (i = 0; i < sargc; i++) {			if (!sargs[i]) /* stop at --protect-args NULL */				break;			write_sbuf(f_out, sargs[i]);			write_byte(f_out, 0);		}		write_byte(f_out, 0);	} else {		for (i = 0; i < sargc; i++)			io_printf(f_out, "%s\n", sargs[i]);		write_sbuf(f_out, "\n");	}	if (protect_args)		send_protected_args(f_out, sargs);	if (protocol_version < 23) {		if (protocol_version == 22 || !am_sender)			io_start_multiplex_in();	}	free(modname);	return 0;}static char *finish_pre_exec(pid_t pid, int fd, char *request,			     char **early_argv, char **argv){	int j = 0, status = -1;	if (!request)		request = "(NONE)";	write_buf(fd, request, strlen(request)+1);	if (early_argv) {		for ( ; *early_argv; early_argv++)			write_buf(fd, *early_argv, strlen(*early_argv)+1);		j = 1; /* Skip arg0 name in argv. */	}	for ( ; argv[j]; j++) {		write_buf(fd, argv[j], strlen(argv[j])+1);		if (argv[j][0] == '.' && argv[j][1] == '\0')			break;	}	write_byte(fd, 0);	close(fd);	if (wait_process(pid, &status, 0) < 0	 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {		char *e;		if (asprintf(&e, "pre-xfer exec returned failure (%d)%s%s\n",			     status, status < 0 ? ": " : "",			     status < 0 ? strerror(errno) : "") < 0)			out_of_memory("finish_pre_exec");		return e;	}	return NULL;}static int read_arg_from_pipe(int fd, char *buf, int limit){	char *bp = buf, *eob = buf + limit - 1;	while (1) {	    int got = read(fd, bp, 1);	    if (got != 1) {		if (got < 0 && errno == EINTR)			continue;		return -1;	    }	    if (*bp == '\0')		break;	    if (bp < eob)		bp++;	}	*bp = '\0';	return bp - buf;}static int path_failure(int f_out, const char *dir, BOOL was_chdir){	if (was_chdir)		rsyserr(FLOG, errno, "chdir %s failed\n", dir);	else		rprintf(FLOG, "normalize_path(%s) failed\n", dir);	io_printf(f_out, "@ERROR: chdir failed\n");	return -1;}static int rsync_module(int f_in, int f_out, int i, char *addr, char *host){	int argc;	char **argv, **orig_argv, **orig_early_argv, *module_chdir;	char line[BIGPATHBUFLEN];	uid_t uid = (uid_t)-2;  /* canonically "nobody" */	gid_t gid = (gid_t)-2;	char *p, *err_msg = NULL;	char *name = lp_name(i);	int use_chroot = lp_use_chroot(i);	int ret, pre_exec_fd = -1;	pid_t pre_exec_pid = 0;	char *request = NULL;#ifdef ICONV_OPTION	iconv_opt = lp_charset(i);	if (*iconv_opt)		setup_iconv();	iconv_opt = NULL;#endif	if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {		rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",			name, host, addr);		if (!lp_list(i))			io_printf(f_out, "@ERROR: Unknown module '%s'\n", name);		else {			io_printf(f_out,				  "@ERROR: access denied to %s from %s (%s)\n",				  name, host, addr);		}		return -1;	}	if (am_daemon && am_server) {		rprintf(FLOG, "rsync allowed access on module %s from %s (%s)\n",			name, host, addr);	}	if (!claim_connection(lp_lock_file(i), lp_max_connections(i))) {		if (errno) {			rsyserr(FLOG, errno, "failed to open lock file %s",				lp_lock_file(i));			io_printf(f_out, "@ERROR: failed to open lock file\n");		} else {			rprintf(FLOG, "max connections (%d) reached\n",				lp_max_connections(i));			io_printf(f_out, "@ERROR: max connections (%d) reached -- try again later\n",				lp_max_connections(i));		}		return -1;	}	auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");	if (!auth_user) {		io_printf(f_out, "@ERROR: auth failed on module %s\n", name);		return -1;	}	module_id = i;	if (lp_read_only(i))		read_only = 1;	if (lp_transfer_logging(i) && !logfile_format)		logfile_format = lp_log_format(i);	if (log_format_has(logfile_format, 'i'))		logfile_format_has_i = 1;	if (logfile_format_has_i || log_format_has(logfile_format, 'o'))		logfile_format_has_o_or_i = 1;	am_root = (MY_UID() == 0);	if (am_root) {		p = lp_uid(i);		if (!name_to_uid(p, &uid)) {			if (!isDigit(p)) {				rprintf(FLOG, "Invalid uid %s\n", p);				io_printf(f_out, "@ERROR: invalid uid %s\n", p);				return -1;			}			uid = atoi(p);		}		p = lp_gid(i);		if (!name_to_gid(p, &gid)) {			if (!isDigit(p)) {				rprintf(FLOG, "Invalid gid %s\n", p);				io_printf(f_out, "@ERROR: invalid gid %s\n", p);				return -1;			}			gid = atoi(p);		}	}	/* TODO: If we're not root, but the configuration requests	 * that we change to some uid other than the current one, then	 * log a warning. */	/* TODO: Perhaps take a list of gids, and make them into the	 * supplementary groups. */	module_dir = lp_path(i);	if (use_chroot) {		if ((p = strstr(module_dir, "/./")) != NULL) {			*p = '\0'; /* Temporary... */			if (!(module_chdir = normalize_path(module_dir, True, NULL)))				return path_failure(f_out, module_dir, False);			*p = '/';			if (!(p = normalize_path(p + 2, True, &module_dirlen)))				return path_failure(f_out, strstr(module_dir, "/./"), False);			if (!(full_module_path = normalize_path(module_dir, False, NULL)))				full_module_path = module_dir;			module_dir = p;		} else {			if (!(module_chdir = normalize_path(module_dir, False, NULL)))				return path_failure(f_out, module_dir, False);			full_module_path = module_chdir;			module_dir = "/";			module_dirlen = 1;		}	} else {		if (!(module_chdir = normalize_path(module_dir, False, &module_dirlen)))			return path_failure(f_out, module_dir, False);		full_module_path = module_dir = module_chdir;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -