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