1 /* $OpenBSD: xmodem.c,v 1.9 2016/02/04 18:33:30 millert 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 <stdint.h> 24 #include <stdio.h> 25 #include <string.h> 26 #include <termios.h> 27 #include <unistd.h> 28 29 #include "cu.h" 30 31 #define XMODEM_BLOCK 128 32 #define XMODEM_RETRIES 10 33 34 #define XMODEM_SOH '\001' 35 #define XMODEM_EOT '\004' 36 #define XMODEM_ACK '\006' 37 #define XMODEM_NAK '\025' 38 #define XMODEM_SUB '\032' 39 #define XMODEM_C '\103' 40 41 volatile sig_atomic_t xmodem_stop; 42 43 void xmodem_signal(int sig); 44 uint16_t xmodem_crc16(const u_char *buf, size_t len); 45 int xmodem_read(char *c); 46 int xmodem_write(const u_char *buf, size_t len); 47 48 void 49 xmodem_signal(int sig) 50 { 51 xmodem_stop = 1; 52 } 53 54 uint16_t 55 xmodem_crc16(const u_char *buf, size_t len) 56 { 57 uint16_t crc; 58 u_int i, j; 59 60 crc = 0; 61 for (i = 0; i < len; i++) { 62 crc = crc ^ *buf++ << 8; 63 for (j = 0; j < 8; j++) 64 if (crc & 0x8000) 65 crc = crc << 1 ^ 0x1021; 66 else 67 crc = crc << 1; 68 } 69 return (crc); 70 } 71 72 int 73 xmodem_read(char *c) 74 { 75 for (;;) { 76 switch (read(line_fd, c, 1)) { 77 case -1: 78 if (errno == EINTR && !xmodem_stop) 79 continue; 80 return (-1); 81 case 0: 82 errno = EPIPE; 83 return (-1); 84 case 1: 85 return (0); 86 } 87 } 88 } 89 90 int 91 xmodem_write(const u_char *buf, size_t len) 92 { 93 ssize_t n; 94 95 while (len > 0) { 96 n = write(line_fd, buf, len); 97 if (n == -1) { 98 if (errno == EINTR && !xmodem_stop) 99 continue; 100 return (-1); 101 } 102 buf += n; 103 len -= n; 104 } 105 return (0); 106 } 107 108 void 109 xmodem_send(const char *file) 110 { 111 FILE *f; 112 u_char buf[3 + XMODEM_BLOCK + 2], c; 113 size_t len, pktlen; 114 uint8_t num; 115 uint16_t crc; 116 int crc_mode; 117 u_int i, total; 118 struct termios tio; 119 struct sigaction act, oact; 120 121 f = fopen(file, "r"); 122 if (f == NULL) { 123 cu_warn("%s", file); 124 return; 125 } 126 127 memset(&act, 0, sizeof(act)); 128 sigemptyset(&act.sa_mask); 129 act.sa_flags = 0; 130 act.sa_handler = xmodem_signal; 131 if (sigaction(SIGINT, &act, &oact) != 0) 132 cu_err(1, "sigaction"); 133 xmodem_stop = 0; 134 135 if (isatty(STDIN_FILENO)) { 136 memcpy(&tio, &saved_tio, sizeof(tio)); 137 tio.c_lflag &= ~ECHO; 138 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) 139 cu_err(1, "tcsetattr"); 140 } 141 set_blocking(line_fd, 1); 142 tcflush(line_fd, TCIFLUSH); 143 144 if (xmodem_read(&c) != 0) 145 goto fail; 146 if (c == XMODEM_C) 147 crc_mode = 1; 148 else if (c == XMODEM_NAK) 149 crc_mode = 0; 150 else { 151 cu_warnx("%s: unexpected response \\%03hho", file, c); 152 goto fail; 153 } 154 155 num = 1; 156 total = 1; 157 pktlen = 3 + XMODEM_BLOCK + (crc_mode ? 2 : 1); 158 for (;;) { 159 len = fread(buf + 3, 1, XMODEM_BLOCK, f); 160 if (len == 0) 161 break; 162 memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len); 163 164 buf[0] = XMODEM_SOH; 165 buf[1] = num; 166 buf[2] = 255 - num; 167 168 if (crc_mode) { 169 crc = xmodem_crc16(buf + 3, XMODEM_BLOCK); 170 buf[3 + XMODEM_BLOCK] = crc >> 8; 171 buf[3 + XMODEM_BLOCK + 1] = crc & 0xFF; 172 } else { 173 buf[3 + XMODEM_BLOCK] = 0; 174 for (i = 0; i < XMODEM_BLOCK; i++) 175 buf[3 + XMODEM_BLOCK] += buf[3 + i]; 176 } 177 178 for (i = 0; i < XMODEM_RETRIES; i++) { 179 if (xmodem_stop) { 180 errno = EINTR; 181 goto fail; 182 } 183 cu_warnx("%s: sending block %u (attempt %u)", file, 184 total, 1 + i); 185 if (xmodem_write(buf, pktlen) != 0) 186 goto fail; 187 188 if (xmodem_read(&c) != 0) 189 goto fail; 190 if (c == XMODEM_ACK) 191 break; 192 if (c != XMODEM_NAK) { 193 cu_warnx("%s: unexpected response \\%03hho", 194 file, c); 195 } 196 } 197 if (i == XMODEM_RETRIES) { 198 cu_warnx("%s: too many retries", file); 199 goto out; 200 } 201 202 if (len < XMODEM_BLOCK) 203 break; 204 num++; 205 total++; 206 } 207 208 buf[0] = XMODEM_EOT; 209 if (xmodem_write(buf, 1) != 0) 210 goto fail; 211 cu_warnx("%s: completed %u blocks", file, num); 212 213 goto out; 214 215 fail: 216 cu_warn("%s", file); 217 218 out: 219 set_blocking(line_fd, 0); 220 set_termios(); 221 222 sigaction(SIGINT, &oact, NULL); 223 224 fclose(f); 225 } 226