1 #include "../burp.h"
2 #include "../asfd.h"
3 #include "../async.h"
4 #include "../cmd.h"
5 #include "../conf.h"
6 #include "../conffile.h"
7 #include "../handy.h"
8 #include "../incexc_recv.h"
9 #include "../incexc_send.h"
10 #include "../iobuf.h"
11 #include "../log.h"
12 #include "autoupgrade.h"
13 #include "extra_comms.h"
14
15 #ifndef HAVE_WIN32
16 #include <sys/utsname.h>
17 #endif
18
server_supports(const char * feat,const char * wanted)19 static const char *server_supports(const char *feat, const char *wanted)
20 {
21 return strstr(feat, wanted);
22 }
23
server_supports_autoupgrade(const char * feat)24 static const char *server_supports_autoupgrade(const char *feat)
25 {
26 // 1.3.0 servers did not list the features, but the only feature
27 // that was supported was autoupgrade.
28 if(!strcmp(feat, "extra_comms_begin ok")) return "ok";
29 return server_supports(feat, ":autoupgrade:");
30 }
31
32 #include <librsync.h>
33
extra_comms_client(struct async * as,struct conf ** confs,enum action * action,struct strlist * failover,char ** incexc)34 int extra_comms_client(struct async *as, struct conf **confs,
35 enum action *action, struct strlist *failover, char **incexc)
36 {
37 int ret=-1;
38 char *feat=NULL;
39 char *seed_src=NULL;
40 char *seed_dst=NULL;
41 struct asfd *asfd;
42 struct iobuf *rbuf;
43 const char *orig_client=NULL;
44 asfd=as->asfd;
45 rbuf=asfd->rbuf;
46
47 if(asfd->write_str(asfd, CMD_GEN, "extra_comms_begin"))
48 {
49 logp("Problem requesting extra_comms_begin\n");
50 goto end;
51 }
52 // Servers greater than 1.3.0 will list the extra_comms
53 // features they support.
54 if(asfd->read(asfd))
55 {
56 logp("Problem reading response to extra_comms_begin\n");
57 goto end;
58 }
59 if(rbuf->cmd!=CMD_GEN
60 || strncmp_w(rbuf->buf, "extra_comms_begin ok"))
61 {
62 iobuf_log_unexpected(rbuf, __func__);
63 goto end;
64 }
65 feat=rbuf->buf;
66 rbuf->buf=NULL;
67 logp("%s\n", feat);
68 iobuf_init(rbuf);
69
70 // Can add extra bits here. The first extra bit is the
71 // autoupgrade stuff.
72 if(server_supports_autoupgrade(feat)
73 && get_string(confs[OPT_AUTOUPGRADE_DIR])
74 && get_string(confs[OPT_AUTOUPGRADE_OS])
75 && autoupgrade_client(as, confs))
76 goto end;
77
78
79 // :srestore: means that the server wants to do a restore.
80 if(server_supports(feat, ":srestore:"))
81 {
82 logp("Server wants to initiate a restore\n");
83 if(*action==ACTION_MONITOR)
84 {
85 logp("Client is in monitor mode, so ignoring\n");
86 }
87 else if(get_int(confs[OPT_SERVER_CAN_RESTORE]))
88 {
89 logp("Client accepts.\n");
90 if(incexc_recv_client_restore(asfd, incexc, confs))
91 goto end;
92 if(*incexc)
93 {
94 if(conf_parse_incexcs_srestore(confs, *incexc))
95 goto end;
96 *action=ACTION_RESTORE;
97 log_restore_settings(confs, 1);
98 }
99 }
100 else
101 {
102 logp("Client configuration says no\n");
103 if(asfd->write_str(asfd, CMD_GEN, "srestore not ok"))
104 goto end;
105 }
106 }
107
108 // Needs to be after the srestore stuff, as the server may set
109 // orig_client in the server-initiated restore file.
110 if((orig_client=get_string(confs[OPT_ORIG_CLIENT])))
111 {
112 char str[512]="";
113 snprintf(str, sizeof(str), "orig_client=%s", orig_client);
114 if(!server_supports(feat, ":orig_client:"))
115 {
116 logp("Server does not support switching client.\n");
117 goto end;
118 }
119 if(asfd->write_str(asfd, CMD_GEN, str)
120 || asfd_read_expect(asfd, CMD_GEN, "orig_client ok"))
121 {
122 logp("Problem requesting %s\n", str);
123 goto end;
124 }
125 logp("Switched to client %s\n", orig_client);
126 }
127
128 // :sincexc: is for the server giving the client the
129 // incexc config.
130 if(*action==ACTION_BACKUP
131 || *action==ACTION_BACKUP_TIMED
132 || *action==ACTION_TIMER_CHECK)
133 {
134 if(!*incexc && server_supports(feat, ":sincexc:"))
135 {
136 logp("Server is setting includes/excludes.\n");
137 if(get_int(confs[OPT_SERVER_CAN_OVERRIDE_INCLUDES]))
138 {
139 logp("Client accepts.\n");
140 if(incexc_recv_client(asfd, incexc, confs))
141 goto end;
142 if(*incexc && conf_parse_incexcs_buf(confs,
143 *incexc)) goto end;
144 }
145 else
146 {
147 logp("Client configuration says no\n");
148 }
149 }
150 }
151
152 if(server_supports(feat, ":counters_json:"))
153 {
154 if(asfd->write_str(asfd, CMD_GEN, "counters_json ok"))
155 goto end;
156 set_int(confs[OPT_SEND_CLIENT_CNTR], 1);
157 }
158
159 // :incexc: is for the client sending the server the
160 // incexc conf so that it better knows what to do on
161 // resume.
162 if(server_supports(feat, ":incexc:")
163 && incexc_send_client(asfd, confs))
164 goto end;
165
166 if(server_supports(feat, ":uname:"))
167 {
168 const char *clientos=NULL;
169 #ifdef HAVE_WIN32
170 #ifdef _WIN64
171 clientos="Windows 64bit";
172 #else
173 clientos="Windows 32bit";
174 #endif
175 #else
176 struct utsname utsname;
177 if(!uname(&utsname))
178 clientos=(const char *)utsname.sysname;
179 #endif
180 if(clientos)
181 {
182 char *msg=NULL;
183 if(astrcat(&msg, "uname=", __func__)
184 || astrcat(&msg, clientos, __func__))
185 goto end;
186 if(asfd->write_str(asfd, CMD_GEN, msg))
187 {
188 free_w(&msg);
189 goto end;
190 }
191 free_w(&msg);
192 }
193 }
194
195 if(server_supports(feat, ":csetproto:"))
196 {
197 char msg[128]="";
198 // Use protocol1 if no choice has been made on client side.
199 if(get_protocol(confs)==PROTO_AUTO)
200 {
201 logp("Server has protocol=0 (auto)\n");
202 set_protocol(confs, PROTO_1);
203 }
204 // Send choice to server.
205 snprintf(msg, sizeof(msg), "protocol=%d",
206 get_protocol(confs));
207 if(asfd->write_str(asfd, CMD_GEN, msg))
208 goto end;
209 logp("Using protocol=%d\n",
210 get_protocol(confs));
211 }
212 else if(server_supports(feat, ":forceproto=1:"))
213 {
214 logp("Server is forcing protocol 1\n");
215 if(get_protocol(confs)!=PROTO_AUTO
216 && get_protocol(confs)!=PROTO_1)
217 {
218 logp("But client has set protocol=%d!\n",
219 get_protocol(confs));
220 goto end;
221 }
222 set_protocol(confs, PROTO_1);
223 }
224 else if(server_supports(feat, ":forceproto=2:"))
225 {
226 logp("Server is forcing protocol 2\n");
227 if(get_protocol(confs)!=PROTO_AUTO
228 && get_protocol(confs)!=PROTO_2)
229 {
230 logp("But client has set protocol=%d!\n",
231 get_protocol(confs));
232 goto end;
233 }
234 set_protocol(confs, PROTO_2);
235 }
236
237 if(get_protocol(confs)==PROTO_2
238 && get_string(confs[OPT_ENCRYPTION_PASSWORD]))
239 {
240 char msg[64]="";
241 snprintf(msg, sizeof(msg),
242 "%s is not supported in protocol 2",
243 confs[OPT_ENCRYPTION_PASSWORD]->field);
244 log_and_send(asfd, msg);
245 goto end;
246 }
247
248 if(server_supports(feat, ":msg:"))
249 {
250 set_int(confs[OPT_MESSAGE], 1);
251 if(asfd->write_str(asfd, CMD_GEN, "msg"))
252 goto end;
253 }
254
255 #ifdef HAVE_BLAKE2
256 if(server_supports(feat, ":rshash=blake2:"))
257 {
258 set_e_rshash(confs[OPT_RSHASH], RSHASH_BLAKE2);
259 // Send choice to server.
260 if(asfd->write_str(asfd, CMD_GEN, "rshash=blake2"))
261 goto end;
262 }
263 else
264 #endif
265 set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4);
266
267 if(server_supports(feat, ":failover:"))
268 {
269 if(*action==ACTION_BACKUP
270 || *action==ACTION_BACKUP_TIMED)
271 {
272 char msg[64]="";
273 int left=0;
274 struct strlist *f=NULL;
275 for(f=failover; f; f=f->next)
276 left++;
277 snprintf(msg, sizeof(msg),
278 "backup_failovers_left=%d", left);
279 if(asfd->write_str(asfd, CMD_GEN, msg))
280 goto end;
281 }
282 }
283
284 seed_src=get_string(confs[OPT_SEED_SRC]);
285 seed_dst=get_string(confs[OPT_SEED_DST]);
286 if(seed_src && *seed_src
287 && seed_dst && *seed_dst
288 && server_supports(feat, ":seed:"))
289 {
290 char *msg=NULL;
291 logp("Seeding from %s\n", seed_src);
292 if(astrcat(&msg, "seed_src=", __func__)
293 || astrcat(&msg, seed_src, __func__)
294 || asfd->write_str(asfd, CMD_GEN, msg))
295 {
296 free_w(&msg);
297 goto end;
298 }
299 free_w(&msg);
300 logp("Seeding to %s\n", seed_dst);
301 if(astrcat(&msg, "seed_dst=", __func__)
302 || astrcat(&msg, seed_dst, __func__)
303 || asfd->write_str(asfd, CMD_GEN, msg))
304 {
305 free_w(&msg);
306 goto end;
307 }
308 free_w(&msg);
309 }
310
311 if(server_supports(feat, ":vss_restore:"))
312 {
313 enum vss_restore vss_restore=(enum vss_restore)
314 get_int(confs[OPT_VSS_RESTORE]);
315 if(vss_restore==VSS_RESTORE_OFF
316 && asfd->write_str(asfd, CMD_GEN, "vss_restore=off"))
317 goto end;
318 if(vss_restore==VSS_RESTORE_OFF_STRIP
319 && asfd->write_str(asfd, CMD_GEN, "vss_restore=strip"))
320 goto end;
321 }
322
323 if(server_supports(feat, ":regex_icase:"))
324 {
325 if(get_int(confs[OPT_REGEX_CASE_INSENSITIVE]))
326 {
327 if(asfd->write_str(asfd, CMD_GEN,
328 "regex_icase=1"))
329 goto end;
330 }
331 }
332
333 if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end")
334 || asfd_read_expect(asfd, CMD_GEN, "extra_comms_end ok"))
335 {
336 logp("Problem requesting extra_comms_end\n");
337 goto end;
338 }
339
340 ret=0;
341 end:
342 free_w(&feat);
343 return ret;
344 }
345