[dhcp] Include gPXE version number within DHCP request
[people/mcb30/gpxe.git] / contrib / 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 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
19 static char sccsid[] = "@(#)tftp.c      5.7 (Berkeley) 6/29/88";
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
40 extern  int errno;
41
42 extern  struct sockaddr_in sin;         /* filled in by main */
43 extern  int     f;                      /* the opened socket */
44 extern  int     trace;
45 extern  int     verbose;
46 extern  int     rexmtval;
47 extern  int     maxtimeout;
48 extern  int     segsize;
49
50 #define PKTSIZE    (1432+4) /* SEGSIZE+4 */
51 char    ackbuf[PKTSIZE];
52 int     timeout;
53 jmp_buf toplevel;
54 jmp_buf timeoutbuf;
55
56 #ifndef OACK
57 #define OACK    6
58 #endif
59
60 void timer(int sig)
61 {
62
63         signal(SIGALRM, timer);
64         timeout += rexmtval;
65         if (timeout >= maxtimeout) {
66                 printf("Transfer timed out.\n");
67                 longjmp(toplevel, -1);
68         }
69         longjmp(timeoutbuf, 1);
70 }
71
72 strnlen(s, n)
73         char *s;
74         int n;
75 {
76         int i = 0;
77
78         while (n-- > 0 && *s++) i++;
79         return(i);
80 }
81
82 /*
83  * Parse an OACK package and set blocksize accordingly
84  */
85 parseoack(cp, sz)
86         char *cp;
87         int sz;
88 {
89         int n;
90         
91         segsize = 512;
92         while (sz > 0 && *cp) {
93                 n = strnlen(cp, sz);
94                 if (n == 7 && !strncmp("blksize", cp, 7)) {
95                         cp += 8;
96                         sz -= 8;
97                         if (sz <= 0)
98                                 break;
99                         for (segsize = 0, n = strnlen(cp, sz); n > 0;
100                              n--, cp++, sz--) {
101                                 if (*cp < '0' || *cp > '9')
102                                         break;
103                                 segsize = 10*segsize + *cp - '0'; }
104                 }
105                 cp += n + 1;
106                 sz -= n + 1;
107         }
108         if (segsize < 8 || segsize > 1432) {
109                 printf("Remote host negotiated illegal blocksize %d\n",
110                        segsize);
111                 segsize = 512;
112                 longjmp(timeoutbuf, -1);
113         }
114 }
115
116 /*
117  * Send the requested file.
118  */
119 sendfile(fd, name, mode)
120         int fd;
121         char *name;
122         char *mode;
123 {
124         register struct tftphdr *ap;       /* data and ack packets */
125         struct tftphdr *r_init(), *dp;
126         register int size, n;
127         u_short block = 0;
128         register unsigned long amount = 0;
129         struct sockaddr_in from;
130         int fromlen;
131         int convert;            /* true if doing nl->crlf conversion */
132         FILE *file;
133
134         startclock();           /* start stat's clock */
135         dp = r_init();          /* reset fillbuf/read-ahead code */
136         ap = (struct tftphdr *)ackbuf;
137         file = fdopen(fd, "r");
138         convert = !strcmp(mode, "netascii");
139
140         signal(SIGALRM, timer);
141         do {
142                 if (block == 0)
143                         size = makerequest(WRQ, name, dp, mode) - 4;
144                 else {
145                 /*      size = read(fd, dp->th_data, SEGSIZE);   */
146                         size = readit(file, &dp, convert);
147                         if (size < 0) {
148                                 nak(errno + 100);
149                                 break;
150                         }
151                         dp->th_opcode = htons((u_short)DATA);
152                         dp->th_block = htons(block);
153                 }
154                 timeout = 0;
155                 (void) setjmp(timeoutbuf);
156 send_data:
157                 if (trace)
158                         tpacket("sent", dp, size + 4);
159                 n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&sin,
160                            sizeof (sin));
161                 if (n != size + 4) {
162                         perror("tftp: sendto");
163                         goto abort;
164                 }
165                 if (block) /* do not start reading until the blocksize
166                               has been negotiated */
167                         read_ahead(file, convert);
168                 for ( ; ; ) {
169                         alarm(rexmtval);
170                         do {
171                                 fromlen = sizeof (from);
172                                 n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
173                                              (struct sockaddr *)&from,
174                                              &fromlen);
175                         } while (n <= 0);
176                         alarm(0);
177                         if (n < 0) {
178                                 perror("tftp: recvfrom");
179                                 goto abort;
180                         }
181                         sin.sin_port = from.sin_port;   /* added */
182                         if (trace)
183                                 tpacket("received", ap, n);
184                         /* should verify packet came from server */
185                         ap->th_opcode = ntohs(ap->th_opcode);
186                         if (ap->th_opcode == ERROR) {
187                                 printf("Error code %d: %s\n", ap->th_code,
188                                         ap->th_msg);
189                                 goto abort;
190                         }
191                         if (ap->th_opcode == ACK) {
192                                 int j;
193
194                                 ap->th_block = ntohs(ap->th_block);
195
196                                 if (block == 0) {
197                                         if (trace)
198                                                 printf("server does not know "
199                                                        "about RFC1782; reset"
200                                                        "ting blocksize\n");
201                                         segsize = 512;
202                                 }
203                                 if (ap->th_block == block) {
204                                         break;
205                                 }
206                                 /* On an error, try to synchronize
207                                  * both sides.
208                                  */
209                                 j = synchnet(f);
210                                 if (j && trace) {
211                                         printf("discarded %d packets\n",
212                                                         j);
213                                 }
214                                 if (ap->th_block == (block-1)) {
215                                         goto send_data;
216                                 }
217                         }
218                         else if (ap->th_opcode == OACK) {
219                                 if (block) {
220                                         printf("protocol violation\n");
221                                         longjmp(toplevel, -1);
222                                 }
223                                 parseoack(&ap->th_stuff, n - 2);
224                                 break;
225                         }
226                 }
227                 if (block > 0)
228                         amount += size;
229                 else
230                         read_ahead(file, convert);
231                 block++;
232         } while (size == segsize || block == 1);
233 abort:
234         fclose(file);
235         stopclock();
236         if (amount > 0)
237                 printstats("Sent", amount);
238 }
239
240 /*
241  * Receive a file.
242  */
243 recvfile(fd, name, mode)
244         int fd;
245         char *name;
246         char *mode;
247 {
248         register struct tftphdr *ap;
249         struct tftphdr *dp, *w_init();
250         register int n, size;
251         u_short block = 1;
252         unsigned long amount = 0;
253         struct sockaddr_in from;
254         int fromlen, firsttrip = 1;
255         FILE *file;
256         int convert;                    /* true if converting crlf -> lf */
257         int waitforoack = 1;
258
259         startclock();
260         dp = w_init();
261         ap = (struct tftphdr *)ackbuf;
262         file = fdopen(fd, "w");
263         convert = !strcmp(mode, "netascii");
264
265         signal(SIGALRM, timer);
266         do {
267                 if (firsttrip) {
268                         size = makerequest(RRQ, name, ap, mode);
269                         firsttrip = 0;
270                 } else {
271                         ap->th_opcode = htons((u_short)ACK);
272                         ap->th_block = htons(block);
273                         size = 4;
274                         block++;
275                 }
276                 timeout = 0;
277                 (void) setjmp(timeoutbuf);
278 send_ack:
279                 if (trace)
280                         tpacket("sent", ap, size);
281                 if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&sin,
282                     sizeof (sin)) != size) {
283                         alarm(0);
284                         perror("tftp: sendto");
285                         goto abort;
286                 }
287                 if (!waitforoack)
288                         write_behind(file, convert);
289                 for ( ; ; ) {
290                         alarm(rexmtval);
291                         do  {
292                                 fromlen = sizeof (from);
293                                 n = recvfrom(f, dp, PKTSIZE, 0,
294                                     (struct sockaddr *)&from, &fromlen);
295                         } while (n <= 0);
296                         alarm(0);
297                         if (n < 0) {
298                                 perror("tftp: recvfrom");
299                                 goto abort;
300                         }
301                         sin.sin_port = from.sin_port;   /* added */
302                         if (trace)
303                                 tpacket("received", dp, n);
304                         /* should verify client address */
305                         dp->th_opcode = ntohs(dp->th_opcode);
306                         if (dp->th_opcode == ERROR) {
307                                 printf("Error code %d: %s\n", dp->th_code,
308                                         dp->th_msg);
309                                 goto abort;
310                         }
311                         if (dp->th_opcode == DATA) {
312                                 int j;
313
314                                 if (waitforoack) {
315                                         if (trace)
316                                                 printf("server does not know "
317                                                        "about RFC1782; reset"
318                                                        "ting blocksize\n");
319                                         waitforoack = 0;
320                                         segsize = 512;
321                                 }
322                                 dp->th_block = ntohs(dp->th_block);
323                                 if (dp->th_block == block) {
324                                         break;          /* have next packet */
325                                 }
326                                 /* On an error, try to synchronize
327                                  * both sides.
328                                  */
329                                 j = synchnet(f);
330                                 if (j && trace) {
331                                         printf("discarded %d packets\n", j);
332                                 }
333                                 if (dp->th_block == (block-1)) {
334                                         goto send_ack;  /* resend ack */
335                                 }
336                         }
337                         else if (dp->th_opcode == OACK) {
338                                 if (block != 1 || !waitforoack) {
339                                         printf("protocol violation\n");
340                                         longjmp(toplevel, -1);
341                                 }
342                                 waitforoack = 0;
343                                 parseoack(&dp->th_stuff, n - 2);
344                                 ap->th_opcode = htons((u_short)ACK);
345                                 ap->th_block = htons(0);
346                                 size = 4;
347                                 goto send_ack;
348                         }
349                 }
350                 /* size = write(fd, dp->th_data, n - 4); */
351                 size = writeit(file, &dp, n - 4, convert);
352                 if (size < 0) {
353                         nak(errno + 100);
354                         break;
355                 }
356                 amount += size;
357         } while (size == segsize);
358 abort:                                          /* ok to ack, since user */
359         ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
360         ap->th_block = htons(block);
361         (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&sin, sizeof (sin));
362         write_behind(file, convert);            /* flush last buffer */
363         fclose(file);
364         stopclock();
365         if (amount > 0)
366                 printstats("Received", amount);
367 }
368
369 makerequest(request, name, tp, mode)
370         int request;
371         char *name, *mode;
372         struct tftphdr *tp;
373 {
374         register char *cp;
375
376         tp->th_opcode = htons((u_short)request);
377         cp = tp->th_stuff;
378         strcpy(cp, name);
379         cp += strlen(name);
380         *cp++ = '\0';
381         strcpy(cp, mode);
382         cp += strlen(mode);
383         *cp++ = '\0';
384         strcpy(cp, "blksize");
385         cp += 7;
386         *cp++ = '\0';
387         sprintf(cp, "%d", segsize);
388         cp += strlen(cp) + 1;
389         return (cp - (char *)tp);
390 }
391
392 struct errmsg {
393         int     e_code;
394         const char      *e_msg;
395 } errmsgs[] = {
396         { EUNDEF,       "Undefined error code" },
397         { ENOTFOUND,    "File not found" },
398         { EACCESS,      "Access violation" },
399         { ENOSPACE,     "Disk full or allocation exceeded" },
400         { EBADOP,       "Illegal TFTP operation" },
401         { EBADID,       "Unknown transfer ID" },
402         { EEXISTS,      "File already exists" },
403         { ENOUSER,      "No such user" },
404         { -1,           0 }
405 };
406
407 /*
408  * Send a nak packet (error message).
409  * Error code passed in is one of the
410  * standard TFTP codes, or a UNIX errno
411  * offset by 100.
412  */
413 nak(error)
414         int error;
415 {
416         register struct tftphdr *tp;
417         int length;
418         register struct errmsg *pe;
419 /*      extern char *sys_errlist[]; */
420
421         tp = (struct tftphdr *)ackbuf;
422         tp->th_opcode = htons((u_short)ERROR);
423         tp->th_code = htons((u_short)error);
424         for (pe = errmsgs; pe->e_code >= 0; pe++)
425                 if (pe->e_code == error)
426                         break;
427         if (pe->e_code < 0) {
428                 pe->e_msg = sys_errlist[error - 100];
429                 tp->th_code = EUNDEF;
430         }
431         strcpy(tp->th_msg, pe->e_msg);
432         length = strlen(pe->e_msg) + 4;
433         if (trace)
434                 tpacket("sent", tp, length);
435         if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&sin, sizeof (sin))
436             != length)
437                 perror("nak");
438 }
439
440 topts(cp, sz)
441         char *cp;
442         int sz;
443 {
444         int n, i = 0;
445         
446         while (sz > 0 && *cp) {
447                 n = strnlen(cp, sz);
448                 if (n > 0) {
449                         printf("%s%s=", i++ ? ", " : "", cp);
450                         cp += n + 1;
451                         sz -= n + 1;
452                         if (sz <= 0)
453                                 break;
454                         n = strnlen(cp, sz);
455                         if (n > 0)
456                                 printf("%s", cp);
457                 }
458                 cp += n + 1;
459                 sz -= n + 1;
460         }
461 }
462
463 tpacket(s, tp, n)
464         char *s;
465         struct tftphdr *tp;
466         int n;
467 {
468         static char *opcodes[] =
469            { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
470         register char *cp, *file;
471         u_short op = ntohs(tp->th_opcode);
472         char *index();
473
474         if (op < RRQ || op > OACK)
475                 printf("%s opcode=%x ", s, op);
476         else
477                 printf("%s %s ", s, opcodes[op]);
478         switch (op) {
479
480         case RRQ:
481         case WRQ:
482                 n -= 2;
483                 file = cp = tp->th_stuff;
484                 cp = index(cp, '\0');
485                 printf("<file=%s, mode=%s, opts: ", file, cp + 1);
486                 topts(index(cp + 1, '\000') + 1, n - strlen(file)
487                       - strlen(cp + 1) - 2);
488                 printf(">\n");
489                 break;
490
491         case DATA:
492                 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
493                 break;
494
495         case ACK:
496                 printf("<block=%d>\n", ntohs(tp->th_block));
497                 break;
498
499         case ERROR:
500                 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
501                 break;
502         case OACK:
503                 printf("<");
504                 topts(tp->th_stuff, n - 2);
505                 printf(">\n");
506                 break;
507         }
508 }
509
510 struct timeval tstart;
511 struct timeval tstop;
512 struct timezone zone;
513
514 startclock() {
515         gettimeofday(&tstart, &zone);
516 }
517
518 stopclock() {
519         gettimeofday(&tstop, &zone);
520 }
521
522 printstats(direction, amount)
523 char *direction;
524 unsigned long amount;
525 {
526         double delta;
527                         /* compute delta in 1/10's second units */
528         delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
529                 ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
530         delta = delta/10.;      /* back to seconds */
531         printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
532         if ((verbose) && (delta >= 0.1))
533                         printf(" [%.0f bits/sec]", (amount*8.)/delta);
534         putchar('\n');
535 }
536