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