AmendHub

Download

jcs

/

subtext

/

nomodem.c

 

(View History)

jcs   nomodem: Read upload data from ibuf in chunks to speed it up a little Latest amendment: 495 on 2023-04-27

1 /*
2 * Copyright (c) 2023 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <string.h>
18 #include "nomodem.h"
19
20 struct nomodem_session *
21 nomodem_send(struct session *s, FILE *fp, char *filename)
22 {
23 struct nomodem_session *ns;
24
25 if (!fp)
26 return NULL;
27
28 ns = xmalloczero(sizeof(struct nomodem_session));
29 if (ns == NULL)
30 return NULL;
31
32 ns->mode = NOMODEM_MODE_SENDING;
33 ns->session = s;
34 ns->file = fp;
35 strlcpy(ns->file_name, filename, sizeof(ns->file_name));
36
37 fseek(ns->file, 0, SEEK_END);
38 ns->file_size = ftell(ns->file);
39 fseek(ns->file, 0, SEEK_SET);
40
41 /* initiator */
42 session_printf(ns->session, "%c%c%c%c%c%c",
43 (char)NOMODEM_START0, (char)NOMODEM_START1,
44 (char)NOMODEM_START2, (char)NOMODEM_START3,
45 (char)NOMODEM_VERSION, (char)NOMODEM_CMD_SEND);
46
47 /* file name size (byte), file name */
48 session_printf(ns->session, "%c%s",
49 (unsigned char)strlen(ns->file_name), ns->file_name);
50
51 /* 32-bit file size (big-endian) */
52 session_printf(ns->session, "%c%c%c%c",
53 (unsigned char)((ns->file_size >> 24) & 0xff),
54 (unsigned char)((ns->file_size >> 16) & 0xff),
55 (unsigned char)((ns->file_size >> 8) & 0xff),
56 (unsigned char)(ns->file_size & 0xff));
57 session_flush(ns->session);
58
59 ns->last_input = Time;
60 ns->need_header_ack = true;
61
62 return ns;
63 }
64
65 struct nomodem_session *
66 nomodem_receive(struct session *s, char *path)
67 {
68 struct nomodem_session *ns;
69
70 if (path == NULL)
71 return NULL;
72
73 ns = xmalloczero(sizeof(struct nomodem_session));
74 if (ns == NULL)
75 return NULL;
76
77 ns->mode = NOMODEM_MODE_RECEIVING;
78 ns->session = s;
79 ns->file_path = xstrdup(path);
80 if (ns->file_path == NULL) {
81 xfree(&ns);
82 return NULL;
83 }
84
85 ns->buf_size = sizeof(ns->session->ibuf);
86 ns->buf = xmalloc(ns->buf_size);
87 if (ns->buf == NULL) {
88 xfree(&ns->file_path);
89 xfree(&ns);
90 return NULL;
91 }
92
93 ns->file = fopen(ns->file_path, "wb+");
94 if (ns->file == NULL) {
95 session_printf(ns->session, "[nomodem] Failed opening %s for writing",
96 ns->file_path);
97 xfree(&ns->file_path);
98 xfree(&ns);
99 return NULL;
100 }
101
102 /* initiator */
103 session_printf(ns->session, "%c%c%c%c%c%c",
104 (char)NOMODEM_START0, (char)NOMODEM_START1,
105 (char)NOMODEM_START2, (char)NOMODEM_START3,
106 (char)NOMODEM_VERSION, (char)NOMODEM_CMD_RECEIVE);
107 session_flush(ns->session);
108
109 ns->last_input = Time;
110 ns->need_header = true;
111
112 return ns;
113 }
114 bool
115 nomodem_timed_out(struct nomodem_session *ns)
116 {
117 if ((Time - ns->last_input) > NOMODEM_TIMEOUT)
118 return true;
119
120 return false;
121 }
122
123 unsigned short
124 nomodem_get_char(struct nomodem_session *ns)
125 {
126 unsigned char b;
127
128 while (session_get_char(ns->session, &b) == 0) {
129 if (ns->session->ending || nomodem_timed_out(ns))
130 return -1;
131 }
132
133 ns->last_input = Time;
134
135 return (short)b;
136 }
137
138 bool
139 nomodem_continue(struct nomodem_session *ns)
140 {
141 size_t size, rsize, pos, tsize;
142 short resp;
143 unsigned short file_name_size, file_name_pos;
144
145 switch (ns->mode) {
146 case NOMODEM_MODE_SENDING:
147 if (ns->need_header_ack) {
148 resp = nomodem_get_char(ns);
149 if (resp < 0)
150 return false;
151 size = ((unsigned long)(resp & 0xff)) << 24;
152
153 resp = nomodem_get_char(ns);
154 if (resp < 0)
155 return false;
156 size |= ((unsigned long)(resp & 0xff)) << 16;
157
158 resp = nomodem_get_char(ns);
159 if (resp < 0)
160 return false;
161 size |= ((unsigned long)(resp & 0xff)) << 8;
162
163 resp = nomodem_get_char(ns);
164 if (resp < 0)
165 return false;
166 size |= (unsigned long)(resp & 0xff);
167
168 if (size != ns->file_size) {
169 session_logf(ns->session,
170 "[nomodem] Header ack size %ld != file size %ld, "
171 "canceling", size, ns->file_size);
172 return false;
173 }
174
175 ns->need_header_ack = false;
176 }
177
178 while (ns->session->obuflen > 0) {
179 session_flush(ns->session);
180 if (ns->session->ending)
181 return false;
182 if (nomodem_timed_out(ns))
183 return false;
184 }
185
186 pos = ftell(ns->file);
187 if (feof(ns->file))
188 return false;
189 size = fread(ns->session->obuf + 4, 1,
190 sizeof(ns->session->obuf) - 4, ns->file);
191 if (size == 0)
192 return false;
193
194 ns->session->obuf[0] = (size >> 24) & 0xff;
195 ns->session->obuf[1] = (size >> 16) & 0xff;
196 ns->session->obuf[2] = (size >> 8) & 0xff;
197 ns->session->obuf[3] = size & 0xff;
198
199 ns->session->obuflen = size + 4;
200 session_flush(ns->session);
201
202 /* wait for ack */
203 resp = nomodem_get_char(ns);
204
205 if (resp != NOMODEM_ACK) {
206 session_logf(ns->session, "[nomodem] Failed ack at chunk "
207 "%ld/%ld: %d", pos, ns->file_size, resp);
208 return false;
209 }
210
211 if (feof(ns->file))
212 return false;
213 break;
214 case NOMODEM_MODE_RECEIVING:
215 if (ns->need_header) {
216 file_name_size = nomodem_get_char(ns);
217 if (file_name_size < 0)
218 return false;
219
220 /* get filename */
221 file_name_pos = 0;
222 while (file_name_size != 0) {
223 resp = nomodem_get_char(ns);
224 if (resp < 0)
225 return false;
226
227 if (file_name_pos < sizeof(ns->file_name) - 1) {
228 ns->file_name[file_name_pos++] = (char)resp;
229 ns->file_name[file_name_pos] = 0;
230 }
231 file_name_size--;
232 }
233
234 /* get file size */
235 resp = nomodem_get_char(ns);
236 if (resp < 0)
237 return false;
238 ns->file_size = ((unsigned long)(resp & 0xff)) << 24;
239
240 resp = nomodem_get_char(ns);
241 if (resp < 0)
242 return false;
243 ns->file_size |= ((unsigned long)(resp & 0xff)) << 16;
244
245 resp = nomodem_get_char(ns);
246 if (resp < 0)
247 return false;
248 ns->file_size |= ((unsigned long)(resp & 0xff)) << 8;
249
250 resp = nomodem_get_char(ns);
251 if (resp < 0)
252 return false;
253 ns->file_size |= (unsigned long)(resp & 0xff);
254
255 /* ack */
256 session_printf(ns->session, "%c%c%c%c",
257 (char)((ns->file_size >> 24) & 0xff),
258 (char)((ns->file_size >> 16) & 0xff),
259 (char)((ns->file_size >> 8) & 0xff),
260 (char)(ns->file_size & 0xff));
261
262 ns->need_header = false;
263 }
264
265 pos = ftell(ns->file);
266 if (pos >= ns->file_size)
267 return false;
268
269 /* get chunk size */
270 resp = nomodem_get_char(ns);
271 if (resp < 0)
272 return false;
273 size = ((unsigned long)(resp & 0xff)) << 24;
274
275 resp = nomodem_get_char(ns);
276 if (resp < 0)
277 return false;
278 size |= ((unsigned long)(resp & 0xff)) << 16;
279
280 resp = nomodem_get_char(ns);
281 if (resp < 0)
282 return false;
283 size |= ((unsigned long)(resp & 0xff)) << 8;
284
285 resp = nomodem_get_char(ns);
286 if (resp < 0)
287 return false;
288 size |= (unsigned long)(resp & 0xff);
289
290 if (size > (1024 * 10)) {
291 session_logf(ns->session, "[nomodem] Chunk size too large: %ld",
292 size);
293 return false;
294 }
295
296 for (rsize = 0; rsize < size; ) {
297 tsize = session_get_chars(ns->session, ns->buf,
298 MIN(ns->buf_size, size - rsize));
299 if (ns->session->ending || nomodem_timed_out(ns))
300 return false;
301 if (tsize == 0) {
302 uthread_yield();
303 continue;
304 }
305
306 ns->last_input = Time;
307
308 if (fwrite(ns->buf, 1, tsize, ns->file) != tsize) {
309 session_logf(ns->session, "[nomodem] Failed writing to %s",
310 ns->file_path);
311 return false;
312 }
313
314 rsize += tsize;
315 }
316
317 session_printf(ns->session, "%c", (char)NOMODEM_ACK);
318 session_flush(ns->session);
319
320 pos = ftell(ns->file);
321 if (pos >= ns->file_size)
322 return false;
323 }
324
325 return true;
326 }
327
328 void
329 nomodem_finish(struct nomodem_session **nsp)
330 {
331 struct nomodem_session *ns = (struct nomodem_session *)*nsp;
332
333 if (ns->file_path != NULL)
334 xfree(&ns->file_path);
335
336 if (ns->file)
337 fclose(ns->file);
338
339 if (ns->buf)
340 xfree(&ns->buf);
341 xfree(nsp);
342 }