1 /* $OpenBSD: xmodem.c,v 1.4 2013/01/17 21:10:24 nicm 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 39 volatile sig_atomic_t xmodem_stop; 40 41 void 42 xmodem_signal(int sig) 43 { 44 xmodem_stop = 1; 45 } 46 47 int 48 xmodem_read(char *c) 49 { 50 for (;;) { 51 switch (read(line_fd, c, 1)) { 52 case -1: 53 if (errno == EINTR && !xmodem_stop) 54 continue; 55 return (-1); 56 case 0: 57 errno = EPIPE; 58 return (-1); 59 case 1: 60 return (0); 61 } 62 } 63 } 64 65 int 66 xmodem_write(const u_char *buf, size_t len) 67 { 68 ssize_t n; 69 70 while (len > 0) { 71 n = write(line_fd, buf, len); 72 if (n == -1) { 73 if (errno == EINTR && !xmodem_stop) 74 continue; 75 return (-1); 76 } 77 buf += n; 78 len -= n; 79 } 80 return (0); 81 } 82 83 void 84 xmodem_send(const char *file) 85 { 86 FILE *f; 87 u_char buf[3 + XMODEM_BLOCK + 1], c; 88 size_t len; 89 uint8_t num; 90 u_int i, total; 91 struct termios tio; 92 struct sigaction act, oact; 93 94 f = fopen(file, "r"); 95 if (f == NULL) { 96 cu_warn("%s", file); 97 return; 98 } 99 100 memset(&act, 0, sizeof(act)); 101 sigemptyset(&act.sa_mask); 102 act.sa_flags = 0; 103 act.sa_handler = xmodem_signal; 104 if (sigaction(SIGINT, &act, &oact) != 0) 105 cu_err(1, "sigaction"); 106 xmodem_stop = 0; 107 108 if (isatty(STDIN_FILENO)) { 109 memcpy(&tio, &saved_tio, sizeof(tio)); 110 tio.c_lflag &= ~ECHO; 111 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) 112 cu_err(1, "tcsetattr"); 113 } 114 115 num = 1; 116 total = 1; 117 for (;;) { 118 len = fread(buf + 3, 1, XMODEM_BLOCK, f); 119 if (len == 0) 120 break; 121 memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len); 122 123 buf[0] = XMODEM_SOH; 124 buf[1] = num; 125 buf[2] = 255 - num; 126 127 buf[3 + XMODEM_BLOCK] = 0; 128 for (i = 0; i < 128; i++) 129 buf[3 + XMODEM_BLOCK] += buf[3 + i]; 130 131 for (i = 0; i < XMODEM_RETRIES; i++) { 132 if (xmodem_stop) { 133 errno = EINTR; 134 goto fail; 135 } 136 cu_warnx("%s: sending block %u (attempt %u)", file, 137 total, 1 + i); 138 if (xmodem_write(buf, sizeof buf) != 0) 139 goto fail; 140 141 if (xmodem_read(&c) != 0) 142 goto fail; 143 if (c == XMODEM_ACK) 144 break; 145 if (c != XMODEM_NAK) { 146 cu_warnx("%s: unexpected response \%03hho", 147 file, c); 148 } 149 } 150 if (i == XMODEM_RETRIES) { 151 cu_warnx("%s: too many retries", file); 152 goto out; 153 } 154 155 if (len < XMODEM_BLOCK) 156 break; 157 num++; 158 total++; 159 } 160 161 buf[0] = XMODEM_EOT; 162 if (xmodem_write(buf, 1) != 0) 163 goto fail; 164 cu_warnx("%s: completed %u blocks", file, num); 165 166 goto out; 167 168 fail: 169 cu_warn("%s", file); 170 171 out: 172 set_termios(); 173 174 sigaction(SIGINT, &oact, NULL); 175 176 fclose(f); 177 } 178