OSDN Git Service

Fix no pic
[uclinux-h8/uClinux-dist.git] / user / tftp / tftp.c
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, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * From: @(#)tftp.c     5.10 (Berkeley) 3/1/91
36  */
37 char tftp_rcsid[] = 
38   "$Id: tftp.c,v 1.6 1996/08/29 22:25:27 dholland Exp $";
39
40 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
41
42 /*
43  * TFTP User Program -- Protocol Machines
44  */
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/time.h>
48
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <arpa/tftp.h>
52
53 #include <signal.h>
54 #include <stdio.h>
55 #include <errno.h>
56 #include <setjmp.h>
57 #include <unistd.h>
58 #include <string.h>
59
60 #include "tftpsubs.h"
61
62 extern  struct sockaddr_in s_inn;         /* filled in by main */
63 extern  int     f;                      /* the opened socket */
64 extern  int     trace;
65 extern  int     verbose;
66 extern  int     rexmtval;
67 extern  int     maxtimeout;
68 extern sigjmp_buf toplevel;
69 void sendfile(int fd, char *name, char *modestr);
70 void recvfile(int fd, char *name, char *modestr);
71
72
73 #define PKTSIZE    SEGSIZE+4
74 static char ackbuf[PKTSIZE];
75 static int timeout;
76 static sigjmp_buf timeoutbuf;
77
78
79 static int makerequest(int request, char *name, 
80                        struct tftphdr *tp, char *mode);
81 static void nak(int errnor);
82 static void tpacket(const char *s, struct tftphdr *tp, int n);
83 static void startclock(void);
84 static void stopclock(void);
85 static void printstats(const char *direction, unsigned long amount);
86
87 static
88 void
89 timer(int signum)
90 {
91         (void)signum;
92
93         timeout += rexmtval;
94         if (timeout >= maxtimeout) {
95                 printf("Transfer timed out.\n");
96                 siglongjmp(toplevel, -1);
97         }
98         siglongjmp(timeoutbuf, 1);
99 }
100
101 /*
102  * Send the requested file.
103  */
104 void
105 sendfile(int fd, char *name, char *mode)
106 {
107         register struct tftphdr *ap;       /* data and ack packets */
108         struct tftphdr *dp;
109         volatile int block = 0, size = 0;
110         int n;
111         volatile unsigned long amount = 0;
112         struct sockaddr_in from;
113         socklen_t fromlen;
114         volatile int convert;            /* true if doing nl->crlf conversion */
115         FILE *file;
116
117         startclock();           /* start stat's clock */
118         dp = r_init();          /* reset fillbuf/read-ahead code */
119         ap = (struct tftphdr *)ackbuf;
120         file = fdopen(fd, "r");
121         convert = !strcmp(mode, "netascii");
122
123         signal(SIGALRM, timer);
124         do {
125                 if (block == 0)
126                         size = makerequest(WRQ, name, dp, mode) - 4;
127                 else {
128                 /*      size = read(fd, dp->th_data, SEGSIZE);   */
129                         size = readit(file, &dp, convert);
130                         if (size < 0) {
131                                 nak(errno + 100);
132                                 break;
133                         }
134                         dp->th_opcode = htons((u_short)DATA);
135                         dp->th_block = htons((u_short)block);
136                 }
137                 timeout = 0;
138                 (void) sigsetjmp(timeoutbuf, 1);
139 send_data:
140                 if (trace)
141                         tpacket("sent", dp, size + 4);
142                 n = sendto(f, dp, size + 4, 0,
143                     (struct sockaddr *)&s_inn, sizeof(s_inn));
144                 if (n != size + 4) {
145                         perror("tftp: sendto");
146                         goto abort;
147                 }
148                 read_ahead(file, convert);
149                 for ( ; ; ) {
150                         alarm(rexmtval);
151                         do {
152                                 fromlen = sizeof (from);
153                                 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
154                                     (struct sockaddr *)&from, &fromlen);
155                         } while (n <= 0);
156                         alarm(0);
157                         if (n < 0) {
158                                 perror("tftp: recvfrom");
159                                 goto abort;
160                         }
161                         s_inn.sin_port = from.sin_port;   /* added */
162                         if (trace)
163                                 tpacket("received", ap, n);
164                         /* should verify packet came from server */
165                         ap->th_opcode = ntohs(ap->th_opcode);
166                         ap->th_block = ntohs(ap->th_block);
167                         if (ap->th_opcode == ERROR) {
168                                 printf("Error code %d: %s\n", ap->th_code,
169                                         ap->th_msg);
170                                 goto abort;
171                         }
172                         if (ap->th_opcode == ACK) {
173                                 volatile int j = 0;
174
175                                 if (ap->th_block == block) {
176                                         break;
177                                 }
178                                 /* On an error, try to synchronize
179                                  * both sides.
180                                  */
181                                 j = synchnet(f);
182                                 if (j && trace) {
183                                         printf("discarded %d packets\n",
184                                                         j);
185                                 }
186                                 if (ap->th_block == (block-1)) {
187                                         goto send_data;
188                                 }
189                         }
190                 }
191                 if (block > 0)
192                         amount += size;
193                 block++;
194         } while (size == SEGSIZE || block == 1);
195 abort:
196         fclose(file);
197         stopclock();
198         if (amount > 0)
199                 printstats("Sent", amount);
200 }
201
202 /*
203  * Receive a file.
204  */
205 void
206 recvfile(int fd, char *name, char *mode)
207 {
208         register struct tftphdr *ap;
209         struct tftphdr *dp;
210         volatile int block = 1, size = 0;
211         int n; 
212         volatile unsigned long amount = 0;
213         struct sockaddr_in from;
214         socklen_t fromlen;
215         volatile int firsttrip = 1;
216         FILE *file;
217         volatile int convert;            /* true if converting crlf -> lf */
218
219         startclock();
220         dp = w_init();
221         ap = (struct tftphdr *)ackbuf;
222         file = fdopen(fd, "w");
223         convert = !strcmp(mode, "netascii");
224
225         signal(SIGALRM, timer);
226         do {
227                 if (firsttrip) {
228                         size = makerequest(RRQ, name, ap, mode);
229                         firsttrip = 0;
230                 } else {
231                         ap->th_opcode = htons((u_short)ACK);
232                         ap->th_block = htons((u_short)(block));
233                         size = 4;
234                         block++;
235                 }
236                 timeout = 0;
237                 (void) sigsetjmp(timeoutbuf, 1);
238 send_ack:
239                 if (trace)
240                         tpacket("sent", ap, size);
241                 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_inn,
242                     sizeof (s_inn)) != size) {
243                         alarm(0);
244                         perror("tftp: sendto");
245                         goto abort;
246                 }
247                 write_behind(file, convert);
248                 for ( ; ; ) {
249                         alarm(rexmtval);
250                         do  {
251                                 fromlen = sizeof (from);
252                                 n = recvfrom(f, dp, PKTSIZE, 0,
253                                     (struct sockaddr *)&from, &fromlen);
254                         } while (n <= 0);
255                         alarm(0);
256                         if (n < 0) {
257                                 perror("tftp: recvfrom");
258                                 goto abort;
259                         }
260                         s_inn.sin_port = from.sin_port;   /* added */
261                         if (trace)
262                                 tpacket("received", dp, n);
263                         /* should verify client address */
264                         dp->th_opcode = ntohs(dp->th_opcode);
265                         dp->th_block = ntohs(dp->th_block);
266                         if (dp->th_opcode == ERROR) {
267                                 printf("Error code %d: %s\n", dp->th_code,
268                                         dp->th_msg);
269                                 goto abort;
270                         }
271                         if (dp->th_opcode == DATA) {
272                                 volatile int j = 0;
273
274                                 if (dp->th_block == block) {
275                                         break;          /* have next packet */
276                                 }
277                                 /* On an error, try to synchronize
278                                  * both sides.
279                                  */
280                                 j = synchnet(f);
281                                 if (j && trace) {
282                                         printf("discarded %d packets\n", j);
283                                 }
284                                 if (dp->th_block == (block-1)) {
285                                         goto send_ack;  /* resend ack */
286                                 }
287                         }
288                 }
289         /*      size = write(fd, dp->th_data, n - 4); */
290                 size = writeit(file, &dp, n - 4, convert);
291                 if (size < 0) {
292                         nak(errno + 100);
293                         break;
294                 }
295                 amount += size;
296         } while (size == SEGSIZE);
297 abort:                                          /* ok to ack, since user */
298         ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
299         ap->th_block = htons((u_short)block);
300         (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&s_inn, sizeof(s_inn));
301         write_behind(file, convert);            /* flush last buffer */
302         fclose(file);
303         stopclock();
304         if (amount > 0)
305                 printstats("Received", amount);
306 }
307
308 int
309 makerequest(int request, char *name, struct tftphdr *tp, char *mode)
310 {
311         register char *cp;
312
313         tp->th_opcode = htons((u_short)request);
314         cp = tp->th_stuff;
315         strcpy(cp, name);
316         cp += strlen(name);
317         *cp++ = '\0';
318         strcpy(cp, mode);
319         cp += strlen(mode);
320         *cp++ = '\0';
321         return (cp - (char *)tp);
322 }
323
324 struct errmsg {
325         int e_code;
326         const char *e_msg;
327 } errmsgs[] = {
328         { EUNDEF,       "Undefined error code" },
329         { ENOTFOUND,    "File not found" },
330         { EACCESS,      "Access violation" },
331         { ENOSPACE,     "Disk full or allocation exceeded" },
332         { EBADOP,       "Illegal TFTP operation" },
333         { EBADID,       "Unknown transfer ID" },
334         { EEXISTS,      "File already exists" },
335         { ENOUSER,      "No such user" },
336         { -1,           0 }
337 };
338
339 /*
340  * Send a nak packet (error message).
341  * Error code passed in is one of the
342  * standard TFTP codes, or a UNIX errno
343  * offset by 100.
344  */
345 void
346 nak(int error)
347 {
348         register struct errmsg *pe;
349         register struct tftphdr *tp;
350         int length;
351
352         tp = (struct tftphdr *)ackbuf;
353         tp->th_opcode = htons((u_short)ERROR);
354         tp->th_code = htons((u_short)error);
355         for (pe = errmsgs; pe->e_code >= 0; pe++)
356                 if (pe->e_code == error)
357                         break;
358         if (pe->e_code < 0) {
359                 pe->e_msg = strerror(error - 100);
360                 tp->th_code = EUNDEF;
361         }
362         strcpy(tp->th_msg, pe->e_msg);
363         length = strlen(pe->e_msg) + 4;
364         if (trace)
365                 tpacket("sent", tp, length);
366         if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&s_inn,
367             sizeof (s_inn)) != length)
368                 perror("nak");
369 }
370
371 static
372 void
373 tpacket(const char *s, struct tftphdr *tp, int n)
374 {
375         static const char *opcodes[] =
376            { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
377         register char *cp, *file;
378         u_short op = ntohs(tp->th_opcode);
379
380         if (op < RRQ || op > ERROR)
381                 printf("%s opcode=%x ", s, op);
382         else
383                 printf("%s %s ", s, opcodes[op]);
384         switch (op) {
385
386         case RRQ:
387         case WRQ:
388                 n -= 2;
389                 file = cp = tp->th_stuff;
390                 cp = cp + strlen(cp);
391                 printf("<file=%s, mode=%s>\n", file, cp + 1);
392                 break;
393
394         case DATA:
395                 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
396                 break;
397
398         case ACK:
399                 printf("<block=%d>\n", ntohs(tp->th_block));
400                 break;
401
402         case ERROR:
403                 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
404                 break;
405         }
406 }
407
408 struct timeval tstart;
409 struct timeval tstop;
410 struct timezone zone;
411
412 void
413 startclock(void) {
414         gettimeofday(&tstart, &zone);
415 }
416
417 void
418 stopclock(void) {
419         gettimeofday(&tstop, &zone);
420 }
421
422 void
423 printstats(const char *direction, unsigned long amount)
424 {
425         double delta;
426                         /* compute delta in 1/10's second units */
427         delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
428                 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
429         delta = delta/10.;      /* back to seconds */
430         printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
431         if (verbose)
432                 printf(" [%.0f bits/sec]", (amount*8.)/delta);
433         putchar('\n');
434 }
435