GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cu/xmodem.c Lines: 0 100 0.0 %
Date: 2016-12-06 Branches: 0 62 0.0 %

Line Branch Exec Source
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
49
xmodem_signal(int sig)
50
{
51
	xmodem_stop = 1;
52
}
53
54
uint16_t
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
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
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
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
}