xref: /csrg-svn/libexec/tftpd/tftpd.c (revision 17188)
1 #ifndef lint
2 static	char sccsid[] = "@(#)tftpd.c	4.14 (Berkeley) 09/13/84";
3 #endif
4 
5 /*
6  * Trivial file transfer protocol server.
7  */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/ioctl.h>
11 #include <sys/wait.h>
12 #include <sys/stat.h>
13 
14 #include <netinet/in.h>
15 
16 #include <arpa/tftp.h>
17 
18 #include <signal.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <ctype.h>
22 #include <netdb.h>
23 #include <setjmp.h>
24 #include <syslog.h>
25 
26 #define	TIMEOUT		5
27 
28 extern	int errno;
29 struct	sockaddr_in sin = { AF_INET };
30 int	peer;
31 int	rexmtval = TIMEOUT;
32 int	maxtimeout = 5*TIMEOUT;
33 char	buf[BUFSIZ];
34 struct	sockaddr_in from;
35 int	fromlen;
36 
37 main()
38 {
39 	register struct tftphdr *tp;
40 	register int n;
41 
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");
48 		exit(1);
49 	}
50 	from.sin_family = AF_INET;
51 	alarm(0);
52 	close(0);
53 	close(1);
54 	peer = socket(AF_INET, SOCK_DGRAM, 0);
55 	if (peer < 0) {
56 		openlog("tftpd", LOG_PID, 0);
57 		syslog(LOG_ERR, "socket: %m");
58 		exit(1);
59 	}
60 	if (bind(peer, (caddr_t)&sin, sizeof (sin)) < 0) {
61 		openlog("tftpd", LOG_PID, 0);
62 		syslog(LOG_ERR, "bind: %m");
63 		exit(1);
64 	}
65 	if (connect(peer, (caddr_t)&from, sizeof(from)) < 0) {
66 		openlog("tftpd", LOG_PID, 0);
67 		syslog(LOG_ERR, "connect: %m");
68 		exit(1);
69 	}
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);
75 }
76 
77 int	validate_access();
78 int	sendfile(), recvfile();
79 
80 struct 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 
94 /*
95  * Handle initial connection protocol.
96  */
97 tftp(tp, size)
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 
106 	filename = cp = tp->th_stuff;
107 again:
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 	}
132 	ecode = (*pf->f_validate)(filename, tp->th_opcode);
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 
144 int	fd;
145 
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  */
154 validate_access(file, mode)
155 	char *file;
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 
177 int	timeout;
178 jmp_buf	timeoutbuf;
179 
180 timer()
181 {
182 
183 	timeout += rexmtval;
184 	if (timeout >= maxtimeout)
185 		exit(1);
186 	longjmp(timeoutbuf, 1);
187 }
188 
189 /*
190  * Send the requested file.
191  */
192 sendfile(pf)
193 	struct format *pf;
194 {
195 	register struct tftphdr *tp;
196 	register int block = 1, size, n;
197 
198 	signal(SIGALRM, timer);
199 	tp = (struct tftphdr *)buf;
200 	do {
201 		size = read(fd, tp->th_data, SEGSIZE);
202 		if (size < 0) {
203 			nak(errno + 100);
204 			return;
205 		}
206 		tp->th_opcode = htons((u_short)DATA);
207 		tp->th_block = htons((u_short)block);
208 		timeout = 0;
209 		(void) setjmp(timeoutbuf);
210 		if (send(peer, buf, size + 4, 0) != size + 4) {
211 			perror("tftpd: send");
212 			return;
213 		}
214 		do {
215 			alarm(rexmtval);
216 			n = recv(peer, buf, sizeof (buf), 0);
217 			alarm(0);
218 			if (n < 0) {
219 				perror("tftpd: recv");
220 				return;
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)
225 				return;
226 		} while (tp->th_opcode != ACK || tp->th_block != block);
227 		block++;
228 	} while (size == SEGSIZE);
229 }
230 
231 /*
232  * Receive a file.
233  */
234 recvfile(pf)
235 	struct format *pf;
236 {
237 	register struct tftphdr *tp;
238 	register int block = 0, n, size;
239 
240 	signal(SIGALRM, timer);
241 	tp = (struct tftphdr *)buf;
242 	do {
243 		timeout = 0;
244 		tp->th_opcode = htons((u_short)ACK);
245 		tp->th_block = htons((u_short)block);
246 		block++;
247 		(void) setjmp(timeoutbuf);
248 		if (send(peer, buf, 4, 0) != 4) {
249 			perror("tftpd: send");
250 			goto abort;
251 		}
252 		do {
253 			alarm(rexmtval);
254 			n = recv(peer, buf, sizeof (buf), 0);
255 			alarm(0);
256 			if (n < 0) {
257 				perror("tftpd: recv");
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);
265 		size = write(fd, tp->th_data, n - 4);
266 		if (size < 0) {
267 			nak(errno + 100);
268 			goto abort;
269 		}
270 	} while (size == SEGSIZE);
271 abort:
272 	tp->th_opcode = htons((u_short)ACK);
273 	tp->th_block = htons((u_short)(block));
274 	(void) send(peer, buf, 4, 0);
275 }
276 
277 struct 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  */
298 nak(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;
318 	if (send(peer, buf, length, 0) != length)
319 		perror("nak");
320 	exit(1);
321 }
322