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