1 /* $OpenBSD: xmodem.c,v 1.6 2013/11/12 13:54:51 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <errno.h> 22 #include <signal.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <termios.h> 26 #include <unistd.h> 27 28 #include "cu.h" 29 30 #define XMODEM_BLOCK 128 31 #define XMODEM_RETRIES 10 32 33 #define XMODEM_SOH '\001' 34 #define XMODEM_EOT '\004' 35 #define XMODEM_ACK '\006' 36 #define XMODEM_NAK '\025' 37 #define XMODEM_SUB '\032' 38 #define XMODEM_C '\103' 39 40 volatile sig_atomic_t xmodem_stop; 41 42 void xmodem_signal(int sig); 43 uint16_t xmodem_crc16(const u_char *buf, size_t len); 44 int xmodem_read(char *c); 45 int xmodem_write(const u_char *buf, size_t len); 46 47 void 48 xmodem_signal(int sig) 49 { 50 xmodem_stop = 1; 51 } 52 53 uint16_t 54 xmodem_crc16(const u_char *buf, size_t len) 55 { 56 uint16_t crc; 57 u_int i, j; 58 59 crc = 0; 60 for (i = 0; i < len; i++) { 61 crc = crc ^ *buf++ << 8; 62 for (j = 0; j < 8; j++) 63 if (crc & 0x8000) 64 crc = crc << 1 ^ 0x1021; 65 else 66 crc = crc << 1; 67 } 68 return (crc); 69 } 70 71 int 72 xmodem_read(char *c) 73 { 74 for (;;) { 75 switch (read(line_fd, c, 1)) { 76 case -1: 77 if (errno == EINTR && !xmodem_stop) 78 continue; 79 return (-1); 80 case 0: 81 errno = EPIPE; 82 return (-1); 83 case 1: 84 return (0); 85 } 86 } 87 } 88 89 int 90 xmodem_write(const u_char *buf, size_t len) 91 { 92 ssize_t n; 93 94 while (len > 0) { 95 n = write(line_fd, buf, len); 96 if (n == -1) { 97 if (errno == EINTR && !xmodem_stop) 98 continue; 99 return (-1); 100 } 101 buf += n; 102 len -= n; 103 } 104 return (0); 105 } 106 107 void 108 xmodem_send(const char *file) 109 { 110 FILE *f; 111 u_char buf[3 + XMODEM_BLOCK + 2], c; 112 size_t len, pktlen; 113 uint8_t num; 114 uint16_t crc; 115 int crc_mode; 116 u_int i, total; 117 struct termios tio; 118 struct sigaction act, oact; 119 120 f = fopen(file, "r"); 121 if (f == NULL) { 122 cu_warn("%s", file); 123 return; 124 } 125 126 memset(&act, 0, sizeof(act)); 127 sigemptyset(&act.sa_mask); 128 act.sa_flags = 0; 129 act.sa_handler = xmodem_signal; 130 if (sigaction(SIGINT, &act, &oact) != 0) 131 cu_err(1, "sigaction"); 132 xmodem_stop = 0; 133 134 if (isatty(STDIN_FILENO)) { 135 memcpy(&tio, &saved_tio, sizeof(tio)); 136 tio.c_lflag &= ~ECHO; 137 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) 138 cu_err(1, "tcsetattr"); 139 } 140 141 tcflush(line_fd, TCIFLUSH); 142 if (xmodem_read(&c) != 0) 143 goto fail; 144 if (c == XMODEM_C) 145 crc_mode = 1; 146 else if (c == XMODEM_NAK) 147 crc_mode = 0; 148 else { 149 cu_warnx("%s: unexpected response \%03hho", file, c); 150 goto fail; 151 } 152 153 num = 1; 154 total = 1; 155 pktlen = 3 + XMODEM_BLOCK + (crc_mode ? 2 : 1); 156 for (;;) { 157 len = fread(buf + 3, 1, XMODEM_BLOCK, f); 158 if (len == 0) 159 break; 160 memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len); 161 162 buf[0] = XMODEM_SOH; 163 buf[1] = num; 164 buf[2] = 255 - num; 165 166 if (crc_mode) { 167 crc = xmodem_crc16(buf + 3, XMODEM_BLOCK); 168 buf[3 + XMODEM_BLOCK] = crc >> 8; 169 buf[3 + XMODEM_BLOCK + 1] = crc & 0xFF; 170 } else { 171 buf[3 + XMODEM_BLOCK] = 0; 172 for (i = 0; i < XMODEM_BLOCK; i++) 173 buf[3 + XMODEM_BLOCK] += buf[3 + i]; 174 } 175 176 for (i = 0; i < XMODEM_RETRIES; i++) { 177 if (xmodem_stop) { 178 errno = EINTR; 179 goto fail; 180 } 181 cu_warnx("%s: sending block %u (attempt %u)", file, 182 total, 1 + i); 183 if (xmodem_write(buf, pktlen) != 0) 184 goto fail; 185 186 if (xmodem_read(&c) != 0) 187 goto fail; 188 if (c == XMODEM_ACK) 189 break; 190 if (c != XMODEM_NAK) { 191 cu_warnx("%s: unexpected response \%03hho", 192 file, c); 193 } 194 } 195 if (i == XMODEM_RETRIES) { 196 cu_warnx("%s: too many retries", file); 197 goto out; 198 } 199 200 if (len < XMODEM_BLOCK) 201 break; 202 num++; 203 total++; 204 } 205 206 buf[0] = XMODEM_EOT; 207 if (xmodem_write(buf, 1) != 0) 208 goto fail; 209 cu_warnx("%s: completed %u blocks", file, num); 210 211 goto out; 212 213 fail: 214 cu_warn("%s", file); 215 216 out: 217 set_termios(); 218 219 sigaction(SIGINT, &oact, NULL); 220 221 fclose(f); 222 } 223