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
xmodem_signal(int sig)49 xmodem_signal(int sig)
50 {
51 xmodem_stop = 1;
52 }
53
54 uint16_t
xmodem_crc16(const u_char * buf,size_t len)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
xmodem_read(char * c)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
xmodem_write(const u_char * buf,size_t len)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
xmodem_send(const char * file)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