1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36#include "libbb.h"
37
38#define SOH 0x01
39#define STX 0x02
40#define EOT 0x04
41#define ACK 0x06
42#define NAK 0x15
43#define BS 0x08
44#define PAD 0x1A
45
46
47
48
49
50
51
52
53
54#define TIMEOUT 1
55#define TIMEOUT_LONG 10
56#define MAXERRORS 10
57
58#define read_fd STDIN_FILENO
59#define write_fd STDOUT_FILENO
60
61static int read_byte(unsigned timeout)
62{
63 unsigned char buf;
64 int n;
65
66 alarm(timeout);
67
68 n = read(read_fd, &buf, 1);
69 alarm(0);
70 if (n == 1)
71 return buf;
72 return -1;
73}
74
75static int receive(int file_fd)
76{
77 unsigned char blockBuf[1024];
78 unsigned blockLength = 0;
79 unsigned errors = 0;
80 unsigned wantBlockNo = 1;
81 unsigned length = 0;
82 int do_crc = 1;
83 char reply_char;
84 unsigned timeout = TIMEOUT_LONG;
85
86
87 tcflush(read_fd, TCIFLUSH);
88
89
90 reply_char = 'C';
91 full_write(write_fd, &reply_char, 1);
92
93 for (;;) {
94 int blockBegin;
95 int blockNo, blockNoOnesCompl;
96 int cksum_or_crc;
97 unsigned expected;
98 int i, j;
99
100 blockBegin = read_byte(timeout);
101 if (blockBegin < 0)
102 goto timeout;
103
104
105 if (blockBegin == EOT) {
106
107
108 if (blockLength >= 3
109 && blockBuf[blockLength - 1] == PAD
110 && blockBuf[blockLength - 2] == PAD
111 && blockBuf[blockLength - 3] == PAD
112 ) {
113 while (blockLength
114 && blockBuf[blockLength - 1] == PAD
115 ) {
116 blockLength--;
117 }
118 }
119 }
120
121 errno = 0;
122 if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
123 bb_perror_msg(bb_msg_write_error);
124 goto fatal;
125 }
126
127 timeout = TIMEOUT;
128 reply_char = NAK;
129
130 switch (blockBegin) {
131 case SOH:
132 case STX:
133 break;
134 case EOT:
135 reply_char = ACK;
136 full_write(write_fd, &reply_char, 1);
137 return length;
138 default:
139 goto error;
140 }
141
142
143 blockNo = read_byte(TIMEOUT);
144 if (blockNo < 0)
145 goto timeout;
146
147
148 blockNoOnesCompl = read_byte(TIMEOUT);
149 if (blockNoOnesCompl < 0)
150 goto timeout;
151
152 if (blockNo != (255 - blockNoOnesCompl)) {
153 bb_error_msg("bad block ones compl");
154 goto error;
155 }
156
157 blockLength = (blockBegin == SOH) ? 128 : 1024;
158
159 for (i = 0; i < blockLength; i++) {
160 int cc = read_byte(TIMEOUT);
161 if (cc < 0)
162 goto timeout;
163 blockBuf[i] = cc;
164 }
165
166 cksum_or_crc = read_byte(TIMEOUT);
167 if (cksum_or_crc < 0)
168 goto timeout;
169 if (do_crc) {
170 cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
171 if (cksum_or_crc < 0)
172 goto timeout;
173 }
174
175 if (blockNo == ((wantBlockNo - 1) & 0xff)) {
176
177
178
179 blockLength = 0;
180 goto next;
181 }
182 if (blockNo != (wantBlockNo & 0xff)) {
183 bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
184 goto error;
185 }
186
187 expected = 0;
188 if (do_crc) {
189 for (i = 0; i < blockLength; i++) {
190 expected = expected ^ blockBuf[i] << 8;
191 for (j = 0; j < 8; j++) {
192 if (expected & 0x8000)
193 expected = (expected << 1) ^ 0x1021;
194 else
195 expected = (expected << 1);
196 }
197 }
198 expected &= 0xffff;
199 } else {
200 for (i = 0; i < blockLength; i++)
201 expected += blockBuf[i];
202 expected &= 0xff;
203 }
204 if (cksum_or_crc != expected) {
205 bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
206 : "checksum error, expected 0x%02x, got 0x%02x",
207 expected, cksum_or_crc);
208 goto error;
209 }
210
211 wantBlockNo++;
212 length += blockLength;
213 next:
214 errors = 0;
215 reply_char = ACK;
216 full_write(write_fd, &reply_char, 1);
217 continue;
218 error:
219 timeout:
220 blockLength = 0;
221 errors++;
222 if (errors == MAXERRORS) {
223
224
225
226 if (reply_char == 'C') {
227 reply_char = NAK;
228 errors = 0;
229 do_crc = 0;
230 goto timeout;
231 }
232 bb_error_msg("too many errors; giving up");
233 fatal:
234
235 safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
236 return -1;
237 }
238
239
240 tcflush(read_fd, TCIFLUSH);
241
242 full_write(write_fd, &reply_char, 1);
243 }
244}
245
246static void sigalrm_handler(int UNUSED_PARAM signum)
247{
248}
249
250int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
251int rx_main(int argc UNUSED_PARAM, char **argv)
252{
253 struct termios tty, orig_tty;
254 int termios_err;
255 int file_fd;
256 int n;
257
258
259
260
261
262 file_fd = xopen(single_argv(argv), O_RDWR|O_CREAT|O_TRUNC);
263
264 termios_err = tcgetattr(read_fd, &tty);
265 if (termios_err == 0) {
266
267 orig_tty = tty;
268 cfmakeraw(&tty);
269 tcsetattr(read_fd, TCSAFLUSH, &tty);
270 }
271
272
273 signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
274
275 n = receive(file_fd);
276
277 if (termios_err == 0)
278 tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
279 if (ENABLE_FEATURE_CLEAN_UP)
280 close(file_fd);
281 fflush_stdout_and_exit(n >= 0);
282}
283