Initial xloong code
[xloong.git] / pmon / netio / tftplib.c
blobeec7176d4075506b95ca1e931410c7d789302d85
1 /* $Id: tftplib.c,v 1.1.1.1 2006/09/14 01:59:08 root Exp $ */
3 /*
4 * Copyright (c) 2001-2002 Opsycon AB (www.opsycon.se / www.opsycon.com)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Opsycon AB, Sweden.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #undef _KERNEL
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
39 #include <sys/file.h>
40 #include <sys/time.h>
41 #include <sys/syslog.h>
42 #include <sys/endian.h>
44 #include <netinet/in.h>
45 #include <arpa/tftp.h>
48 #include <stdio.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <string.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <file.h>
55 #include <net/if.h>
57 #include "netio.h"
59 #include <pmon.h>
61 extern void log __P((int kind, const char *fmt, ...));
62 extern in_addr_t inet_addr __P((const char *));
64 #ifdef TFTPDBG
65 static int tftptrace;
66 #endif
68 static const char spinner[] = "-\\|/";
70 static const int tftperrmap[] = {
71 EIO, /* EUNDEF not defined */
72 ENOENT, /* ENOTFOUND file not found */
73 EACCES, /* EACCESS access violation */
74 ENOSPC, /* ENOSPACE disk full or allocation exceeded */
75 EIO, /* EBADOP illegal TFTP operation */
76 EINVAL, /* EBADID unknown transfer ID */
77 EEXIST, /* EEXISTS file already exists */
78 EACCES /* ENOUSER no such user */
81 #define TIMEOUT 2 /* secs between rexmts */
82 #define MAXREXMT 0x7fffffff /* no of rexmts */
83 #define PKTSIZE (SEGSIZE+4)
85 struct tftpfile {
86 struct sockaddr_in sin;
87 u_short block;
88 short flags;
89 short eof;
90 int sock;
91 int start;
92 int end;
93 int foffs;
94 char buf[PKTSIZE];
97 static int tftpopen (int, struct Url *, int, int);
98 static int tftpread (int, void *, int);
99 static int tftpwrite (int, const void *, int);
100 static off_t tftplseek (int, long, int);
101 static int tftpioctl (int, int, void *);
102 static int tftpclose (int);
104 static NetFileOps tftpops = {
105 "tftp",
106 tftpopen,
107 tftpread,
108 tftpwrite,
109 tftplseek,
110 tftpioctl,
111 tftpclose
114 static void init_netfs __P((void)) __attribute__ ((constructor));
116 static void
117 init_netfs()
120 * Install ram based file system.
122 netfs_init(&tftpops);
126 static int tftprrq (struct tftpfile *tfp, struct tftphdr *req, int size);
127 static int tftpwrq (struct tftpfile *tfp, struct tftphdr *req, int size);
128 static void tftpnxtblk (struct tftpfile *tfp);
129 static int makerequest (int request, const char *name, struct tftphdr *tp,
130 const char *mode);
131 #ifdef TFTPDBG
132 static void tpacket (char *s, struct tftphdr *tp, int n);
133 #endif
134 static int synchnet (int f);
136 extern char activeif_name[];
139 static void myifup()
141 #if 0
142 struct ifreq ifr;
143 printf(".");
144 bzero (&ifr, sizeof(ifr));
145 strncpy(ifr.ifr_name, activeif_name, IFNAMSIZ);
146 ifr.ifr_flags=IFF_UP;
147 ifioctl(0,SIOCSIFFLAGS,&ifr);
148 #endif
153 static int
154 tftpopen (int fd, struct Url *url, int flags, int perms)
156 struct hostent *hp;
157 struct servent *sp;
158 const char *mode;
159 char *host;
160 char hbuf[MAXHOSTNAMELEN];
161 char reqbuf[PKTSIZE];
162 struct tftpfile *tfp;
163 struct tftphdr *rp;
164 int oflags, size;
165 NetFile *nfp = (NetFile *)_file[fd].data;
167 oflags = flags & O_ACCMODE;
168 if (oflags != O_RDONLY && oflags != O_WRONLY) {
169 errno = EACCES;
170 return -1;
173 sp = getservbyname("tftp", "udp");
174 if (!sp) {
175 errno = EPROTONOSUPPORT;
176 return -1;
179 #ifdef TFTPDBG
180 tftptrace = getenv ("tftptrace") ? 1 : 0;
181 #endif
183 if (strlen(url->hostname) != 0) {
184 host = url->hostname;
185 } else {
186 host = getenv ("tftphost");
187 if (!host) {
188 log (LOG_INFO, "tftp: missing/bad host name: %s\n", url->filename);
189 errno = EDESTADDRREQ;
190 return -1;
194 tfp = (struct tftpfile *) malloc (sizeof (struct tftpfile));
195 if (!tfp) {
196 errno = ENOBUFS;
197 return -1;
199 bzero (tfp, sizeof (struct tftpfile));
200 nfp->data = (void *)tfp;
202 tfp->sock = socket(AF_INET, SOCK_DGRAM, 0);
203 if (tfp->sock < 0)
204 goto error;
206 tfp->sin.sin_family = AF_INET;
207 if (bind(tfp->sock, (struct sockaddr *)&tfp->sin, sizeof (tfp->sin)) < 0)
208 goto error;
210 hp = gethostbyname(host);
211 if (hp) {
212 tfp->sin.sin_family = hp->h_addrtype;
213 bcopy(hp->h_addr, (void *)&tfp->sin.sin_addr, hp->h_length);
214 strncpy (hbuf, hp->h_name, sizeof(hbuf) - 1);
215 hbuf[sizeof(hbuf)-1] = '\0';
216 host = hbuf;
217 } else {
218 tfp->sin.sin_family = AF_INET;
219 tfp->sin.sin_addr.s_addr = inet_addr (host);
220 if (tfp->sin.sin_addr.s_addr == -1) {
221 log (LOG_INFO, "tftp: bad internet address: %s\n", host);
222 errno = EADDRNOTAVAIL;
223 goto error;
227 tfp->sin.sin_port = sp->s_port;
229 rp = (struct tftphdr *)reqbuf;
230 tfp->flags = flags;
231 mode = "octet";
234 if (oflags == O_RDONLY) {
235 tfp->block = 1;
236 size = makerequest(RRQ, url->filename, rp, mode);
237 size = tftprrq (tfp, rp, size);
238 tfp->end = tfp->start + size;
239 } else {
240 tfp->block = 0;
241 size = makerequest(WRQ, url->filename, rp, mode);
242 size = tftpwrq (tfp, rp, size - 4);
245 if (size >= 0)
246 return 0;
249 error:
250 if (tfp->sock >= 0)
251 close (tfp->sock);
252 free (tfp);
253 return -1;
256 static int
257 tftpread (fd, buf, nread)
258 int fd;
259 void *buf;
260 int nread;
262 NetFile *nfp;
263 struct tftpfile *tfp;
264 struct tftphdr *rp;
265 int nb, n;
267 nfp = (NetFile *)_file[fd].data;
268 tfp = (struct tftpfile *)nfp->data;
270 if ((tfp->flags & O_ACCMODE) != O_RDONLY) {
271 errno = EPERM;
272 return (-1);
275 rp = (struct tftphdr *) tfp->buf;
277 /* continue while more bytes wanted, and more available */
278 for (nb = nread; nb != 0 && tfp->start < tfp->end; ) {
280 if (tfp->foffs >= tfp->start && tfp->foffs < tfp->end) {
281 /* got some data that's in range */
282 n = tfp->end - tfp->foffs;
283 if (n > nb) n = nb;
284 bcopy (&rp->th_data[tfp->foffs - tfp->start], buf, n);
285 tfp->foffs += n;
286 buf += n;
287 nb -= n;
290 if (tfp->foffs >= tfp->end) {
291 /* buffer is empty, ack last packet and refill */
292 struct tftphdr ack;
293 ack.th_opcode = htons((u_short)ACK);
294 ack.th_block = htons((u_short)tfp->block);
295 tftpnxtblk (tfp);
296 n = tftprrq (tfp, &ack, 4);
297 if (n < 0)
298 return (-1);
299 tfp->start = tfp->end;
300 tfp->end = tfp->start + n;
304 return nread - nb;
307 static int
308 tftpwrite (fd, buf, nwrite)
309 int fd;
310 const void *buf;
311 int nwrite;
313 NetFile *nfp;
314 struct tftpfile *tfp;
315 struct tftphdr *dp;
316 int nb, n;
318 nfp = (NetFile *)_file[fd].data;
319 tfp = (struct tftpfile *)nfp->data;
321 if ((tfp->flags & O_ACCMODE) != O_WRONLY) {
322 errno = EPERM;
323 return (-1);
326 dp = (struct tftphdr *)tfp->buf;
327 for (nb = nwrite; nb != 0; ) {
328 n = SEGSIZE - tfp->end;
329 if (n > nb) n = nb;
330 bcopy (buf, &dp->th_data[tfp->end], n);
331 tfp->end += n;
332 tfp->foffs += n;
333 buf += n;
334 nb -= n;
336 if (tfp->end == SEGSIZE) {
337 /* buffer is full, send it */
338 tftpnxtblk (tfp);
339 dp->th_opcode = htons((u_short)DATA);
340 dp->th_block = htons((u_short)tfp->block);
341 n = tftpwrq (tfp, dp, tfp->end);
342 if (n < 0)
343 return (-1);
344 tfp->end = 0;
347 return nwrite - nb;
350 static off_t
351 tftplseek (fd, offs, how)
352 int fd;
353 long offs;
354 int how;
356 NetFile *nfp;
357 struct tftpfile *tfp;
358 long noffs;
360 nfp = (NetFile *)_file[fd].data;
361 tfp = (struct tftpfile *)nfp->data;
363 switch (how) {
364 case SEEK_SET:
365 noffs = offs;
366 break;
367 case SEEK_CUR:
368 noffs = tfp->foffs + offs;
369 break;
370 case SEEK_END:
371 default:
372 return -1;
375 if ((tfp->flags & O_ACCMODE) == O_WRONLY) {
376 if (noffs != tfp->foffs) {
377 errno = ESPIPE;
378 return (-1);
380 } else {
381 if (noffs < tfp->start) {
382 errno = ESPIPE;
383 return (-1);
385 tfp->foffs = noffs;
388 return (tfp->foffs);
392 static int
393 tftpioctl (fd, op, argp)
394 int fd;
395 int op;
396 void *argp;
398 errno = ENOTTY;
399 return -1;
403 static int
404 tftpclose (fd)
405 int fd;
407 NetFile *nfp;
408 struct tftpfile *tfp;
409 struct tftphdr *tp;
411 nfp = (NetFile *)_file[fd].data;
412 tfp = (struct tftpfile *)nfp->data;
414 tp = (struct tftphdr *) tfp->buf;
415 if ((tfp->flags & O_ACCMODE) == O_WRONLY) {
416 int n = 0;
417 /* flush last block */
418 tftpnxtblk (tfp);
419 tp->th_opcode = htons((u_short)DATA);
420 tp->th_block = htons((u_short)tfp->block);
421 n = tftpwrq (tfp, tp, tfp->end);
422 if (n >= 0 && tfp->end == SEGSIZE) {
423 /* last block == SEGSIZE, so send empty one to terminate */
424 tftpnxtblk (tfp);
425 tp->th_opcode = htons((u_short)DATA);
426 tp->th_block = htons((u_short)tfp->block);
427 (void) tftpwrq (tfp, tp, 0);
429 } else {
430 if (tfp->foffs < tfp->end || !tfp->eof) {
431 const char *msg;
432 int length;
434 /* didn't reach eof, so send a nak to terminate */
435 tp->th_opcode = htons((u_short)ERROR);
436 tp->th_code = htons((u_short)EUNDEF);
437 msg = "file closed";
438 strcpy(tp->th_msg, msg);
439 length = strlen(msg) + 4;
440 #ifdef TFTPDBG
441 if (tftptrace)
442 tpacket("sent", tp, length);
443 #endif
444 if (sendto(tfp->sock, tp, length, 0, (struct sockaddr *)&tfp->sin,
445 sizeof (tfp->sin)) != length)
446 perror("sendto(eof)");
451 close (tfp->sock);
452 free (tfp);
453 return (0);
456 static void
457 tftpnxtblk (tfp)
458 struct tftpfile *tfp;
460 tfp->block++;
461 if (tfp->flags & O_NONBLOCK)
462 dotik (20000, 0);
466 static int
467 tftprrq (tfp, req, size)
468 struct tftpfile *tfp;
469 struct tftphdr *req;
470 int size;
472 struct tftphdr *rp;
473 struct sockaddr_in from;
474 fd_set ifds;
475 int fromlen, n;
476 struct timeval timo;
477 int rexmt = 0;
479 while (1) {
480 #ifdef TFTPDBG
481 if (tftptrace)
482 tpacket("sent", req, size);
483 #endif
484 if (sendto(tfp->sock, req, size, 0, (struct sockaddr *)&tfp->sin,
485 sizeof (tfp->sin)) != size) {
486 perror("tftp: sendto");
487 return (-1);
490 if (tfp->eof)
491 /* reached eof, no more to read */
492 return 0;
494 FD_ZERO(&ifds);
495 FD_SET(tfp->sock, &ifds);
496 timo.tv_sec = TIMEOUT; timo.tv_usec = 0;
497 switch (select (tfp->sock + 1, &ifds, 0, 0, &timo)) {
498 case -1:
499 perror("tftp: select");
500 return (-1);
501 case 0:
502 if(rexmt==(MAXREXMT>>1))myifup();
503 if (++rexmt > MAXREXMT) {
504 errno = ETIMEDOUT;
505 return (-1);
507 log (LOG_INFO, "tftp: timeout, retry %d\n", rexmt);
508 continue;
511 fromlen = sizeof (from);
512 rp = (struct tftphdr *) tfp->buf;
513 n = recvfrom(tfp->sock, rp, PKTSIZE, 0,
514 (struct sockaddr *)&from, &fromlen);
515 if (n < 0) {
516 perror("tftp: recvfrom");
517 return (-1);
519 #ifdef TFTPDBG
520 if (tftptrace)
521 tpacket("received", rp, n);
522 #endif
524 if (tfp->block <= 1)
525 tfp->sin.sin_port = from.sin_port;
526 else if (from.sin_port != tfp->sin.sin_port)
527 continue;
528 /* should verify client address completely? */
530 rp->th_opcode = ntohs(rp->th_opcode);
531 rp->th_block = ntohs(rp->th_block);
532 if (rp->th_opcode == ERROR) {
533 if (rp->th_msg[0])
534 log(LOG_INFO, "tftp: %s\n", rp->th_msg);
535 errno = tftperrmap[rp->th_code & 7];
536 return (-1);
539 if (rp->th_opcode == DATA) {
540 int j;
541 if (rp->th_block == tfp->block) {
542 /* got the packet */
543 n -= 4;
544 if (n < SEGSIZE)
545 tfp->eof = 1;
546 return (n);
549 /* On an error, try to synchronize
550 * both sides.
552 j = synchnet(tfp->sock);
553 if (j)
554 log (LOG_INFO, "tftp: discarded %d packets\n", j);
556 if (rp->th_block != tfp->block - 1)
557 return (-1);
562 static int
563 tftpwrq (tfp, req, size)
564 struct tftpfile *tfp;
565 struct tftphdr *req;
566 int size;
568 char ackbuf[PKTSIZE];
569 struct tftphdr *rp;
570 struct sockaddr_in from;
571 fd_set ifds;
572 int fromlen, n;
573 struct timeval timo;
574 int rexmt = 0;
576 while (1) {
577 #ifdef TFTPDBG
578 if (tftptrace)
579 tpacket("sent", req, size);
580 #endif
581 if (sendto(tfp->sock, req, size+4, 0, (struct sockaddr *)&tfp->sin,
582 sizeof (tfp->sin)) != size+4) {
583 perror("tftp: sendto");
584 return (-1);
587 FD_ZERO(&ifds);
588 FD_SET(tfp->sock, &ifds);
589 timo.tv_sec = TIMEOUT; timo.tv_usec = 0;
590 switch (select (tfp->sock + 1, &ifds, 0, 0, &timo)) {
591 case -1:
592 perror("tftp: select");
593 return (-1);
594 case 0:
595 if(rexmt==(MAXREXMT>>1))myifup();
596 if (++rexmt > MAXREXMT) {
597 errno = ETIMEDOUT;
598 return (-1);
600 log (LOG_INFO, "tftp: timeout, retry %d\n", rexmt);
601 continue;
604 fromlen = sizeof (from);
605 rp = (struct tftphdr *) ackbuf;
606 n = recvfrom(tfp->sock, rp, PKTSIZE, 0,
607 (struct sockaddr *)&from, &fromlen);
608 if (n < 0) {
609 perror("tftp: recvfrom");
610 return (-1);
612 #ifdef TFTPDBG
613 if (tftptrace)
614 tpacket("received", rp, n);
615 #endif
617 if (tfp->block == 0)
618 tfp->sin.sin_port = from.sin_port;
619 else if (from.sin_port != tfp->sin.sin_port)
620 continue;
621 /* should verify client address completely? */
623 rp->th_opcode = ntohs(rp->th_opcode);
624 rp->th_block = ntohs(rp->th_block);
625 if (rp->th_opcode == ERROR) {
626 if (rp->th_msg[0])
627 log(LOG_INFO, "tftp: %s\n", rp->th_msg);
628 errno = tftperrmap[rp->th_code & 7];
629 return (-1);
632 if (rp->th_opcode == ACK) {
633 int j;
634 if (rp->th_block == tfp->block)
635 /* acknowledged packet */
636 return (size);
638 /* On an error, try to synchronize
639 * both sides.
641 j = synchnet(tfp->sock);
642 if (j)
643 log (LOG_INFO, "tftp: discarded %d packets\n", j);
645 if (rp->th_block != tfp->block - 1)
646 return (-1);
651 static int
652 makerequest(request, name, tp, mode)
653 int request;
654 const char *name, *mode;
655 struct tftphdr *tp;
657 register char *cp;
659 tp->th_opcode = htons((u_short)request);
660 cp = tp->th_stuff;
661 strcpy(cp, name);
662 cp += strlen(name);
663 *cp++ = '\0';
664 strcpy(cp, mode);
665 cp += strlen(mode);
666 *cp++ = '\0';
667 return (cp - (char *)tp);
671 #ifdef TFTPDBG
672 static void
673 tpacket(s, tp, n)
674 char *s;
675 struct tftphdr *tp;
676 int n;
678 static const char * const opcodes[] =
679 { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
680 register char *cp, *file;
681 u_short op = ntohs(tp->th_opcode);
683 if (op < RRQ || op > ERROR)
684 printf("%s opcode=%x ", s, op);
685 else
686 printf("%s %s ", s, opcodes[op]);
687 switch (op) {
689 case RRQ:
690 case WRQ:
691 n -= 2;
692 file = cp = tp->th_stuff;
693 cp += strlen(cp);
694 printf("<file=%s, mode=%s>\n", file, cp + 1);
695 break;
697 case DATA:
698 printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
699 break;
701 case ACK:
702 printf("<block=%d>\n", ntohs(tp->th_block));
703 break;
705 case ERROR:
706 printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
707 break;
710 #endif
713 /* When an error has occurred, it is possible that the two sides
714 * are out of synch. Ie: that what I think is the other side's
715 * response to packet N is really their response to packet N-1.
717 * So, to try to prevent that, we flush all the input queued up
718 * for us on the network connection on our host.
720 * We return the number of packets we flushed (mostly for reporting
721 * when trace is active).
724 static int
725 synchnet(f)
726 int f; /* socket to flush */
728 int i, j = 0;
729 char rbuf[PKTSIZE];
730 struct sockaddr_in from;
731 int fromlen;
732 while (1) {
733 (void) ioctl(f, FIONREAD, &i);
734 if (i) {
735 j++;
736 fromlen = sizeof from;
737 (void) recvfrom(f, rbuf, sizeof (rbuf), 0,
738 (struct sockaddr *)&from, &fromlen);
739 } else {
740 return(j);