Пример #1
0
int extra_comms(struct async *as, struct conf **confs,
	enum action *action, char **incexc)
{
	int ret=-1;
	char *feat=NULL;
	struct asfd *asfd;
	struct iobuf *rbuf;
	const char *orig_client=get_string(confs[OPT_ORIG_CLIENT]);
	asfd=as->asfd;
	rbuf=asfd->rbuf;

	if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin"))
	{
		logp("Problem requesting extra_comms_begin\n");
		goto end;
	}
	// Servers greater than 1.3.0 will list the extra_comms
	// features they support.
	if(asfd->read(asfd))
	{
		logp("Problem reading response to extra_comms_begin\n");
		goto end;
	}
	feat=rbuf->buf;
	if(rbuf->cmd!=CMD_GEN
	  || strncmp_w(feat, "extra_comms_begin ok"))
	{
		iobuf_log_unexpected(rbuf, __func__);
		goto end;
	}
	logp("%s\n", feat);
	iobuf_init(rbuf);

	// Can add extra bits here. The first extra bit is the
	// autoupgrade stuff.
	if(server_supports_autoupgrade(feat)
	  && get_string(confs[OPT_AUTOUPGRADE_DIR])
	  && get_string(confs[OPT_AUTOUPGRADE_OS])
	  && autoupgrade_client(as, confs))
		goto end;

	// :srestore: means that the server wants to do a restore.
	if(server_supports(feat, ":srestore:"))
	{
		if(get_int(confs[OPT_SERVER_CAN_RESTORE]))
		{
			logp("Server is initiating a restore\n");
			if(incexc_recv_client_restore(asfd, incexc, confs))
				goto end;
			if(*incexc)
			{
				if(conf_parse_incexcs_buf(confs, *incexc))
					goto end;
				*action=ACTION_RESTORE;
				log_restore_settings(confs, 1);
			}
		}
		else
		{
			logp("Server wants to initiate a restore\n");
			logp("Client configuration says no\n");
			if(asfd->write_str(asfd, CMD_GEN, "srestore not ok"))
				goto end;
		}
	}

	if(orig_client)
	{
		char str[512]="";
		snprintf(str, sizeof(str), "orig_client=%s", orig_client);
		if(!server_supports(feat, ":orig_client:"))
		{
			logp("Server does not support switching client.\n");
			goto end;
		}
		if(asfd->write_str(asfd, CMD_GEN, str)
		  || asfd->read_expect(asfd, CMD_GEN, "orig_client ok"))
		{
			logp("Problem requesting %s\n", str);
			goto end;
		}
		logp("Switched to client %s\n", orig_client);
	}

	// :sincexc: is for the server giving the client the
	// incexc config.
	if(*action==ACTION_BACKUP
	  || *action==ACTION_BACKUP_TIMED
	  || *action==ACTION_TIMER_CHECK)
	{
		if(!*incexc && server_supports(feat, ":sincexc:"))
		{
			logp("Server is setting includes/excludes.\n");
			if(incexc_recv_client(asfd, incexc, confs))
				goto end;
			if(*incexc && conf_parse_incexcs_buf(confs,
				*incexc)) goto end;
		}
	}

	if(server_supports(feat, ":counters:"))
	{
		if(asfd->write_str(asfd, CMD_GEN, "countersok"))
			goto end;
		set_int(confs[OPT_SEND_CLIENT_CNTR], 1);
	}

	// :incexc: is for the client sending the server the
	// incexc conf so that it better knows what to do on
	// resume.
	if(server_supports(feat, ":incexc:")
	  && incexc_send_client(asfd, confs))
		goto end;

	if(server_supports(feat, ":uname:"))
	{
		const char *clientos=NULL;
#ifdef HAVE_WIN32
#ifdef _WIN64
		clientos="Windows 64bit";
#else
		clientos="Windows 32bit";
#endif
#else
		struct utsname utsname;
		if(!uname(&utsname))
			clientos=(const char *)utsname.sysname;
#endif
		if(clientos)
		{
			char msg[128]="";
			snprintf(msg, sizeof(msg),
				"uname=%s", clientos);
			if(asfd->write_str(asfd, CMD_GEN, msg))
				goto end;
		}
	}

