Bug Summary

File:src/usr.bin/rsync/socket.c
Warning:line 108, column 11
Although the value stored to 'c' is used in the enclosing expression, the value is never actually read from 'c'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name socket.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rsync/obj -resource-dir /usr/local/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rsync/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/rsync/socket.c
1/* $OpenBSD: socket.c,v 1.33 2022/12/26 19:16:02 jmc Exp $ */
2/*
3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include <sys/stat.h>
18#include <sys/socket.h>
19#include <arpa/inet.h>
20#include <netinet/in.h>
21
22#include <assert.h>
23#include <ctype.h>
24#include <errno(*__errno()).h>
25#include <fcntl.h>
26#include <inttypes.h>
27#include <netdb.h>
28#include <poll.h>
29#include <resolv.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <err.h>
34
35#include "extern.h"
36
37/*
38 * Defines a resolved IP address for the host
39 * There can be many, IPV4 or IPV6.
40 */
41struct source {
42 int family; /* PF_INET or PF_INET6 */
43 char ip[INET6_ADDRSTRLEN46]; /* formatted string */
44 struct sockaddr_storage sa; /* socket */
45 socklen_t salen; /* length of socket buffer */
46};
47
48/*
49 * Try to bind to a local IP address matching the address family passed.
50 * Return -1 on failure to bind to any address, 0 on success.
51 */
52static int
53inet_bind(int s, sa_family_t af, const struct source *bsrc, size_t bsrcsz)
54{
55 size_t i;
56
57 if (bsrc == NULL((void *)0))
58 return 0;
59 for (i = 0; i < bsrcsz; i++) {
60 if (bsrc[i].family != af)
61 continue;
62 if (bind(s, (const struct sockaddr *)&bsrc[i].sa,
63 bsrc[i].salen) == -1)
64 continue;
65 return 0;
66 }
67 return -1;
68}
69
70/*
71 * Connect to an IP address representing a host.
72 * Return <0 on failure, 0 on try another address, >0 on success.
73 */
74static int
75inet_connect(int *sd, const struct source *src, const char *host,
76 const struct source *bsrc, size_t bsrcsz)
77{
78 struct pollfd pfd;
79 socklen_t optlen;
80 int c;
81 int optval;
82
83 if (*sd != -1)
84 close(*sd);
85
86 LOG2("trying: %s, %s", src->ip, host)rsync_log( 1, ("trying: %s, %s"), src->ip, host);
87
88 if ((*sd = socket(src->family, SOCK_STREAM1 | SOCK_NONBLOCK0x4000, 0))
89 == -1) {
90 ERR("socket")rsync_err( ("socket"));
91 return -1;
92 }
93
94 if (inet_bind(*sd, src->family, bsrc, bsrcsz) == -1) {
95 ERR("bind")rsync_err( ("bind"));
96 return -1;
97 }
98
99 /*
100 * Initiate blocking connection.
101 * We use non-blocking connect() so we can poll() for contimeout.
102 */
103
104 if ((c = connect(*sd, (const struct sockaddr *)&src->sa, src->salen))
105 != 0 && errno(*__errno()) == EINPROGRESS36) {
106 pfd.fd = *sd;
107 pfd.events = POLLOUT0x0004;
108 switch (c = poll(&pfd, 1, poll_contimeout)) {
Although the value stored to 'c' is used in the enclosing expression, the value is never actually read from 'c'
109 case 1:
110 optlen = sizeof(optval);
111 if ((c = getsockopt(*sd, SOL_SOCKET0xffff, SO_ERROR0x1007, &optval,
112 &optlen)) == 0) {
113 errno(*__errno()) = optval;
114 if (optval != 0)
115 c = -1;
116 }
117 break;
118 case 0:
119 errno(*__errno()) = ETIMEDOUT60;
120 WARNX("connect timeout: %s, %s", src->ip, host)rsync_warnx( ("connect timeout: %s, %s"), src->ip, host);
121 return 0;
122 default:
123 ERR("poll failed")rsync_err( ("poll failed"));
124 return -1;
125 }
126 }
127 if (c == -1) {
128 if (errno(*__errno()) == EADDRNOTAVAIL49)
129 return 0;
130 if (errno(*__errno()) == ECONNREFUSED61 || errno(*__errno()) == EHOSTUNREACH65) {
131 WARNX("connect refused: %s, %s", src->ip, host)rsync_warnx( ("connect refused: %s, %s"), src->ip, host);
132 return 0;
133 }
134 ERR("connect")rsync_err( ("connect"));
135 return -1;
136 }
137
138 return 1;
139}
140
141/*
142 * Resolve the socket addresses for host, both in IPV4 and IPV6.
143 * Once completed, the "dns" pledge may be dropped.
144 * Returns the addresses on success, NULL on failure (sz is always zero,
145 * in this case).
146 */
147static struct source *
148inet_resolve(struct sess *sess, const char *host, size_t *sz, int passive)
149{
150 struct addrinfo hints, *res0, *res;
151 struct sockaddr *sa;
152 struct source *src = NULL((void *)0);
153 const char *port = sess->opts->port;
154 size_t i, srcsz = 0;
155 int error;
156
157 *sz = 0;
158
159 memset(&hints, 0, sizeof(hints));
160 hints.ai_family = PF_UNSPEC0;
161 hints.ai_socktype = SOCK_STREAM1;
162 if (passive) {
163 hints.ai_flags = SOCK_STREAM1;
164 port = NULL((void *)0);
165 }
166
167 error = getaddrinfo(host, port, &hints, &res0);
168
169 LOG2("resolving: %s", host)rsync_log( 1, ("resolving: %s"), host);
170
171 if (error == EAI_AGAIN-3 || error == EAI_NONAME-2) {
172 ERRX("could not resolve hostname %s: %s",rsync_errx( ("could not resolve hostname %s: %s"), host, gai_strerror
(error))
173 host, gai_strerror(error))rsync_errx( ("could not resolve hostname %s: %s"), host, gai_strerror
(error))
;
174 return NULL((void *)0);
175 } else if (error == EAI_SERVICE-8) {
176 ERRX("could not resolve service rsync: %s",rsync_errx( ("could not resolve service rsync: %s"), gai_strerror
(error))
177 gai_strerror(error))rsync_errx( ("could not resolve service rsync: %s"), gai_strerror
(error))
;
178 return NULL((void *)0);
179 } else if (error) {
180 ERRX("getaddrinfo: %s: %s", host, gai_strerror(error))rsync_errx( ("getaddrinfo: %s: %s"), host, gai_strerror(error
))
;
181 return NULL((void *)0);
182 }
183
184 /* Allocate for all available addresses. */
185
186 for (res = res0; res != NULL((void *)0); res = res->ai_next)
187 if (res->ai_family == AF_INET2 ||
188 res->ai_family == AF_INET624)
189 srcsz++;
190
191 if (srcsz == 0) {
192 ERRX("no addresses resolved: %s", host)rsync_errx( ("no addresses resolved: %s"), host);
193 freeaddrinfo(res0);
194 return NULL((void *)0);
195 }
196
197 src = calloc(srcsz, sizeof(struct source));
198 if (src == NULL((void *)0)) {
199 ERRX("calloc")rsync_errx( ("calloc"));
200 freeaddrinfo(res0);
201 return NULL((void *)0);
202 }
203
204 for (i = 0, res = res0; res != NULL((void *)0); res = res->ai_next) {
205 if (res->ai_family != AF_INET2 &&
206 res->ai_family != AF_INET624)
207 continue;
208
209 assert(i < srcsz)((i < srcsz) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 209, __func__, "i < srcsz"))
;
210
211 /* Copy the socket address. */
212
213 src[i].salen = res->ai_addrlen;
214 memcpy(&src[i].sa, res->ai_addr, src[i].salen);
215
216 /* Format as a string, too. */
217
218 sa = res->ai_addr;
219 if (res->ai_family == AF_INET2) {
220 src[i].family = PF_INET2;
221 inet_ntop(AF_INET2,
222 &(((struct sockaddr_in *)sa)->sin_addr),
223 src[i].ip, INET6_ADDRSTRLEN46);
224 } else {
225 src[i].family = PF_INET624;
226 inet_ntop(AF_INET624,
227 &(((struct sockaddr_in6 *)sa)->sin6_addr),
228 src[i].ip, INET6_ADDRSTRLEN46);
229 }
230
231 LOG2("hostname resolved: %s: %s", host, src[i].ip)rsync_log( 1, ("hostname resolved: %s: %s"), host, src[i].ip);
232 i++;
233 }
234
235 freeaddrinfo(res0);
236 *sz = srcsz;
237 return src;
238}
239
240/*
241 * Process an rsyncd preamble line.
242 * This is either free-form text or @RSYNCD commands.
243 * Return <0 on failure, 0 on try more lines, >0 on finished.
244 */
245static int
246protocol_line(struct sess *sess, __attribute__((unused)) const char *host,
247 const char *cp)
248{
249 int major, minor;
250
251 if (strncmp(cp, "@RSYNCD: ", 9)) {
252 if (sess->opts->no_motd == 0)
253 LOG1("%s", cp)rsync_log( 0, ("%s"), cp);
254 return 0;
255 }
256
257 cp += 9;
258 while (isspace((unsigned char)*cp))
259 cp++;
260
261 /* @RSYNCD: OK indicates that we're finished. */
262
263 if (strcmp(cp, "OK") == 0)
264 return 1;
265
266 /*
267 * Otherwise, all we have left is our version.
268 * There are two formats: x.y (w/submodule) and x.
269 */
270
271 if (sscanf(cp, "%d.%d", &major, &minor) == 2) {
272 sess->rver = major;
273 return 0;
274 } else if (sscanf(cp, "%d", &major) == 1) {
275 sess->rver = major;
276 return 0;
277 }
278
279 ERRX("rsyncd protocol error: unknown command")rsync_errx( ("rsyncd protocol error: unknown command"));
280 return -1;
281}
282
283/*
284 * Connect to a remote rsync://-enabled server sender.
285 * Returns exit code 0 on success, 1 on failure.
286 */
287int
288rsync_connect(const struct opts *opts, int *sd, const struct fargs *f)
289{
290 struct sess sess;
291 struct source *src = NULL((void *)0), *bsrc = NULL((void *)0);
292 size_t i, srcsz = 0, bsrcsz = 0;
293 int c, rc = 1;
294
295 if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil",
296 NULL((void *)0)) == -1)
297 err(ERR_IPC14, "pledge");
298
299 memset(&sess, 0, sizeof(struct sess));
300 sess.opts = opts;
301
302 assert(f->host != NULL)((f->host != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 302, __func__, "f->host != NULL"))
;
303
304 /* Resolve all IP addresses from the host. */
305
306 if ((src = inet_resolve(&sess, f->host, &srcsz, 0)) == NULL((void *)0)) {
307 ERRX1("inet_resolve")rsync_errx1( ("inet_resolve"));
308 exit(1);
309 }
310 if (opts->address != NULL((void *)0))
311 if ((bsrc = inet_resolve(&sess, opts->address, &bsrcsz, 1)) ==
312 NULL((void *)0)) {
313 ERRX1("inet_resolve bind")rsync_errx1( ("inet_resolve bind"));
314 exit(1);
315 }
316
317 /* Drop the DNS pledge. */
318
319 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw inet unveil",
320 NULL((void *)0)) == -1) {
321 ERR("pledge")rsync_err( ("pledge"));
322 exit(1);
323 }
324
325 /*
326 * Iterate over all addresses, trying to connect.
327 * When we succeed, then continue using the connected socket.
328 */
329
330 assert(srcsz)((srcsz) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 330, __func__, "srcsz"))
;
331 for (i = 0; i < srcsz; i++) {
332 c = inet_connect(sd, &src[i], f->host, bsrc, bsrcsz);
333 if (c < 0) {
334 ERRX1("inet_connect")rsync_errx1( ("inet_connect"));
335 goto out;
336 } else if (c > 0)
337 break;
338 }
339
340 /* Drop the inet pledge. */
341 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil",
342 NULL((void *)0)) == -1) {
343 ERR("pledge")rsync_err( ("pledge"));
344 goto out;
345 }
346
347 if (i == srcsz) {
348 ERRX("cannot connect to host: %s", f->host)rsync_errx( ("cannot connect to host: %s"), f->host);
349 goto out;
350 }
351
352 LOG2("connected: %s, %s", src[i].ip, f->host)rsync_log( 1, ("connected: %s, %s"), src[i].ip, f->host);
353
354 free(src);
355 free(bsrc);
356 return 0;
357out:
358 free(src);
359 free(bsrc);
360 if (*sd != -1)
361 close(*sd);
362 return rc;
363}
364
365/*
366 * Talk to a remote rsync://-enabled server sender.
367 * Returns exit code 0 on success, 1 on failure, 2 on failure with
368 * incompatible protocols.
369 */
370int
371rsync_socket(const struct opts *opts, int sd, const struct fargs *f)
372{
373 struct sess sess;
374 size_t i, skip;
375 int c, rc = 1;
376 char **args, buf[BUFSIZ1024];
377 uint8_t byte;
378
379 if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil",
380 NULL((void *)0)) == -1)
381 err(ERR_IPC14, "pledge");
382
383 memset(&sess, 0, sizeof(struct sess));
384 sess.lver = RSYNC_PROTOCOL(27);
385 sess.opts = opts;
386
387 assert(f->host != NULL)((f->host != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 387, __func__, "f->host != NULL"))
;
388 assert(f->module != NULL)((f->module != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 388, __func__, "f->module != NULL"))
;
389
390 args = fargs_cmdline(&sess, f, &skip);
391
392 /* Initiate with the rsyncd version and module request. */
393
394 (void)snprintf(buf, sizeof(buf), "@RSYNCD: %d", sess.lver);
395 if (!io_write_line(&sess, sd, buf)) {
396 ERRX1("io_write_line")rsync_errx1( ("io_write_line"));
397 goto out;
398 }
399
400 LOG2("requesting module: %s, %s", f->module, f->host)rsync_log( 1, ("requesting module: %s, %s"), f->module, f->
host)
;
401
402 if (!io_write_line(&sess, sd, f->module)) {
403 ERRX1("io_write_line")rsync_errx1( ("io_write_line"));
404 goto out;
405 }
406
407 /*
408 * Now we read the server's response, byte-by-byte, one newline
409 * terminated at a time, limited to BUFSIZ line length.
410 * For this protocol version, this consists of either @RSYNCD
411 * followed by some text (just "ok" and the remote version) or
412 * the message of the day.
413 */
414
415 for (;;) {
416 for (i = 0; i < sizeof(buf); i++) {
417 if (!io_read_byte(&sess, sd, &byte)) {
418 ERRX1("io_read_byte")rsync_errx1( ("io_read_byte"));
419 goto out;
420 }
421 if ((buf[i] = byte) == '\n')
422 break;
423 }
424 if (i == sizeof(buf)) {
425 ERRX("line buffer overrun")rsync_errx( ("line buffer overrun"));
426 goto out;
427 } else if (i == 0)
428 continue;
429
430 /*
431 * The rsyncd protocol isn't very clear as to whether we
432 * get a CRLF or not: I don't actually see this being
433 * transmitted over the wire.
434 */
435
436 assert(i > 0)((i > 0) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 436, __func__, "i > 0"))
;
437 buf[i] = '\0';
438 if (buf[i - 1] == '\r')
439 buf[i - 1] = '\0';
440
441 if ((c = protocol_line(&sess, f->host, buf)) < 0) {
442 ERRX1("protocol_line")rsync_errx1( ("protocol_line"));
443 goto out;
444 } else if (c > 0)
445 break;
446 }
447
448 /*
449 * Now we've exchanged all of our protocol information.
450 * We want to send our command-line arguments over the wire,
451 * each with a newline termination.
452 * Use the same arguments when invoking the server, but leave
453 * off the binary name(s).
454 * Emit a standalone newline afterward.
455 */
456
457 for (i = skip ; args[i] != NULL((void *)0); i++)
458 if (!io_write_line(&sess, sd, args[i])) {
459 ERRX1("io_write_line")rsync_errx1( ("io_write_line"));
460 goto out;
461 }
462 if (!io_write_byte(&sess, sd, '\n')) {
463 ERRX1("io_write_line")rsync_errx1( ("io_write_line"));
464 goto out;
465 }
466
467 /*
468 * All data after this point is going to be multiplexed, so turn
469 * on the multiplexer for our reads and writes.
470 */
471
472 /* Protocol exchange: get the random seed. */
473
474 if (!io_read_int(&sess, sd, &sess.seed)) {
475 ERRX1("io_read_int")rsync_errx1( ("io_read_int"));
476 goto out;
477 }
478
479 /* Now we've completed the handshake. */
480
481 if (sess.rver < sess.lver) {
482 ERRX("remote protocol is older than our own (%d < %d): "rsync_errx( ("remote protocol is older than our own (%d < %d): "
"this is not supported"), sess.rver, sess.lver)
483 "this is not supported",rsync_errx( ("remote protocol is older than our own (%d < %d): "
"this is not supported"), sess.rver, sess.lver)
484 sess.rver, sess.lver)rsync_errx( ("remote protocol is older than our own (%d < %d): "
"this is not supported"), sess.rver, sess.lver)
;
485 rc = 2;
486 goto out;
487 }
488
489 sess.mplex_reads = 1;
490 LOG2("read multiplexing enabled")rsync_log( 1, ("read multiplexing enabled"));
491
492 LOG2("socket detected client version %d, server version %d, seed %d",rsync_log( 1, ("socket detected client version %d, server version %d, seed %d"
), sess.lver, sess.rver, sess.seed)
493 sess.lver, sess.rver, sess.seed)rsync_log( 1, ("socket detected client version %d, server version %d, seed %d"
), sess.lver, sess.rver, sess.seed)
;
494
495 assert(f->mode == FARGS_RECEIVER)((f->mode == FARGS_RECEIVER) ? (void)0 : __assert2("/usr/src/usr.bin/rsync/socket.c"
, 495, __func__, "f->mode == FARGS_RECEIVER"))
;
496
497 LOG2("client starting receiver: %s", f->host)rsync_log( 1, ("client starting receiver: %s"), f->host);
498 if (!rsync_receiver(&sess, sd, sd, f->sink)) {
499 ERRX1("rsync_receiver")rsync_errx1( ("rsync_receiver"));
500 goto out;
501 }
502
503#if 0
504 /* Probably the EOF. */
505 if (io_read_check(&sess, sd))
506 WARNX("data remains in read pipe")rsync_warnx( ("data remains in read pipe"));
507#endif
508
509 rc = 0;
510out:
511 free(args);
512 return rc;
513}