1 /* $Id: server.c,v 1.4 2019/02/11 21:41:22 deraadt 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 19 #include <assert.h> 20 #include <fcntl.h> 21 #include <inttypes.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "extern.h" 27 28 static int 29 fcntl_nonblock(struct sess *sess, int fd) 30 { 31 int fl; 32 33 if ((fl = fcntl(fd, F_GETFL, 0)) == -1) 34 ERR(sess, "fcntl: F_GETFL"); 35 else if (fcntl(fd, F_SETFL, fl|O_NONBLOCK) == -1) 36 ERR(sess, "fcntl: F_SETFL"); 37 else 38 return 1; 39 40 return 0; 41 } 42 43 /* 44 * The server (remote) side of the system. 45 * This parses the arguments given it by the remote shell then moves 46 * into receiver or sender mode depending upon those arguments. 47 * 48 * Pledges: unveil rpath, cpath, wpath, stdio, fattr. 49 * 50 * Pledges (dry-run): -cpath, -wpath, -fattr. 51 * Pledges (!preserve_times): -fattr. 52 */ 53 int 54 rsync_server(const struct opts *opts, size_t argc, char *argv[]) 55 { 56 struct sess sess; 57 int fdin = STDIN_FILENO, 58 fdout = STDOUT_FILENO, c = 0; 59 60 memset(&sess, 0, sizeof(struct sess)); 61 sess.opts = opts; 62 63 /* Begin by making descriptors non-blocking. */ 64 65 if (!fcntl_nonblock(&sess, fdin) || 66 !fcntl_nonblock(&sess, fdout)) { 67 ERRX1(&sess, "fcntl_nonblock"); 68 goto out; 69 } 70 71 /* Standard rsync preamble, server side. */ 72 73 sess.lver = RSYNC_PROTOCOL; 74 sess.seed = arc4random(); 75 76 if (!io_read_int(&sess, fdin, &sess.rver)) { 77 ERRX1(&sess, "io_read_int"); 78 goto out; 79 } else if (!io_write_int(&sess, fdout, sess.lver)) { 80 ERRX1(&sess, "io_write_int"); 81 goto out; 82 } else if (!io_write_int(&sess, fdout, sess.seed)) { 83 ERRX1(&sess, "io_write_int"); 84 goto out; 85 } 86 87 sess.mplex_writes = 1; 88 89 if (sess.rver < sess.lver) { 90 ERRX(&sess, "remote protocol is older " 91 "than our own (%" PRId32 " < %" PRId32 "): " 92 "this is not supported", 93 sess.rver, sess.lver); 94 goto out; 95 } 96 97 LOG2(&sess, "server detected client version %" PRId32 98 ", server version %" PRId32 ", seed %" PRId32, 99 sess.rver, sess.lver, sess.seed); 100 101 if (sess.opts->sender) { 102 LOG2(&sess, "server starting sender"); 103 104 /* 105 * At this time, I always get a period as the first 106 * argument of the command line. 107 * Let's make it a requirement until I figure out when 108 * that differs. 109 * rsync [flags] "." <source> <...> 110 */ 111 112 if (strcmp(argv[0], ".")) { 113 ERRX(&sess, "first argument must " 114 "be a standalone period"); 115 goto out; 116 } 117 argv++; 118 argc--; 119 if (argc == 0) { 120 ERRX(&sess, "must have arguments"); 121 goto out; 122 } 123 124 if (!rsync_sender(&sess, fdin, fdout, argc, argv)) { 125 ERRX1(&sess, "rsync_sender"); 126 goto out; 127 } 128 } else { 129 LOG2(&sess, "server starting receiver"); 130 131 /* 132 * I don't understand why this calling convention 133 * exists, but we must adhere to it. 134 * rsync [flags] "." <destination> 135 */ 136 137 if (argc != 2) { 138 ERRX(&sess, "server receiver mode " 139 "requires two argument"); 140 goto out; 141 } else if (strcmp(argv[0], ".")) { 142 ERRX(&sess, "first argument must " 143 "be a standalone period"); 144 goto out; 145 } 146 147 if (!rsync_receiver(&sess, fdin, fdout, argv[1])) { 148 ERRX1(&sess, "rsync_receiver"); 149 goto out; 150 } 151 } 152 153 #if 0 154 /* Probably the EOF. */ 155 if (io_read_check(&sess, fdin)) 156 WARNX(&sess, "data remains in read pipe"); 157 #endif 158 159 c = 1; 160 out: 161 return c; 162 } 163