BSD 4_3_Tahoe release
[unix-history] / usr / src / ucb / tftp / tftp.c
CommitLineData
d0aeaf5a 1/*
f6e43951
KB
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b36fc510
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
d0aeaf5a
DF
16 */
17
524aa063 18#ifndef lint
ca67e7b4 19static char sccsid[] = "@(#)tftp.c 5.7 (Berkeley) 6/29/88";
f6e43951 20#endif /* not lint */
d3fc13cc 21
6457cfd6
GM
22/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
23
d3fc13cc
SL
24/*
25 * TFTP User Program -- Protocol Machines
26 */
27#include <sys/types.h>
d3fc13cc 28#include <sys/socket.h>
6457cfd6 29#include <sys/time.h>
de3b21e8
SL
30
31#include <netinet/in.h>
6457cfd6 32
86fe57d4
SL
33#include <arpa/tftp.h>
34
d3fc13cc
SL
35#include <signal.h>
36#include <stdio.h>
37#include <errno.h>
38#include <setjmp.h>
de3b21e8 39
d3fc13cc 40extern int errno;
6457cfd6
GM
41
42extern struct sockaddr_in sin; /* filled in by main */
43extern int f; /* the opened socket */
44extern int trace;
45extern int verbose;
46extern int rexmtval;
47extern int maxtimeout;
48
49#define PKTSIZE SEGSIZE+4
50char ackbuf[PKTSIZE];
d3fc13cc
SL
51int timeout;
52jmp_buf toplevel;
61ad9979 53jmp_buf timeoutbuf;
d3fc13cc
SL
54
55timer()
56{
61ad9979
SL
57
58 timeout += rexmtval;
59 if (timeout >= maxtimeout) {
d3fc13cc
SL
60 printf("Transfer timed out.\n");
61 longjmp(toplevel, -1);
62 }
61ad9979 63 longjmp(timeoutbuf, 1);
d3fc13cc
SL
64}
65
66/*
67 * Send the requested file.
68 */
6457cfd6 69sendfile(fd, name, mode)
d3fc13cc
SL
70 int fd;
71 char *name;
6457cfd6 72 char *mode;
d3fc13cc 73{
6457cfd6
GM
74 register struct tftphdr *ap; /* data and ack packets */
75 struct tftphdr *r_init(), *dp;
76 register int block = 0, size, n;
77 register unsigned long amount = 0;
78 struct sockaddr_in from;
79 int fromlen;
80 int convert; /* true if doing nl->crlf conversion */
81 FILE *file;
82
83 startclock(); /* start stat's clock */
84 dp = r_init(); /* reset fillbuf/read-ahead code */
85 ap = (struct tftphdr *)ackbuf;
86 file = fdopen(fd, "r");
87 convert = !strcmp(mode, "netascii");
88
61ad9979 89 signal(SIGALRM, timer);
d3fc13cc 90 do {
61ad9979 91 if (block == 0)
6457cfd6 92 size = makerequest(WRQ, name, dp, mode) - 4;
61ad9979 93 else {
6457cfd6
GM
94 /* size = read(fd, dp->th_data, SEGSIZE); */
95 size = readit(file, &dp, convert);
d3fc13cc 96 if (size < 0) {
6457cfd6 97 nak(errno + 100);
d3fc13cc
SL
98 break;
99 }
6457cfd6
GM
100 dp->th_opcode = htons((u_short)DATA);
101 dp->th_block = htons((u_short)block);
d3fc13cc
SL
102 }
103 timeout = 0;
61ad9979 104 (void) setjmp(timeoutbuf);
f65510cf 105send_data:
d3fc13cc 106 if (trace)
6457cfd6
GM
107 tpacket("sent", dp, size + 4);
108 n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin));
de3b21e8 109 if (n != size + 4) {
61ad9979 110 perror("tftp: sendto");
6457cfd6 111 goto abort;
d3fc13cc 112 }
6457cfd6 113 read_ahead(file, convert);
f65510cf 114 for ( ; ; ) {
61ad9979
SL
115 alarm(rexmtval);
116 do {
117 fromlen = sizeof (from);
6457cfd6 118 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
61ad9979
SL
119 (caddr_t)&from, &fromlen);
120 } while (n <= 0);
d3fc13cc 121 alarm(0);
61ad9979
SL
122 if (n < 0) {
123 perror("tftp: recvfrom");
6457cfd6 124 goto abort;
61ad9979 125 }
6457cfd6 126 sin.sin_port = from.sin_port; /* added */
61ad9979 127 if (trace)
6457cfd6 128 tpacket("received", ap, n);
61ad9979 129 /* should verify packet came from server */
6457cfd6
GM
130 ap->th_opcode = ntohs(ap->th_opcode);
131 ap->th_block = ntohs(ap->th_block);
132 if (ap->th_opcode == ERROR) {
133 printf("Error code %d: %s\n", ap->th_code,
134 ap->th_msg);
135 goto abort;
61ad9979 136 }
f65510cf 137 if (ap->th_opcode == ACK) {
15631344
GM
138 int j;
139
f65510cf
GM
140 if (ap->th_block == block) {
141 break;
15631344
GM
142 }
143 /* On an error, try to synchronize
144 * both sides.
145 */
146 j = synchnet(f);
147 if (j && trace) {
148 printf("discarded %d packets\n",
149 j);
150 }
151 if (ap->th_block == (block-1)) {
152 goto send_data;
f65510cf
GM
153 }
154 }
155 }
d3fc13cc
SL
156 if (block > 0)
157 amount += size;
158 block++;
159 } while (size == SEGSIZE || block == 1);
6457cfd6
GM
160abort:
161 fclose(file);
162 stopclock();
163 if (amount > 0)
164 printstats("Sent", amount);
d3fc13cc
SL
165}
166
167/*
168 * Receive a file.
169 */
6457cfd6 170recvfile(fd, name, mode)
d3fc13cc
SL
171 int fd;
172 char *name;
6457cfd6 173 char *mode;
d3fc13cc 174{
6457cfd6
GM
175 register struct tftphdr *ap;
176 struct tftphdr *dp, *w_init();
177 register int block = 1, n, size;
178 unsigned long amount = 0;
179 struct sockaddr_in from;
180 int fromlen, firsttrip = 1;
181 FILE *file;
182 int convert; /* true if converting crlf -> lf */
183
184 startclock();
185 dp = w_init();
186 ap = (struct tftphdr *)ackbuf;
187 file = fdopen(fd, "w");
188 convert = !strcmp(mode, "netascii");
189
61ad9979 190 signal(SIGALRM, timer);
d3fc13cc 191 do {
61ad9979 192 if (firsttrip) {
6457cfd6 193 size = makerequest(RRQ, name, ap, mode);
61ad9979
SL
194 firsttrip = 0;
195 } else {
6457cfd6
GM
196 ap->th_opcode = htons((u_short)ACK);
197 ap->th_block = htons((u_short)(block));
61ad9979
SL
198 size = 4;
199 block++;
200 }
d3fc13cc 201 timeout = 0;
61ad9979 202 (void) setjmp(timeoutbuf);
6457cfd6 203send_ack:
d3fc13cc 204 if (trace)
6457cfd6
GM
205 tpacket("sent", ap, size);
206 if (sendto(f, ackbuf, size, 0, (caddr_t)&sin,
207 sizeof (sin)) != size) {
d3fc13cc 208 alarm(0);
61ad9979 209 perror("tftp: sendto");
6457cfd6 210 goto abort;
d3fc13cc 211 }
6457cfd6
GM
212 write_behind(file, convert);
213 for ( ; ; ) {
61ad9979 214 alarm(rexmtval);
6457cfd6 215 do {
fc945f1a 216 fromlen = sizeof (from);
6457cfd6 217 n = recvfrom(f, dp, PKTSIZE, 0,
61ad9979 218 (caddr_t)&from, &fromlen);
fc945f1a 219 } while (n <= 0);
61ad9979
SL
220 alarm(0);
221 if (n < 0) {
222 perror("tftp: recvfrom");
6457cfd6 223 goto abort;
61ad9979 224 }
6457cfd6 225 sin.sin_port = from.sin_port; /* added */
61ad9979 226 if (trace)
6457cfd6
GM
227 tpacket("received", dp, n);
228 /* should verify client address */
229 dp->th_opcode = ntohs(dp->th_opcode);
230 dp->th_block = ntohs(dp->th_block);
231 if (dp->th_opcode == ERROR) {
232 printf("Error code %d: %s\n", dp->th_code,
233 dp->th_msg);
234 goto abort;
235 }
236 if (dp->th_opcode == DATA) {
15631344
GM
237 int j;
238
239 if (dp->th_block == block) {
6457cfd6 240 break; /* have next packet */
15631344
GM
241 }
242 /* On an error, try to synchronize
243 * both sides.
244 */
245 j = synchnet(f);
246 if (j && trace) {
247 printf("discarded %d packets\n", j);
248 }
249 if (dp->th_block == (block-1)) {
6457cfd6 250 goto send_ack; /* resend ack */
15631344 251 }
61ad9979 252 }
6457cfd6
GM
253 }
254 /* size = write(fd, dp->th_data, n - 4); */
255 size = writeit(file, &dp, n - 4, convert);
d3fc13cc 256 if (size < 0) {
6457cfd6
GM
257 nak(errno + 100);
258 break;
d3fc13cc
SL
259 }
260 amount += size;
261 } while (size == SEGSIZE);
6457cfd6
GM
262abort: /* ok to ack, since user */
263 ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
264 ap->th_block = htons((u_short)block);
265 (void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin));
266 write_behind(file, convert); /* flush last buffer */
267 fclose(file);
268 stopclock();
269 if (amount > 0)
270 printstats("Received", amount);
d3fc13cc
SL
271}
272
6457cfd6 273makerequest(request, name, tp, mode)
d3fc13cc 274 int request;
6457cfd6
GM
275 char *name, *mode;
276 struct tftphdr *tp;
d3fc13cc 277{
d3fc13cc
SL
278 register char *cp;
279
6457cfd6
GM
280 tp->th_opcode = htons((u_short)request);
281 cp = tp->th_stuff;
282 strcpy(cp, name);
283 cp += strlen(name);
d3fc13cc
SL
284 *cp++ = '\0';
285 strcpy(cp, mode);
6457cfd6 286 cp += strlen(mode);
d3fc13cc 287 *cp++ = '\0';
6457cfd6 288 return (cp - (char *)tp);
d3fc13cc
SL
289}
290
291struct errmsg {
292 int e_code;
293 char *e_msg;
294} errmsgs[] = {
295 { EUNDEF, "Undefined error code" },
296 { ENOTFOUND, "File not found" },
297 { EACCESS, "Access violation" },
298 { ENOSPACE, "Disk full or allocation exceeded" },
299 { EBADOP, "Illegal TFTP operation" },
300 { EBADID, "Unknown transfer ID" },
301 { EEXISTS, "File already exists" },
302 { ENOUSER, "No such user" },
303 { -1, 0 }
304};
305
306/*
307 * Send a nak packet (error message).
308 * Error code passed in is one of the
309 * standard TFTP codes, or a UNIX errno
310 * offset by 100.
311 */
6457cfd6 312nak(error)
d3fc13cc
SL
313 int error;
314{
6457cfd6 315 register struct tftphdr *tp;
d3fc13cc
SL
316 int length;
317 register struct errmsg *pe;
318 extern char *sys_errlist[];
319
6457cfd6
GM
320 tp = (struct tftphdr *)ackbuf;
321 tp->th_opcode = htons((u_short)ERROR);
322 tp->th_code = htons((u_short)error);
d3fc13cc
SL
323 for (pe = errmsgs; pe->e_code >= 0; pe++)
324 if (pe->e_code == error)
325 break;
6457cfd6 326 if (pe->e_code < 0) {
d3fc13cc 327 pe->e_msg = sys_errlist[error - 100];
6457cfd6
GM
328 tp->th_code = EUNDEF;
329 }
330 strcpy(tp->th_msg, pe->e_msg);
d3fc13cc
SL
331 length = strlen(pe->e_msg) + 4;
332 if (trace)
6457cfd6
GM
333 tpacket("sent", tp, length);
334 if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length)
335 perror("nak");
d3fc13cc
SL
336}
337
6457cfd6 338tpacket(s, tp, n)
7457289f 339 char *s;
d3fc13cc
SL
340 struct tftphdr *tp;
341 int n;
342{
343 static char *opcodes[] =
344 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
345 register char *cp, *file;
346 u_short op = ntohs(tp->th_opcode);
347 char *index();
348
349 if (op < RRQ || op > ERROR)
6457cfd6 350 printf("%s opcode=%x ", s, op);
d3fc13cc 351 else
6457cfd6 352 printf("%s %s ", s, opcodes[op]);
d3fc13cc
SL
353 switch (op) {
354
355 case RRQ:
356 case WRQ:
357 n -= 2;
358 file = cp = tp->th_stuff;
359 cp = index(cp, '\0');
360 printf("<file=%s, mode=%s>\n", file, cp + 1);
361 break;
362
363 case DATA:
364 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
365 break;
366
367 case ACK:
368 printf("<block=%d>\n", ntohs(tp->th_block));
369 break;
370
371 case ERROR:
372 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
373 break;
374 }
375}
6457cfd6
GM
376
377struct timeval tstart;
378struct timeval tstop;
379struct timezone zone;
380
381startclock() {
382 gettimeofday(&tstart, &zone);
383}
384
385stopclock() {
386 gettimeofday(&tstop, &zone);
387}
388
389printstats(direction, amount)
390char *direction;
391unsigned long amount;
392{
393 double delta;
394 /* compute delta in 1/10's second units */
395 delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
396 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
397 delta = delta/10.; /* back to seconds */
398 printf("%s %d bytes in %.1f seconds", direction, amount, delta);
399 if (verbose)
400 printf(" [%.0f bits/sec]", (amount*8.)/delta);
401 putchar('\n');
402}
403