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