Count SIGIO's (for debugging). Separate out noasynch between
[unix-history] / usr / src / usr.bin / tftp / tftp.c
... / ...
CommitLineData
1/*
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
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.
16 */
17
18#ifndef lint
19static char sccsid[] = "@(#)tftp.c 5.7 (Berkeley) %G%";
20#endif /* not lint */
21
22/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
23
24/*
25 * TFTP User Program -- Protocol Machines
26 */
27#include <sys/types.h>
28#include <sys/socket.h>
29#include <sys/time.h>
30
31#include <netinet/in.h>
32
33#include <arpa/tftp.h>
34
35#include <signal.h>
36#include <stdio.h>
37#include <errno.h>
38#include <setjmp.h>
39
40extern int errno;
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];
51int timeout;
52jmp_buf toplevel;
53jmp_buf timeoutbuf;
54
55timer()
56{
57
58 timeout += rexmtval;
59 if (timeout >= maxtimeout) {
60 printf("Transfer timed out.\n");
61 longjmp(toplevel, -1);
62 }
63 longjmp(timeoutbuf, 1);
64}
65
66/*
67 * Send the requested file.
68 */
69sendfile(fd, name, mode)
70 int fd;
71 char *name;
72 char *mode;
73{
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
89 signal(SIGALRM, timer);
90 do {
91 if (block == 0)
92 size = makerequest(WRQ, name, dp, mode) - 4;
93 else {
94 /* size = read(fd, dp->th_data, SEGSIZE); */
95 size = readit(file, &dp, convert);
96 if (size < 0) {
97 nak(errno + 100);
98 break;
99 }
100 dp->th_opcode = htons((u_short)DATA);
101 dp->th_block = htons((u_short)block);
102 }
103 timeout = 0;
104 (void) setjmp(timeoutbuf);
105send_data:
106 if (trace)
107 tpacket("sent", dp, size + 4);
108 n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin));
109 if (n != size + 4) {
110 perror("tftp: sendto");
111 goto abort;
112 }
113 read_ahead(file, convert);
114 for ( ; ; ) {
115 alarm(rexmtval);
116 do {
117 fromlen = sizeof (from);
118 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
119 (caddr_t)&from, &fromlen);
120 } while (n <= 0);
121 alarm(0);
122 if (n < 0) {
123 perror("tftp: recvfrom");
124 goto abort;
125 }
126 sin.sin_port = from.sin_port; /* added */
127 if (trace)
128 tpacket("received", ap, n);
129 /* should verify packet came from server */
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;
136 }
137 if (ap->th_opcode == ACK) {
138 int j;
139
140 if (ap->th_block == block) {
141 break;
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;
153 }
154 }
155 }
156 if (block > 0)
157 amount += size;
158 block++;
159 } while (size == SEGSIZE || block == 1);
160abort:
161 fclose(file);
162 stopclock();
163 if (amount > 0)
164 printstats("Sent", amount);
165}
166
167/*
168 * Receive a file.
169 */
170recvfile(fd, name, mode)
171 int fd;
172 char *name;
173 char *mode;
174{
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
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);
203send_ack:
204 if (trace)
205 tpacket("sent", ap, size);
206 if (sendto(f, ackbuf, size, 0, (caddr_t)&sin,
207 sizeof (sin)) != 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 (caddr_t)&from, &fromlen);
219 } while (n <= 0);
220 alarm(0);
221 if (n < 0) {
222 perror("tftp: recvfrom");
223 goto abort;
224 }
225 sin.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);
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);
271}
272
273makerequest(request, name, tp, mode)
274 int request;
275 char *name, *mode;
276 struct tftphdr *tp;
277{
278 register char *cp;
279
280 tp->th_opcode = htons((u_short)request);
281 cp = tp->th_stuff;
282 strcpy(cp, name);
283 cp += strlen(name);
284 *cp++ = '\0';
285 strcpy(cp, mode);
286 cp += strlen(mode);
287 *cp++ = '\0';
288 return (cp - (char *)tp);
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 */
312nak(error)
313 int error;
314{
315 register struct tftphdr *tp;
316 int length;
317 register struct errmsg *pe;
318 extern char *sys_errlist[];
319
320 tp = (struct tftphdr *)ackbuf;
321 tp->th_opcode = htons((u_short)ERROR);
322 tp->th_code = htons((u_short)error);
323 for (pe = errmsgs; pe->e_code >= 0; pe++)
324 if (pe->e_code == error)
325 break;
326 if (pe->e_code < 0) {
327 pe->e_msg = sys_errlist[error - 100];
328 tp->th_code = EUNDEF;
329 }
330 strcpy(tp->th_msg, pe->e_msg);
331 length = strlen(pe->e_msg) + 4;
332 if (trace)
333 tpacket("sent", tp, length);
334 if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length)
335 perror("nak");
336}
337
338tpacket(s, tp, n)
339 char *s;
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)
350 printf("%s opcode=%x ", s, op);
351 else
352 printf("%s %s ", s, opcodes[op]);
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}
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