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