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