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