2 * Copyright (C) 2016 J. Ali Harlow <ali@juiblex.co.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <netinet/ip.h>
30 #include <arpa/tftp.h>
33 * A simple TFTP server suitable only for use on the loopback interface
34 * (it has no support for retransmitting lost packets).
37 char inbuf[SEGSIZE+4];
38 char outbuf[SEGSIZE+4];
40 int send_error(int s, const struct sockaddr *client, socklen_t client_addr_len,
41 unsigned short code,const char *message)
47 len = 4 + strlen(message) + 1;
49 ((unsigned short *)buf)[0] = htons(ERROR);
50 ((unsigned short *)buf)[1] = htons(code);
51 strcpy(buf + 4, message);
52 r = sendto(s, buf, len, 0, client, client_addr_len);
58 int send_file(int s, const struct sockaddr *client, socklen_t client_addr_len,
59 const char *path, const char *mode)
61 const char *dotdot, *p;
66 if (strcasecmp(mode, "octet")) {
67 send_error(s, client, client_addr_len, EBADOP, "Bad mode");
71 if (!*path || *path == '/') {
72 send_error(s, client, client_addr_len, EACCESS,
78 dotdot = strstr(p, "..");
81 if ((dotdot == path || dotdot[-1] == '/') &&
82 (dotdot[2] == '/' || dotdot[2] == '\0')) {
83 send_error(s, client, client_addr_len, EACCESS,
90 fp = fopen(path, "rb");
94 send_error(s, client, client_addr_len, ENOTFOUND,
96 else if (errno == EACCES)
97 send_error(s, client, client_addr_len, EACCESS,
100 send_error(s, client, client_addr_len, EUNDEF,
105 ((unsigned short *)outbuf)[0] = htons(DATA);
107 while((nb = fread(outbuf + 4, 1, SEGSIZE, fp)) >= 0)
109 ((unsigned short *)outbuf)[1] = htons(++block);
110 if (sendto(s, outbuf, nb + 4, 0, client, client_addr_len) < 0) {
117 (void)recvfrom(s, inbuf, sizeof(inbuf), 0, NULL, NULL);
130 char *filename, *mode;
131 struct sockaddr_storage client;
132 socklen_t client_addr_len;
136 client_addr_len = sizeof(client);
137 nb = recvfrom(s, inbuf, sizeof(inbuf), 0,
138 (struct sockaddr *)&client, &client_addr_len);
146 switch (ntohs(*(unsigned short *)inbuf)) {
148 filename = inbuf + 2;
149 mode = memchr(filename, '\0', nb - 2);
152 (struct sockaddr *)&client,
153 client_addr_len, EBADOP,
158 if (!memchr(mode, '\0', nb - (mode - inbuf))) {
160 (struct sockaddr *)&client,
161 client_addr_len, EBADOP,
165 send_file(s, (struct sockaddr *)&client,
166 client_addr_len, filename, mode);
169 send_error(s, (struct sockaddr *)&client,
170 client_addr_len, EACCESS,
182 main(int argc, char **argv)
188 struct sockaddr_in sin = {0,};
190 s = socket(AF_INET, SOCK_DGRAM, 0);
196 sin.sin_family = AF_INET;
197 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
199 for (port = 0;; port++) {
200 sin.sin_port = htons(IPPORT_USERRESERVED + port);
201 if (!bind(s, (struct sockaddr *)&sin, sizeof(sin)))
203 if (errno != EADDRINUSE || port >= 1023) {
210 fp = fopen(argv[2], "w");
212 fprintf(fp, "%ld\n", (long)ntohs(sin.sin_port));
218 fp = fopen(argv[1], "w");
220 fprintf(fp, "%ld\n", (long)pid);
226 printf("%d\n", ntohs(sin.sin_port));