1*169f012fSmillert /* $OpenBSD: xmodem.c,v 1.9 2016/02/04 18:33:30 millert Exp $ */
2649fc061Snicm
3649fc061Snicm /*
4649fc061Snicm * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
5649fc061Snicm *
6649fc061Snicm * Permission to use, copy, modify, and distribute this software for any
7649fc061Snicm * purpose with or without fee is hereby granted, provided that the above
8649fc061Snicm * copyright notice and this permission notice appear in all copies.
9649fc061Snicm *
10649fc061Snicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11649fc061Snicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12649fc061Snicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13649fc061Snicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14649fc061Snicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15649fc061Snicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16649fc061Snicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17649fc061Snicm */
18649fc061Snicm
19649fc061Snicm #include <sys/types.h>
20649fc061Snicm
21649fc061Snicm #include <errno.h>
22649fc061Snicm #include <signal.h>
23*169f012fSmillert #include <stdint.h>
24649fc061Snicm #include <stdio.h>
25649fc061Snicm #include <string.h>
26649fc061Snicm #include <termios.h>
27649fc061Snicm #include <unistd.h>
28649fc061Snicm
29649fc061Snicm #include "cu.h"
30649fc061Snicm
31649fc061Snicm #define XMODEM_BLOCK 128
32649fc061Snicm #define XMODEM_RETRIES 10
33649fc061Snicm
34649fc061Snicm #define XMODEM_SOH '\001'
35649fc061Snicm #define XMODEM_EOT '\004'
36649fc061Snicm #define XMODEM_ACK '\006'
37649fc061Snicm #define XMODEM_NAK '\025'
38649fc061Snicm #define XMODEM_SUB '\032'
391c4d8f22Snaddy #define XMODEM_C '\103'
40649fc061Snicm
41649fc061Snicm volatile sig_atomic_t xmodem_stop;
42649fc061Snicm
4343a6699fSderaadt void xmodem_signal(int sig);
4443a6699fSderaadt uint16_t xmodem_crc16(const u_char *buf, size_t len);
4543a6699fSderaadt int xmodem_read(char *c);
4643a6699fSderaadt int xmodem_write(const u_char *buf, size_t len);
4743a6699fSderaadt
48649fc061Snicm void
xmodem_signal(int sig)49649fc061Snicm xmodem_signal(int sig)
50649fc061Snicm {
51649fc061Snicm xmodem_stop = 1;
52649fc061Snicm }
53649fc061Snicm
541c4d8f22Snaddy uint16_t
xmodem_crc16(const u_char * buf,size_t len)551c4d8f22Snaddy xmodem_crc16(const u_char *buf, size_t len)
561c4d8f22Snaddy {
571c4d8f22Snaddy uint16_t crc;
581c4d8f22Snaddy u_int i, j;
591c4d8f22Snaddy
601c4d8f22Snaddy crc = 0;
611c4d8f22Snaddy for (i = 0; i < len; i++) {
621c4d8f22Snaddy crc = crc ^ *buf++ << 8;
631c4d8f22Snaddy for (j = 0; j < 8; j++)
641c4d8f22Snaddy if (crc & 0x8000)
651c4d8f22Snaddy crc = crc << 1 ^ 0x1021;
661c4d8f22Snaddy else
671c4d8f22Snaddy crc = crc << 1;
681c4d8f22Snaddy }
691c4d8f22Snaddy return (crc);
701c4d8f22Snaddy }
711c4d8f22Snaddy
72649fc061Snicm int
xmodem_read(char * c)73649fc061Snicm xmodem_read(char *c)
74649fc061Snicm {
75649fc061Snicm for (;;) {
76649fc061Snicm switch (read(line_fd, c, 1)) {
77649fc061Snicm case -1:
78649fc061Snicm if (errno == EINTR && !xmodem_stop)
79649fc061Snicm continue;
80649fc061Snicm return (-1);
81649fc061Snicm case 0:
82649fc061Snicm errno = EPIPE;
83649fc061Snicm return (-1);
84649fc061Snicm case 1:
85649fc061Snicm return (0);
86649fc061Snicm }
87649fc061Snicm }
88649fc061Snicm }
89649fc061Snicm
90649fc061Snicm int
xmodem_write(const u_char * buf,size_t len)91649fc061Snicm xmodem_write(const u_char *buf, size_t len)
92649fc061Snicm {
93649fc061Snicm ssize_t n;
94649fc061Snicm
95649fc061Snicm while (len > 0) {
96649fc061Snicm n = write(line_fd, buf, len);
97649fc061Snicm if (n == -1) {
98649fc061Snicm if (errno == EINTR && !xmodem_stop)
99649fc061Snicm continue;
100649fc061Snicm return (-1);
101649fc061Snicm }
102649fc061Snicm buf += n;
103649fc061Snicm len -= n;
104649fc061Snicm }
105649fc061Snicm return (0);
106649fc061Snicm }
107649fc061Snicm
108649fc061Snicm void
xmodem_send(const char * file)109649fc061Snicm xmodem_send(const char *file)
110649fc061Snicm {
111649fc061Snicm FILE *f;
1121c4d8f22Snaddy u_char buf[3 + XMODEM_BLOCK + 2], c;
1131c4d8f22Snaddy size_t len, pktlen;
114649fc061Snicm uint8_t num;
1151c4d8f22Snaddy uint16_t crc;
1161c4d8f22Snaddy int crc_mode;
1177ca3ddfaSnicm u_int i, total;
118649fc061Snicm struct termios tio;
119649fc061Snicm struct sigaction act, oact;
120649fc061Snicm
121649fc061Snicm f = fopen(file, "r");
122649fc061Snicm if (f == NULL) {
123649fc061Snicm cu_warn("%s", file);
124649fc061Snicm return;
125649fc061Snicm }
126649fc061Snicm
127649fc061Snicm memset(&act, 0, sizeof(act));
128649fc061Snicm sigemptyset(&act.sa_mask);
129649fc061Snicm act.sa_flags = 0;
130649fc061Snicm act.sa_handler = xmodem_signal;
131649fc061Snicm if (sigaction(SIGINT, &act, &oact) != 0)
132649fc061Snicm cu_err(1, "sigaction");
133649fc061Snicm xmodem_stop = 0;
134649fc061Snicm
135649fc061Snicm if (isatty(STDIN_FILENO)) {
136649fc061Snicm memcpy(&tio, &saved_tio, sizeof(tio));
137649fc061Snicm tio.c_lflag &= ~ECHO;
138649fc061Snicm if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0)
139649fc061Snicm cu_err(1, "tcsetattr");
140649fc061Snicm }
141668574b2Snicm set_blocking(line_fd, 1);
1421c4d8f22Snaddy tcflush(line_fd, TCIFLUSH);
143668574b2Snicm
1441c4d8f22Snaddy if (xmodem_read(&c) != 0)
1451c4d8f22Snaddy goto fail;
1461c4d8f22Snaddy if (c == XMODEM_C)
1471c4d8f22Snaddy crc_mode = 1;
1481c4d8f22Snaddy else if (c == XMODEM_NAK)
1491c4d8f22Snaddy crc_mode = 0;
1501c4d8f22Snaddy else {
151224be063Sdaniel cu_warnx("%s: unexpected response \\%03hho", file, c);
1521c4d8f22Snaddy goto fail;
1531c4d8f22Snaddy }
1541c4d8f22Snaddy
155649fc061Snicm num = 1;
1567ca3ddfaSnicm total = 1;
1571c4d8f22Snaddy pktlen = 3 + XMODEM_BLOCK + (crc_mode ? 2 : 1);
158649fc061Snicm for (;;) {
159649fc061Snicm len = fread(buf + 3, 1, XMODEM_BLOCK, f);
160649fc061Snicm if (len == 0)
161649fc061Snicm break;
162649fc061Snicm memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len);
163649fc061Snicm
164649fc061Snicm buf[0] = XMODEM_SOH;
165649fc061Snicm buf[1] = num;
166649fc061Snicm buf[2] = 255 - num;
167649fc061Snicm
1681c4d8f22Snaddy if (crc_mode) {
1691c4d8f22Snaddy crc = xmodem_crc16(buf + 3, XMODEM_BLOCK);
1701c4d8f22Snaddy buf[3 + XMODEM_BLOCK] = crc >> 8;
1711c4d8f22Snaddy buf[3 + XMODEM_BLOCK + 1] = crc & 0xFF;
1721c4d8f22Snaddy } else {
173649fc061Snicm buf[3 + XMODEM_BLOCK] = 0;
1741c4d8f22Snaddy for (i = 0; i < XMODEM_BLOCK; i++)
175649fc061Snicm buf[3 + XMODEM_BLOCK] += buf[3 + i];
1761c4d8f22Snaddy }
177649fc061Snicm
178649fc061Snicm for (i = 0; i < XMODEM_RETRIES; i++) {
179649fc061Snicm if (xmodem_stop) {
180649fc061Snicm errno = EINTR;
181649fc061Snicm goto fail;
182649fc061Snicm }
183649fc061Snicm cu_warnx("%s: sending block %u (attempt %u)", file,
1847ca3ddfaSnicm total, 1 + i);
1851c4d8f22Snaddy if (xmodem_write(buf, pktlen) != 0)
186649fc061Snicm goto fail;
187649fc061Snicm
188649fc061Snicm if (xmodem_read(&c) != 0)
189649fc061Snicm goto fail;
190649fc061Snicm if (c == XMODEM_ACK)
191649fc061Snicm break;
192649fc061Snicm if (c != XMODEM_NAK) {
193224be063Sdaniel cu_warnx("%s: unexpected response \\%03hho",
194649fc061Snicm file, c);
195649fc061Snicm }
196649fc061Snicm }
197649fc061Snicm if (i == XMODEM_RETRIES) {
198649fc061Snicm cu_warnx("%s: too many retries", file);
199649fc061Snicm goto out;
200640d54c5Snicm }
201649fc061Snicm
202649fc061Snicm if (len < XMODEM_BLOCK)
203649fc061Snicm break;
204649fc061Snicm num++;
2057ca3ddfaSnicm total++;
206649fc061Snicm }
207649fc061Snicm
208649fc061Snicm buf[0] = XMODEM_EOT;
209649fc061Snicm if (xmodem_write(buf, 1) != 0)
210649fc061Snicm goto fail;
211649fc061Snicm cu_warnx("%s: completed %u blocks", file, num);
212649fc061Snicm
213649fc061Snicm goto out;
214649fc061Snicm
215649fc061Snicm fail:
216649fc061Snicm cu_warn("%s", file);
217649fc061Snicm
218649fc061Snicm out:
219668574b2Snicm set_blocking(line_fd, 0);
220649fc061Snicm set_termios();
221649fc061Snicm
222649fc061Snicm sigaction(SIGINT, &oact, NULL);
223649fc061Snicm
224f289678eSnicm fclose(f);
225649fc061Snicm }
226