BOOL trim_string_w(smb_ucs2_t *s, const smb_ucs2_t *front, const smb_ucs2_t *back) { BOOL ret = False; size_t len, front_len, back_len; if (!s) { return False; } len = strlen_w(s); if (front && *front) { front_len = strlen_w(front); while (len && strncmp_w(s, front, front_len) == 0) { memmove(s, (s + front_len), (len - front_len + 1) * sizeof(smb_ucs2_t)); len -= front_len; ret = True; } } if (back && *back) { back_len = strlen_w(back); while (len && strncmp_w((s + (len - back_len)), back, back_len) == 0) { s[len - back_len] = 0; len -= back_len; ret = True; } } return ret; }
static int run_list(struct asfd *asfd, struct sdirs *sdirs, struct conf **cconfs) { int ret=-1; char *cp=NULL; char *backupno=NULL; char *browsedir=NULL; char *listregex=NULL; struct iobuf *rbuf=asfd->rbuf; if(!client_can_generic(cconfs, OPT_CLIENT_CAN_LIST)) { logp("Not allowing list of %s\n", get_string(cconfs[OPT_CNAME])); asfd->write_str(asfd, CMD_GEN, "Client list is not allowed"); goto end; } if(!strncmp_w(rbuf->buf, "list ")) { if((cp=strchr(rbuf->buf, ':'))) { *cp='\0'; if(!(listregex=strdup_w(cp+1, __func__))) goto end; } if(!(backupno=strdup_w(rbuf->buf+strlen("list "), __func__))) goto end; } else if(!strncmp_w(rbuf->buf, "listb ")) { if((cp=strchr(rbuf->buf, ':'))) { *cp='\0'; if(!(browsedir=strdup_w(cp+1, __func__))) goto end; } strip_trailing_slashes(&browsedir); if(!(backupno=strdup_w(rbuf->buf+strlen("listb "), __func__))) goto end; } if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end; iobuf_free_content(asfd->rbuf); if(list_server_init(asfd, sdirs, get_cntr(cconfs), get_protocol(cconfs), backupno, listregex, browsedir)) goto end; ret=do_list_server(); end: free_w(&backupno); free_w(&browsedir); free_w(&listregex); list_server_free(); return ret; }
static enum cliret maybe_check_timer(struct asfd *asfd, enum action action, const char *phase1str, struct conf *conf, int *resume) { int complen=0; struct iobuf *rbuf=asfd->rbuf; if(asfd->write_str(asfd, CMD_GEN, phase1str)) goto error; if(asfd->read(asfd)) goto error; if(rbuf->cmd!=CMD_GEN) { iobuf_free_content(rbuf); iobuf_log_unexpected(rbuf, __func__); goto error; } if(!strcmp(rbuf->buf, "timer conditions not met")) { iobuf_free_content(rbuf); logp("Timer conditions on the server were not met\n"); goto timer_not_met; } else if(!strcmp(rbuf->buf, "timer conditions met")) { iobuf_free_content(rbuf); logp("Timer conditions on the server were met\n"); if(action==ACTION_TIMER_CHECK) goto end; } if(!strncmp_w(rbuf->buf, "ok")) complen=3; else if(!strncmp_w(rbuf->buf, "resume")) { complen=7; *resume=1; logp("server wants to resume previous backup.\n"); } else { iobuf_log_unexpected(rbuf, __func__); iobuf_free_content(rbuf); goto error; } // The server now tells us the compression level in the OK response. if(strlen(rbuf->buf)>3) conf->compression=atoi(rbuf->buf+complen); logp("Compression level: %d\n", conf->compression); iobuf_free_content(rbuf); end: return CLIENT_OK; error: return CLIENT_ERROR; timer_not_met: return CLIENT_SERVER_TIMER_NOT_MET; }
static const char *buf_to_notify_str(struct iobuf *rbuf) { const char *buf=rbuf->buf; if(!strncmp_w(buf, "backup")) return "backup"; else if(!strncmp_w(buf, "restore")) return "restore"; else if(!strncmp_w(buf, "verify")) return "verify"; else if(!strncmp_w(buf, "delete")) return "delete"; else if(!strncmp_w(buf, "list")) return "list"; else return "unknown"; }
static int deal_with_client_rbuf(struct asfd *asfd, const char *directory, struct scores *scores) { if(asfd->rbuf->cmd==CMD_GEN) { if(!strncmp_w(asfd->rbuf->buf, "cname:")) { struct iobuf wbuf; free_w(&asfd->desc); if(!(asfd->desc=strdup_w(asfd->rbuf->buf +strlen("cname:"), __func__))) goto error; logp("%s: fd %d\n", asfd->desc, asfd->fd); iobuf_set(&wbuf, CMD_GEN, (char *)"cname ok", strlen("cname ok")); if(asfd->write(asfd, &wbuf)) goto error; } else if(!strncmp_w(asfd->rbuf->buf, "sigs_end")) { //printf("Was told no more sigs\n"); if(deduplicate(asfd, directory, scores)<0) goto error; } else { iobuf_log_unexpected(asfd->rbuf, __func__); goto error; } } else if(asfd->rbuf->cmd==CMD_SIG) { if(champ_server_deal_with_rbuf_sig(asfd, directory, scores)) goto error; } else if(asfd->rbuf->cmd==CMD_MANIFEST) { // Client has completed a manifest file. Want to start using // it as a dedup candidate now. if(candidate_add_fresh(asfd->rbuf->buf, directory, scores)) goto error; } else { iobuf_log_unexpected(asfd->rbuf, __func__); goto error; } iobuf_free_content(asfd->rbuf); return 0; error: iobuf_free_content(asfd->rbuf); return -1; }
static int parse_restore_str(const char *str, enum action *act, char **backupnostr, char **restoreregex) { int ret=-1; char *cp=NULL; char *copy=NULL; if(!str) { logp("NULL passed to %s\n", __func__); goto end; } if(!(copy=strdup_w(str, __func__))) goto end; if(!strncmp_w(copy, "restore ")) *act=ACTION_RESTORE; else if(!strncmp_w(copy, "verify ")) *act=ACTION_VERIFY; else { logp("Could not parse %s in %s\n", copy, __func__); goto end; } if(!(cp=strchr(copy, ' '))) { logp("Could not parse %s in %s\n", copy, __func__); goto end; } cp++; if(!(*backupnostr=strdup_w(cp, __func__))) goto end; if((cp=strchr(*backupnostr, ':'))) { *cp='\0'; cp++; if(!(*restoreregex=strdup_w(cp, __func__))) goto end; } ret=0; end: free_w(©); return ret; }
static int srestore_matches(struct strlist *s, const char *path) { int r=0; if(!s->flag) return 0; // Do not know how to do excludes yet. if((r=strncmp_w(path, s->path))) return 0; // no match if(!r) return 1; // exact match if(*(path+strlen(s->path)+1)=='/') return 1; // matched directory contents return 0; // no match }
static enum asl_ret maybe_check_timer_func(struct asfd *asfd, struct conf **confs, void *param) { int complen=0; struct tchk *tchk=(struct tchk *)param; if(!strcmp(asfd->rbuf->buf, "timer conditions not met")) { logp("Timer conditions on the server were not met\n"); tchk->ret=CLIENT_SERVER_TIMER_NOT_MET; return ASL_END_OK; } else if(!strcmp(asfd->rbuf->buf, "timer conditions met")) { // Only happens on 'timer check only'. logp("Timer conditions on the server were met\n"); tchk->ret=CLIENT_OK; return ASL_END_OK; } if(!strncmp_w(asfd->rbuf->buf, "ok")) complen=3; else if(!strncmp_w(asfd->rbuf->buf, "resume")) { complen=7; tchk->resume=1; logp("server wants to resume previous backup.\n"); } else { iobuf_log_unexpected(asfd->rbuf, __func__); return ASL_END_ERROR; } // The server now tells us the compression level in the OK response. if(strlen(asfd->rbuf->buf)>3) set_int(confs[OPT_COMPRESSION], atoi(asfd->rbuf->buf+complen)); logp("Compression level: %d\n", get_int(confs[OPT_COMPRESSION])); return ASL_END_OK; }
smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins) { smb_ucs2_t *r; size_t slen, inslen; if (!s || !*s || !ins || !*ins) return NULL; slen = strlen_w(s); inslen = strlen_w(ins); r = (smb_ucs2_t *)s; while ((r = strchr_w(r, *ins))) { if (strncmp_w(r, ins, inslen) == 0) return r; r++; } return NULL; }
static int parse_action(enum action *act, const char *optarg) { if(!strncmp(optarg, "backup", 1)) *act=ACTION_BACKUP; else if(!strncmp(optarg, "timedbackup", 1)) *act=ACTION_BACKUP_TIMED; else if(!strncmp(optarg, "Timercheck", 1)) *act=ACTION_TIMER_CHECK; else if(!strncmp(optarg, "restore", 1)) *act=ACTION_RESTORE; else if(!strncmp(optarg, "verify", 1)) *act=ACTION_VERIFY; else if(!strncmp(optarg, "list", 1)) *act=ACTION_LIST; else if(!strncmp(optarg, "List", 1)) *act=ACTION_LIST_LONG; else if(!strncmp(optarg, "status", 1)) *act=ACTION_STATUS; else if(!strncmp(optarg, "Status", 1)) *act=ACTION_STATUS_SNAPSHOT; else if(!strncmp(optarg, "estimate", 1)) *act=ACTION_ESTIMATE; // Make them spell 'delete' out fully so that it is less likely to be // used accidently. else if(!strncmp_w(optarg, "delete")) *act=ACTION_DELETE; else if(!strncmp(optarg, "champchooser", 1)) *act=ACTION_CHAMP_CHOOSER; else if(!strncmp(optarg, "diff", 1)) *act=ACTION_DIFF; else if(!strncmp(optarg, "Diff", 1)) *act=ACTION_DIFF_LONG; else if(!strncmp(optarg, "monitor", 1)) *act=ACTION_MONITOR; else { usage(); return -1; } return 0; }
smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins) { const smb_ucs2_t *r; size_t inslen; if (!s || !*s || !ins || !*ins) { return NULL; } inslen = strlen_w(ins); r = s; while ((r = strchr_w(r, *ins))) { if (strncmp_w(r, ins, inslen) == 0) { return discard_const_p(smb_ucs2_t, r); } r++; } return NULL; }
static int run_diff(struct asfd *asfd, struct sdirs *sdirs, struct conf **cconfs) { int ret=-1; char *backup1=NULL; char *backup2=NULL; struct iobuf *rbuf=asfd->rbuf; if(!client_can_generic(cconfs, OPT_CLIENT_CAN_DIFF)) { logp("Not allowing diff of %s\n", get_string(cconfs[OPT_CNAME])); asfd->write_str(asfd, CMD_GEN, "Client diff is not allowed"); goto end; } if(!strncmp_w(rbuf->buf, "diff ")) { char *cp; if((cp=strchr(rbuf->buf, ':'))) { *cp='\0'; if(!(backup2=strdup_w(cp+1, __func__))) goto end; } if(!(backup1=strdup_w(rbuf->buf+strlen("diff "), __func__))) goto end; } if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end; iobuf_free_content(asfd->rbuf); ret=do_diff_server(asfd, sdirs, get_cntr(cconfs), get_protocol(cconfs), backup1, backup2); end: return ret; }
static enum asl_ret restore_spool_func(struct asfd *asfd, struct conf **confs, void *param) { static char **datpath; static struct iobuf *rbuf; datpath=(char **)param; rbuf=asfd->rbuf; if(!strncmp_w(rbuf->buf, "dat=")) { char *fpath=NULL; if(!(fpath=prepend_s(*datpath, rbuf->buf+4)) || build_path_w(fpath) || receive_a_file(asfd, fpath, get_cntr(confs))) return ASL_END_ERROR; iobuf_free_content(rbuf); } else if(!strcmp(rbuf->buf, "datfilesend")) { if(asfd->write_str(asfd, CMD_GEN, "datfilesend_ok")) return ASL_END_ERROR; return ASL_END_OK; } return ASL_CONTINUE; }
int authorise_server(struct asfd *asfd, struct conf **globalcs, struct conf **cconfs) { int ret=-1; char *cp=NULL; char *password=NULL; char *cname=NULL; char whoareyou[256]=""; struct iobuf *rbuf=asfd->rbuf; const char *peer_version=NULL; if(asfd->read(asfd)) { logp("unable to read initial message\n"); goto end; } if(rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "hello")) { iobuf_log_unexpected(rbuf, __func__); goto end; } // String may look like... // "hello" // "hello:(version)" // (version) is a version number if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp && set_string(cconfs[OPT_PEER_VERSION], cp)) goto end; } iobuf_free_content(rbuf); snprintf(whoareyou, sizeof(whoareyou), "whoareyou"); peer_version=get_string(cconfs[OPT_PEER_VERSION]); if(peer_version) { long min_ver=0; long cli_ver=0; if((min_ver=version_to_long("1.3.2"))<0 || (cli_ver=version_to_long(peer_version))<0) return -1; // Stick the server version on the end of the whoareyou string. // if the client version is recent enough. if(min_ver<=cli_ver) snprintf(whoareyou, sizeof(whoareyou), "whoareyou:%s", VERSION); } if(asfd->write_str(asfd, CMD_GEN, whoareyou) || asfd->read(asfd)) { logp("unable to get client name\n"); goto end; } if(!(cname=strdup_w(rbuf->buf, __func__))) goto end; if(!get_int(globalcs[OPT_CNAME_FQDN])) strip_fqdn(&cname); if(get_int(globalcs[OPT_CNAME_LOWERCASE])) strlwr(cname); if(set_string(cconfs[OPT_CNAME], cname)) goto end; iobuf_free_content(rbuf); if(asfd->write_str(asfd, CMD_GEN, "okpassword") || asfd->read(asfd)) { logp("unable to get password for client %s\n", get_string(cconfs[OPT_CNAME])); goto end; } password=rbuf->buf; iobuf_init(rbuf); if(check_client_and_password(globalcs, password, cconfs)) goto end; if(get_int(cconfs[OPT_VERSION_WARN])) version_warn(asfd, globalcs, cconfs); logp("auth ok for: %s%s\n", get_string(cconfs[OPT_CNAME]), get_int(cconfs[OPT_PASSWORD_CHECK])? "":" (no password needed)"); if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end; ret=0; end: iobuf_free_content(rbuf); free_w(&password); free_w(&cname); return ret; }
int authorise_client(struct asfd *asfd, char **server_version, const char *cname, const char *password, struct cntr *cntr) { int ret=-1; char hello[256]=""; struct iobuf *rbuf=asfd->rbuf; snprintf(hello, sizeof(hello), "hello:%s", VERSION); if(asfd->write_str(asfd, CMD_GEN, hello)) { logp("problem with auth\n"); goto end; } if(asfd->read(asfd) || rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "whoareyou")) { logp("problem with auth\n"); goto end; } if(rbuf->buf) { char *cp=NULL; if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp && !(*server_version=strdup_w(cp, __func__))) goto end; } iobuf_free_content(rbuf); } if(asfd->write_str(asfd, CMD_GEN, cname) || asfd_read_expect(asfd, CMD_GEN, "okpassword") || asfd->write_str(asfd, CMD_GEN, password) || asfd->read(asfd)) { logp("problem with auth\n"); goto end; } if(rbuf->cmd==CMD_WARNING) // special case for the version warning { //logw(conf->p1cntr, rbuf->buf); logp("WARNING: %s\n", rbuf->buf); cntr_add(cntr, rbuf->cmd, 0); iobuf_free_content(rbuf); if(asfd->read(asfd)) { logp("problem with auth\n"); goto end; } } if(rbuf->cmd==CMD_GEN && !strcmp(rbuf->buf, "ok")) { // It is OK. logp("auth ok\n"); } else { iobuf_log_unexpected(rbuf, __func__); goto end; } ret=0; end: iobuf_free_content(rbuf); return ret; }
int run_backup(struct async *as, struct sdirs *sdirs, struct conf **cconfs, const char *incexc, int *timer_ret, int resume) { int ret; char okstr[32]=""; struct asfd *asfd=as->asfd; struct iobuf *rbuf=asfd->rbuf; const char *cname=get_string(cconfs[OPT_CNAME]); if(get_string(cconfs[OPT_RESTORE_CLIENT])) { // This client is not the original client, so a backup might // cause all sorts of trouble. logp("Not allowing backup of %s\n", cname); return asfd->write_str(asfd, CMD_GEN, "Backup is not allowed"); } // Set quality of service bits on backups. asfd->set_bulk_packets(asfd); if(!strncmp_w(rbuf->buf, "backupphase1timed")) { int a=0; const char *args[12]; int checkonly=!strncmp_w(rbuf->buf, "backupphase1timedcheck"); if(checkonly) logp("Client asked for a timer check only.\n"); args[a++]=get_string(cconfs[OPT_TIMER_SCRIPT]); args[a++]=cname; args[a++]=sdirs->current; args[a++]=sdirs->clients; args[a++]="reserved1"; args[a++]="reserved2"; args[a++]=NULL; if((*timer_ret=run_script(asfd, args, get_strlist(cconfs[OPT_TIMER_ARG]), cconfs, 1 /* wait */, 1 /* use logp */, 0 /* no log_remote */ ))<0) { logp("Error running timer script for %s\n", cname); return *timer_ret; } if(*timer_ret) { if(!checkonly) logp("Not running backup of %s\n", cname); return asfd->write_str(asfd, CMD_GEN, "timer conditions not met"); } if(checkonly) { logp("Client asked for a timer check only,\n"); logp("so a backup is not happening right now.\n"); return asfd->write_str(asfd, CMD_GEN, "timer conditions met"); } logp("Running backup of %s\n", cname); } else if(!get_int(cconfs[OPT_CLIENT_CAN_FORCE_BACKUP])) { logp("Not allowing forced backup of %s\n", cname); return asfd->write_str(asfd, CMD_GEN, "Forced backup is not allowed"); } snprintf(okstr, sizeof(okstr), "%s:%d", resume?"resume":"ok", get_int(cconfs[OPT_COMPRESSION])); if(asfd->write_str(asfd, CMD_GEN, okstr)) return -1; if((ret=do_backup_server(as, sdirs, cconfs, incexc, resume))) goto end; if((ret=delete_backups(sdirs, cname, get_strlist(cconfs[OPT_KEEP]), get_string(cconfs[OPT_MANUAL_DELETE])))) goto end; end: return ret; }
static int extra_comms_read(struct async *as, struct vers *vers, int *srestore, char **incexc, struct conf **globalcs, struct conf **cconfs) { int ret=-1; struct asfd *asfd; struct iobuf *rbuf; asfd=as->asfd; rbuf=asfd->rbuf; while(1) { iobuf_free_content(rbuf); if(asfd->read(asfd)) goto end; if(rbuf->cmd!=CMD_GEN) { iobuf_log_unexpected(rbuf, __func__); goto end; } if(!strcmp(rbuf->buf, "extra_comms_end")) { if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end ok")) goto end; break; } else if(!strncmp_w(rbuf->buf, "autoupgrade:")) { char *os=NULL; const char *autoupgrade_dir= get_string(globalcs[OPT_AUTOUPGRADE_DIR]); os=rbuf->buf+strlen("autoupgrade:"); iobuf_free_content(rbuf); if(os && *os && autoupgrade_server(as, vers->ser, vers->cli, os, get_cntr(globalcs), autoupgrade_dir)) goto end; } else if(!strcmp(rbuf->buf, "srestore ok")) { iobuf_free_content(rbuf); // Client can accept the restore. // Load the restore config, then send it. *srestore=1; if(conf_parse_incexcs_path(cconfs, get_string(cconfs[OPT_RESTORE_PATH])) || incexc_send_server_restore(asfd, cconfs)) goto end; // Do not unlink it here - wait until // the client says that it wants to do the // restore. // Also need to leave it around if the // restore is to an alternative client, so // that the code below that reloads the config // can read it again. //unlink(get_string(cconfs[OPT_RESTORE_PATH])); } else if(!strcmp(rbuf->buf, "srestore not ok")) { const char *restore_path=get_string( cconfs[OPT_RESTORE_PATH]); // Client will not accept the restore. unlink(restore_path); if(set_string(cconfs[OPT_RESTORE_PATH], NULL)) goto end; logp("Client not accepting server initiated restore.\n"); } else if(!strcmp(rbuf->buf, "sincexc ok")) { // Client can accept incexc conf from the // server. iobuf_free_content(rbuf); if(incexc_send_server(asfd, cconfs)) goto end; } else if(!strcmp(rbuf->buf, "incexc")) { // Client is telling server its incexc // configuration so that it can better decide // what to do on resume. iobuf_free_content(rbuf); if(incexc_recv_server(asfd, incexc, globalcs)) goto end; if(*incexc) { char *tmp=NULL; char comp[32]=""; snprintf(comp, sizeof(comp), "compression = %d\n", get_int(cconfs[OPT_COMPRESSION])); if(!(tmp=prepend(*incexc, comp))) goto end; free_w(incexc); *incexc=tmp; } } else if(!strcmp(rbuf->buf, "countersok")) { // Client can accept counters on // resume/verify/restore. logp("Client supports being sent counters.\n"); set_int(cconfs[OPT_SEND_CLIENT_CNTR], 1); } else if(!strncmp_w(rbuf->buf, "uname=") && strlen(rbuf->buf)>strlen("uname=")) { char *uname=rbuf->buf+strlen("uname="); if(!strncasecmp("Windows", uname, strlen("Windows"))) set_int(cconfs[OPT_CLIENT_IS_WINDOWS], 1); } else if(!strncmp_w(rbuf->buf, "orig_client=") && strlen(rbuf->buf)>strlen("orig_client=")) { if(conf_switch_to_orig_client(globalcs, cconfs, rbuf->buf+strlen("orig_client="))) goto end; // If this started out as a server-initiated // restore, need to load the restore file // again. if(*srestore) { if(conf_parse_incexcs_path(cconfs, get_string(cconfs[OPT_RESTORE_PATH]))) goto end; } if(asfd->write_str(asfd, CMD_GEN, "orig_client ok")) goto end; } else if(!strncmp_w(rbuf->buf, "restore_spool=")) { // Client supports temporary spool directory // for restores. if(set_string(cconfs[OPT_RESTORE_SPOOL], rbuf->buf+strlen("restore_spool="))) goto end; } else if(!strncmp_w(rbuf->buf, "protocol=")) { char msg[128]=""; // Client wants to set protocol. enum protocol protocol=get_protocol(cconfs); if(protocol!=PROTO_AUTO) { snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s but server is set to protocol=%d\n", rbuf->buf, protocol); log_and_send_oom(asfd, __func__); goto end; } else if(!strcmp(rbuf->buf+strlen("protocol="), "1")) { set_protocol(cconfs, PROTO_1); set_protocol(globalcs, PROTO_1); } else if(!strcmp(rbuf->buf+strlen("protocol="), "2")) { set_protocol(cconfs, PROTO_2); set_protocol(globalcs, PROTO_2); } else { snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s, which is unknown\n", rbuf->buf); log_and_send_oom(asfd, __func__); goto end; } logp("Client has set protocol=%d\n", (int)get_protocol(cconfs)); } else if(!strncmp_w(rbuf->buf, "rshash=blake2")) { #ifdef RS_DEFAULT_STRONG_LEN logp("Client is trying to use librsync hash blake2, but server does not support it.\n"); goto end; #else set_e_rshash(cconfs[OPT_RSHASH], RSHASH_BLAKE2); set_e_rshash(globalcs[OPT_RSHASH], RSHASH_BLAKE2); #endif } else if(!strncmp_w(rbuf->buf, "msg")) { set_int(cconfs[OPT_MESSAGE], 1); set_int(globalcs[OPT_MESSAGE], 1); } else { iobuf_log_unexpected(rbuf, __func__); goto end; } } ret=0; end: iobuf_free_content(rbuf); return ret; }
int authorise_server(struct asfd *asfd, struct conf *conf, struct conf *cconf) { int ret=-1; char *cp=NULL; char *password=NULL; char whoareyou[256]=""; struct iobuf *rbuf=asfd->rbuf; if(asfd->read(asfd)) { logp("unable to read initial message\n"); goto end; } if(rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "hello")) { iobuf_log_unexpected(rbuf, __func__); goto end; } // String may look like... // "hello" // "hello:(version)" // (version) is a version number if((cp=strchr(rbuf->buf, ':'))) { cp++; if(cp && !(cconf->peer_version=strdup_w(cp, __func__))) goto end; } iobuf_free_content(rbuf); snprintf(whoareyou, sizeof(whoareyou), "whoareyou"); if(cconf->peer_version) { long min_ver=0; long cli_ver=0; if((min_ver=version_to_long("1.3.2"))<0 || (cli_ver=version_to_long(cconf->peer_version))<0) return -1; // Stick the server version on the end of the whoareyou string. // if the client version is recent enough. if(min_ver<=cli_ver) snprintf(whoareyou, sizeof(whoareyou), "whoareyou:%s", VERSION); } asfd->write_str(asfd, CMD_GEN, whoareyou); if(asfd->read(asfd)) { logp("unable to get client name\n"); goto end; } cconf->cname=rbuf->buf; iobuf_init(rbuf); asfd->write_str(asfd, CMD_GEN, "okpassword"); if(asfd->read(asfd)) { logp("unable to get password for client %s\n", cconf->cname); goto end; } password=rbuf->buf; iobuf_init(rbuf); if(check_client_and_password(conf, password, cconf)) goto end; if(cconf->version_warn) version_warn(asfd, conf, cconf); logp("auth ok for: %s%s\n", cconf->cname, cconf->password_check?"":" (no password needed)"); asfd->write_str(asfd, CMD_GEN, "ok"); ret=0; end: iobuf_free_content(rbuf); free_w(&password); return ret; }
static int run_action_server_do(struct async *as, struct sdirs *sdirs, const char *incexc, int srestore, int *timer_ret, struct conf **cconfs) { int ret; int resume=0; char msg[256]=""; struct iobuf *rbuf=as->asfd->rbuf; // Make sure some directories exist. if(mkpath(&sdirs->current, sdirs->dedup)) { snprintf(msg, sizeof(msg), "could not mkpath %s", sdirs->current); log_and_send(as->asfd, msg); return -1; } if(rbuf->cmd!=CMD_GEN) return unknown_command(as->asfd); // List and diff should work even while backups are running. if(!strncmp_w(rbuf->buf, "list ") || !strncmp_w(rbuf->buf, "listb ")) return run_list(as->asfd, sdirs, cconfs); if(!strncmp_w(rbuf->buf, "diff ")) return run_diff(as->asfd, sdirs, cconfs); switch((ret=get_lock_sdirs(as->asfd, sdirs))) { case 0: break; // OK. case 1: return 1; // Locked out. default: // Error. maybe_do_notification(as->asfd, ret, "", "error in get_lock_sdirs()", "", buf_to_notify_str(rbuf), cconfs); return -1; } switch((ret=check_for_rubble(as, sdirs, incexc, &resume, cconfs))) { case 0: break; // OK. case 1: return 1; // Now finalising. default: // Error. maybe_do_notification(as->asfd, ret, "", "error in check_for_rubble()", "", buf_to_notify_str(rbuf), cconfs); return -1; } if(!strncmp_w(rbuf->buf, "backup")) { ret=run_backup(as, sdirs, cconfs, incexc, timer_ret, resume); if(*timer_ret<0) maybe_do_notification(as->asfd, ret, "", "error running timer script", "", "backup", cconfs); else if(!*timer_ret) maybe_do_notification(as->asfd, ret, sdirs->client, sdirs->current, "log", "backup", cconfs); return ret; } if(!strncmp_w(rbuf->buf, "restore ") || !strncmp_w(rbuf->buf, "verify ")) return run_restore(as->asfd, sdirs, cconfs, srestore); if(!strncmp_w(rbuf->buf, "Delete ")) return run_delete(as->asfd, sdirs, cconfs); // Old clients will send 'delete', possibly accidentally due to the // user trying to use the new diff/long diff options. // Stop them from working, just to be safe. if(!strncmp_w(rbuf->buf, "delete ")) { logp("old style delete from %s denied\n", get_string(cconfs[OPT_CNAME])); as->asfd->write_str(as->asfd, CMD_ERROR, "old style delete is not supported on this server"); return -1; } return unknown_command(as->asfd); }
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; }
static int run_restore(struct asfd *asfd, struct sdirs *sdirs, struct conf **cconfs, int srestore) { int ret=-1; char *cp=NULL; char *copy=NULL; enum action act; char *backupnostr=NULL; char *restoreregex=NULL; char *dir_for_notify=NULL; struct iobuf *rbuf=asfd->rbuf; const char *cname=get_string(cconfs[OPT_CNAME]); if(!(copy=strdup_w(rbuf->buf, __func__))) goto end; iobuf_free_content(rbuf); if(!strncmp_w(copy, "restore ")) act=ACTION_RESTORE; else act=ACTION_VERIFY; if(!(backupnostr=strchr(copy, ' '))) { logp("Could not parse %s in %s\n", copy, __func__); goto end; } backupnostr++; if(set_string(cconfs[OPT_BACKUP], backupnostr)) goto end; // FIX THIS. if((cp=strchr(cconfs[OPT_BACKUP]->data.s, ':'))) *cp='\0'; if(act==ACTION_RESTORE) { int r; if((r=client_can_restore(cconfs))<0) goto end; else if(!r) { logp("Not allowing restore of %s\n", cname); if(!asfd->write_str(asfd, CMD_GEN, "Client restore is not allowed")) ret=0; goto end; } } if(act==ACTION_VERIFY && !(client_can_generic(cconfs, OPT_CLIENT_CAN_VERIFY))) { logp("Not allowing verify of %s\n", cname); if(!asfd->write_str(asfd, CMD_GEN, "Client verify is not allowed")) ret=0; goto end; } if((restoreregex=strchr(copy, ':'))) { *restoreregex='\0'; restoreregex++; } if(restoreregex && *restoreregex && set_string(cconfs[OPT_REGEX], restoreregex)) goto end; if(asfd->write_str(asfd, CMD_GEN, "ok")) goto end; ret=do_restore_server(asfd, sdirs, act, srestore, &dir_for_notify, cconfs); if(dir_for_notify) maybe_do_notification(asfd, ret, sdirs->client, dir_for_notify, act==ACTION_RESTORE?"restorelog":"verifylog", act==ACTION_RESTORE?"restore":"verify", cconfs); end: free_w(©); free_w(&dir_for_notify); return ret; }