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