blob: fa6e3c92846ac4eaaa3f384c4abdc161a827b663 [file] [log] [blame]
Amit Pundird477f822020-02-07 22:26:08 +05301/*
2 * Copyright (c) 2018, Linaro Ltd.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include <arpa/inet.h>
32#include <sys/stat.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <libgen.h>
36#include <libqrtr.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include "list.h"
43#include "translate.h"
44
45#define MAX(x, y) ((x) > (y) ? (x) : (y))
46
47enum {
48 OP_RRQ = 1,
49 OP_WRQ,
50 OP_DATA,
51 OP_ACK,
52 OP_ERROR,
53 OP_OACK,
54};
55
56struct tftp_client {
57 struct list_head node;
58
59 struct sockaddr_qrtr sq;
60
61 int sock;
62 int fd;
63
64 size_t block;
65
66 size_t blksize;
67 size_t rsize;
68 size_t wsize;
69 unsigned int timeoutms;
70};
71
72static struct list_head readers = LIST_INIT(readers);
73static struct list_head writers = LIST_INIT(writers);
74
75static ssize_t tftp_send_data(struct tftp_client *client,
76 unsigned int block, size_t offset)
77{
78 ssize_t len;
79 char *buf;
80 char *p;
81
82 buf = malloc(4 + client->blksize);
83 p = buf;
84
85 *p++ = 0;
86 *p++ = OP_DATA;
87
88 *p++ = (block >> 8) & 0xff;
89 *p++ = block & 0xff;
90
91 len = pread(client->fd, p, client->blksize, offset);
92 if (len <= 0) {
93 if (len < 0)
94 printf("[TQFTP] failed to read data\n");
95 free(buf);
96 return len;
97 }
98
99 p += len;
100
101 // printf("[TQFTP] Sending %zd bytes of DATA\n", p - buf);
102 len = send(client->sock, buf, p - buf, 0);
103
104 free(buf);
105
106 return len;
107}
108
109
110static int tftp_send_ack(int sock, int block)
111{
112 struct {
113 uint16_t opcode;
114 uint16_t block;
115 } ack = { htons(OP_ACK), htons(block) };
116
117 return send(sock, &ack, sizeof(ack), 0);
118}
119
120static int tftp_send_oack(int sock, size_t *blocksize, size_t *tsize,
121 size_t *wsize, unsigned int *timeoutms, size_t *rsize)
122{
123 char buf[512];
124 char *p = buf;
125 int n;
126
127 *p++ = 0;
128 *p++ = OP_OACK;
129
130 if (blocksize) {
131 strcpy(p, "blksize");
132 p += 8;
133
134 n = sprintf(p, "%zd", *blocksize);
135 p += n;
136 *p++ = '\0';
137 }
138
139 if (timeoutms) {
140 strcpy(p, "timeoutms");
141 p += 10;
142
143 n = sprintf(p, "%d", *timeoutms);
144 p += n;
145 *p++ = '\0';
146 }
147
148 if (tsize && *tsize != -1) {
149 strcpy(p, "tsize");
150 p += 6;
151
152 n = sprintf(p, "%zd", *tsize);
153 p += n;
154 *p++ = '\0';
155 }
156
157 if (wsize) {
158 strcpy(p, "wsize");
159 p += 6;
160
161 n = sprintf(p, "%zd", *wsize);
162 p += n;
163 *p++ = '\0';
164 }
165
166 if (rsize) {
167 strcpy(p, "rsize");
168 p += 6;
169
170 n = sprintf(p, "%zd", *rsize);
171 p += n;
172 *p++ = '\0';
173 }
174
175 return send(sock, buf, p - buf, 0);
176}
177
178static int tftp_send_error(int sock, int code, const char *msg)
179{
180 size_t len;
181 char *buf;
182 int rc;
183
184 len = 4 + strlen(msg) + 1;
185
186 buf = calloc(1, len);
187 if (!buf)
188 return -1;
189
190 *(uint16_t*)buf = htons(OP_ERROR);
191 *(uint16_t*)(buf + 2) = htons(code);
192 strcpy(buf + 4, msg);
193
194 rc = send(sock, buf, len, 0);
195 free(buf);
196 return rc;
197}
198
199static void handle_rrq(const char *buf, size_t len, struct sockaddr_qrtr *sq)
200{
201 struct tftp_client *client;
202 const char *filename;
203 const char *value;
204 const char *mode;
205 const char *opt;
206 struct stat sb;
207 const char *p;
208 ssize_t tsize = -1;
209 size_t blksize = 512;
210 unsigned int timeoutms = 1000;
211 size_t rsize = 0;
212 size_t wsize = 0;
213 bool do_oack = false;
214 int sock;
215 int ret;
216 int fd;
217
218 p = buf + 2;
219
220 filename = p;
221 p += strlen(p) + 1;
222
223 mode = p;
224 p += strlen(p) + 1;
225
226 if (strcasecmp(mode, "octet")) {
227 /* XXX: error */
228 printf("[TQFTP] not octet, reject\n");
229 return;
230 }
231
232 printf("[TQFTP] RRQ: %s (%s)\n", filename, mode);
233
234 if (p < buf + len) {
235 do_oack = true;
236
237 while (p < buf + len) {
238 /* XXX: ensure we're not running off the end */
239 opt = p;
240 p += strlen(p) + 1;
241
242 /* XXX: ensure we're not running off the end */
243 value = p;
244 p += strlen(p) + 1;
245
246 if (!strcmp(opt, "blksize")) {
247 blksize = atoi(value);
248 } else if (!strcmp(opt, "timeoutms")) {
249 timeoutms = atoi(value);
250 } else if (!strcmp(opt, "tsize")) {
251 tsize = atoi(value);
252 } else if (!strcmp(opt, "rsize")) {
253 rsize = atoi(value);
254 } else if (!strcmp(opt, "wsize")) {
255 wsize = atoi(value);
256 } else {
257 printf("[TQFTP] Ignoring unknown option '%s'\n", opt);
258 }
259 }
260 }
261
262 sock = qrtr_open(0);
263 if (sock < 0) {
264 /* XXX: error */
265 printf("[TQFTP] unable to create new qrtr socket, reject\n");
266 return;
267 }
268
269 ret = connect(sock, (struct sockaddr *)sq, sizeof(*sq));
270 if (ret < 0) {
271 /* XXX: error */
272 printf("[TQFTP] unable to connect new qrtr socket to remote\n");
273 return;
274 }
275
276 fd = translate_open(filename, O_RDONLY);
277 if (fd < 0) {
278 printf("[TQFTP] unable to open %s (%d), reject\n", filename, errno);
279 tftp_send_error(sock, 1, "file not found");
280 return;
281 }
282
283 if (tsize != -1) {
284 fstat(fd, &sb);
285 tsize = sb.st_size;
286 }
287
288 client = calloc(1, sizeof(*client));
289 client->sq = *sq;
290 client->sock = sock;
291 client->fd = fd;
292 client->blksize = blksize;
293 client->rsize = rsize;
294 client->wsize = wsize;
295 client->timeoutms = timeoutms;
296
297 // printf("[TQFTP] new reader added\n");
298
299 list_add(&readers, &client->node);
300
301 if (do_oack) {
302 tftp_send_oack(client->sock, &blksize,
303 tsize ? (size_t*)&tsize : NULL,
304 wsize ? &wsize : NULL,
305 &client->timeoutms,
306 rsize ? &rsize: NULL);
307 } else {
308 tftp_send_data(client, 1, 0);
309 }
310}
311
John Stultz26656692020-02-26 23:41:07 +0000312static void handle_wrq(const char *buf, size_t len __unused, struct sockaddr_qrtr *sq)
Amit Pundird477f822020-02-07 22:26:08 +0530313{
314 struct tftp_client *client;
315 const char *filename;
316 const char *mode;
317 int sock;
318 int ret;
319 int fd;
320
321 filename = buf + 2;
322 mode = buf + 2 + strlen(filename) + 1;
323
324 if (strcasecmp(mode, "octet")) {
325 /* XXX: error */
326 printf("[TQFTP] not octet, reject\n");
327 return;
328 }
329
330 printf("[TQFTP] WRQ: %s (%s)\n", filename, mode);
331
332 fd = translate_open(filename, O_WRONLY | O_CREAT);
333 if (fd < 0) {
334 /* XXX: error */
335 printf("[TQFTP] unable to open %s (%d), reject\n", filename, errno);
336 return;
337 }
338
339 sock = qrtr_open(0);
340 if (sock < 0) {
341 /* XXX: error */
342 printf("[TQFTP] unable to create new qrtr socket, reject\n");
343 return;
344 }
345
346 ret = connect(sock, (struct sockaddr *)sq, sizeof(*sq));
347 if (ret < 0) {
348 /* XXX: error */
349 printf("[TQFTP] unable to connect new qrtr socket to remote\n");
350 return;
351 }
352
353 client = calloc(1, sizeof(*client));
354 client->sq = *sq;
355 client->sock = sock;
356 client->fd = fd;
357
358 ret = tftp_send_ack(client->sock, 0);
359 if (ret < 0) {
360 printf("[TQFTP] unable to send ack\n");
361 close(sock);
362 close(fd);
363 free(client);
364 return;
365 }
366
367 // printf("[TQFTP] new writer added\n");
368
369 list_add(&writers, &client->node);
370}
371
372static int handle_reader(struct tftp_client *client)
373{
374 struct sockaddr_qrtr sq;
375 uint16_t block;
376 uint16_t last;
377 char buf[128];
378 socklen_t sl;
379 ssize_t len;
380 ssize_t n = 0;
381 int opcode;
382 int ret;
383
384 sl = sizeof(sq);
385 len = recvfrom(client->sock, buf, sizeof(buf), 0, (void *)&sq, &sl);
386 if (len < 0) {
387 ret = -errno;
388 if (ret != -ENETRESET)
389 fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret);
390 return -1;
391 }
392
393 /* Drop unsolicited messages */
394 if (sq.sq_node != client->sq.sq_node ||
395 sq.sq_port != client->sq.sq_port) {
396 printf("[TQFTP] Discarding spoofed message\n");
397 return -1;
398 }
399
400 opcode = buf[0] << 8 | buf[1];
401 if (opcode == OP_ERROR) {
402 buf[len] = '\0';
403 printf("[TQFTP] Remote returned an error: %s\n", buf + 4);
404 return -1;
405 } else if (opcode != OP_ACK) {
406 printf("[TQFTP] Expected ACK, got %d\n", opcode);
407 return -1;
408 }
409
410 last = buf[2] << 8 | buf[3];
411 // printf("[TQFTP] Got ack for %d\n", last);
412
413 for (block = last; block < last + client->wsize; block++) {
414 n = tftp_send_data(client, block + 1,
415 block * client->blksize);
416 if (n < 0) {
417 printf("[TQFTP] Sent block %d failed: %zd\n", block + 1, n);
418 break;
419 }
420 // printf("[TQFTP] Sent block %d of %zd\n", block + 1, n);
421 if (n == 0)
422 break;
423 }
424
425 return 1;
426}
427
428static int handle_writer(struct tftp_client *client)
429{
430 struct sockaddr_qrtr sq;
431 uint16_t block;
432 size_t payload;
433 char buf[516];
434 socklen_t sl;
435 ssize_t len;
436 int opcode;
437 int ret;
438
439 sl = sizeof(sq);
440 len = recvfrom(client->sock, buf, sizeof(buf), 0, (void *)&sq, &sl);
441 if (len < 0) {
442 ret = -errno;
443 if (ret != -ENETRESET)
444 fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret);
445 return -1;
446 }
447
448 /* Drop unsolicited messages */
449 if (sq.sq_node != client->sq.sq_node ||
450 sq.sq_port != client->sq.sq_port)
451 return -1;
452
453 opcode = buf[0] << 8 | buf[1];
454 block = buf[2] << 8 | buf[3];
455 if (opcode != OP_DATA) {
456 printf("[TQFTP] Expected DATA opcode, got %d\n", opcode);
457 tftp_send_error(client->sock, 4, "Expected DATA opcode");
458 return -1;
459 }
460
461 payload = len - 4;
462
463 ret = write(client->fd, buf + 4, payload);
464 if (ret < 0) {
465 /* XXX: report error */
466 printf("[TQFTP] failed to write data\n");
467 return -1;
468 }
469
470 tftp_send_ack(client->sock, block);
471
472 return payload == 512 ? 1 : 0;
473}
474
475static void client_close_and_free(struct tftp_client *client)
476{
477 list_del(&client->node);
478 close(client->sock);
479 close(client->fd);
480 free(client);
481}
482
John Stultz26656692020-02-26 23:41:07 +0000483int main(int argc __unused, char **argv __unused)
Amit Pundird477f822020-02-07 22:26:08 +0530484{
485 struct tftp_client *client;
486 struct tftp_client *next;
487 struct sockaddr_qrtr sq;
488 struct qrtr_packet pkt;
489 socklen_t sl;
490 ssize_t len;
491 char buf[4096];
492 fd_set rfds;
493 int nfds;
494 int opcode;
495 int ret;
496 int fd;
497
498 fd = qrtr_open(0);
499 if (fd < 0) {
500 fprintf(stderr, "failed to open qrtr socket\n");
501 exit(1);
502 }
503
504 ret = qrtr_publish(fd, 4096, 1, 0);
505 if (ret < 0) {
506 fprintf(stderr, "failed to publish service registry service\n");
507 exit(1);
508 }
509
510 for (;;) {
511 FD_ZERO(&rfds);
512 FD_SET(fd, &rfds);
513 nfds = fd;
514
515 list_for_each_entry(client, &writers, node) {
516 FD_SET(client->sock, &rfds);
517 nfds = MAX(nfds, client->sock);
518 }
519
520 list_for_each_entry(client, &readers, node) {
521 FD_SET(client->sock, &rfds);
522 nfds = MAX(nfds, client->sock);
523 }
524
525 ret = select(nfds + 1, &rfds, NULL, NULL, NULL);
526 if (ret < 0) {
527 if (errno == EINTR) {
528 continue;
529 } else {
530 fprintf(stderr, "select failed\n");
531 break;
532 }
533 }
534
535 list_for_each_entry_safe(client, next, &writers, node) {
536 if (FD_ISSET(client->sock, &rfds)) {
537 ret = handle_writer(client);
538 if (ret <= 0)
539 client_close_and_free(client);
540 }
541 }
542
543 list_for_each_entry_safe(client, next, &readers, node) {
544 if (FD_ISSET(client->sock, &rfds)) {
545 ret = handle_reader(client);
546 if (ret <= 0)
547 client_close_and_free(client);
548 }
549 }
550
551 if (FD_ISSET(fd, &rfds)) {
552 sl = sizeof(sq);
553 len = recvfrom(fd, buf, sizeof(buf), 0, (void *)&sq, &sl);
554 if (len < 0) {
555 ret = -errno;
556 if (ret != -ENETRESET)
557 fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret);
558 return ret;
559 }
560
561 /* Ignore control messages */
562 if (sq.sq_port == QRTR_PORT_CTRL) {
563 ret = qrtr_decode(&pkt, buf, len, &sq);
564 if (ret < 0) {
565 fprintf(stderr, "[TQFTP] unable to decode qrtr packet\n");
566 return ret;
567 }
568
569 switch (pkt.type) {
570 case QRTR_TYPE_BYE:
571 // fprintf(stderr, "[TQFTP] got bye\n");
572 list_for_each_entry_safe(client, next, &writers, node) {
573 if (client->sq.sq_node == sq.sq_node)
574 client_close_and_free(client);
575 }
576 break;
577 case QRTR_TYPE_DEL_CLIENT:
578 // fprintf(stderr, "[TQFTP] got del_client\n");
579 list_for_each_entry_safe(client, next, &writers, node) {
580 if (!memcmp(&client->sq, &sq, sizeof(sq)))
581 client_close_and_free(client);
582 }
583 break;
584 }
585 } else {
586 if (len < 2)
587 continue;
588
589 opcode = buf[0] << 8 | buf[1];
590 switch (opcode) {
591 case OP_RRQ:
592 handle_rrq(buf, len, &sq);
593 break;
594 case OP_WRQ:
595 // printf("[TQFTP] write\n");
596 handle_wrq(buf, len, &sq);
597 break;
598 default:
599 printf("[TQFTP] unhandled op %d\n", opcode);
600 break;
601 }
602 }
603 }
604 }
605
606 close(fd);
607
608 return 0;
609}