xref: /openbsd-src/usr.bin/cu/xmodem.c (revision 169f012f6dd604ce5dfb9f685252148bfe4122d5)
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