1 #include "../burp.h"
2 #include "../alloc.h"
3 #include "../asfd.h"
4 #include "../async.h"
5 #include "../cmd.h"
6 #include "../conf.h"
7 #include "../conffile.h"
8 #include "../fsops.h"
9 #include "../handy.h"
10 #include "../incexc_recv.h"
11 #include "../incexc_send.h"
12 #include "../iobuf.h"
13 #include "../log.h"
14 #include "../pathcmp.h"
15 #include "../prepend.h"
16 #include "autoupgrade.h"
17 #include "extra_comms.h"
18
19 #include <librsync.h>
20
append_to_feat(char ** feat,const char * str)21 static int append_to_feat(char **feat, const char *str)
22 {
23 char *tmp=NULL;
24 if(!*feat)
25 {
26 if(!(*feat=strdup_w(str, __func__)))
27 return -1;
28 return 0;
29 }
30 if(!(tmp=prepend(*feat, str)))
31 return -1;
32 free_w(feat);
33 *feat=tmp;
34 return 0;
35 }
36
37 // It is unfortunate that we are having to figure out the server-initiated
38 // restore paths here instead of setting it in a struct sdirs.
39 // But doing the extra_comms needs to come before setting the sdirs, because
40 // extra_comms sets up a bunch of settings that sdirs need to know.
get_restorepath_proto1(struct conf ** cconfs)41 static char *get_restorepath_proto1(struct conf **cconfs)
42 {
43 char *tmp=NULL;
44 char *restorepath=NULL;
45 if((tmp=prepend_s(get_string(cconfs[OPT_DIRECTORY]),
46 get_string(cconfs[OPT_CNAME]))))
47 restorepath=prepend_s(tmp, "restore");
48 free_w(&tmp);
49 return restorepath;
50 }
51
get_restorepath_proto2(struct conf ** cconfs)52 static char *get_restorepath_proto2(struct conf **cconfs)
53 {
54 char *tmp1=NULL;
55 char *tmp2=NULL;
56 char *restorepath=NULL;
57 if(!(tmp1=prepend_s(get_string(cconfs[OPT_DIRECTORY]),
58 get_string(cconfs[OPT_DEDUP_GROUP]))))
59 goto error;
60 if(!(tmp2=prepend_s(tmp1, "clients")))
61 goto error;
62 free_w(&tmp1);
63 if(!(tmp1=prepend_s(tmp2, get_string(cconfs[OPT_CNAME]))))
64 goto error;
65 if(!(restorepath=prepend_s(tmp1, "restore")))
66 goto error;
67 goto end;
68 error:
69 free_w(&restorepath);
70 end:
71 free_w(&tmp1);
72 free_w(&tmp2);
73 return restorepath;
74 }
75
set_restore_path(struct conf ** cconfs,char ** feat)76 static int set_restore_path(struct conf **cconfs, char **feat)
77 {
78 int ret=-1;
79 char *restorepath1=NULL;
80 char *restorepath2=NULL;
81 if(!(restorepath1=get_restorepath_proto1(cconfs))
82 || !(restorepath2=get_restorepath_proto2(cconfs)))
83 goto end;
84 if(is_reg_lstat(restorepath1)==1
85 && set_string(cconfs[OPT_RESTORE_PATH], restorepath1))
86 goto end;
87 else if(is_reg_lstat(restorepath2)==1
88 && set_string(cconfs[OPT_RESTORE_PATH], restorepath2))
89 goto end;
90 if(get_string(cconfs[OPT_RESTORE_PATH])
91 && append_to_feat(feat, "srestore:"))
92 goto end;
93 ret=0;
94 end:
95 free_w(&restorepath1);
96 free_w(&restorepath2);
97 return ret;
98 }
99
100 struct vers
101 {
102 long min;
103 long cli;
104 long ser;
105 long feat_list;
106 long directory_tree;
107 long burp2;
108 long counters_json;
109 };
110
send_features(struct asfd * asfd,struct conf ** cconfs,struct vers * vers)111 static int send_features(struct asfd *asfd, struct conf **cconfs,
112 struct vers *vers)
113 {
114 int ret=-1;
115 char *feat=NULL;
116 enum protocol protocol=get_protocol(cconfs);
117 struct strlist *startdir=get_strlist(cconfs[OPT_STARTDIR]);
118 struct strlist *incglob=get_strlist(cconfs[OPT_INCGLOB]);
119
120 if(append_to_feat(&feat, "extra_comms_begin ok:")
121 /* clients can autoupgrade */
122 || append_to_feat(&feat, "autoupgrade:")
123 /* clients can give server incexc conf so that the
124 server knows better what to do on resume */
125 || append_to_feat(&feat, "incexc:")
126 /* clients can give the server an alternative client
127 to restore from */
128 || append_to_feat(&feat, "orig_client:")
129 /* clients can tell the server what kind of system they are. */
130 || append_to_feat(&feat, "uname:")
131 || append_to_feat(&feat, "failover:")
132 || append_to_feat(&feat, "vss_restore:")
133 || append_to_feat(&feat, "regex_icase:"))
134 goto end;
135
136 /* Clients can receive restore initiated from the server. */
137 if(set_restore_path(cconfs, &feat))
138 goto end;
139
140 /* Clients can receive incexc conf from the server.
141 Only give it as an option if the server has some starting
142 directory configured in the clientconfdir. */
143 if((startdir || incglob)
144 && append_to_feat(&feat, "sincexc:"))
145 goto end;
146
147 if(vers->cli>=vers->counters_json)
148 {
149 /* Clients can be sent cntrs on resume/verify/restore. */
150 if(append_to_feat(&feat, "counters_json:"))
151 goto end;
152 }
153
154 // We support CMD_MESSAGE.
155 if(append_to_feat(&feat, "msg:"))
156 goto end;
157
158 if(protocol==PROTO_AUTO)
159 {
160 /* If the server is configured to use either protocol, let the
161 client know that it can choose. */
162 logp("Server is using protocol=0 (auto)\n");
163 if(append_to_feat(&feat, "csetproto:"))
164 goto end;
165 }
166 else
167 {
168 char p[32]="";
169 /* Tell the client what we are going to use. */
170 logp("Server is using protocol=%d\n", (int)protocol);
171 snprintf(p, sizeof(p), "forceproto=%d:", (int)protocol);
172 if(append_to_feat(&feat, p))
173 goto end;
174 }
175
176 #ifdef HAVE_BLAKE2
177 if(append_to_feat(&feat, "rshash=blake2:"))
178 goto end;
179 #endif
180
181 if(append_to_feat(&feat, "seed:"))
182 goto end;
183
184 //printf("feat: %s\n", feat);
185
186 if(asfd->write_str(asfd, CMD_GEN, feat))
187 {
188 logp("problem in extra_comms\n");
189 goto end;
190 }
191
192 ret=0;
193 end:
194 free_w(&feat);
195 return ret;
196 }
197
do_autoupgrade(struct asfd * asfd,struct vers * vers,struct conf ** globalcs)198 static int do_autoupgrade(struct asfd *asfd, struct vers *vers,
199 struct conf **globalcs)
200 {
201 int ret=-1;
202 char *os=NULL;
203 struct iobuf *rbuf=asfd->rbuf;
204 const char *autoupgrade_dir=get_string(globalcs[OPT_AUTOUPGRADE_DIR]);
205
206 if(!(os=strdup_w(rbuf->buf+strlen("autoupgrade:"), __func__)))
207 goto end;
208 iobuf_free_content(rbuf);
209 ret=0;
210 if(os && *os)
211 {
212 // Sanitise path separators
213 for(char *i=os; *i; ++i)
214 if(*i == '/' || *i == '\\' || *i == ':')
215 *i='-';
216
217 ret=autoupgrade_server(asfd, vers->ser,
218 vers->cli, os, get_cntr(globalcs),
219 autoupgrade_dir);
220 }
221 end:
222 free_w(&os);
223 return ret;
224 }
225
setup_seed(struct asfd * asfd,struct conf ** cconfs,struct iobuf * rbuf,const char * what,enum conf_opt opt)226 static int setup_seed(
227 struct asfd *asfd,
228 struct conf **cconfs,
229 struct iobuf *rbuf,
230 const char *what,
231 enum conf_opt opt
232 ) {
233 int ret=-1;
234 char *tmp=NULL;
235 char *str=NULL;
236
237 str=rbuf->buf+strlen(what)+1;
238 strip_trailing_slashes(&str);
239
240 if(!is_absolute(str))
241 {
242 char msg[128];
243 snprintf(msg, sizeof(msg), "A %s needs to be absolute!", what);
244 log_and_send(asfd, msg);
245 goto end;
246 }
247 if(opt==OPT_SEED_SRC && *str!='/')
248 {
249 printf("here: %s\n", str);
250 // More windows hacks - add a slash to the beginning of things
251 // like 'C:'.
252 if(astrcat(&tmp, "/", __func__)
253 || astrcat(&tmp, str, __func__))
254 goto end;
255 str=tmp;
256 }
257 if(set_string(cconfs[opt], str))
258 goto end;
259 ret=0;
260 end:
261 free_w(&tmp);
262 return ret;
263 }
264
extra_comms_read(struct async * as,struct vers * vers,int * srestore,char ** incexc,struct conf ** globalcs,struct conf ** cconfs)265 static int extra_comms_read(struct async *as,
266 struct vers *vers, int *srestore,
267 char **incexc, struct conf **globalcs, struct conf **cconfs)
268 {
269 int ret=-1;
270 struct asfd *asfd;
271 struct iobuf *rbuf;
272 asfd=as->asfd;
273 rbuf=asfd->rbuf;
274
275 while(1)
276 {
277 iobuf_free_content(rbuf);
278 if(asfd->read(asfd)) goto end;
279
280 if(rbuf->cmd!=CMD_GEN)
281 {
282 iobuf_log_unexpected(rbuf, __func__);
283 goto end;
284 }
285
286 if(!strcmp(rbuf->buf, "extra_comms_end"))
287 {
288 if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end ok"))
289 goto end;
290 break;
291 }
292 else if(!strncmp_w(rbuf->buf, "autoupgrade:"))
293 {
294 if(do_autoupgrade(asfd, vers, globalcs))
295 goto end;
296 }
297 else if(!strcmp(rbuf->buf, "srestore ok"))
298 {
299 char *restore_path=get_string(cconfs[OPT_RESTORE_PATH]);
300 if(!restore_path)
301 {
302 logp("got srestore ok without a restore_path");
303 goto end;
304 }
305
306 iobuf_free_content(rbuf);
307 // Client can accept the restore.
308 // Load the restore config, then send it.
309 *srestore=1;
310 // Need to wipe out OPT_INCEXDIR, as it is needed for
311 // srestore includes. If it is not wiped out, it can
312 // interfere if cconfs[OPT_RESTORE_PATH] contained no
313 // includes.
314 set_strlist(cconfs[OPT_INCEXCDIR], NULL);
315 if(conf_parse_incexcs_path(cconfs, restore_path)
316 || incexc_send_server_restore(asfd, cconfs))
317 goto end;
318 // Do not unlink it here - wait until
319 // the client says that it wants to do the
320 // restore.
321 // Also need to leave it around if the
322 // restore is to an alternative client, so
323 // that the code below that reloads the config
324 // can read it again.
325 // NOTE: that appears to be in
326 // src/server/run_action.c::client_can_restore()
327 //unlink(get_string(cconfs[OPT_RESTORE_PATH]));
328 }
329 else if(!strcmp(rbuf->buf, "srestore not ok"))
330 {
331 const char *restore_path=get_string(
332 cconfs[OPT_RESTORE_PATH]);
333 // Client will not accept the restore.
334 if (restore_path)
335 unlink(restore_path);
336 if(set_string(cconfs[OPT_RESTORE_PATH], NULL))
337 goto end;
338 logp("Client not accepting server initiated restore.\n");
339 }
340 else if(!strcmp(rbuf->buf, "sincexc ok"))
341 {
342 // Client can accept incexc conf from the
343 // server.
344 iobuf_free_content(rbuf);
345 if(incexc_send_server(asfd, cconfs))
346 goto end;
347 }
348 else if(!strcmp(rbuf->buf, "incexc"))
349 {
350 // Client is telling server its incexc
351 // configuration so that it can better decide
352 // what to do on resume.
353 iobuf_free_content(rbuf);
354 if(incexc_recv_server(asfd, incexc, globalcs))
355 goto end;
356 if(*incexc)
357 {
358 char *tmp=NULL;
359 char comp[32]="";
360 snprintf(comp, sizeof(comp),
361 "compression = %d\n",
362 get_int(cconfs[OPT_COMPRESSION]));
363 if(!(tmp=prepend(*incexc, comp)))
364 goto end;
365 free_w(incexc);
366 *incexc=tmp;
367 }
368 }
369 else if(!strcmp(rbuf->buf, "counters_json ok"))
370 {
371 // Client can accept counters on
372 // resume/verify/restore.
373 logp("Client supports being sent json counters.\n");
374 set_int(cconfs[OPT_SEND_CLIENT_CNTR], 1);
375 }
376 else if(!strncmp_w(rbuf->buf, "uname=")
377 && strlen(rbuf->buf)>strlen("uname="))
378 {
379 char *uname=rbuf->buf+strlen("uname=");
380 if(!strncasecmp("Windows", uname, strlen("Windows")))
381 set_int(cconfs[OPT_CLIENT_IS_WINDOWS], 1);
382 }
383 else if(!strncmp_w(rbuf->buf, "orig_client=")
384 && strlen(rbuf->buf)>strlen("orig_client="))
385 {
386 if(conf_switch_to_orig_client(globalcs, cconfs,
387 rbuf->buf+strlen("orig_client=")))
388 goto end;
389 // If this started out as a server-initiated
390 // restore, need to load the restore file
391 // again.
392 if(*srestore)
393 {
394 if(conf_parse_incexcs_path(cconfs,
395 get_string(cconfs[OPT_RESTORE_PATH])))
396 goto end;
397 }
398 if(asfd->write_str(asfd, CMD_GEN, "orig_client ok"))
399 goto end;
400 }
401 else if(!strncmp_w(rbuf->buf, "restore_spool="))
402 {
403 // Removed.
404 }
405 else if(!strncmp_w(rbuf->buf, "protocol="))
406 {
407 char msg[128]="";
408 // Client wants to set protocol.
409 enum protocol protocol;
410 enum protocol cprotocol;
411 const char *cliproto=NULL;
412 protocol=get_protocol(cconfs);
413 cliproto=rbuf->buf+strlen("protocol=");
414 cprotocol=atoi(cliproto);
415
416 if(protocol!=PROTO_AUTO)
417 {
418 if(protocol==cprotocol)
419 {
420 logp("Client is forcing protocol=%d\n", (int)protocol);
421 continue;
422 }
423 snprintf(msg, sizeof(msg), "Client is trying to use protocol=%d but server is set to protocol=%d\n", (int)cprotocol, (int)protocol);
424 log_and_send(asfd, msg);
425 goto end;
426 }
427 else if(cprotocol==PROTO_1)
428 {
429 set_protocol(cconfs, cprotocol);
430 set_protocol(globalcs, cprotocol);
431 }
432 else if(cprotocol==PROTO_2)
433 {
434 set_protocol(cconfs, cprotocol);
435 set_protocol(globalcs, cprotocol);
436 }
437 else
438 {
439 snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s, which is unknown\n", cliproto);
440 log_and_send(asfd, msg);
441 goto end;
442 }
443 logp("Client has set protocol=%d\n",
444 (int)get_protocol(cconfs));
445 }
446 else if(!strncmp_w(rbuf->buf, "rshash=blake2"))
447 {
448 #ifdef HAVE_BLAKE2
449 set_e_rshash(cconfs[OPT_RSHASH], RSHASH_BLAKE2);
450 set_e_rshash(globalcs[OPT_RSHASH], RSHASH_BLAKE2);
451 #else
452 logp("Client is trying to use librsync hash blake2, but server does not support it.\n");
453 goto end;
454 #endif
455 }
456 else if(!strncmp_w(rbuf->buf, "msg"))
457 {
458 set_int(cconfs[OPT_MESSAGE], 1);
459 set_int(globalcs[OPT_MESSAGE], 1);
460 }
461 else if(!strncmp_w(rbuf->buf, "backup_failovers_left="))
462 {
463 int l;
464 l=atoi(rbuf->buf+strlen("backup_failovers_left="));
465 set_int(cconfs[OPT_BACKUP_FAILOVERS_LEFT], l);
466 set_int(globalcs[OPT_BACKUP_FAILOVERS_LEFT], l);
467 }
468 else if(!strncmp_w(rbuf->buf, "seed_src="))
469 {
470 if(setup_seed(asfd, cconfs,
471 rbuf, "seed_src", OPT_SEED_SRC))
472 goto end;
473 }
474 else if(!strncmp_w(rbuf->buf, "seed_dst="))
475 {
476 if(setup_seed(asfd, cconfs,
477 rbuf, "seed_dst", OPT_SEED_DST))
478 goto end;
479 }
480 else if(!strncmp_w(rbuf->buf, "vss_restore=off"))
481 {
482 set_int(cconfs[OPT_VSS_RESTORE], VSS_RESTORE_OFF);
483 set_int(globalcs[OPT_VSS_RESTORE], VSS_RESTORE_OFF);
484 }
485 else if(!strncmp_w(rbuf->buf, "vss_restore=strip"))
486 {
487 set_int(cconfs[OPT_VSS_RESTORE], VSS_RESTORE_OFF_STRIP);
488 set_int(globalcs[OPT_VSS_RESTORE], VSS_RESTORE_OFF_STRIP);
489 }
490 else if(!strncmp_w(rbuf->buf, "regex_icase=1"))
491 {
492 set_int(cconfs[OPT_REGEX_CASE_INSENSITIVE], 1);
493 set_int(globalcs[OPT_REGEX_CASE_INSENSITIVE], 1);
494 }
495 else
496 {
497 iobuf_log_unexpected(rbuf, __func__);
498 goto end;
499 }
500 }
501
502 ret=0;
503 end:
504 iobuf_free_content(rbuf);
505 return ret;
506 }
507
vers_init(struct vers * vers,struct conf ** cconfs)508 static int vers_init(struct vers *vers, struct conf **cconfs)
509 {
510 memset(vers, 0, sizeof(struct vers));
511 return ((vers->min=version_to_long("1.2.7"))<0
512 || (vers->cli=version_to_long(get_string(cconfs[OPT_PEER_VERSION])))<0
513 || (vers->ser=version_to_long(PACKAGE_VERSION))<0
514 || (vers->feat_list=version_to_long("1.3.0"))<0
515 || (vers->directory_tree=version_to_long("1.3.6"))<0
516 || (vers->burp2=version_to_long("2.0.0"))<0
517 || (vers->counters_json=version_to_long("2.0.46"))<0);
518 }
519
check_seed(struct asfd * asfd,struct conf ** cconfs)520 static int check_seed(struct asfd *asfd, struct conf **cconfs)
521 {
522 char msg[128]="";
523 const char *src=get_string(cconfs[OPT_SEED_SRC]);
524 const char *dst=get_string(cconfs[OPT_SEED_DST]);
525 if(!src && !dst)
526 return 0;
527 if(src && dst)
528 {
529 logp("Seeding '%s' -> '%s'\n", src, dst);
530 return 0;
531 }
532 snprintf(msg, sizeof(msg),
533 "You must specify %s and %s options together, or not at all.",
534 cconfs[OPT_SEED_SRC]->field,
535 cconfs[OPT_SEED_DST]->field);
536 log_and_send(asfd, msg);
537 return -1;
538 }
539
extra_comms(struct async * as,char ** incexc,int * srestore,struct conf ** confs,struct conf ** cconfs)540 int extra_comms(struct async *as,
541 char **incexc, int *srestore, struct conf **confs, struct conf **cconfs)
542 {
543 struct vers vers;
544 struct asfd *asfd;
545 asfd=as->asfd;
546 //char *restorepath=NULL;
547 const char *peer_version=NULL;
548
549 if(vers_init(&vers, cconfs))
550 goto error;
551
552 if(vers.cli<vers.directory_tree)
553 {
554 set_int(confs[OPT_DIRECTORY_TREE], 0);
555 set_int(cconfs[OPT_DIRECTORY_TREE], 0);
556 }
557
558 // Clients before 1.2.7 did not know how to do extra comms, so skip
559 // this section for them.
560 if(vers.cli<vers.min)
561 return 0;
562
563 if(asfd_read_expect(asfd, CMD_GEN, "extra_comms_begin"))
564 {
565 logp("problem reading in extra_comms\n");
566 goto error;
567 }
568 // Want to tell the clients the extra comms features that are
569 // supported, so that new clients are more likely to work with old
570 // servers.
571 if(vers.cli==vers.feat_list)
572 {
573 // 1.3.0 did not support the feature list.
574 if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin ok"))
575 {
576 logp("problem writing in extra_comms\n");
577 goto error;
578 }
579 }
580 else
581 {
582 if(send_features(asfd, cconfs, &vers))
583 goto error;
584 }
585
586 if(extra_comms_read(as, &vers, srestore, incexc, confs, cconfs))
587 goto error;
588
589 peer_version=get_string(cconfs[OPT_PEER_VERSION]);
590
591 // This needs to come after extra_comms_read, as the client might
592 // have set PROTO_1 or PROTO_2.
593 switch(get_protocol(cconfs))
594 {
595 case PROTO_AUTO:
596 // The protocol has not been specified. Make a choice.
597 if(vers.cli<vers.burp2)
598 {
599 // Client is burp-1.x.x, use protocol1.
600 set_protocol(confs, PROTO_1);
601 set_protocol(cconfs, PROTO_1);
602 logp("Client is %s-%s - using protocol=%d\n",
603 PACKAGE_TARNAME,
604 peer_version, PROTO_1);
605 }
606 else
607 {
608 // Client is burp-2.x.x, use protocol2.
609 // This will probably never be reached because
610 // the negotiation will take care of it.
611 /*
612 set_protocol(confs, PROTO_2);
613 set_protocol(cconfs, PROTO_2);
614 logp("Client is %s-%s - using protocol=%d\n",
615 PACKAGE_TARNAME,
616 peer_version, PROTO_2);
617 */
618 // PROTO_1 is safer for now.
619 set_protocol(confs, PROTO_1);
620 set_protocol(cconfs, PROTO_1);
621 logp("Client is %s-%s - using protocol=%d\n",
622 PACKAGE_TARNAME,
623 peer_version, PROTO_1);
624 }
625 break;
626 case PROTO_1:
627 // It is OK for the client to be burp1 and for the
628 // server to be forced to protocol1.
629 break;
630 case PROTO_2:
631 if(vers.cli>=vers.burp2)
632 break;
633 logp("protocol=%d is set server side, "
634 "but client is %s version %s\n",
635 PROTO_2, PACKAGE_TARNAME, peer_version);
636 goto error;
637 }
638
639 if(get_protocol(cconfs)==PROTO_1)
640 {
641 if(get_e_rshash(cconfs[OPT_RSHASH])==RSHASH_UNSET)
642 {
643 set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);
644 set_e_rshash(cconfs[OPT_RSHASH], RSHASH_MD4);
645 }
646 }
647
648 if(check_seed(asfd, cconfs))
649 goto error;
650
651 return 0;
652 error:
653 return -1;
654 }
655