	if(server_supports(feat, ":csetproto:"))
	{
		char msg[128]="";
		// Use protocol2 if no choice has been made on client side.
		if(get_e_protocol(confs[OPT_PROTOCOL])==PROTO_AUTO)
		{
			logp("Server has protocol=0 (auto)\n");
			set_e_protocol(confs[OPT_PROTOCOL], PROTO_2);
		}
		// Send choice to server.
		snprintf(msg, sizeof(msg), "protocol=%d",
			get_e_protocol(confs[OPT_PROTOCOL]));
		if(asfd->write_str(asfd, CMD_GEN, msg))
			goto end;
		logp("Using protocol=%d\n",
			get_e_protocol(confs[OPT_PROTOCOL]));
	}
	else if(server_supports(feat, ":forceproto=1:"))
	{
		logp("Server is forcing protocol 1\n");
		if(get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_AUTO
		  && get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_1)
		{
			logp("But client has set protocol=%d!\n",
				get_e_protocol(confs[OPT_PROTOCOL]));
			goto end;
		}
		set_e_protocol(confs[OPT_PROTOCOL], PROTO_1);
	}
	else if(server_supports(feat, ":forceproto=2:"))
	{
		logp("Server is forcing protocol 2\n");
		if(get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_AUTO
		  && get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_2)
		{
			logp("But client has set protocol=%d!\n",
				get_e_protocol(confs[OPT_PROTOCOL]));
			goto end;
		}
		set_e_protocol(confs[OPT_PROTOCOL], PROTO_2);
	}

	if(server_supports(feat, ":msg:"))
	{
		set_int(confs[OPT_MESSAGE], 1);
		if(asfd->write_str(asfd, CMD_GEN, "msg"))
			goto end;
	}

#ifndef RS_DEFAULT_STRONG_LEN
	if(server_supports(feat, ":rshash=blake2:"))
	{
		set_e_rshash(confs[OPT_RSHASH], RSHASH_BLAKE2);
		// Send choice to server.
		if(asfd->write_str(asfd, CMD_GEN, "rshash=blake2"))
			goto end;
	}
	else
#endif
		set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);

	if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end")
	  || asfd->read_expect(asfd, CMD_GEN, "extra_comms_end ok"))
	{
		logp("Problem requesting extra_comms_end\n");
		goto end;
	}

	ret=0;
