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 "../prepend.h"
15 : #include "autoupgrade.h"
16 : #include "extra_comms.h"
17 :
18 : #include <librsync.h>
19 :
20 314 : static int append_to_feat(char **feat, const char *str)
21 : {
22 314 : char *tmp=NULL;
23 314 : if(!*feat)
24 : {
25 35 : if(!(*feat=strdup_w(str, __func__)))
26 : return -1;
27 35 : return 0;
28 : }
29 279 : if(!(tmp=prepend(*feat, str)))
30 : return -1;
31 279 : free_w(feat);
32 279 : *feat=tmp;
33 279 : return 0;
34 : }
35 :
36 : // It is unfortunate that we are having to figure out the server-initiated
37 : // restore paths here instead of setting it in a struct sdirs.
38 : // But doing the extra_comms needs to come before setting the sdirs, because
39 : // extra_comms sets up a bunch of settings that sdirs need to know.
40 35 : static char *get_restorepath_proto1(struct conf **cconfs)
41 : {
42 35 : char *tmp=NULL;
43 35 : char *restorepath=NULL;
44 35 : if((tmp=prepend_s(get_string(cconfs[OPT_DIRECTORY]),
45 35 : get_string(cconfs[OPT_CNAME]))))
46 35 : restorepath=prepend_s(tmp, "restore");
47 35 : free_w(&tmp);
48 35 : return restorepath;
49 : }
50 :
51 35 : static char *get_restorepath_proto2(struct conf **cconfs)
52 : {
53 35 : char *tmp1=NULL;
54 35 : char *tmp2=NULL;
55 35 : char *restorepath=NULL;
56 35 : if(!(tmp1=prepend_s(get_string(cconfs[OPT_DIRECTORY]),
57 35 : get_string(cconfs[OPT_DEDUP_GROUP]))))
58 : goto error;
59 35 : if(!(tmp2=prepend_s(tmp1, "clients")))
60 : goto error;
61 35 : free_w(&tmp1);
62 35 : if(!(tmp1=prepend_s(tmp2, get_string(cconfs[OPT_CNAME]))))
63 : goto error;
64 35 : if(!(restorepath=prepend_s(tmp1, "restore")))
65 : goto error;
66 : goto end;
67 : error:
68 0 : free_w(&restorepath);
69 : end:
70 35 : free_w(&tmp1);
71 35 : free_w(&tmp2);
72 35 : return restorepath;
73 : }
74 :
75 35 : static int set_restore_path(struct conf **cconfs, char **feat)
76 : {
77 35 : int ret=-1;
78 35 : char *restorepath1=NULL;
79 35 : char *restorepath2=NULL;
80 35 : if(!(restorepath1=get_restorepath_proto1(cconfs))
81 35 : || !(restorepath2=get_restorepath_proto2(cconfs)))
82 : goto end;
83 35 : if(is_reg_lstat(restorepath1)==1
84 4 : && set_string(cconfs[OPT_RESTORE_PATH], restorepath1))
85 : goto end;
86 35 : else if(is_reg_lstat(restorepath2)==1
87 0 : && set_string(cconfs[OPT_RESTORE_PATH], restorepath2))
88 : goto end;
89 35 : if(get_string(cconfs[OPT_RESTORE_PATH])
90 4 : && append_to_feat(feat, "srestore:"))
91 : goto end;
92 : ret=0;
93 : end:
94 35 : free_w(&restorepath1);
95 35 : free_w(&restorepath2);
96 35 : return ret;
97 : }
98 :
99 : struct vers
100 : {
101 : long min;
102 : long cli;
103 : long ser;
104 : long feat_list;
105 : long directory_tree;
106 : long burp2;
107 : long counters_json;
108 : };
109 :
110 35 : static int send_features(struct asfd *asfd, struct conf **cconfs,
111 : struct vers *vers)
112 : {
113 35 : int ret=-1;
114 35 : char *feat=NULL;
115 35 : enum protocol protocol=get_protocol(cconfs);
116 35 : struct strlist *startdir=get_strlist(cconfs[OPT_STARTDIR]);
117 35 : struct strlist *incglob=get_strlist(cconfs[OPT_INCGLOB]);
118 :
119 35 : if(append_to_feat(&feat, "extra_comms_begin ok:")
120 : /* clients can autoupgrade */
121 35 : || 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 35 : || append_to_feat(&feat, "incexc:")
125 : /* clients can give the server an alternative client
126 : to restore from */
127 35 : || append_to_feat(&feat, "orig_client:")
128 : /* clients can tell the server what kind of system they are. */
129 35 : || append_to_feat(&feat, "uname:")
130 35 : || append_to_feat(&feat, "failover:"))
131 : goto end;
132 :
133 : /* Clients can receive restore initiated from the server. */
134 35 : if(set_restore_path(cconfs, &feat))
135 : goto end;
136 :
137 : /* Clients can receive incexc conf from the server.
138 : Only give it as an option if the server has some starting
139 : directory configured in the clientconfdir. */
140 35 : if((startdir || incglob)
141 0 : && append_to_feat(&feat, "sincexc:"))
142 : goto end;
143 :
144 35 : if(vers->cli>=vers->counters_json)
145 : {
146 : /* Clients can be sent cntrs on resume/verify/restore. */
147 30 : if(append_to_feat(&feat, "counters_json:"))
148 : goto end;
149 : }
150 :
151 : // We support CMD_MESSAGE.
152 35 : if(append_to_feat(&feat, "msg:"))
153 : goto end;
154 :
155 35 : if(protocol==PROTO_AUTO)
156 : {
157 : /* If the server is configured to use either protocol, let the
158 : client know that it can choose. */
159 25 : logp("Server is using protocol=0 (auto)\n");
160 25 : if(append_to_feat(&feat, "csetproto:"))
161 : goto end;
162 : }
163 : else
164 : {
165 10 : char p[32]="";
166 : /* Tell the client what we are going to use. */
167 10 : logp("Server is using protocol=%d\n", (int)protocol);
168 10 : snprintf(p, sizeof(p), "forceproto=%d:", (int)protocol);
169 10 : if(append_to_feat(&feat, p))
170 : goto end;
171 : }
172 :
173 : #ifdef HAVE_BLAKE2
174 : if(append_to_feat(&feat, "rshash=blake2:"))
175 : goto end;
176 : #endif
177 :
178 : //printf("feat: %s\n", feat);
179 :
180 35 : if(asfd->write_str(asfd, CMD_GEN, feat))
181 : {
182 1 : logp("problem in extra_comms\n");
183 : goto end;
184 : }
185 :
186 : ret=0;
187 : end:
188 35 : free_w(&feat);
189 35 : return ret;
190 : }
191 :
192 2 : static int do_autoupgrade(struct asfd *asfd, struct vers *vers,
193 : struct conf **globalcs)
194 : {
195 2 : int ret=-1;
196 2 : char *os=NULL;
197 2 : struct iobuf *rbuf=asfd->rbuf;
198 2 : const char *autoupgrade_dir=get_string(globalcs[OPT_AUTOUPGRADE_DIR]);
199 :
200 2 : if(!(os=strdup_w(rbuf->buf+strlen("autoupgrade:"), __func__)))
201 : goto end;
202 2 : iobuf_free_content(rbuf);
203 2 : ret=0;
204 2 : if(os && *os)
205 : {
206 : // Sanitise path separators
207 7 : for(char *i=os; *i; ++i)
208 7 : if(*i == '/' || *i == '\\' || *i == ':')
209 0 : *i='-';
210 :
211 1 : ret=autoupgrade_server(asfd, vers->ser,
212 : vers->cli, os, get_cntr(globalcs),
213 : autoupgrade_dir);
214 : }
215 : end:
216 2 : free_w(&os);
217 2 : return ret;
218 : }
219 :
220 34 : 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 34 : int ret=-1;
225 : struct asfd *asfd;
226 : struct iobuf *rbuf;
227 34 : asfd=as->asfd;
228 34 : rbuf=asfd->rbuf;
229 :
230 : while(1)
231 : {
232 54 : iobuf_free_content(rbuf);
233 54 : if(asfd->read(asfd)) goto end;
234 :
235 54 : if(rbuf->cmd!=CMD_GEN)
236 : {
237 1 : iobuf_log_unexpected(rbuf, __func__);
238 : goto end;
239 : }
240 :
241 53 : if(!strcmp(rbuf->buf, "extra_comms_end"))
242 : {
243 24 : if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end ok"))
244 : goto end;
245 : break;
246 : }
247 29 : else if(!strncmp_w(rbuf->buf, "autoupgrade:"))
248 : {
249 2 : if(do_autoupgrade(asfd, vers, globalcs))
250 : goto end;
251 : }
252 27 : 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 23 : 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 22 : 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 21 : 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 20 : 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 19 : 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 17 : 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 14 : else if(!strncmp_w(rbuf->buf, "restore_spool="))
357 : {
358 : // Removed.
359 : }
360 14 : else if(!strncmp_w(rbuf->buf, "protocol="))
361 : {
362 11 : char msg[128]="";
363 : // Client wants to set protocol.
364 : enum protocol protocol;
365 : enum protocol cprotocol;
366 11 : const char *cliproto=NULL;
367 11 : protocol=get_protocol(cconfs);
368 11 : cliproto=rbuf->buf+strlen("protocol=");
369 11 : cprotocol=atoi(cliproto);
370 :
371 11 : if(protocol!=PROTO_AUTO)
372 : {
373 6 : if(protocol==cprotocol)
374 : {
375 2 : logp("Client is forcing protocol=%d\n", (int)protocol);
376 2 : continue;
377 : }
378 4 : snprintf(msg, sizeof(msg), "Client is trying to use protocol=%d but server is set to protocol=%d\n", (int)cprotocol, (int)protocol);
379 4 : log_and_send(asfd, msg);
380 5 : goto end;
381 : }
382 5 : else if(cprotocol==PROTO_1)
383 : {
384 2 : set_protocol(cconfs, cprotocol);
385 2 : set_protocol(globalcs, cprotocol);
386 : }
387 3 : else if(cprotocol==PROTO_2)
388 : {
389 2 : set_protocol(cconfs, cprotocol);
390 2 : set_protocol(globalcs, cprotocol);
391 : }
392 : else
393 : {
394 1 : snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s, which is unknown\n", cliproto);
395 1 : log_and_send(asfd, msg);
396 : goto end;
397 : }
398 4 : logp("Client has set protocol=%d\n",
399 4 : (int)get_protocol(cconfs));
400 : }
401 3 : else if(!strncmp_w(rbuf->buf, "rshash=blake2"))
402 : {
403 : #ifdef HAVE_BLAKE2
404 : set_e_rshash(cconfs[OPT_RSHASH], RSHASH_BLAKE2);
405 : set_e_rshash(globalcs[OPT_RSHASH], RSHASH_BLAKE2);
406 : #else
407 1 : logp("Client is trying to use librsync hash blake2, but server does not support it.\n");
408 : goto end;
409 : #endif
410 : }
411 2 : else if(!strncmp_w(rbuf->buf, "msg"))
412 : {
413 1 : set_int(cconfs[OPT_MESSAGE], 1);
414 1 : set_int(globalcs[OPT_MESSAGE], 1);
415 : }
416 1 : else if(!strncmp_w(rbuf->buf, "backup_failovers_left="))
417 : {
418 : int l;
419 0 : l=atoi(rbuf->buf+strlen("backup_failovers_left="));
420 0 : set_int(cconfs[OPT_BACKUP_FAILOVERS_LEFT], l);
421 0 : set_int(globalcs[OPT_BACKUP_FAILOVERS_LEFT], l);
422 : }
423 : else
424 : {
425 1 : iobuf_log_unexpected(rbuf, __func__);
426 : goto end;
427 : }
428 : }
429 :
430 24 : ret=0;
431 : end:
432 34 : iobuf_free_content(rbuf);
433 34 : return ret;
434 : }
435 :
436 39 : static int vers_init(struct vers *vers, struct conf **cconfs)
437 : {
438 39 : memset(vers, 0, sizeof(struct vers));
439 39 : return ((vers->min=version_to_long("1.2.7"))<0
440 39 : || (vers->cli=version_to_long(get_string(cconfs[OPT_PEER_VERSION])))<0
441 39 : || (vers->ser=version_to_long(PACKAGE_VERSION))<0
442 39 : || (vers->feat_list=version_to_long("1.3.0"))<0
443 39 : || (vers->directory_tree=version_to_long("1.3.6"))<0
444 39 : || (vers->burp2=version_to_long("2.0.0"))<0
445 78 : || (vers->counters_json=version_to_long("2.0.46"))<0);
446 : }
447 :
448 39 : int extra_comms(struct async *as,
449 : char **incexc, int *srestore, struct conf **confs, struct conf **cconfs)
450 : {
451 : struct vers vers;
452 : struct asfd *asfd;
453 39 : asfd=as->asfd;
454 : //char *restorepath=NULL;
455 39 : const char *peer_version=NULL;
456 :
457 39 : if(vers_init(&vers, cconfs))
458 : goto error;
459 :
460 39 : if(vers.cli<vers.directory_tree)
461 : {
462 3 : set_int(confs[OPT_DIRECTORY_TREE], 0);
463 3 : set_int(cconfs[OPT_DIRECTORY_TREE], 0);
464 : }
465 :
466 : // Clients before 1.2.7 did not know how to do extra comms, so skip
467 : // this section for them.
468 39 : if(vers.cli<vers.min)
469 : return 0;
470 :
471 37 : if(asfd_read_expect(asfd, CMD_GEN, "extra_comms_begin"))
472 : {
473 1 : logp("problem reading in extra_comms\n");
474 1 : goto error;
475 : }
476 : // Want to tell the clients the extra comms features that are
477 : // supported, so that new clients are more likely to work with old
478 : // servers.
479 36 : if(vers.cli==vers.feat_list)
480 : {
481 : // 1.3.0 did not support the feature list.
482 1 : if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin ok"))
483 : {
484 1 : logp("problem writing in extra_comms\n");
485 1 : goto error;
486 : }
487 : }
488 : else
489 : {
490 35 : if(send_features(asfd, cconfs, &vers))
491 : goto error;
492 : }
493 :
494 34 : if(extra_comms_read(as, &vers, srestore, incexc, confs, cconfs))
495 : goto error;
496 :
497 24 : peer_version=get_string(cconfs[OPT_PEER_VERSION]);
498 :
499 : // This needs to come after extra_comms_read, as the client might
500 : // have set PROTO_1 or PROTO_2.
501 24 : switch(get_protocol(cconfs))
502 : {
503 : case PROTO_AUTO:
504 : // The protocol has not been specified. Make a choice.
505 14 : if(vers.cli<vers.burp2)
506 : {
507 : // Client is burp-1.x.x, use protocol1.
508 1 : set_protocol(confs, PROTO_1);
509 1 : set_protocol(cconfs, PROTO_1);
510 1 : logp("Client is %s-%s - using protocol=%d\n",
511 : PACKAGE_TARNAME,
512 : peer_version, PROTO_1);
513 : }
514 : else
515 : {
516 : // Client is burp-2.x.x, use protocol2.
517 : // This will probably never be reached because
518 : // the negotiation will take care of it.
519 : /*
520 : set_protocol(confs, PROTO_2);
521 : set_protocol(cconfs, PROTO_2);
522 : logp("Client is %s-%s - using protocol=%d\n",
523 : PACKAGE_TARNAME,
524 : peer_version, PROTO_2);
525 : */
526 : // PROTO_1 is safer for now.
527 13 : set_protocol(confs, PROTO_1);
528 13 : set_protocol(cconfs, PROTO_1);
529 13 : logp("Client is %s-%s - using protocol=%d\n",
530 : PACKAGE_TARNAME,
531 : peer_version, PROTO_1);
532 : }
533 : break;
534 : case PROTO_1:
535 : // It is OK for the client to be burp1 and for the
536 : // server to be forced to protocol1.
537 : break;
538 : case PROTO_2:
539 5 : if(vers.cli>=vers.burp2)
540 : break;
541 2 : logp("protocol=%d is set server side, "
542 : "but client is %s version %s\n",
543 : PROTO_2, PACKAGE_TARNAME, peer_version);
544 2 : goto error;
545 : }
546 :
547 22 : if(get_protocol(cconfs)==PROTO_1)
548 : {
549 19 : if(get_e_rshash(cconfs[OPT_RSHASH])==RSHASH_UNSET)
550 : {
551 19 : set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);
552 19 : set_e_rshash(cconfs[OPT_RSHASH], RSHASH_MD4);
553 : }
554 : }
555 :
556 : return 0;
557 : error:
558 : return -1;
559 : }
|