convert to 4.1c directory layout
[unix-history] / usr / src / libexec / tftpd / tftpd.c
CommitLineData
4d5e33bd 1/* tftpd.c 4.3 82/10/08 */
f8675e8e
SL
2
3/*
4 * Trivial file transfer protocol server.
5 */
6#include <sys/types.h>
7#include <net/in.h>
8#include <sys/socket.h>
9#include <signal.h>
10#include <sys/ioctl.h>
11#include <stat.h>
12#include <stdio.h>
13#include <wait.h>
14#include <errno.h>
15#include <ctype.h>
4d5e33bd 16#include <netdb.h>
f8675e8e
SL
17#include "tftp.h"
18
f8675e8e 19extern int errno;
4d5e33bd 20struct sockaddr_in sin = { AF_INET };
f8675e8e
SL
21int f;
22int options;
23char buf[BUFSIZ];
24
25main(argc, argv)
26 char *argv[];
27{
28 union wait status;
29 struct sockaddr_in from;
30 register struct tftphdr *tp;
31 register int n;
4d5e33bd 32 struct servent *sp;
f8675e8e 33
4d5e33bd
SL
34 sp = getservbyname("tftp", "udp");
35 if (sp == 0) {
36 fprintf(stderr, "tftpd: udp/tftp: unknown service\n");
37 exit(1);
38 }
39 sin.sin_port = htons(sp->s_port);
f8675e8e
SL
40#ifndef DEBUG
41 if (fork())
42 exit(0);
43 for (f = 0; f < 10; f++)
44 (void) close(f);
45 (void) open("/", 0);
46 (void) dup2(0, 1);
47 (void) dup2(0, 2);
48 { int t = open("/dev/tty", 2);
49 if (t >= 0) {
50 ioctl(t, TIOCNOTTY, (char *)0);
51 (void) close(t);
52 }
53 }
f8675e8e
SL
54#endif
55 argc--, argv++;
56 if (argc > 0 && !strcmp(argv[0], "-d"))
57 options |= SO_DEBUG;
58 for (;;) {
59 errno = 0;
60 f = socket(SOCK_DGRAM, 0, &sin, options);
61 if (f < 0) {
62 perror("socket");
63 sleep(5);
64 continue;
65 }
66again:
67 n = receive(f, &from, buf, sizeof (buf));
68 if (n <= 0) {
69 if (n < 0)
70 perror("receive");
71 goto again;
72 }
73 tp = (struct tftphdr *)buf;
74#if vax || pdp11
75 tp->th_opcode = ntohs(tp->th_opcode);
76#endif
77 if (tp->th_opcode == RRQ || tp->th_opcode == WRQ)
78 if (fork() == 0)
79 tftp(&from, tp, n);
80 (void) close(f);
81#ifdef notdef
82 while (wait3(status, WNOHANG, 0) > 0)
83#else
84 while (wait3(status, 0, 0) > 0)
85 continue;
86 }
87}
88
89int validate_access();
90int sendfile(), recvfile();
91
92struct formats {
93 char *f_mode;
94 int (*f_validate)();
95 int (*f_send)();
96 int (*f_recv)();
97} formats[] = {
98 { "netascii", validate_access, sendfile, recvfile },
99 { "octet", validate_access, sendfile, recvfile },
100#ifdef notdef
101 { "mail", validate_user, sendmail, recvmail },
102#endif
103 { 0 }
104};
105
106int fd; /* file being transferred */
107
108/*
109 * Handle initial connection protocol.
110 */
111tftp(client, tp, size)
112 struct sockaddr_in *client;
113 struct tftphdr *tp;
114 int size;
115{
116 register char *cp;
117 int first = 1, ecode;
118 register struct formats *pf;
119 char *filename, *mode;
120
121 if (connect(f, client) < 0) {
122 perror("connect");
123 exit(1);
124 }
125 filename = cp = tp->th_stuff;
126again:
127 while (cp < buf + size) {
128 if (*cp == '\0')
129 break;
130 cp++;
131 }
132 if (*cp != '\0') {
133 nak(EBADOP);
134 exit(1);
135 }
136 if (first) {
137 mode = ++cp;
138 first = 0;
139 goto again;
140 }
141 for (cp = mode; *cp; cp++)
142 if (isupper(*cp))
143 *cp = tolower(*cp);
144 for (pf = formats; pf->f_mode; pf++)
145 if (strcmp(pf->f_mode, mode) == 0)
146 break;
147 if (pf->f_mode == 0) {
148 nak(EBADOP);
149 exit(1);
150 }
151 ecode = (*pf->f_validate)(filename, client, tp->th_opcode);
152 if (ecode) {
153 nak(ecode);
154 exit(1);
155 }
156 if (tp->th_opcode == WRQ)
157 (*pf->f_recv)(pf);
158 else
159 (*pf->f_send)(pf);
160 exit(0);
161}
162
163/*
164 * Validate file access. Since we
165 * have no uid or gid, for now require
166 * file to exist and be publicly
167 * readable/writable.
168 * Note also, full path name must be
169 * given as we have no login directory.
170 */
171validate_access(file, client, mode)
172 char *file;
173 struct sockaddr_in *client;
174 int mode;
175{
176 struct stat stbuf;
177
178 if (*file != '/')
179 return (EACCESS);
180 if (stat(file, &stbuf) < 0)
181 return (errno == ENOENT ? ENOTFOUND : EACCESS);
182 if (mode == RRQ) {
183 if ((stbuf.st_mode&(S_IREAD >> 6)) == 0)
184 return (EACCESS);
185 } else {
186 if ((stbuf.st_mode&(S_IWRITE >> 6)) == 0)
187 return (EACCESS);
188 }
189 fd = open(file, mode == RRQ ? 0 : 1);
190 if (fd < 0)
191 return (errno + 100);
192 return (0);
193}
194
195int timeout;
196
197timer()
198{
199 timeout += TIMEOUT;
200 if (timeout >= MAXTIMEOUT)
201 exit(1);
202 alarm(TIMEOUT);
203}
204
205/*
206 * Send the requested file.
207 */
208sendfile(pf)
209 struct format *pf;
210{
211 register struct tftphdr *tp;
212 register int block = 1, size, n;
213
214 sigset(SIGALRM, timer);
215 tp = (struct tftphdr *)buf;
216 do {
217 size = read(fd, tp->th_data, SEGSIZE);
218 if (size < 0) {
219 nak(errno + 100);
220 break;
221 }
222 tp->th_opcode = htons((u_short)DATA);
223 tp->th_block = htons((u_short)block);
224 timeout = 0;
225 alarm(TIMEOUT);
226rexmt:
227 if (write(f, buf, size + 4) != size + 4) {
228 perror("send");
229 break;
230 }
231again:
232 n = read(f, buf, sizeof (buf));
233 if (n <= 0) {
234 if (n == 0)
235 goto again;
236 if (errno == EINTR)
237 goto rexmt;
238 alarm(0);
239 perror("receive");
240 break;
241 }
242 alarm(0);
243#if vax || pdp11
244 tp->th_opcode = ntohs(tp->th_opcode);
245 tp->th_block = ntohs(tp->th_block);
246#endif
247 if (tp->th_opcode == ERROR)
248 break;
249 if (tp->th_opcode != ACK || tp->th_block != block)
250 goto again;
251 block++;
252 } while (size == SEGSIZE);
253 (void) close(fd);
254}
255
256/*
257 * Receive a file.
258 */
259recvfile(pf)
260 struct format *pf;
261{
262 register struct tftphdr *tp;
263 register int block = 0, n, size;
264
265 sigset(SIGALRM, timer);
266 tp = (struct tftphdr *)buf;
267 do {
268 timeout = 0;
269 alarm(TIMEOUT);
270 tp->th_opcode = htons((u_short)ACK);
271 tp->th_block = htons((u_short)block);
272 block++;
273rexmt:
274 if (write(f, buf, 4) != 4) {
275 perror("ack");
276 break;
277 }
278again:
279 n = read(f, buf, sizeof (buf));
280 if (n <= 0) {
281 if (n == 0)
282 goto again;
283 if (errno == EINTR)
284 goto rexmt;
285 alarm(0);
286 perror("receive");
287 break;
288 }
289 alarm(0);
290#if vax || pdp11
291 tp->th_opcode = ntohs(tp->th_opcode);
292 tp->th_block = ntohs(tp->th_block);
293#endif
294 if (tp->th_opcode == ERROR)
295 break;
296 if (tp->th_opcode != DATA || block != tp->th_block)
297 goto again;
298 size = write(fd, tp->th_data, n - 4);
299 if (size < 0) {
300 nak(errno + 100);
301 break;
302 }
303 } while (size == SEGSIZE);
304 tp->th_opcode = htons((u_short)ACK);
305 tp->th_block = htons((u_short)(block));
306 (void) write(f, buf, 4);
307 (void) close(fd);
308}
309
310struct errmsg {
311 int e_code;
312 char *e_msg;
313} errmsgs[] = {
314 { EUNDEF, "Undefined error code" },
315 { ENOTFOUND, "File not found" },
316 { EACCESS, "Access violation" },
317 { ENOSPACE, "Disk full or allocation exceeded" },
318 { EBADOP, "Illegal TFTP operation" },
319 { EBADID, "Unknown transfer ID" },
320 { EEXISTS, "File already exists" },
321 { ENOUSER, "No such user" },
322 { -1, 0 }
323};
324
325/*
326 * Send a nak packet (error message).
327 * Error code passed in is one of the
328 * standard TFTP codes, or a UNIX errno
329 * offset by 100.
330 */
331nak(error)
332 int error;
333{
334 register struct tftphdr *tp;
335 int length;
336 register struct errmsg *pe;
337 extern char *sys_errlist[];
338
339 tp = (struct tftphdr *)buf;
340 tp->th_opcode = htons((u_short)ERROR);
341 tp->th_code = htons((u_short)error);
342 for (pe = errmsgs; pe->e_code >= 0; pe++)
343 if (pe->e_code == error)
344 break;
345 if (pe->e_code < 0)
346 pe->e_msg = sys_errlist[error - 100];
347 strcpy(tp->th_msg, pe->e_msg);
348 length = strlen(pe->e_msg);
349 tp->th_msg[length] = '\0';
350 length += 5;
351 if (write(f, buf, length) != length)
352 perror("nak");
353}