end:
	return ret;
}
Пример #2
0
/* May return 1 to mean try again. This happens after a successful certificate
   signing request so that it connects again straight away with the new
   key/certificate.
   Returns 2 if there were restore/verify warnings.
   Returns 3 if timer conditions were not met.
*/
static int do_client(struct config *conf, enum action act, int vss_restore, int json)
{
	int ret=0;
	int rfd=-1;
	int resume=0;
	SSL *ssl=NULL;
	BIO *sbio=NULL;
	char buf[256]="";
	SSL_CTX *ctx=NULL;
	struct cntr cntr;
	struct cntr p1cntr;
	char *incexc=NULL;
	char *server_version=NULL;
	const char *phase1str="backupphase1";

	reset_filecounter(&p1cntr, time(NULL));
	reset_filecounter(&cntr, time(NULL));

	setup_signals_client();
//	settimers(0, 100);
	logp("begin client\n");

	if(act!=ACTION_ESTIMATE)
	{
		ssl_load_globals();
		if(!(ctx=ssl_initialise_ctx(conf)))
		{
			logp("error initialising ssl ctx\n");
			ret=-1;
			goto end;
		}

		SSL_CTX_set_session_id_context(ctx,
			(const unsigned char *)&s_server_session_id_context,
			sizeof(s_server_session_id_context));

		if((rfd=init_client_socket(conf->server, conf->port))<0)
		{
			ret=-1;
			goto end;
		}

		if(!(ssl=SSL_new(ctx))
		  || !(sbio=BIO_new_socket(rfd, BIO_NOCLOSE)))
		{
			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
			logp("Problem joining SSL to the socket: %s\n", buf);
			ret=-1;
			goto end;
		}
		SSL_set_bio(ssl, sbio, sbio);
		if(SSL_connect(ssl)<=0)
		{
			ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
			logp("SSL connect error: %s\n", buf);
			ret=-1;
			goto end;
		}
	}

	if((ret=async_init(rfd, ssl, conf, act==ACTION_ESTIMATE)))
		goto end;

	// Set quality of service bits on backup packets.
	if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED)
		set_bulk_packets();

	if(act!=ACTION_ESTIMATE)
	{
		char cmd=0;
		size_t len=0;
		char *feat=NULL;
		int ca_ret=0;
		if((ret=authorise_client(conf, &server_version, &p1cntr)))
			goto end;

		if(server_version)
		{
			logp("Server version: %s\n", server_version);
			// Servers before 1.3.2 did not tell us their versions.
			// 1.3.2 and above can do the automatic CA stuff that
			// follows.
			if((ca_ret=ca_client_setup(conf, &p1cntr))<0)
			{
				// Error
				logp("Error with certificate signing request\n");
				ret=-1;
				goto end;
			}
			else if(ca_ret>0)
			{
				// Certificate signed successfully.
				// Everything is OK, but we will reconnect now, in
				// order to use the new keys/certificates.
				ret=1;
				goto end;
			}
		}

		set_non_blocking(rfd);

		if((ret=ssl_check_cert(ssl, conf)))
		{
			logp("check cert failed\n");
			goto end;
		}

		if((ret=async_write_str(CMD_GEN, "extra_comms_begin")))
		{
			logp("Problem requesting extra_comms_begin\n");
			goto end;
		}
		// Servers greater than 1.3.0 will list the extra_comms
		// features they support.
		else if((ret=async_read(&cmd, &feat, &len)))
		{
			logp("Problem reading response to extra_comms_begin\n");
			goto end;
		}
		else if(cmd!=CMD_GEN)
		{
			logp("Unexpected command from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat);
			ret=-1;
			goto end;
		}
		else if(strncmp(feat,
		  "extra_comms_begin ok", strlen("extra_comms_begin ok")))
		{
			logp("Unexpected response from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat);
			ret=-1;
			goto end;
		}

		// Can add extra bits here. The first extra bit is the
		// autoupgrade stuff.

		if(server_supports_autoupgrade(feat))
		{
			if(conf->autoupgrade_dir && conf->autoupgrade_os
			  && (ret=autoupgrade_client(conf, &p1cntr)))
				goto end;
		}

		// :srestore: means that the server wants to do a restore.
		if(server_supports(feat, ":srestore:"))
		{
			if(conf->server_can_restore)
			{
				logp("Server is initiating a restore\n");
				if(incexc) { free(incexc); incexc=NULL; }
				if((ret=incexc_recv_client_restore(&incexc,
					conf, &p1cntr)))
						goto end;
				if(incexc)
				{
					if((ret=parse_incexcs_buf(conf,
						incexc))) goto end;
					act=ACTION_RESTORE;
					log_restore_settings(conf, 1);
				}
			}
			else
			{
				logp("Server wants to initiate a restore\n");
				logp("Client configuration says no\n");
				if(async_write_str(CMD_GEN, "srestore not ok"))
				{
					ret=-1;
					goto end;
				}
			}
		}

		if(conf->orig_client)
		{
			char str[512]="";
			snprintf(str, sizeof(str),
				"orig_client=%s", conf->orig_client);
			if(!server_supports(feat, ":orig_client:"))
			{
				logp("Server does not support switching client.\n");
				ret=-1;
				goto end;
			}
			if((ret=async_write_str(CMD_GEN, str))
			  || (ret=async_read_expect(CMD_GEN, "orig_client ok")))
			{
				logp("Problem requesting %s\n", str);
				ret=-1;
				goto end;
			}
			logp("Switched to client %s\n", conf->orig_client);
		}

		// :sincexc: is for the server giving the client the
		// incexc config.
		if(act==ACTION_BACKUP
		  || act==ACTION_BACKUP_TIMED)
		{
			if(!incexc && server_supports(feat, ":sincexc:"))
			{
				logp("Server is setting includes/excludes.\n");
				if(incexc) { free(incexc); incexc=NULL; }
				if((ret=incexc_recv_client(&incexc,
					conf, &p1cntr))) goto end;
				if(incexc && (ret=parse_incexcs_buf(conf,
					incexc))) goto end;
			}
		}

		if(server_supports(feat, ":counters:"))
		{
			if(async_write_str(CMD_GEN, "countersok"))
				goto end;
			conf->send_client_counters=1;
		}

		// :incexc: is for the client sending the server the
		// incexc config so that it better knows what to do on
		// resume.
		if(server_supports(feat, ":incexc:")
		  && (ret=incexc_send_client(conf, &p1cntr)))
			goto end;

		if((ret=async_write_str(CMD_GEN, "extra_comms_end"))
		  || (ret=async_read_expect(CMD_GEN, "extra_comms_end ok")))
		{
			logp("Problem requesting extra_comms_end\n");
			goto end;
		}

		if(feat) free(feat);
	}

	rfd=-1;
	switch(act)
	{
		case ACTION_BACKUP_TIMED:
			phase1str="backupphase1timed";
		case ACTION_BACKUP:
		{
			// Set bulk packets quality of service flags on backup.
			if(incexc)
			{
				logp("Server is overriding the configuration\n");
				logp("with the following settings:\n");
				if(log_incexcs_buf(incexc))
				{
					ret=-1;
					goto end;
				}
			}
			if(!conf->sdcount)
			{
				logp("Found no include paths!\n");
				ret=-1;
				goto end;
			}

			if(!(ret=maybe_check_timer(phase1str,
				conf, &resume)))
			{
				if(conf->backup_script_pre)
				{
					int a=0;
					const char *args[12];
					args[a++]=conf->backup_script_pre;
					args[a++]="pre";
					args[a++]="reserved2";
					args[a++]="reserved3";
					args[a++]="reserved4";
					args[a++]="reserved5";
					args[a++]=NULL;
					if(run_script(args,
						conf->backup_script_pre_arg,
						conf->bprecount,
						&p1cntr, 1, 1)) ret=-1;
				}

				if(!ret && do_backup_client(conf,
					resume, 0, &p1cntr, &cntr))
						ret=-1;

				if((conf->backup_script_post_run_on_fail
				  || !ret) && conf->backup_script_post)
				{
					int a=0;
					const char *args[12];
					args[a++]=conf->backup_script_post;
					args[a++]="post";
					// Tell post script whether the restore
					// failed.
					args[a++]=ret?"1":"0";
					args[a++]="reserved3";
					args[a++]="reserved4";
					args[a++]="reserved5";
					args[a++]=NULL;
					if(run_script(args,
						conf->backup_script_post_arg,
						conf->bpostcount,
						&cntr, 1, 1)) ret=-1;
				}
			}

			if(ret<0)
				logp("error in backup\n");
			else if(ret>0)
			{
				// Timer script said no.
				// Have a distinct return value to
				// differentiate between other cases
				// (ssl reconnection and restore/verify
				// warnings).
				ret=3;
			}
			else
				logp("backup finished ok\n");
			
			break;
		}
		case ACTION_RESTORE:
		case ACTION_VERIFY:
		{
			if(conf->restore_script_pre)
			{
				int a=0;
				const char *args[12];
				args[a++]=conf->restore_script_pre;
				args[a++]="pre";
				args[a++]="reserved2";
				args[a++]="reserved3";
				args[a++]="reserved4";
				args[a++]="reserved5";
				args[a++]=NULL;
				if(run_script(args,
					conf->restore_script_pre_arg,
					conf->rprecount, &cntr, 1, 1)) ret=-1;
			}
			if(!ret && do_restore_client(conf,
				act, vss_restore, &p1cntr, &cntr)) ret=-1;
			if((conf->restore_script_post_run_on_fail
			  || !ret) && conf->restore_script_post)
			{
				int a=0;
				const char *args[12];
				args[a++]=conf->restore_script_pre;
				args[a++]="post";
				// Tell post script whether the restore
				// failed.
				args[a++]=ret?"1":"0";
				args[a++]="reserved3";
				args[a++]="reserved4";
				args[a++]="reserved5";
				args[a++]=NULL;
				if(run_script(args,
					conf->restore_script_post_arg,
					conf->rpostcount, &cntr, 1, 1)) ret=-1;
			}

			// Return non-zero if there were warnings,
			// so that the test script can easily check.
			if(p1cntr.warning+cntr.warning)
				ret=2;

			break;
		}
		case ACTION_ESTIMATE:
			if(!ret) ret=do_backup_client(conf, 0, 1,
					&p1cntr, &cntr);
			break;
		case ACTION_DELETE:
			if(!ret) ret=do_delete_client(conf);
			break;
		case ACTION_LIST:
		case ACTION_LONG_LIST:
		default:
			ret=do_list_client(conf, act, json);
			break;
	}

end:
	close_fd(&rfd);
	async_free();
	if(act!=ACTION_ESTIMATE) ssl_destroy_ctx(ctx);

	if(incexc) free(incexc);
	if(server_version) free(server_version);

        //logp("end client\n");
	return ret;
}