summaryrefslogtreecommitdiff
path: root/miscutils/rx.c (plain)
blob: f794abeb3bbbfb5fefce55a1b2ec1c1543f20005
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright: Copyright (C) 2001, Hewlett-Packard Company
4 * Author: Christopher Hoover <ch@hpl.hp.com>
5 * Description: xmodem functionality for uploading of kernels
6 * and the like
7 * Created at: Thu Dec 20 01:58:08 PST 2001
8 *
9 * xmodem functionality for uploading of kernels and the like
10 *
11 * Copyright (C) 2001 Hewlett-Packard Laboratories
12 *
13 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
14 *
15 * This was originally written for blob and then adapted for busybox.
16 */
17//config:config RX
18//config: bool "rx"
19//config: default y
20//config: select PLATFORM_LINUX
21//config: help
22//config: Receive files using the Xmodem protocol.
23
24//applet:IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP))
25
26//kbuild:lib-$(CONFIG_RX) += rx.o
27
28//usage:#define rx_trivial_usage
29//usage: "FILE"
30//usage:#define rx_full_usage "\n\n"
31//usage: "Receive a file using the xmodem protocol"
32//usage:
33//usage:#define rx_example_usage
34//usage: "$ rx /tmp/foo\n"
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/*
47Cf:
48 http://www.textfiles.com/apple/xmodem
49 http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
50 http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
51 http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
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 /* NOT safe_read! We want ALRM to interrupt us */
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 read_fd, */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 /* Flush pending input */
87 tcflush(read_fd, TCIFLUSH);
88
89 /* Ask for CRC; if we get errors, we will go with checksum */
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 int expected;
98 unsigned i, j;
99
100 blockBegin = read_byte(timeout);
101 if (blockBegin < 0)
102 goto timeout;
103
104 /* If last block, remove padding */
105 if (blockBegin == EOT) {
106 /* Data blocks can be padded with ^Z characters */
107 /* This code tries to detect and remove them */
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 /* Write previously received block */
121 errno = 0;
122 if ((unsigned) 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 /* Block no */
143 blockNo = read_byte(TIMEOUT);
144 if (blockNo < 0)
145 goto timeout;
146
147 /* Block no, in one's complement form */
148 blockNoOnesCompl = read_byte(TIMEOUT);
149 if (blockNoOnesCompl < 0)
150 goto timeout;
151
152 if (blockNo != (int) (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 == (int) ((wantBlockNo - 1) & 0xff)) {
176 /* a repeat of the last block is ok, just ignore it. */
177 /* this also ignores the initial block 0 which is */
178 /* meta data. nt)*/
179 blockLength = 0;
180 goto next;
181 }
182 if (blockNo != (int) (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 /* Abort */
224
225 /* If were asking for crc, try again w/o crc */
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 /* 5 CAN followed by 5 BS. Don't try too hard... */
235 safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
236 return -1;
237 }
238
239 /* Flush pending input */
240 tcflush(read_fd, TCIFLUSH);
241
242 full_write(write_fd, &reply_char, 1);
243 } /* for (;;) */
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 /* Disabled by vda:
259 * why we can't receive from stdin? Why we *require*
260 * controlling tty?? */
261 /*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
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 orig_tty = tty;
267 cfmakeraw(&tty);
268 tcsetattr(read_fd, TCSAFLUSH, &tty);
269 }
270
271 /* No SA_RESTART: we want ALRM to interrupt read() */
272 signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
273
274 n = receive(file_fd);
275
276 if (termios_err == 0)
277 tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
278 if (ENABLE_FEATURE_CLEAN_UP)
279 close(file_fd);
280 fflush_stdout_and_exit(n >= 0);
281}
282