|
ali@476
|
1 |
/*
|
|
ali@476
|
2 |
* Copyright (C) 2016 J. Ali Harlow <ali@juiblex.co.uk>
|
|
ali@476
|
3 |
*
|
|
ali@476
|
4 |
* This program is free software; you can redistribute it and/or modify
|
|
ali@476
|
5 |
* it under the terms of the GNU General Public License as published by
|
|
ali@476
|
6 |
* the Free Software Foundation; either version 2 of the License, or
|
|
ali@476
|
7 |
* (at your option) any later version.
|
|
ali@476
|
8 |
*
|
|
ali@476
|
9 |
* This program is distributed in the hope that it will be useful,
|
|
ali@476
|
10 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
ali@476
|
11 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
ali@476
|
12 |
* GNU General Public License for more details.
|
|
ali@476
|
13 |
*
|
|
ali@476
|
14 |
* You should have received a copy of the GNU General Public License along
|
|
ali@476
|
15 |
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
ali@476
|
16 |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
ali@476
|
17 |
*/
|
|
ali@476
|
18 |
|
|
ali@476
|
19 |
#include "config.h"
|
|
ali@476
|
20 |
|
|
ali@476
|
21 |
#include <stdlib.h>
|
|
ali@476
|
22 |
#include <string.h>
|
|
ali@476
|
23 |
#include <stdio.h>
|
|
ali@476
|
24 |
#include <errno.h>
|
|
ali@476
|
25 |
#include <unistd.h>
|
|
ali@476
|
26 |
#include <sys/types.h>
|
|
ali@476
|
27 |
#include <sys/socket.h>
|
|
ali@476
|
28 |
#include <netinet/in.h>
|
|
ali@476
|
29 |
#include <netinet/ip.h>
|
|
ali@476
|
30 |
#include <arpa/tftp.h>
|
|
ali@476
|
31 |
|
|
ali@476
|
32 |
/*
|
|
ali@476
|
33 |
* A simple TFTP server suitable only for use on the loopback interface
|
|
ali@476
|
34 |
* (it has no support for retransmitting lost packets).
|
|
ali@476
|
35 |
*/
|
|
ali@476
|
36 |
|
|
ali@476
|
37 |
char inbuf[SEGSIZE+4];
|
|
ali@476
|
38 |
char outbuf[SEGSIZE+4];
|
|
ali@476
|
39 |
|
|
ali@476
|
40 |
int send_error(int s, const struct sockaddr *client, socklen_t client_addr_len,
|
|
ali@476
|
41 |
unsigned short code,const char *message)
|
|
ali@476
|
42 |
{
|
|
ali@476
|
43 |
int r;
|
|
ali@476
|
44 |
char *buf;
|
|
ali@476
|
45 |
size_t len;
|
|
ali@476
|
46 |
|
|
ali@476
|
47 |
len = 4 + strlen(message) + 1;
|
|
ali@476
|
48 |
buf = malloc(len);
|
|
ali@476
|
49 |
((unsigned short *)buf)[0] = htons(ERROR);
|
|
ali@476
|
50 |
((unsigned short *)buf)[1] = htons(code);
|
|
ali@476
|
51 |
strcpy(buf + 4, message);
|
|
ali@476
|
52 |
r = sendto(s, buf, len, 0, client, client_addr_len);
|
|
ali@476
|
53 |
free(buf);
|
|
ali@476
|
54 |
|
|
ali@476
|
55 |
return r;
|
|
ali@476
|
56 |
}
|
|
ali@476
|
57 |
|
|
ali@476
|
58 |
int send_file(int s, const struct sockaddr *client, socklen_t client_addr_len,
|
|
ali@476
|
59 |
const char *path, const char *mode)
|
|
ali@476
|
60 |
{
|
|
ali@476
|
61 |
const char *dotdot, *p;
|
|
ali@476
|
62 |
FILE *fp;
|
|
ali@476
|
63 |
size_t nb;
|
|
ali@476
|
64 |
int block = 0;
|
|
ali@476
|
65 |
|
|
ali@476
|
66 |
if (strcasecmp(mode, "octet")) {
|
|
ali@476
|
67 |
send_error(s, client, client_addr_len, EBADOP, "Bad mode");
|
|
ali@476
|
68 |
return -1;
|
|
ali@476
|
69 |
}
|
|
ali@476
|
70 |
|
|
ali@476
|
71 |
if (!*path || *path == '/') {
|
|
ali@476
|
72 |
send_error(s, client, client_addr_len, EACCESS,
|
|
ali@476
|
73 |
"Access denied");
|
|
ali@476
|
74 |
return -1;
|
|
ali@476
|
75 |
}
|
|
ali@476
|
76 |
|
|
ali@476
|
77 |
for (p = path; *p;) {
|
|
ali@476
|
78 |
dotdot = strstr(p, "..");
|
|
ali@476
|
79 |
if (!dotdot)
|
|
ali@476
|
80 |
break;
|
|
ali@476
|
81 |
if ((dotdot == path || dotdot[-1] == '/') &&
|
|
ali@476
|
82 |
(dotdot[2] == '/' || dotdot[2] == '\0')) {
|
|
ali@476
|
83 |
send_error(s, client, client_addr_len, EACCESS,
|
|
ali@476
|
84 |
"Access denied");
|
|
ali@476
|
85 |
return -1;
|
|
ali@476
|
86 |
}
|
|
ali@476
|
87 |
p = dotdot + 2;
|
|
ali@476
|
88 |
}
|
|
ali@476
|
89 |
|
|
ali@476
|
90 |
fp = fopen(path, "rb");
|
|
ali@476
|
91 |
|
|
ali@476
|
92 |
if (!fp) {
|
|
ali@476
|
93 |
if (errno == ENOENT)
|
|
ali@476
|
94 |
send_error(s, client, client_addr_len, ENOTFOUND,
|
|
ali@476
|
95 |
strerror(errno));
|
|
ali@476
|
96 |
else if (errno == EACCES)
|
|
ali@476
|
97 |
send_error(s, client, client_addr_len, EACCESS,
|
|
ali@476
|
98 |
strerror(errno));
|
|
ali@476
|
99 |
else
|
|
ali@476
|
100 |
send_error(s, client, client_addr_len, EUNDEF,
|
|
ali@476
|
101 |
strerror(errno));
|
|
ali@476
|
102 |
return -1;
|
|
ali@476
|
103 |
}
|
|
ali@476
|
104 |
|
|
ali@476
|
105 |
((unsigned short *)outbuf)[0] = htons(DATA);
|
|
ali@476
|
106 |
|
|
ali@476
|
107 |
while((nb = fread(outbuf + 4, 1, SEGSIZE, fp)) >= 0)
|
|
ali@476
|
108 |
{
|
|
ali@476
|
109 |
((unsigned short *)outbuf)[1] = htons(++block);
|
|
ali@476
|
110 |
if (sendto(s, outbuf, nb + 4, 0, client, client_addr_len) < 0) {
|
|
ali@476
|
111 |
perror("sendto");
|
|
ali@476
|
112 |
fclose(fp);
|
|
ali@476
|
113 |
return -1;
|
|
ali@476
|
114 |
}
|
|
ali@476
|
115 |
|
|
ali@476
|
116 |
/* Discard ACKs */
|
|
ali@476
|
117 |
(void)recvfrom(s, inbuf, sizeof(inbuf), 0, NULL, NULL);
|
|
ali@476
|
118 |
|
|
ali@476
|
119 |
if (nb < SEGSIZE)
|
|
ali@476
|
120 |
break;
|
|
ali@476
|
121 |
}
|
|
ali@476
|
122 |
|
|
ali@476
|
123 |
fclose(fp);
|
|
ali@476
|
124 |
|
|
ali@476
|
125 |
return 0;
|
|
ali@476
|
126 |
}
|
|
ali@476
|
127 |
|
|
ali@476
|
128 |
void serve(int s)
|
|
ali@476
|
129 |
{
|
|
ali@476
|
130 |
char *filename, *mode;
|
|
ali@476
|
131 |
struct sockaddr_storage client;
|
|
ali@476
|
132 |
socklen_t client_addr_len;
|
|
ali@476
|
133 |
ssize_t nb;
|
|
ali@476
|
134 |
|
|
ali@476
|
135 |
for(;;) {
|
|
ali@476
|
136 |
client_addr_len = sizeof(client);
|
|
ali@476
|
137 |
nb = recvfrom(s, inbuf, sizeof(inbuf), 0,
|
|
ali@476
|
138 |
(struct sockaddr *)&client, &client_addr_len);
|
|
ali@476
|
139 |
|
|
ali@476
|
140 |
if (nb < 0) {
|
|
ali@476
|
141 |
perror("recvfrom");
|
|
ali@476
|
142 |
exit(1);
|
|
ali@476
|
143 |
}
|
|
ali@476
|
144 |
|
|
ali@476
|
145 |
if (nb >= 2) {
|
|
ali@476
|
146 |
switch (ntohs(*(unsigned short *)inbuf)) {
|
|
ali@476
|
147 |
case RRQ:
|
|
ali@476
|
148 |
filename = inbuf + 2;
|
|
ali@476
|
149 |
mode = memchr(filename, '\0', nb - 2);
|
|
ali@476
|
150 |
if (!mode) {
|
|
ali@476
|
151 |
send_error(s,
|
|
ali@476
|
152 |
(struct sockaddr *)&client,
|
|
ali@476
|
153 |
client_addr_len, EBADOP,
|
|
ali@476
|
154 |
"Bad request");
|
|
ali@476
|
155 |
break;
|
|
ali@476
|
156 |
}
|
|
ali@476
|
157 |
mode++;
|
|
ali@476
|
158 |
if (!memchr(mode, '\0', nb - (mode - inbuf))) {
|
|
ali@476
|
159 |
send_error(s,
|
|
ali@476
|
160 |
(struct sockaddr *)&client,
|
|
ali@476
|
161 |
client_addr_len, EBADOP,
|
|
ali@476
|
162 |
"Bad request");
|
|
ali@476
|
163 |
break;
|
|
ali@476
|
164 |
}
|
|
ali@476
|
165 |
send_file(s, (struct sockaddr *)&client,
|
|
ali@476
|
166 |
client_addr_len, filename, mode);
|
|
ali@476
|
167 |
break;
|
|
ali@476
|
168 |
case WRQ:
|
|
ali@476
|
169 |
send_error(s, (struct sockaddr *)&client,
|
|
ali@476
|
170 |
client_addr_len, EACCESS,
|
|
ali@476
|
171 |
"Access denied");
|
|
ali@476
|
172 |
break;
|
|
ali@476
|
173 |
case DATA:
|
|
ali@476
|
174 |
case ACK:
|
|
ali@476
|
175 |
case ERROR:
|
|
ali@476
|
176 |
break;
|
|
ali@476
|
177 |
}
|
|
ali@476
|
178 |
}
|
|
ali@476
|
179 |
}
|
|
ali@476
|
180 |
}
|
|
ali@476
|
181 |
|
|
ali@476
|
182 |
main(int argc, char **argv)
|
|
ali@476
|
183 |
{
|
|
ali@476
|
184 |
int s;
|
|
ali@476
|
185 |
pid_t pid;
|
|
ali@476
|
186 |
FILE *fp;
|
|
ali@476
|
187 |
in_port_t port;
|
|
ali@476
|
188 |
struct sockaddr_in sin = {0,};
|
|
ali@476
|
189 |
|
|
ali@476
|
190 |
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
ali@476
|
191 |
if (s < 0) {
|
|
ali@476
|
192 |
perror("socket");
|
|
ali@476
|
193 |
exit(1);
|
|
ali@476
|
194 |
}
|
|
ali@476
|
195 |
|
|
ali@476
|
196 |
sin.sin_family = AF_INET;
|
|
ali@476
|
197 |
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
ali@476
|
198 |
|
|
ali@476
|
199 |
for (port = 0;; port++) {
|
|
ali@476
|
200 |
sin.sin_port = htons(IPPORT_USERRESERVED + port);
|
|
ali@476
|
201 |
if (!bind(s, (struct sockaddr *)&sin, sizeof(sin)))
|
|
ali@476
|
202 |
break;
|
|
ali@476
|
203 |
if (errno != EADDRINUSE || port >= 1023) {
|
|
ali@476
|
204 |
perror("bind");
|
|
ali@476
|
205 |
exit(1);
|
|
ali@476
|
206 |
}
|
|
ali@476
|
207 |
}
|
|
ali@476
|
208 |
|
|
ali@476
|
209 |
if (argc > 2) {
|
|
ali@476
|
210 |
fp = fopen(argv[2], "w");
|
|
ali@476
|
211 |
if (fp) {
|
|
ali@476
|
212 |
fprintf(fp, "%ld\n", (long)ntohs(sin.sin_port));
|
|
ali@476
|
213 |
fclose(fp);
|
|
ali@476
|
214 |
}
|
|
ali@476
|
215 |
|
|
ali@476
|
216 |
pid = fork();
|
|
ali@476
|
217 |
if (pid) {
|
|
ali@476
|
218 |
fp = fopen(argv[1], "w");
|
|
ali@476
|
219 |
if (fp) {
|
|
ali@476
|
220 |
fprintf(fp, "%ld\n", (long)pid);
|
|
ali@476
|
221 |
fclose(fp);
|
|
ali@476
|
222 |
}
|
|
ali@476
|
223 |
exit(0);
|
|
ali@476
|
224 |
}
|
|
ali@476
|
225 |
} else
|
|
ali@476
|
226 |
printf("%d\n", ntohs(sin.sin_port));
|
|
ali@476
|
227 |
|
|
ali@476
|
228 |
serve(s);
|
|
ali@476
|
229 |
|
|
ali@476
|
230 |
exit(0);
|
|
ali@476
|
231 |
}
|