Hi all, I am using perl and Perl/Tk do develop a small software installer which uses DHCP,FTP and TFTP, it works fine but a great enhencement of this software could be the implementation of a progress bar with 3 differents colors to show the progression of the the 3 services used(DHCP,FTP,TFTP),How can I do this? I am under windows 98 and use cygwin environemnt (a port of GNu tools over Windows).U can see a attachment the tftp that i use.
/* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */ /* * TFTP User Program -- Protocol Machines */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <sys/types.h> #include <sys/socket.h> #ifdef TIME_WITH_SYS_TIME # include <sys/time.h> # include <time.h> #else # ifdef HAVE_SYS_TIME_H # include <sys/time.h> # else # include <time.h> # endif #endif #include <netinet/in.h> #include <arpa/tftp.h> #include <errno.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include "extern.h" #include "tftpsubs.h" #ifndef HAVE_ERRNO_DECL extern int errno; #endif #ifndef HAVE_STRERROR_DECL extern const char *strerror __P ((int)); #endif extern struct sockaddr_in peeraddr; /* filled in by main */ extern int f; /* the opened socket */ extern int trace; extern int verbose; extern int rexmtval; extern int maxtimeout; #define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; jmp_buf timeoutbuf; static void nak __P((int)); static int makerequest __P((int, const char *, struct tftphdr *, const char *)); static void printstats __P((const char *, unsigned long)); static void startclock __P((void)); static void stopclock __P((void)); static void timer __P((int)); static void tpacket __P((const char *, struct tftphdr *, int)); /* * Send the requested file. */ void sendfile(fd, name, mode) int fd; char *name; char *mode; { register struct tftphdr *ap; /* data and ack packets */ struct tftphdr *r_init(), *dp; register int n; volatile int block, size, convert; volatile unsigned long amount; struct sockaddr_in from; int fromlen; FILE *file; startclock(); /* start stat's clock */ dp = r_init(); /* reset fillbuf/read-ahead code */ ap = (struct tftphdr *)ackbuf; #ifdef __CYGWIN__ file = fdopen(fd, "rb"); #else file = fdopen(fd, "r"); #endif convert = !strcmp(mode, "netascii"); block = 0; amount = 0; signal(SIGALRM, timer); do { if (block == 0) size = makerequest(WRQ, name, dp, mode) - 4; else { /* size = read(fd, dp->th_data, SEGSIZE); */ size = readit(file, &dp, convert); if (size < 0) { nak(errno + 100); break; } dp->th_opcode = htons((u_short)DATA); dp->th_block = htons((u_short)block); } timeout = 0; (void) setjmp(timeoutbuf); send_data: if (trace) tpacket("sent", dp, size + 4); n = sendto(f, (const char *)dp, size + 4, 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)); if (n != size + 4) { perror("tftp: sendto"); goto abort; } read_ahead(file, convert); for ( ; ; ) { alarm(rexmtval); do { fromlen = sizeof(from); n = recvfrom(f, ackbuf, sizeof(ackbuf), 0, (struct sockaddr *)&from, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { perror("tftp: recvfrom"); goto abort; } peeraddr.sin_port = from.sin_port; /* added */ if (trace) tpacket("received", ap, n); /* should verify packet came from server */ ap->th_opcode = ntohs(ap->th_opcode); ap->th_block = ntohs(ap->th_block); if (ap->th_opcode == ERROR) { printf("Error code %d: %s\n", ap->th_code, ap->th_msg); goto abort; } if (ap->th_opcode == ACK) { int j; if (ap->th_block == block) { break; } /* On an error, try to synchronize * both sides. */ j = synchnet(f); if (j && trace) { printf("discarded %d packets\n", j); } if (ap->th_block == (block-1)) { goto send_data; } } } if (block > 0) amount += size; block++; } while (size == SEGSIZE || block == 1); abort: fclose(file); stopclock(); if (amount > 0) printstats("Sent", amount); } /* * Receive a file. */ void recvfile(fd, name, mode) int fd; char *name; char *mode; { register struct tftphdr *ap; struct tftphdr *dp, *w_init(); register int n; volatile int block, size, firsttrip; volatile unsigned long amount; struct sockaddr_in from; int fromlen; FILE *file; volatile int convert; /* true if converting crlf -> lf */ startclock(); dp = w_init(); ap = (struct tftphdr *)ackbuf; #ifdef __CYGWIN__ file = fdopen(fd, "wb"); #else file = fdopen(fd, "w"); #endif convert = !strcmp(mode, "netascii"); block = 1; firsttrip = 1; amount = 0; signal(SIGALRM, timer); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); firsttrip = 0; } else { ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)(block)); size = 4; block++; } timeout = 0; (void) setjmp(timeoutbuf); send_ack: if (trace) tpacket("sent", ap, size); if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)) != size) { alarm(0); perror("tftp: sendto"); goto abort; } write_behind(file, convert); for ( ; ; ) { alarm(rexmtval); do { fromlen = sizeof(from); n = recvfrom(f, (char *)dp, PKTSIZE, 0, (struct sockaddr *)&from, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { perror("tftp: recvfrom"); goto abort; } peeraddr.sin_port = from.sin_port; /* added */ if (trace) tpacket("received", dp, n); /* should verify client address */ dp->th_opcode = ntohs(dp->th_opcode); dp->th_block = ntohs(dp->th_block); if (dp->th_opcode == ERROR) { printf("Error code %d: %s\n", dp->th_code, dp->th_msg); goto abort; } if (dp->th_opcode == DATA) { int j; if (dp->th_block == block) { break; /* have next packet */ } /* On an error, try to synchronize * both sides. */ j = synchnet(f); if (j && trace) { printf("discarded %d packets\n", j); } if (dp->th_block == (block-1)) { goto send_ack; /* resend ack */ } } } /* size = write(fd, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, convert); if (size < 0) { nak(errno + 100); break; } amount += size; } while (size == SEGSIZE); abort: /* ok to ack, since user */ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */ ap->th_block = htons((u_short)block); (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); if (amount > 0) printstats("Received", amount); } static int makerequest(request, name, tp, mode) int request; const char *name; struct tftphdr *tp; const char *mode; { register char *cp; tp->th_opcode = htons((u_short)request); cp = tp->th_stuff; strcpy(cp, name); cp += strlen(name); *cp++ = '\0'; strcpy(cp, mode); cp += strlen(mode); *cp++ = '\0'; return (cp - (char *)tp); } struct errmsg { int e_code; const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { -1, 0 } }; /* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ static void nak(error) int error; { register struct errmsg *pe; register struct tftphdr *tp; int length; tp = (struct tftphdr *)ackbuf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg) + 4; if (trace) tpacket("sent", tp, length); if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)) != length) perror("nak"); } static void tpacket(s, tp, n) const char *s; struct tftphdr *tp; int n; { static char *opcodes[] = { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" }; register char *cp, *file; u_short op = ntohs(tp->th_opcode); if (op < RRQ || op > ERROR) printf("%s opcode=%x ", s, op); else printf("%s %s ", s, opcodes[op]); switch (op) { case RRQ: case WRQ: n -= 2; file = cp = tp->th_stuff; cp = strchr (cp, '\0'); printf("<file=%s, mode=%s>\n", file, cp + 1); break; case DATA: printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4); break; case ACK: printf("<block=%d>\n", ntohs(tp->th_block)); break; case ERROR: printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg); break; } } struct timeval tstart; struct timeval tstop; static void startclock() { (void)gettimeofday(&tstart, NULL); } static void stopclock() { (void)gettimeofday(&tstop, NULL); } static void printstats(direction, amount) const char *direction; unsigned long amount; { double delta; /* compute delta in 1/10's second units */ delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) - ((tstart.tv_sec*10.)+(tstart.tv_usec/100000)); delta = delta/10.; /* back to seconds */ printf("%s %d bytes in %.1f seconds", direction, amount, delta); if (verbose) printf(" [%.0f bits/sec]", (amount*8.)/delta); putchar('\n'); } static void timer(sig) int sig; { timeout += rexmtval; if (timeout >= maxtimeout) { printf("Transfer timed out.\n"); longjmp(toplevel, -1); } longjmp(timeoutbuf, 1); }
/* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ /* * Trivial file transfer protocol server. * * This version includes many modifications by Jim Guyton * <guyton@rand-unix>. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <sys/param.h> #include <sys/ioctl.h> #ifdef HAVE_SYS_FILIO_H #include <sys/filio.h> #endif #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/tftp.h> #include <arpa/inet.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <netdb.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <unistd.h> #include "tftpsubs.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef HAVE_STRERROR_DECL extern const char *strerror __P ((int)); #endif #define TIMEOUT 5 #ifndef LOG_FTP #define LOG_FTP LOG_DAEMON /* Use generic facility. */ #endif int peer; int rexmtval = TIMEOUT; int maxtimeout = 5*TIMEOUT; #define PKTSIZE SEGSIZE+4 char buf[PKTSIZE]; char ackbuf[PKTSIZE]; struct sockaddr_in from; int fromlen; void tftp __P((struct tftphdr *, int)); /* * Null-terminated directory prefix list for absolute pathname requests and * search list for relative pathname requests. * * MAXDIRS should be at least as large as the number of arguments that * inetd allows (currently 20). */ #define MAXDIRS 20 static struct dirlist { char *name; int len; } dirs[MAXDIRS+1]; static int suppress_naks; static int logging; static const char *errtomsg __P((int)); static void nak __P((int)); static const char *verifyhost __P((struct sockaddr_in *)); int main(argc, argv) int argc; char *argv[]; { register struct tftphdr *tp; register int n; int ch, on; struct sockaddr_in sin; openlog("tftpd", LOG_PID, LOG_FTP); while ((ch = getopt(argc, argv, "ln")) != EOF) { switch (ch) { case 'l': logging = 1; break; case 'n': suppress_naks = 1; break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } } if (optind < argc) { struct dirlist *dirp; /* Get list of directory prefixes. Skip relative pathnames. */ for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; optind++) { if (argv[optind][0] == '/') { dirp->name = argv[optind]; dirp->len = strlen(dirp->name); dirp++; } } } on = 1; if (ioctl(0, FIONBIO, &on) < 0) { syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); exit(1); } fromlen = sizeof (from); n = recvfrom(0, buf, sizeof (buf), 0, (struct sockaddr *)&from, &fromlen); if (n < 0) { syslog(LOG_ERR, "recvfrom: %m\n"); exit(1); } /* * Now that we have read the message out of the UDP * socket, we fork and exit. Thus, inetd will go back * to listening to the tftp port, and the next request * to come in will start up a new instance of tftpd. * * We do this so that inetd can run tftpd in "wait" mode. * The problem with tftpd running in "nowait" mode is that * inetd may get one or more successful "selects" on the * tftp port before we do our receive, so more than one * instance of tftpd may be started up. Worse, if tftpd * break before doing the above "recvfrom", inetd would * spawn endless instances, clogging the system. */ { int pid; int i, j; for (i = 1; i < 20; i++) { pid = fork(); if (pid < 0) { sleep(i); /* * flush out to most recently sent request. * * This may drop some request, but those * will be resent by the clients when * they timeout. The positive effect of * this flush is to (try to) prevent more * than one tftpd being started up to service * a single request from a single client. */ j = sizeof from; i = recvfrom(0, buf, sizeof (buf), 0, (struct sockaddr *)&from, &j); if (i > 0) { n = i; fromlen = j; } } else { break; } } if (pid < 0) { syslog(LOG_ERR, "fork: %m\n"); exit(1); } else if (pid != 0) { exit(0); } } from.sin_family = AF_INET; alarm(0); close(0); close(1); peer = socket(AF_INET, SOCK_DGRAM, 0); if (peer < 0) { syslog(LOG_ERR, "socket: %m\n"); exit(1); } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { syslog(LOG_ERR, "bind: %m\n"); exit(1); } if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { syslog(LOG_ERR, "connect: %m\n"); exit(1); } tp = (struct tftphdr *)buf; tp->th_opcode = ntohs(tp->th_opcode); if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) tftp(tp, n); exit(1); } struct formats; int validate_access __P((char **, int)); void sendfile __P((struct formats *)); void recvfile __P((struct formats *)); struct formats { char *f_mode; int (*f_validate) __P((char **, int)); void (*f_send) __P((struct formats *)); void (*f_recv) __P((struct formats *)); int f_convert; } formats[] = { { "netascii", validate_access, sendfile, recvfile, 1 }, { "octet", validate_access, sendfile, recvfile, 0 }, #ifdef notdef { "mail", validate_user, sendmail, recvmail, 1 }, #endif { 0 } }; /* * Handle initial connection protocol. */ void tftp(tp, size) struct tftphdr *tp; int size; { register char *cp; int first = 1, ecode; register struct formats *pf; char *filename, *mode; filename = cp = tp->th_stuff; again: while (cp < buf + size) { if (*cp == '\0') break; cp++; } if (*cp != '\0') { nak(EBADOP); exit(1); } if (first) { mode = ++cp; first = 0; goto again; } for (cp = mode; *cp; cp++) if (isupper(*cp)) *cp = tolower(*cp); for (pf = formats; pf->f_mode; pf++) if (strcmp(pf->f_mode, mode) == 0) break; if (pf->f_mode == 0) { nak(EBADOP); exit(1); } ecode = (*pf->f_validate)(&filename, tp->th_opcode); if (logging) { syslog(LOG_INFO, "%s: %s request for %s: %s", verifyhost(&from), tp->th_opcode == WRQ ? "write" : "read", filename, errtomsg(ecode)); } if (ecode) { /* * Avoid storms of naks to a RRQ broadcast for a relative * bootfile pathname from a diskless Sun. */ if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) exit(0); nak(ecode); exit(1); } if (tp->th_opcode == WRQ) (*pf->f_recv)(pf); else (*pf->f_send)(pf); exit(0); } FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require * file to exist and be publicly * readable/writable. * If we were invoked with arguments * from inetd then the file must also be * in one of the given directory prefixes. * Note also, full path name must be * given as we have no login directory. */ int validate_access(filep, mode) char **filep; int mode; { struct stat stbuf; int fd; struct dirlist *dirp; static char *pathname = 0; char *filename = *filep; /* * Prevent tricksters from getting around the directory restrictions */ if (strstr(filename, "/../")) return (EACCESS); if (*filename == '/') { /* * Allow the request if it's in one of the approved locations. * Special case: check the null prefix ("/") by looking * for length = 1 and relying on the arg. processing that * it's a /. */ for (dirp = dirs; dirp->name != NULL; dirp++) { if (dirp->len == 1 || (!strncmp(filename, dirp->name, dirp->len) && filename[dirp->len] == '/')) break; } /* If directory list is empty, allow access to any file */ if (dirp->name == NULL && dirp != dirs) return (EACCESS); if (stat(filename, &stbuf) < 0) return (errno == ENOENT ? ENOTFOUND : EACCESS); if ((stbuf.st_mode & S_IFMT) != S_IFREG) return (ENOTFOUND); if (mode == RRQ) { if ((stbuf.st_mode & S_IROTH) == 0) return (EACCESS); } else { if ((stbuf.st_mode & S_IWOTH) == 0) return (EACCESS); } } else { int err; /* * Relative file name: search the approved locations for it. * Don't allow write requests or ones that avoid directory * restrictions. */ if (mode != RRQ || !strncmp(filename, "../", 3)) return (EACCESS); /* * If the file exists in one of the directories and isn't * readable, continue looking. However, change the error code * to give an indication that the file exists. */ err = ENOTFOUND; for (dirp = dirs; dirp->name != NULL; dirp++) { if (pathname) free (pathname); pathname = malloc (strlen (dirp->name) + 1 + strlen (filename) + 1); if (! pathname) return ENOMEM; sprintf(pathname, "%s/%s", dirp->name, filename); if (stat(pathname, &stbuf) == 0 && (stbuf.st_mode & S_IFMT) == S_IFREG) { if ((stbuf.st_mode & S_IROTH) != 0) { break; } err = EACCESS; } } if (dirp->name == NULL) return (err); *filep = filename = pathname; } fd = open(filename, (mode == RRQ ? O_RDONLY : (O_WRONLY | O_TRUNC)) | O_BINARY); if (fd < 0) return (errno + 100); #ifdef __CYGWIN__ file = fdopen(fd, (mode == RRQ)? "rb":"wb"); #else file = fdopen(fd, (mode == RRQ)? "r":"w"); #endif if (file == NULL) { return errno+100; } return (0); } int timeout; jmp_buf timeoutbuf; void timer(sig) int sig; { timeout += rexmtval; if (timeout >= maxtimeout) exit(1); longjmp(timeoutbuf, 1); } /* * Send the requested file. */ void sendfile(pf) struct formats *pf; { struct tftphdr *dp, *r_init(); register struct tftphdr *ap; /* ack packet */ register int size, n; volatile int block; signal(SIGALRM, timer); dp = r_init(); ap = (struct tftphdr *)ackbuf; block = 1; do { size = readit(file, &dp, pf->f_convert); if (size < 0) { nak(errno + 100); goto abort; } dp->th_opcode = htons((u_short)DATA); dp->th_block = htons((u_short)block); timeout = 0; (void)setjmp(timeoutbuf); send_data: if (send(peer, (const char *)dp, size + 4, 0) != size + 4) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } read_ahead(file, pf->f_convert); for ( ; ; ) { alarm(rexmtval); /* read the ack */ n = recv(peer, ackbuf, sizeof (ackbuf), 0); alarm(0); if (n < 0) { syslog(LOG_ERR, "tftpd: read: %m\n"); goto abort; } ap->th_opcode = ntohs((u_short)ap->th_opcode); ap->th_block = ntohs((u_short)ap->th_block); if (ap->th_opcode == ERROR) goto abort; if (ap->th_opcode == ACK) { if (ap->th_block == block) break; /* Re-synchronize with the other side */ (void) synchnet(peer); if (ap->th_block == (block -1)) goto send_data; } } block++; } while (size == SEGSIZE); abort: (void) fclose(file); } void justquit(sig) int sig; { exit(0); } /* * Receive a file. */ void recvfile(pf) struct formats *pf; { struct tftphdr *dp, *w_init(); register struct tftphdr *ap; /* ack buffer */ register int n, size; volatile int block; signal(SIGALRM, timer); dp = w_init(); ap = (struct tftphdr *)ackbuf; block = 0; do { timeout = 0; ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)block); block++; (void) setjmp(timeoutbuf); send_ack: if (send(peer, ackbuf, 4, 0) != 4) { syslog(LOG_ERR, "tftpd: write: %m\n"); goto abort; } write_behind(file, pf->f_convert); for ( ; ; ) { alarm(rexmtval); n = recv(peer, (char *)dp, PKTSIZE, 0); alarm(0); if (n < 0) { /* really? */ syslog(LOG_ERR, "tftpd: read: %m\n"); goto abort; } dp->th_opcode = ntohs((u_short)dp->th_opcode); dp->th_block = ntohs((u_short)dp->th_block); if (dp->th_opcode == ERROR) goto abort; if (dp->th_opcode == DATA) { if (dp->th_block == block) { break; /* normal */ } /* Re-synchronize with the other side */ (void) synchnet(peer); if (dp->th_block == (block-1)) goto send_ack; /* rexmit */ } } /* size = write(file, dp->th_data, n - 4); */ size = writeit(file, &dp, n - 4, pf->f_convert); if (size != (n-4)) { /* ahem */ if (size < 0) nak(errno + 100); else nak(ENOSPACE); goto abort; } } while (size == SEGSIZE); write_behind(file, pf->f_convert); (void) fclose(file); /* close data file */ ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ ap->th_block = htons((u_short)(block)); (void) send(peer, ackbuf, 4, 0); signal(SIGALRM, justquit); /* just quit on timeout */ alarm(rexmtval); n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ alarm(0); if (n >= 4 && /* if read some data */ dp->th_opcode == DATA && /* and got a data block */ block == dp->th_block) { /* then my last ack was lost */ (void) send(peer, ackbuf, 4, 0); /* resend final ack */ } abort: return; } struct errmsg { int e_code; const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { -1, 0 } }; static const char * errtomsg(error) int error; { static char buf[20]; register struct errmsg *pe; if (error == 0) return "success"; for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return pe->e_msg; sprintf(buf, "error %d", error); return buf; } /* * Send a nak packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ static void nak(error) int error; { register struct tftphdr *tp; int length; register struct errmsg *pe; tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; /* set 'undef' errorcode */ } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg); tp->th_msg[length] = '\0'; length += 5; if (send(peer, buf, length, 0) != length) syslog(LOG_ERR, "nak: %m\n"); } static const char * verifyhost(fromp) struct sockaddr_in *fromp; { struct hostent *hp; hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), fromp->sin_family); if (hp) return hp->h_name; else return inet_ntoa(fromp->sin_addr); }