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