1 /*-
2  * Copyright (c) 2000-2005 MAEKAWA Masahide <maekawa@cvsync.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 
34 #include <limits.h>
35 #include <pthread.h>
36 #include <string.h>
37 
38 #include "compat_stdbool.h"
39 #include "compat_stdint.h"
40 #include "compat_inttypes.h"
41 #include "compat_limits.h"
42 #include "basedef.h"
43 
44 #include "attribute.h"
45 #include "collection.h"
46 #include "cvsync.h"
47 #include "hash.h"
48 #include "logmsg.h"
49 #include "mux.h"
50 #include "network.h"
51 #include "version.h"
52 
53 #include "defs.h"
54 
55 bool collection_exchange_list(int, struct collection *);
56 bool collection_exchange_rcs(int, struct collection *);
57 
58 bool
protocol_exchange(int sock,struct config * cf)59 protocol_exchange(int sock, struct config *cf)
60 {
61 	uint8_t cmd[CVSYNC_MAXCMDLEN], mn;
62 	size_t len;
63 
64 	SetWord(cmd, 2);
65 	cmd[2] = CVSYNC_PROTO_MAJOR;
66 	cmd[3] = CVSYNC_PROTO_MINOR;
67 	if (!sock_send(sock, cmd, 4)) {
68 		logmsg_err("Send: protocol version");
69 		return (false);
70 	}
71 
72 	if (!sock_recv(sock, cmd, 2)) {
73 		logmsg_err("Recv: protocol version length");
74 		return (false);
75 	}
76 	if ((len = GetWord(cmd)) != 2) {
77 		logmsg_err("Recv: protocol version: invalid length: %u", len);
78 		return (false);
79 	}
80 	if (!sock_recv(sock, cmd, len)) {
81 		logmsg_err("Recv: protocol version");
82 		return (false);
83 	}
84 
85 	if (cmd[0] == CVSYNC_PROTO_ERROR) {
86 		switch (cmd[1]) {
87 		case CVSYNC_ERROR_DENIED:
88 			logmsg_err("Access denied");
89 			break;
90 		case CVSYNC_ERROR_LIMITED:
91 			logmsg_err("Access limited");
92 			break;
93 		case CVSYNC_ERROR_UNAVAIL:
94 			logmsg_err("Service unavailable");
95 			break;
96 		case CVSYNC_ERROR_UNSPEC:
97 		default:
98 			logmsg_err("Your software seems to be old.\n"
99 				   "Please upgrade to the newer version.\n"
100 				   "URL: %s", CVSYNC_URL);
101 			break;
102 		}
103 		SetWord(cmd, 2);
104 		cmd[2] = CVSYNC_PROTO_ERROR;
105 		cmd[3] = CVSYNC_ERROR_UNSPEC;
106 		sock_send(sock, cmd, 4);
107 		return (false);
108 	}
109 
110 	if (cmd[0] != CVSYNC_PROTO_MAJOR) {
111 		logmsg_err("The server(%u.%u) seems to be too old.", cmd[0],
112 			   cmd[1]);
113 		SetWord(cmd, 2);
114 		cmd[2] = CVSYNC_PROTO_ERROR;
115 		cmd[3] = CVSYNC_ERROR_UNSPEC;
116 		sock_send(sock, cmd, 4);
117 		return (false);
118 	}
119 
120 	if ((cmd[0] == 0) && (cmd[1] < 20)) {
121 		logmsg_err("The server (%u.%u) seems to be old.", cmd[0],
122 			   cmd[1]);
123 		SetWord(cmd, 2);
124 		cmd[2] = CVSYNC_PROTO_ERROR;
125 		cmd[3] = CVSYNC_ERROR_UNSPEC;
126 		sock_send(sock, cmd, 4);
127 		return (false);
128 	}
129 
130 	if ((cmd[0] == 0) && (cmd[1] < CVSYNC_PROTO_MINOR))
131 		mn = cmd[1];
132 	else
133 		mn = CVSYNC_PROTO_MINOR;
134 
135 	SetWord(cmd, 2);
136 	cmd[2] = CVSYNC_PROTO_MAJOR;
137 	cmd[3] = mn;
138 	if (!sock_send(sock, cmd, 4)) {
139 		logmsg_err("Send: protocol version: %u.%u", CVSYNC_PROTO_MAJOR,
140 			   mn);
141 		return (false);
142 	}
143 
144 	cf->cf_proto = CVSYNC_PROTO(CVSYNC_PROTO_MAJOR, mn);
145 
146 	logmsg_verbose("Protocol: %u.%u", cmd[2], cmd[3]);
147 
148 	return (true);
149 }
150 
151 bool
hash_exchange(int sock,struct config * cf)152 hash_exchange(int sock, struct config *cf)
153 {
154 	uint8_t cmd[CVSYNC_MAXCMDLEN];
155 	char name[CVSYNC_NAME_MAX + 1];
156 	size_t len;
157 
158 	if ((len = hash_ntop(cf->cf_hash, name, sizeof(name))) == 0)
159 		return (false);
160 
161 	SetWord(cmd, len);
162 	if (!sock_send(sock, cmd, 2)) {
163 		logmsg_err("Send: hash type");
164 		return (false);
165 	}
166 	if (!sock_send(sock, name, len)) {
167 		logmsg_err("Send: hash type");
168 		return (false);
169 	}
170 
171 	if (!sock_recv(sock, cmd, 2)) {
172 		logmsg_err("Recv: hash type length");
173 		return (false);
174 	}
175 	len = GetWord(cmd);
176 	if ((len == 0) || (len >= sizeof(name))) {
177 		logmsg_err("Recv: hash type: invaild length: %u", len);
178 		return (false);
179 	}
180 	if (!sock_recv(sock, name, len)) {
181 		logmsg_err("Recv: hash type");
182 		return (false);
183 	}
184 
185 	if ((cf->cf_hash = hash_pton(name, len)) == HASH_UNSPEC) {
186 		logmsg_err("Unknwon hash type: %.*s", len, name);
187 		return (false);
188 	}
189 
190 	logmsg_verbose("Hash: %.*s", len, name);
191 
192 	return (true);
193 }
194 
195 bool
collectionlist_exchange(int sock,struct config * cf)196 collectionlist_exchange(int sock, struct config *cf)
197 {
198 	struct collection *cl;
199 	uint8_t cmd[CVSYNC_MAXCMDLEN];
200 	size_t len;
201 	int enabled;
202 
203 	logmsg_verbose("Exchanging collection list...");
204 
205 	enabled = 0;
206 	for (cl = cf->cf_collections ; cl != NULL ; cl = cl->cl_next) {
207 		switch (cvsync_release_pton(cl->cl_release)) {
208 		case CVSYNC_RELEASE_LIST:
209 			if (!collection_exchange_list(sock, cl))
210 				return (false);
211 			break;
212 		case CVSYNC_RELEASE_RCS:
213 			if (!collection_exchange_rcs(sock, cl))
214 				return (false);
215 			break;
216 		default:
217 			return (false);
218 		}
219 
220 		if (!(cl->cl_flags & CLFLAGS_DISABLE))
221 			enabled++;
222 	}
223 
224 	SetWord(cmd, 4);
225 	cmd[2] = 1;
226 	cmd[3] = 1;
227 	cmd[4] = '.';
228 	cmd[5] = '.';
229 	if (!sock_send(sock, cmd, 6))
230 		return (false);
231 
232 	if (!sock_recv(sock, cmd, 2))
233 		return (false);
234 	if ((len = GetWord(cmd)) != 4)
235 		return (false);
236 	if (!sock_recv(sock, cmd, len))
237 		return (false);
238 	if ((cmd[0] != 1) || (cmd[1] != 1) ||
239 	    (cmd[2] != '.') || (cmd[3] != '.')) {
240 		return (false);
241 	}
242 
243 	if (!collection_resolv_prefix(cf->cf_collections))
244 		return (false);
245 
246 	if (enabled == 0) {
247 		logmsg_err("No collections is available");
248 		return (false);
249 	}
250 
251 	return (true);
252 }
253 
254 bool
collection_exchange_list(int sock,struct collection * cl)255 collection_exchange_list(int sock, struct collection *cl)
256 {
257 	uint8_t cmd[CVSYNC_MAXCMDLEN];
258 	size_t namelen, relnamelen, len;
259 
260 	if ((namelen = strlen(cl->cl_name)) >= sizeof(cl->cl_name))
261 		return (false);
262 	if ((relnamelen = strlen(cl->cl_release)) >= sizeof(cl->cl_release))
263 		return (false);
264 	if ((len = namelen + relnamelen + 4) >= sizeof(cmd))
265 		return (false);
266 
267 	SetWord(cmd, len - 2);
268 	cmd[2] = namelen;
269 	cmd[3] = relnamelen;
270 	if (!sock_send(sock, cmd, 4))
271 		return (false);
272 	if (!sock_send(sock, cl->cl_name, namelen))
273 		return (false);
274 	if (!sock_send(sock, cl->cl_release, relnamelen))
275 		return (false);
276 
277 	if (!sock_recv(sock, cmd, 2))
278 		return (false);
279 	if ((len = GetWord(cmd)) > sizeof(cmd) - 2)
280 		return (false);
281 	if (len == 0) {
282 		logmsg("Not found such a collection %s/%s", cl->cl_name,
283 		       cl->cl_release);
284 		cl->cl_flags |= CLFLAGS_DISABLE;
285 		return (true);
286 	}
287 	if (len != namelen + relnamelen + 2)
288 		return (false);
289 
290 	if (!sock_recv(sock, cmd, len))
291 		return (false);
292 
293 	if ((cmd[0] != namelen) || (cmd[1] != relnamelen)) {
294 		logmsg_err("Not match name/release length");
295 		return (false);
296 	}
297 	if ((memcmp(&cmd[2], cl->cl_name, namelen) != 0) ||
298 	    (memcmp(&cmd[namelen + 2], cl->cl_release, relnamelen) != 0)) {
299 		logmsg_err("Not match collection name/release");
300 		return (false);
301 	}
302 
303 	logmsg_verbose(" collection name \"%s\" release \"%s\"",
304 		       cl->cl_name, cl->cl_release);
305 
306 	return (true);
307 }
308 
309 bool
collection_exchange_rcs(int sock,struct collection * cl)310 collection_exchange_rcs(int sock, struct collection *cl)
311 {
312 	uint8_t cmd[CVSYNC_MAXCMDLEN];
313 	size_t namelen, relnamelen, len;
314 
315 	if ((namelen = strlen(cl->cl_name)) >= sizeof(cl->cl_name))
316 		return (false);
317 	if ((relnamelen = strlen(cl->cl_release)) >= sizeof(cl->cl_release))
318 		return (false);
319 	if ((len = namelen + relnamelen + 6) >= sizeof(cmd))
320 		return (false);
321 
322 	SetWord(cmd, len - 2);
323 	cmd[2] = namelen;
324 	cmd[3] = relnamelen;
325 	if (!sock_send(sock, cmd, 4))
326 		return (false);
327 	if (!sock_send(sock, cl->cl_name, namelen))
328 		return (false);
329 	if (!sock_send(sock, cl->cl_release, relnamelen))
330 		return (false);
331 
332 	SetWord(cmd, cl->cl_umask);
333 	if (!sock_send(sock, cmd, 2))
334 		return (false);
335 
336 	if (!sock_recv(sock, cmd, 2))
337 		return (false);
338 	if ((len = GetWord(cmd)) > sizeof(cmd) - 2)
339 		return (false);
340 	if (len == 0) {
341 		logmsg("Not found such a collection %s/%s", cl->cl_name,
342 		       cl->cl_release);
343 		cl->cl_flags |= CLFLAGS_DISABLE;
344 		return (true);
345 	}
346 
347 	if (len < namelen + relnamelen + 4)
348 		return (false);
349 
350 	if (!sock_recv(sock, cmd, len))
351 		return (false);
352 
353 	if ((cmd[0] != namelen) || (cmd[1] != relnamelen)) {
354 		logmsg_err("Not match name/release length");
355 		return (false);
356 	}
357 	if ((memcmp(&cmd[2], cl->cl_name, namelen) != 0) ||
358 	    (memcmp(&cmd[namelen + 2], cl->cl_release, relnamelen) != 0)) {
359 		logmsg_err("Not match collection name/release");
360 		return (false);
361 	}
362 
363 	cl->cl_umask = GetWord(&cmd[namelen + relnamelen + 2]);
364 	if (cl->cl_umask & ~CVSYNC_ALLPERMS)
365 		return (false);
366 
367 	cl->cl_rprefixlen = len - namelen - relnamelen - 4;
368 	if (cl->cl_rprefixlen > sizeof(cl->cl_rprefix))
369 		return (false);
370 	(void)memcpy(cl->cl_rprefix, &cmd[namelen + relnamelen + 4],
371 		     cl->cl_rprefixlen);
372 	cl->cl_rprefix[cl->cl_rprefixlen] = '/';
373 
374 	logmsg_verbose(" collection name \"%s\" release \"%s\" umask %03o",
375 		       cl->cl_name, cl->cl_release, cl->cl_umask);
376 
377 	return (true);
378 }
379 
380 bool
compress_exchange(int sock,struct config * cf)381 compress_exchange(int sock, struct config *cf)
382 {
383 	const char *name;
384 	uint8_t cmd[CVSYNC_MAXCMDLEN];
385 	size_t len;
386 
387 	if (cf->cf_proto < CVSYNC_PROTO(0, 22)) {
388 		cf->cf_compress = CVSYNC_COMPRESS_NO;
389 		cf->cf_mss = MUX_MAX_MSS;
390 		return (true);
391 	}
392 
393 	if (cf->cf_proto == CVSYNC_PROTO(0, 22))
394 		cf->cf_compress = CVSYNC_COMPRESS_NO;
395 
396 	name = cvsync_compress_ntop(cf->cf_compress);
397 
398 	len = strlen(name);
399 	SetWord(cmd, len);
400 	if (!sock_send(sock, cmd, 2))
401 		return (false);
402 	if (!sock_send(sock, name, len))
403 		return (false);
404 
405 	if (!sock_recv(sock, cmd, 2))
406 		return (false);
407 	len = GetWord(cmd);
408 	if ((len == 0) || (len >= sizeof(cmd)))
409 		return (false);
410 	if (!sock_recv(sock, cmd, len))
411 		return (false);
412 	cmd[len] = '\0';
413 
414 	cf->cf_compress = cvsync_compress_pton((const char *)cmd);
415 	if (cf->cf_compress == CVSYNC_COMPRESS_UNSPEC) {
416 		logmsg_err("Unsupported compression type: %s", name);
417 		return (false);
418 	}
419 
420 	name = cvsync_compress_ntop(cf->cf_compress);
421 	len = strlen(name);
422 	SetWord(cmd, len);
423 	if (!sock_send(sock, cmd, 2))
424 		return (false);
425 	if (!sock_send(sock, name, len))
426 		return (false);
427 
428 	if ((cf->cf_proto > CVSYNC_PROTO(0, 22)) &&
429 	    (cf->cf_compress != CVSYNC_COMPRESS_NO)) {
430 		cf->cf_mss = MUX_MAX_MSS_ZLIB;
431 	} else {
432 		cf->cf_mss = MUX_DEFAULT_MSS;
433 	}
434 
435 	logmsg_verbose("Compression: %s", name);
436 
437 	return (true);
438 }
439 
440 struct mux *
channel_establish(int sock,struct config * cf)441 channel_establish(int sock, struct config *cf)
442 {
443 	struct mux *mx;
444 	struct muxbuf *mxb;
445 	uint8_t cmd[CVSYNC_MAXCMDLEN];
446 	size_t len;
447 	int i;
448 
449 	logmsg_verbose("Trying to establish the multiplexed channel...");
450 
451 	if ((mx = mux_init(sock, cf->cf_mss, cf->cf_compress,
452 			   CVSYNC_COMPRESS_LEVEL_BEST)) == NULL) {
453 		return (NULL);
454 	}
455 
456 	for (i = 0 ; i < MUX_MAXCHANNELS ; i++) {
457 		mxb = &mx->mx_buffer[MUX_IN][i];
458 
459 		SetWord(cmd, 7);
460 		cmd[2] = i;
461 		SetWord(&cmd[3], mxb->mxb_mss);
462 		SetDWord(&cmd[5], mxb->mxb_bufsize);
463 		if (!sock_send(sock, cmd, 9)) {
464 			mux_destroy(mx);
465 			return (NULL);
466 		}
467 
468 		if (!sock_recv(sock, cmd, 2)) {
469 			mux_destroy(mx);
470 			return (NULL);
471 		}
472 		if ((len = GetWord(cmd)) != 7) {
473 			mux_destroy(mx);
474 			return (NULL);
475 		}
476 		if (!sock_recv(sock, cmd, len)) {
477 			mux_destroy(mx);
478 			return (NULL);
479 		}
480 		if (cmd[0] != i) {
481 			mux_destroy(mx);
482 			return (NULL);
483 		}
484 
485 		mxb = &mx->mx_buffer[MUX_OUT][i];
486 
487 		if (!muxbuf_init(mxb, GetWord(&cmd[1]), GetDWord(&cmd[3]),
488 				 cf->cf_compress)) {
489 			mux_destroy(mx);
490 			return (NULL);
491 		}
492 	}
493 
494 	return (mx);
495 }
496