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 "../handy.h"
9 : #include "../incexc_recv.h"
10 : #include "../incexc_send.h"
11 : #include "../iobuf.h"
12 : #include "../log.h"
13 : #include "../prepend.h"
14 : #include "autoupgrade.h"
15 : #include "extra_comms.h"
16 :
17 : #include <librsync.h>
18 :
19 279 : static int append_to_feat(char **feat, const char *str)
20 : {
21 279 : char *tmp=NULL;
22 279 : if(!*feat)
23 : {
24 35 : if(!(*feat=strdup_w(str, __func__)))
25 : return -1;
26 35 : return 0;
27 : }
28 244 : if(!(tmp=prepend(*feat, str)))
29 : return -1;
30 244 : free_w(feat);
31 244 : *feat=tmp;
32 244 : return 0;
33 : }
34 :
35 35 : static char *get_restorepath(struct conf **cconfs)
36 : {
37 35 : char *tmp=NULL;
38 35 : char *restorepath=NULL;
39 35 : if((tmp=prepend_s(get_string(cconfs[OPT_DIRECTORY]),
40 35 : get_string(cconfs[OPT_CNAME]))))
41 35 : restorepath=prepend_s(tmp, "restore");
42 35 : free_w(&tmp);
43 35 : return restorepath;
44 : }
45 :
46 : struct vers
47 : {
48 : long min;
49 : long cli;
50 : long ser;
51 : long feat_list;
52 : long directory_tree;
53 : long burp2;
54 : long counters_json;
55 : };
56 :
57 35 : static int send_features(struct asfd *asfd, struct conf **cconfs,
58 : struct vers *vers)
59 : {
60 35 : int ret=-1;
61 35 : char *feat=NULL;
62 : struct stat statp;
63 35 : char *restorepath=NULL;
64 35 : enum protocol protocol=get_protocol(cconfs);
65 35 : struct strlist *startdir=get_strlist(cconfs[OPT_STARTDIR]);
66 35 : struct strlist *incglob=get_strlist(cconfs[OPT_INCGLOB]);
67 :
68 35 : if(append_to_feat(&feat, "extra_comms_begin ok:")
69 : /* clients can autoupgrade */
70 35 : || append_to_feat(&feat, "autoupgrade:")
71 : /* clients can give server incexc conf so that the
72 : server knows better what to do on resume */
73 35 : || append_to_feat(&feat, "incexc:")
74 : /* clients can give the server an alternative client
75 : to restore from */
76 35 : || append_to_feat(&feat, "orig_client:")
77 : /* clients can tell the server what kind of system they are. */
78 35 : || append_to_feat(&feat, "uname:"))
79 : goto end;
80 :
81 : /* Clients can receive restore initiated from the server. */
82 35 : if(!(restorepath=get_restorepath(cconfs))
83 35 : || set_string(cconfs[OPT_RESTORE_PATH], restorepath))
84 : goto end;
85 70 : if(!lstat(restorepath, &statp) && S_ISREG(statp.st_mode)
86 4 : && append_to_feat(&feat, "srestore:"))
87 : goto end;
88 :
89 : /* Clients can receive incexc conf from the server.
90 : Only give it as an option if the server has some starting
91 : directory configured in the clientconfdir. */
92 35 : if((startdir || incglob)
93 0 : && append_to_feat(&feat, "sincexc:"))
94 : goto end;
95 :
96 35 : if(vers->cli>=vers->counters_json)
97 : {
98 : /* Clients can be sent cntrs on resume/verify/restore. */
99 30 : if(append_to_feat(&feat, "counters_json:"))
100 : goto end;
101 : }
102 :
103 : // We support CMD_MESSAGE.
104 35 : if(append_to_feat(&feat, "msg:"))
105 : goto end;
106 :
107 35 : if(protocol==PROTO_AUTO)
108 : {
109 : /* If the server is configured to use either protocol, let the
110 : client know that it can choose. */
111 25 : logp("Server is using protocol=0 (auto)\n");
112 25 : if(append_to_feat(&feat, "csetproto:"))
113 : goto end;
114 : }
115 : else
116 : {
117 10 : char p[32]="";
118 : /* Tell the client what we are going to use. */
119 10 : logp("Server is using protocol=%d\n", (int)protocol);
120 10 : snprintf(p, sizeof(p), "forceproto=%d:", (int)protocol);
121 10 : if(append_to_feat(&feat, p))
122 : goto end;
123 : }
124 :
125 : #ifndef RS_DEFAULT_STRONG_LEN
126 : if(append_to_feat(&feat, "rshash=blake2:"))
127 : goto end;
128 : #endif
129 :
130 : //printf("feat: %s\n", feat);
131 :
132 35 : if(asfd->write_str(asfd, CMD_GEN, feat))
133 : {
134 1 : logp("problem in extra_comms\n");
135 : goto end;
136 : }
137 :
138 : ret=0;
139 : end:
140 35 : free_w(&feat);
141 35 : free_w(&restorepath);
142 35 : return ret;
143 : }
144 :
145 34 : static int extra_comms_read(struct async *as,
146 : struct vers *vers, int *srestore,
147 : char **incexc, struct conf **globalcs, struct conf **cconfs)
148 : {
149 34 : int ret=-1;
150 : struct asfd *asfd;
151 : struct iobuf *rbuf;
152 34 : asfd=as->asfd;
153 34 : rbuf=asfd->rbuf;
154 :
155 : while(1)
156 : {
157 52 : iobuf_free_content(rbuf);
158 52 : if(asfd->read(asfd)) goto end;
159 :
160 52 : if(rbuf->cmd!=CMD_GEN)
161 : {
162 1 : iobuf_log_unexpected(rbuf, __func__);
163 : goto end;
164 : }
165 :
166 51 : if(!strcmp(rbuf->buf, "extra_comms_end"))
167 : {
168 22 : if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end ok"))
169 : goto end;
170 : break;
171 : }
172 29 : else if(!strncmp_w(rbuf->buf, "autoupgrade:"))
173 : {
174 2 : char *os=NULL;
175 2 : const char *autoupgrade_dir=
176 2 : get_string(globalcs[OPT_AUTOUPGRADE_DIR]);
177 2 : os=rbuf->buf+strlen("autoupgrade:");
178 2 : iobuf_free_content(rbuf);
179 2 : if(os && *os && autoupgrade_server(as, vers->ser,
180 : vers->cli, os, get_cntr(globalcs),
181 : autoupgrade_dir))
182 : goto end;
183 : }
184 27 : else if(!strcmp(rbuf->buf, "srestore ok"))
185 : {
186 4 : iobuf_free_content(rbuf);
187 : // Client can accept the restore.
188 : // Load the restore config, then send it.
189 4 : *srestore=1;
190 4 : if(conf_parse_incexcs_path(cconfs,
191 4 : get_string(cconfs[OPT_RESTORE_PATH]))
192 4 : || incexc_send_server_restore(asfd, cconfs))
193 : goto end;
194 : // Do not unlink it here - wait until
195 : // the client says that it wants to do the
196 : // restore.
197 : // Also need to leave it around if the
198 : // restore is to an alternative client, so
199 : // that the code below that reloads the config
200 : // can read it again.
201 : //unlink(get_string(cconfs[OPT_RESTORE_PATH]));
202 : }
203 23 : else if(!strcmp(rbuf->buf, "srestore not ok"))
204 : {
205 1 : const char *restore_path=get_string(
206 : cconfs[OPT_RESTORE_PATH]);
207 : // Client will not accept the restore.
208 1 : unlink(restore_path);
209 1 : if(set_string(cconfs[OPT_RESTORE_PATH], NULL))
210 : goto end;
211 1 : logp("Client not accepting server initiated restore.\n");
212 : }
213 22 : else if(!strcmp(rbuf->buf, "sincexc ok"))
214 : {
215 : // Client can accept incexc conf from the
216 : // server.
217 1 : iobuf_free_content(rbuf);
218 1 : if(incexc_send_server(asfd, cconfs))
219 : goto end;
220 : }
221 21 : else if(!strcmp(rbuf->buf, "incexc"))
222 : {
223 : // Client is telling server its incexc
224 : // configuration so that it can better decide
225 : // what to do on resume.
226 1 : iobuf_free_content(rbuf);
227 1 : if(incexc_recv_server(asfd, incexc, globalcs))
228 : goto end;
229 1 : if(*incexc)
230 : {
231 1 : char *tmp=NULL;
232 1 : char comp[32]="";
233 1 : snprintf(comp, sizeof(comp),
234 : "compression = %d\n",
235 : get_int(cconfs[OPT_COMPRESSION]));
236 1 : if(!(tmp=prepend(*incexc, comp)))
237 : goto end;
238 1 : free_w(incexc);
239 1 : *incexc=tmp;
240 : }
241 : }
242 20 : else if(!strcmp(rbuf->buf, "counters_json ok"))
243 : {
244 : // Client can accept counters on
245 : // resume/verify/restore.
246 1 : logp("Client supports being sent json counters.\n");
247 1 : set_int(cconfs[OPT_SEND_CLIENT_CNTR], 1);
248 : }
249 19 : else if(!strncmp_w(rbuf->buf, "uname=")
250 2 : && strlen(rbuf->buf)>strlen("uname="))
251 : {
252 2 : char *uname=rbuf->buf+strlen("uname=");
253 2 : if(!strncasecmp("Windows", uname, strlen("Windows")))
254 1 : set_int(cconfs[OPT_CLIENT_IS_WINDOWS], 1);
255 : }
256 17 : else if(!strncmp_w(rbuf->buf, "orig_client=")
257 3 : && strlen(rbuf->buf)>strlen("orig_client="))
258 : {
259 3 : if(conf_switch_to_orig_client(globalcs, cconfs,
260 : rbuf->buf+strlen("orig_client=")))
261 : goto end;
262 : // If this started out as a server-initiated
263 : // restore, need to load the restore file
264 : // again.
265 2 : if(*srestore)
266 : {
267 1 : if(conf_parse_incexcs_path(cconfs,
268 1 : get_string(cconfs[OPT_RESTORE_PATH])))
269 : goto end;
270 : }
271 2 : if(asfd->write_str(asfd, CMD_GEN, "orig_client ok"))
272 : goto end;
273 : }
274 14 : else if(!strncmp_w(rbuf->buf, "restore_spool="))
275 : {
276 : // Removed.
277 : }
278 14 : else if(!strncmp_w(rbuf->buf, "protocol="))
279 : {
280 11 : char msg[128]="";
281 : // Client wants to set protocol.
282 11 : enum protocol protocol=get_protocol(cconfs);
283 11 : const char *cliproto=rbuf->buf+strlen("protocol=");
284 11 : if(protocol!=PROTO_AUTO)
285 : {
286 : snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s but server is set to protocol=%d\n", cliproto, protocol);
287 6 : log_and_send(asfd, msg);
288 7 : goto end;
289 : }
290 5 : else if(!strcmp(cliproto, "1"))
291 : {
292 2 : set_protocol(cconfs, PROTO_1);
293 2 : set_protocol(globalcs, PROTO_1);
294 : }
295 3 : else if(!strcmp(cliproto, "2"))
296 : {
297 2 : set_protocol(cconfs, PROTO_2);
298 2 : set_protocol(globalcs, PROTO_2);
299 : }
300 : else
301 : {
302 : snprintf(msg, sizeof(msg), "Client is trying to use protocol=%s, which is unknown\n", cliproto);
303 1 : log_and_send(asfd, msg);
304 : goto end;
305 : }
306 4 : logp("Client has set protocol=%d\n",
307 4 : (int)get_protocol(cconfs));
308 : }
309 3 : else if(!strncmp_w(rbuf->buf, "rshash=blake2"))
310 : {
311 : #ifdef RS_DEFAULT_STRONG_LEN
312 1 : logp("Client is trying to use librsync hash blake2, but server does not support it.\n");
313 : goto end;
314 : #else
315 : set_e_rshash(cconfs[OPT_RSHASH], RSHASH_BLAKE2);
316 : set_e_rshash(globalcs[OPT_RSHASH], RSHASH_BLAKE2);
317 : #endif
318 : }
319 2 : else if(!strncmp_w(rbuf->buf, "msg"))
320 : {
321 1 : set_int(cconfs[OPT_MESSAGE], 1);
322 1 : set_int(globalcs[OPT_MESSAGE], 1);
323 : }
324 : else
325 : {
326 1 : iobuf_log_unexpected(rbuf, __func__);
327 : goto end;
328 : }
329 : }
330 :
331 22 : ret=0;
332 : end:
333 34 : iobuf_free_content(rbuf);
334 34 : return ret;
335 : }
336 :
337 39 : static int vers_init(struct vers *vers, struct conf **cconfs)
338 : {
339 : memset(vers, 0, sizeof(struct vers));
340 78 : return ((vers->min=version_to_long("1.2.7"))<0
341 39 : || (vers->cli=version_to_long(get_string(cconfs[OPT_PEER_VERSION])))<0
342 39 : || (vers->ser=version_to_long(VERSION))<0
343 39 : || (vers->feat_list=version_to_long("1.3.0"))<0
344 39 : || (vers->directory_tree=version_to_long("1.3.6"))<0
345 39 : || (vers->burp2=version_to_long("2.0.0"))<0
346 78 : || (vers->counters_json=version_to_long("2.0.46"))<0);
347 : }
348 :
349 39 : int extra_comms(struct async *as,
350 : char **incexc, int *srestore, struct conf **confs, struct conf **cconfs)
351 : {
352 : struct vers vers;
353 : struct asfd *asfd;
354 39 : asfd=as->asfd;
355 : //char *restorepath=NULL;
356 39 : const char *peer_version=NULL;
357 :
358 39 : if(vers_init(&vers, cconfs))
359 : goto error;
360 :
361 39 : if(vers.cli<vers.directory_tree)
362 : {
363 3 : set_int(confs[OPT_DIRECTORY_TREE], 0);
364 3 : set_int(cconfs[OPT_DIRECTORY_TREE], 0);
365 : }
366 :
367 : // Clients before 1.2.7 did not know how to do extra comms, so skip
368 : // this section for them.
369 39 : if(vers.cli<vers.min)
370 : return 0;
371 :
372 37 : if(asfd_read_expect(asfd, CMD_GEN, "extra_comms_begin"))
373 : {
374 1 : logp("problem reading in extra_comms\n");
375 1 : goto error;
376 : }
377 : // Want to tell the clients the extra comms features that are
378 : // supported, so that new clients are more likely to work with old
379 : // servers.
380 36 : if(vers.cli==vers.feat_list)
381 : {
382 : // 1.3.0 did not support the feature list.
383 1 : if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin ok"))
384 : {
385 1 : logp("problem writing in extra_comms\n");
386 1 : goto error;
387 : }
388 : }
389 : else
390 : {
391 35 : if(send_features(asfd, cconfs, &vers))
392 : goto error;
393 : }
394 :
395 34 : if(extra_comms_read(as, &vers, srestore, incexc, confs, cconfs))
396 : goto error;
397 :
398 22 : peer_version=get_string(cconfs[OPT_PEER_VERSION]);
399 :
400 : // This needs to come after extra_comms_read, as the client might
401 : // have set PROTO_1 or PROTO_2.
402 22 : switch(get_protocol(cconfs))
403 : {
404 : case PROTO_AUTO:
405 : // The protocol has not been specified. Make a choice.
406 14 : if(vers.cli<vers.burp2)
407 : {
408 : // Client is burp-1.x.x, use protocol1.
409 1 : set_protocol(confs, PROTO_1);
410 1 : set_protocol(cconfs, PROTO_1);
411 1 : logp("Client is burp-%s - using protocol=%d\n",
412 : peer_version, PROTO_1);
413 : }
414 : else
415 : {
416 : // Client is burp-2.x.x, use protocol2.
417 : // This will probably never be reached because
418 : // the negotiation will take care of it.
419 : /*
420 : set_protocol(confs, PROTO_2);
421 : set_protocol(cconfs, PROTO_2);
422 : logp("Client is burp-%s - using protocol=%d\n",
423 : peer_version, PROTO_2);
424 : */
425 : // PROTO_1 is safer for now.
426 13 : set_protocol(confs, PROTO_1);
427 13 : set_protocol(cconfs, PROTO_1);
428 13 : logp("Client is burp-%s - using protocol=%d\n",
429 : peer_version, PROTO_1);
430 : }
431 : break;
432 : case PROTO_1:
433 : // It is OK for the client to be burp1 and for the
434 : // server to be forced to protocol1.
435 : break;
436 : case PROTO_2:
437 4 : if(vers.cli>=vers.burp2)
438 : break;
439 2 : logp("protocol=%d is set server side, "
440 : "but client is burp version %s\n",
441 : PROTO_2, peer_version);
442 2 : goto error;
443 : }
444 :
445 20 : if(get_protocol(cconfs)==PROTO_1)
446 : {
447 18 : if(get_e_rshash(cconfs[OPT_RSHASH])==RSHASH_UNSET)
448 : {
449 18 : set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);
450 18 : set_e_rshash(cconfs[OPT_RSHASH], RSHASH_MD4);
451 : }
452 : }
453 :
454 : return 0;
455 : error:
456 : return -1;
457 : }
|