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