netkit-tftp-0.17/ 40700 144 144 0 7141142035 13104 5ustar dhollandpeoplenetkit-tftp-0.17/tftp/ 40700 144 144 0 7141142035 14061 5ustar dhollandpeoplenetkit-tftp-0.17/tftp/.cvsignore100644 144 144 5 6774167006 16123 0ustar dhollandpeopletftp netkit-tftp-0.17/tftp/Makefile100644 144 144 505 7024761722 15622 0ustar dhollandpeopleall: tftp include ../MCONFIG include ../MRULES OBJS = tftp.o main.o tftpsubs.o tftp: $(OBJS) $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ $(OBJS): tftpsubs.h tftp.o: ../version.h install: tftp install -s -m$(BINMODE) tftp $(INSTALLROOT)$(BINDIR) install -m$(MANMODE) tftp.1 $(INSTALLROOT)$(MANDIR)/man1 clean: rm -f *.o tftp netkit-tftp-0.17/tftp/main.c100644 144 144 36352 7136370265 15325 0ustar dhollandpeople/* * Copyright (c) 1983 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. */ char copyright[] = "@(#) Copyright (c) 1983 Regents of the University of California.\n" "All rights reserved.\n"; /* * From: @(#)main.c 5.10 (Berkeley) 3/1/91 */ char main_rcsid[] = "$Id: main.c,v 1.15 2000/07/22 19:06:29 dholland Exp $"; /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Command Interface. */ #include #include #include #include /* #include <--- unused? */ #include #include #include #include #include #include #include #include #include #include #include "tftpsubs.h" /* for mysignal() */ #define TIMEOUT 5 /* secs between rexmt's */ struct sockaddr_in s_inn; int f; int trace; int verbose; int rexmtval = TIMEOUT; int maxtimeout = 5 * TIMEOUT; sigjmp_buf toplevel; void sendfile(int fd, char *name, char *modestr); void recvfile(int fd, char *name, char *modestr); static int connected; static short port; static char mode[32]; static char line[200]; static int margc; static char *margv[20]; static const char *prompt = "tftp"; static struct servent *sp; static void intr(int); void makeargv(void); void command(int top); void quit(int argc, char *argv[]); void help(int argc, char *argv[]); void setverbose(int argc, char *argv[]); void settrace(int argc, char *argv[]); void status(int argc, char *argv[]); void get(int argc, char *argv[]); void put(int argc, char *argv[]); void setpeer(int argc, char *argv[]); void modecmd(int argc, char *argv[]); void setrexmt(int argc, char *argv[]); void settimeout(int argc, char *argv[]); void setbinary(int argc, char *argv[]); void setascii(int argc, char *argv[]); void setmode(const char *newmode); void putusage(const char *s); void getusage(const char *s); #define HELPINDENT ((int) sizeof("connect")) struct cmd { const char *name; const char *help; void (*handler)(int, char *[]); }; char vhelp[] = "toggle verbose mode"; char thelp[] = "toggle packet tracing"; char chelp[] = "connect to remote tftp"; char qhelp[] = "exit tftp"; char hhelp[] = "print help information"; char shelp[] = "send file"; char rhelp[] = "receive file"; char mhelp[] = "set file transfer mode"; char sthelp[] = "show current status"; char xhelp[] = "set per-packet retransmission timeout"; char ihelp[] = "set total retransmission timeout"; char ashelp[] = "set mode to netascii"; char bnhelp[] = "set mode to octet"; struct cmd cmdtab[] = { { "connect", chelp, setpeer }, { "mode", mhelp, modecmd }, { "put", shelp, put }, { "get", rhelp, get }, { "quit", qhelp, quit }, { "verbose", vhelp, setverbose }, { "trace", thelp, settrace }, { "status", sthelp, status }, { "binary", bnhelp, setbinary }, { "ascii", ashelp, setascii }, { "rexmt", xhelp, setrexmt }, { "timeout", ihelp, settimeout }, { "?", hhelp, help }, { 0,0,0 } }; static struct cmd *getcmd(const char *name); static char *tail(char *filename); int main(int argc, char *argv[]) { struct sockaddr_in s_in; int top; sp = getservbyname("tftp", "udp"); if (sp == 0) { fprintf(stderr, "tftp: udp/tftp: unknown service\n"); exit(1); } f = socket(AF_INET, SOCK_DGRAM, 0); if (f < 0) { perror("tftp: socket"); exit(3); } memset(&s_in, 0, sizeof(s_in)); s_in.sin_family = AF_INET; if (bind(f, (struct sockaddr *)&s_in, sizeof (s_in)) < 0) { perror("tftp: bind"); exit(1); } strcpy(mode, "netascii"); mysignal(SIGINT, intr); if (argc > 1) { if (sigsetjmp(toplevel, 1) != 0) exit(0); setpeer(argc, argv); } top = sigsetjmp(toplevel, 1) == 0; for (;;) command(top); } static char hostname[100]; void setpeer(int argc, char *argv[]) { struct hostent *host; size_t len; if (argc < 2) { strcpy(line, "Connect "); printf("(to) "); len = strlen(line); fgets(line+len, sizeof(line)-len, stdin); makeargv(); argc = margc; argv = margv; } if (argc > 3) { printf("usage: %s host-name [port]\n", argv[0]); return; } host = gethostbyname(argv[1]); if (host) { s_inn.sin_family = host->h_addrtype; if (host->h_length > (int)sizeof(s_inn.sin_addr)) { host->h_length = sizeof(s_inn.sin_addr); } memcpy(&s_inn.sin_addr, host->h_addr, host->h_length); strncpy(hostname, host->h_name, sizeof(hostname)); hostname[sizeof(hostname)-1] = 0; } else { s_inn.sin_family = AF_INET; if (!inet_aton(argv[1], &s_inn.sin_addr)) { connected = 0; printf("%s: unknown host\n", argv[1]); return; } strcpy(hostname, argv[1]); } port = sp->s_port; if (argc == 3) { port = atoi(argv[2]); if (port < 0) { printf("%s: bad port number\n", argv[2]); connected = 0; return; } port = htons(port); } connected = 1; } struct modes { const char *m_name; const char *m_mode; } modes[] = { { "ascii", "netascii" }, { "netascii", "netascii" }, { "binary", "octet" }, { "image", "octet" }, { "octet", "octet" }, /* { "mail", "mail" }, */ { 0, 0 } }; void modecmd(int argc, char *argv[]) { register struct modes *p; const char *sep; if (argc < 2) { printf("Using %s mode to transfer files.\n", mode); return; } if (argc == 2) { for (p = modes; p->m_name; p++) if (strcmp(argv[1], p->m_name) == 0) break; if (p->m_name) { setmode(p->m_mode); return; } printf("%s: unknown mode\n", argv[1]); /* drop through and print usage message */ } printf("usage: %s [", argv[0]); sep = " "; for (p = modes; p->m_name; p++) { printf("%s%s", sep, p->m_name); if (*sep == ' ') sep = " | "; } printf(" ]\n"); return; } void setbinary(int argc, char *argv[]) { (void)argc; (void)argv; setmode("octet"); } void setascii(int argc, char *argv[]) { (void)argc; (void)argv; setmode("netascii"); } void setmode(const char *newmode) { strcpy(mode, newmode); if (verbose) printf("mode set to %s\n", mode); } /* * Send file(s). */ void put(int argc, char *argv[]) { int fd; register int n; register char *ccp, *targ; size_t len; if (argc < 2) { strcpy(line, "send "); printf("(file) "); len = strlen(line); fgets(line+len, sizeof(line)-len, stdin); makeargv(); argc = margc; argv = margv; } if (argc < 2) { putusage(argv[0]); return; } targ = argv[argc - 1]; if (strchr(argv[argc - 1], ':')) { char *cp; struct hostent *hp; for (n = 1; n < argc - 1; n++) if (strchr(argv[n], ':')) { putusage(argv[0]); return; } cp = argv[argc - 1]; targ = strchr(cp, ':'); *targ++ = 0; hp = gethostbyname(cp); if (hp == NULL) { fprintf(stderr, "tftp: %s: ", cp); herror((char *)NULL); return; } if (hp->h_length > (int)sizeof(s_inn.sin_addr)) { hp->h_length = sizeof(s_inn.sin_addr); } memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length); s_inn.sin_family = hp->h_addrtype; connected = 1; strncpy(hostname, hp->h_name, sizeof(hostname)); hostname[sizeof(hostname)-1] = 0; } if (!connected) { printf("No target machine specified.\n"); return; } if (argc < 4) { ccp = argc == 2 ? tail(targ) : argv[1]; fd = open(ccp, O_RDONLY); if (fd < 0) { fprintf(stderr, "tftp: "); perror(ccp); return; } if (verbose) printf("putting %s to %s:%s [%s]\n", ccp, hostname, targ, mode); s_inn.sin_port = port; sendfile(fd, targ, mode); return; } /* this assumes the target is a directory */ /* on a remote unix system. hmmmm. */ ccp = targ+strlen(targ); *ccp++ = '/'; for (n = 1; n < argc - 1; n++) { strcpy(ccp, tail(argv[n])); fd = open(argv[n], O_RDONLY); if (fd < 0) { fprintf(stderr, "tftp: "); perror(argv[n]); continue; } if (verbose) printf("putting %s to %s:%s [%s]\n", argv[n], hostname, targ, mode); s_inn.sin_port = port; sendfile(fd, targ, mode); } } void putusage(const char *s) { printf("usage: %s file ... host:target, or\n", s); printf(" %s file ... target (when already connected)\n", s); } /* * Receive file(s). */ void get(int argc, char *argv[]) { int fd; register int n; register char *cp; char *src; size_t len; if (argc < 2) { strcpy(line, "get "); printf("(files) "); len = strlen(line); fgets(line+len, sizeof(line)-len, stdin); makeargv(); argc = margc; argv = margv; } if (argc < 2) { getusage(argv[0]); return; } if (!connected) { for (n = 1; n < argc ; n++) if (strchr(argv[n], ':') == 0) { getusage(argv[0]); return; } } for (n = 1; n < argc ; n++) { src = strchr(argv[n], ':'); if (src == NULL) src = argv[n]; else { struct hostent *hp; *src++ = 0; hp = gethostbyname(argv[n]); if (hp == NULL) { fprintf(stderr, "tftp: %s: ", argv[n]); herror(NULL); continue; } if (hp->h_length > (int)sizeof(s_inn.sin_addr)) { hp->h_length = sizeof(s_inn.sin_addr); } memcpy(&s_inn.sin_addr, hp->h_addr, hp->h_length); s_inn.sin_family = hp->h_addrtype; connected = 1; strncpy(hostname, hp->h_name, sizeof(hostname)); hostname[sizeof(hostname)-1] = 0; } if (argc < 4) { cp = argc == 3 ? argv[2] : tail(src); fd = creat(cp, 0644); if (fd < 0) { fprintf(stderr, "tftp: "); perror(cp); return; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); s_inn.sin_port = port; recvfile(fd, src, mode); break; } cp = tail(src); /* new .. jdg */ fd = creat(cp, 0644); if (fd < 0) { fprintf(stderr, "tftp: "); perror(cp); continue; } if (verbose) printf("getting from %s:%s to %s [%s]\n", hostname, src, cp, mode); s_inn.sin_port = port; recvfile(fd, src, mode); } } void getusage(const char *s) { printf("usage: %s host:file host:file ... file, or\n", s); printf(" %s file file ... file if connected\n", s); } void setrexmt(int argc, char *argv[]) { int t; size_t len; if (argc < 2) { strcpy(line, "Rexmt-timeout "); printf("(value) "); len = strlen(line); fgets(line+len, sizeof(line)-len, stdin); makeargv(); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) printf("%s: bad value\n", argv[1]); else rexmtval = t; } void settimeout(int argc, char *argv[]) { int t; size_t len; if (argc < 2) { strcpy(line, "Maximum-timeout "); printf("(value) "); len = strlen(line); fgets(line+len, sizeof(line)-len, stdin); makeargv(); argc = margc; argv = margv; } if (argc != 2) { printf("usage: %s value\n", argv[0]); return; } t = atoi(argv[1]); if (t < 0) printf("%s: bad value\n", argv[1]); else maxtimeout = t; } void status(int argc, char *argv[]) { (void)argc; (void)argv; if (connected) printf("Connected to %s.\n", hostname); else printf("Not connected.\n"); printf("Mode: %s Verbose: %s Tracing: %s\n", mode, verbose ? "on" : "off", trace ? "on" : "off"); printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n", rexmtval, maxtimeout); } static void intr(int ignore) { (void)ignore; mysignal(SIGALRM, SIG_IGN); alarm(0); siglongjmp(toplevel, -1); } static char * tail(char *filename) { register char *s; while (*filename) { s = strrchr(filename, '/'); if (s == NULL) break; if (s[1]) return (s + 1); *s = '\0'; } return filename; } /* * Command parser. */ void command(int top) { register struct cmd *c; if (!top) putchar('\n'); for (;;) { printf("%s> ", prompt); if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin)) { quit(0, NULL); } else { continue; } } if (line[0] == 0) continue; makeargv(); if (margc<1) { /* empty line */ continue; } c = getcmd(margv[0]); if (c == (struct cmd *)-1) { printf("?Ambiguous command\n"); continue; } if (c == 0) { printf("?Invalid command\n"); continue; } (*c->handler)(margc, margv); } } struct cmd * getcmd(const char *name) { const char *p, *q; struct cmd *c, *found; int nmatches, longest; longest = 0; nmatches = 0; found = 0; for (c = cmdtab; (p = c->name)!=NULL; c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return (c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return ((struct cmd *)-1); return (found); } /* * Slice a string up into argc/argv. */ void makeargv(void) { register char *cp; register char **argp = margv; margc = 0; for (cp = line; *cp;) { while (isspace(*cp)) cp++; if (*cp == '\0') break; *argp++ = cp; margc += 1; while (*cp != '\0' && !isspace(*cp)) cp++; if (*cp == '\0') break; *cp++ = '\0'; } *argp++ = 0; } void quit(int ign1, char *ign2[]) { (void)ign1; (void)ign2; exit(0); } /* * Help command. */ void help(int argc, char *argv[]) { register struct cmd *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); return; } while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) printf("?Ambiguous help command %s\n", arg); else if (c == (struct cmd *)0) printf("?Invalid help command %s\n", arg); else printf("%s\n", c->help); } } void settrace(int ign1, char *ign2[]) { (void)ign1; (void)ign2; trace = !trace; printf("Packet tracing %s.\n", trace ? "on" : "off"); } void setverbose(int ign1, char *ign2[]) { (void)ign1; (void)ign2; verbose = !verbose; printf("Verbose mode %s.\n", verbose ? "on" : "off"); } netkit-tftp-0.17/tftp/tftp.1100644 144 144 11733 7141140326 15255 0ustar dhollandpeople.\" Copyright (c) 1990 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. .\" .\" from: @(#)tftp.1 5.4 (Berkeley) 4/22/91 .\" $Id: tftp.1,v 1.11 2000/07/30 23:57:10 dholland Exp $ .\" .Dd August 15, 1999 .Dt TFTP 1 .Os "Linux NetKit (0.17)" .Sh NAME .Nm tftp .Nd trivial file transfer program .Sh SYNOPSIS .Nm tftp .Op Ar host .Sh DESCRIPTION .Nm Tftp is the user interface to the Internet .Tn TFTP (Trivial File Transfer Protocol), which allows users to transfer files to and from a remote machine. The remote .Ar host may be specified on the command line, in which case .Nm tftp uses .Ar host as the default host for future transfers (see the .Cm connect command below). .Sh COMMANDS Once .Nm tftp is running, it issues the prompt .LI tftp> and recognizes the following commands: .Pp .Bl -tag -width verbose -compact .It Cm \&? Ar command-name ... Print help information. .Pp .It Cm ascii Shorthand for "mode ascii" .Pp .It Cm binary Shorthand for "mode binary" .Pp .It Cm connect Ar host-name Op Ar port Set the .Ar host (and optionally .Ar port ) for transfers. Note that the .Tn TFTP protocol, unlike the .Tn FTP protocol, does not maintain connections betweeen transfers; thus, the .Cm connect command does not actually create a connection, but merely remembers what host is to be used for transfers. You do not have to use the .Cm connect command; the remote host can be specified as part of the .Cm get or .Cm put commands. .Pp .It Cm get Ar filename .It Cm get Ar remotename localname .It Cm get Ar file1 file2 ... fileN Get a file or set of files from the specified .Ar sources . .Ar Source can be in one of two forms: a filename on the remote host, if the host has already been specified, or a string of the form .Ar hosts:filename to specify both a host and filename at the same time. If the latter form is used, the last hostname specified becomes the default for future transfers. .Pp .It Cm mode Ar transfer-mode Set the mode for transfers; .Ar transfer-mode may be one of .Em ascii or .Em binary . The default is .Em ascii . .Pp .It Cm put Ar file .It Cm put Ar localfile remotefile .It Cm put Ar file1 file2 ... fileN remote-directory Put a file or set of files to the specified remote file or directory. The destination can be in one of two forms: a filename on the remote host, if the host has already been specified, or a string of the form .Ar hosts:filename to specify both a host and filename at the same time. If the latter form is used, the hostname specified becomes the default for future transfers. If the remote-directory form is used, the remote host is assumed to be a .Tn UNIX machine. .Pp .It Cm quit Exit .Nm tftp . An end of file also exits. .Pp .It Cm rexmt Ar retransmission-timeout Set the per-packet retransmission timeout, in seconds. .Pp .It Cm status Show current status. .Pp .It Cm timeout Ar total-transmission-timeout Set the total transmission timeout, in seconds. .Pp .It Cm trace Toggle packet tracing. .Pp .It Cm verbose Toggle verbose mode. .El .Sh BUGS .Pp Because there is no user-login or validation within the .Tn TFTP protocol, the remote site will probably have some sort of file-access restrictions in place. The exact methods are specific to each site and therefore difficult to document here. .Sh HISTORY The .Nm command appeared in .Bx 4.3 . netkit-tftp-0.17/tftp/tftp.c100644 144 144 25544 7136370265 15357 0ustar dhollandpeople/* * Copyright (c) 1983 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. */ /* * From: @(#)tftp.c 5.10 (Berkeley) 3/1/91 */ char tftp_rcsid[] = "$Id: tftp.c,v 1.10 2000/07/22 19:06:29 dholland Exp $"; /* Many bug fixes are from Jim Guyton */ /* * TFTP User Program -- Protocol Machines */ #include #include #include #include /* #include <--- unused? */ #include #include #include #include #include #include #include #include "tftpsubs.h" #include "../version.h" extern struct sockaddr_in s_inn; /* filled in by main */ extern int f; /* the opened socket */ extern int trace; extern int verbose; extern int rexmtval; extern int maxtimeout; extern sigjmp_buf toplevel; void sendfile(int fd, char *name, char *modestr); void recvfile(int fd, char *name, char *modestr); static char ackbuf[PKTSIZE]; static int timeout; static sigjmp_buf timeoutbuf; static int makerequest(int request, char *name, struct tftphdr *tp, char *mode); static void nak(int errnor); static void tpacket(const char *s, struct tftphdr *tp, int n); static void startclock(void); static void stopclock(void); static void printstats(const char *direction, unsigned long amount); static void timer(int signum) { (void)signum; timeout += rexmtval; if (timeout >= maxtimeout) { printf("Transfer timed out.\n"); siglongjmp(toplevel, -1); } siglongjmp(timeoutbuf, 1); } /* * Send the requested file. */ void sendfile(int fd, char *name, char *mode) { register struct tftphdr *ap; /* data and ack packets */ struct tftphdr *dp; volatile int size = 0; volatile u_int16_t block = 0; int n; volatile unsigned long amount = 0; struct sockaddr_in from; socklen_t fromlen; volatile int convert; /* true if doing nl->crlf conversion */ FILE *file; volatile int firsttrip = 1; startclock(); /* start stat's clock */ dp = r_init(); /* reset fillbuf/read-ahead code */ ap = (struct tftphdr *)ackbuf; file = fdopen(fd, "r"); convert = !strcmp(mode, "netascii"); mysignal(SIGALRM, timer); do { if (firsttrip) { 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) sigsetjmp(timeoutbuf, 1); send_data: if (trace) tpacket("sent", dp, size + 4); n = sendto(f, dp, size + 4, 0, (struct sockaddr *)&s_inn, sizeof(s_inn)); 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; } s_inn.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) { volatile int j = 0; 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 (firsttrip) { firsttrip = 0; } else { amount += size; } block++; } while (size == SEGSIZE); abort: fclose(file); stopclock(); if (amount > 0) printstats("Sent", amount); } /* * Receive a file. */ void recvfile(int fd, char *name, char *mode) { register struct tftphdr *ap; struct tftphdr *dp; volatile int size = 0; volatile u_int16_t block = 1; int n; volatile unsigned long amount = 0; struct sockaddr_in from; socklen_t fromlen; volatile int firsttrip = 1; FILE *file; volatile int convert; /* true if converting crlf -> lf */ startclock(); dp = w_init(); ap = (struct tftphdr *)ackbuf; file = fdopen(fd, "w"); convert = !strcmp(mode, "netascii"); mysignal(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) sigsetjmp(timeoutbuf, 1); send_ack: if (trace) tpacket("sent", ap, size); if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&s_inn, sizeof (s_inn)) != size) { alarm(0); perror("tftp: sendto"); goto abort; } write_behind(file, convert); for ( ; ; ) { alarm(rexmtval); do { fromlen = sizeof (from); n = recvfrom(f, dp, PKTSIZE, 0, (struct sockaddr *)&from, &fromlen); } while (n <= 0); alarm(0); if (n < 0) { perror("tftp: recvfrom"); goto abort; } s_inn.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) { volatile int j = 0; 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 *)&s_inn, sizeof(s_inn)); write_behind(file, convert); /* flush last buffer */ fclose(file); stopclock(); if (amount > 0) printstats("Received", amount); } int makerequest(int request, char *name, struct tftphdr *tp, 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. */ void nak(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 *)&s_inn, sizeof (s_inn)) != length) perror("nak"); } static void tpacket(const char *s, struct tftphdr *tp, int n) { static const 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 = cp + strlen(cp); printf("\n", file, cp + 1); break; case DATA: printf("\n", ntohs(tp->th_block), n - 4); break; case ACK: printf("\n", ntohs(tp->th_block)); break; case ERROR: printf("\n", ntohs(tp->th_code), tp->th_msg); break; } } struct timeval tstart; struct timeval tstop; struct timezone zone; void startclock(void) { gettimeofday(&tstart, &zone); } void stopclock(void) { gettimeofday(&tstop, &zone); } void printstats(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 %ld bytes in %.1f seconds", direction, amount, delta); if (verbose) printf(" [%.0f bits/sec]", (amount*8.)/delta); putchar('\n'); } netkit-tftp-0.17/tftp/tftpsubs.c100644 144 144 21103 7136370265 16237 0ustar dhollandpeople/* * Copyright (c) 1983 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. */ /* * From: @(#)tftpsubs.c 5.6 (Berkeley) 2/28/91 */ char subs_rcsid[] = "$Id: tftpsubs.c,v 1.8 2000/07/22 19:06:29 dholland Exp $"; /* Simple minded read-ahead/write-behind subroutines for tftp user and server. Written originally with multiple buffers in mind, but current implementation has two buffer logic wired in. Todo: add some sort of final error check so when the write-buffer is finally flushed, the caller can detect if the disk filled up (or had an i/o error) and return a nak to the other side. Jim Guyton 10/85 */ #include #include #include #include #include #include #include #include #ifndef FIONREAD #if defined(__sun__) && defined(__svr4__) #include #define FIONREAD I_NREAD #endif /* slowaris */ #endif /* FIONREAD */ #include "tftpsubs.h" struct bf { int counter; /* size of data in buffer, or flag */ char buf[PKTSIZE]; /* room for data packet */ } bfs[2]; /* Values for bf.counter */ #define BF_ALLOC -3 /* alloc'd but not yet filled */ #define BF_FREE -2 /* free */ /* [-1 .. SEGSIZE] = size of data in the data buffer */ static int nextone; /* index of next buffer to use */ static int current; /* index of buffer in use */ /* control flags for crlf conversions */ int newline = 0; /* fillbuf: in middle of newline expansion */ int prevchar = -1; /* putbuf: previous char (cr check) */ void read_ahead(FILE *file, int convert /* if true, convert to ascii */); int write_behind(FILE *file, int convert); struct tftphdr *rw_init(int); struct tftphdr *w_init(void) { return rw_init(0); } /* write-behind */ struct tftphdr *r_init(void) { return rw_init(1); } /* read-ahead */ struct tftphdr * rw_init(int x) /* init for either read-ahead or write-behind */ { /* zero for write-behind, one for read-head */ newline = 0; /* init crlf flag */ prevchar = -1; bfs[0].counter = BF_ALLOC; /* pass out the first buffer */ current = 0; bfs[1].counter = BF_FREE; nextone = x; /* ahead or behind? */ return (struct tftphdr *)bfs[0].buf; } /* Have emptied current buffer by sending to net and getting ack. Free it and return next buffer filled with data. */ int readit(FILE *file, struct tftphdr **dpp, int convert /* if true, convert to ascii */) { struct bf *b; bfs[current].counter = BF_FREE; /* free old one */ current = !current; /* "incr" current */ b = &bfs[current]; /* look at new buffer */ if (b->counter == BF_FREE) /* if it's empty */ read_ahead(file, convert); /* fill it */ /* assert(b->counter != BF_FREE); */ /* check */ *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */ return b->counter; } /* * fill the input buffer, doing ascii conversions if requested * conversions are lf -> cr,lf and cr -> cr, nul */ void read_ahead(FILE *file, int convert /* if true, convert to ascii */) { register int i; register char *p; register int c; struct bf *b; struct tftphdr *dp; b = &bfs[nextone]; /* look at "next" buffer */ if (b->counter != BF_FREE) /* nop if not free */ return; nextone = !nextone; /* "incr" next buffer ptr */ dp = (struct tftphdr *)b->buf; if (convert == 0) { b->counter = read(fileno(file), dp->th_data, SEGSIZE); return; } p = dp->th_data; for (i = 0 ; i < SEGSIZE; i++) { if (newline) { if (prevchar == '\n') c = '\n'; /* lf to cr,lf */ else c = '\0'; /* cr to cr,nul */ newline = 0; } else { c = getc(file); if (c == EOF) break; if (c == '\n' || c == '\r') { prevchar = c; c = '\r'; newline = 1; } } *p++ = c; } b->counter = (int)(p - dp->th_data); } /* Update count associated with the buffer, get new buffer from the queue. Calls write_behind only if next buffer not available. */ int writeit(FILE *file, struct tftphdr **dpp, int ct, int convert) { bfs[current].counter = ct; /* set size of data to write */ current = !current; /* switch to other buffer */ if (bfs[current].counter != BF_FREE) /* if not free */ write_behind(file, convert); /* flush it */ bfs[current].counter = BF_ALLOC; /* mark as alloc'd */ *dpp = (struct tftphdr *)bfs[current].buf; return ct; /* this is a lie of course */ } /* * Output a buffer to a file, converting from netascii if requested. * CR,NUL -> CR and CR,LF => LF. * Note spec is undefined if we get CR as last byte of file or a * CR followed by anything else. In this case we leave it alone. */ int write_behind(FILE *file, int convert) { char *buf; int count; register int ct; register char *p; register int c; /* current character */ struct bf *b; struct tftphdr *dp; b = &bfs[nextone]; if (b->counter < -1) /* anything to flush? */ return 0; /* just nop if nothing to do */ count = b->counter; /* remember byte count */ b->counter = BF_FREE; /* reset flag */ dp = (struct tftphdr *)b->buf; nextone = !nextone; /* incr for next time */ buf = dp->th_data; if (count <= 0) return -1; /* nak logic? */ if (convert == 0) return write(fileno(file), buf, count); p = buf; ct = count; while (ct--) { /* loop over the buffer */ c = *p++; /* pick up a character */ if (prevchar == '\r') { /* if prev char was cr */ if (c == '\n') /* if have cr,lf then just */ fseek(file, -1, 1); /* smash lf on top of the cr */ else if (c == '\0') /* if have cr,nul then */ goto skipit; /* just skip over the putc */ /* else just fall through and allow it */ } putc(c, file); skipit: prevchar = c; } return count; } /* When an error has occurred, it is possible that the two sides * are out of synch. Ie: that what I think is the other side's * response to packet N is really their response to packet N-1. * * So, to try to prevent that, we flush all the input queued up * for us on the network connection on our host. * * We return the number of packets we flushed (mostly for reporting * when trace is active). */ int synchnet(int f /* socket to flush */) { int i, j = 0; char rbuf[PKTSIZE]; struct sockaddr_in from; socklen_t fromlen; while (1) { (void) ioctl(f, FIONREAD, &i); if (i) { j++; fromlen = sizeof from; (void) recvfrom(f, rbuf, sizeof (rbuf), 0, (struct sockaddr *)&from, &fromlen); } else { return(j); } } } /* * Like signal(), but with well-defined semantics. */ void mysignal(int sig, void (*handler)(int)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigaction(sig, &sa, NULL); } netkit-tftp-0.17/tftp/tftpsubs.h100644 144 144 654 7136370265 16214 0ustar dhollandpeople#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */ int synchnet(int); struct tftphdr *r_init(void); struct tftphdr *w_init(void); int readit(FILE *file, struct tftphdr **dpp, int convert); int writeit(FILE *file, struct tftphdr **dpp, int ct, int convert); void read_ahead(FILE *file, int convert /* if true, convert to ascii */); int write_behind(FILE *file, int convert); void mysignal(int, void (*func)(int)); netkit-tftp-0.17/.cvsignore100644 144 144 10 7024703561 15151 0ustar dhollandpeopleMCONFIG netkit-tftp-0.17/BUGS100644 144 144 215 7075224505 13665 0ustar dhollandpeopleWhen tftp checks incoming packets for being from the right IP address, it needs to check them against _all_ listed addresses for the server. netkit-tftp-0.17/ChangeLog100644 144 144 5155 7140622137 15000 0ustar dhollandpeople29-Jul-2000: Have tftp bind to the same address inetd was listening to, if it's not INADDR_ANY. 22-Jul-2000: Fixes for file size limit bugs. Also, a few Solaris patches. (Ignacio Goyret, igoyret@acm.org) Switch to sigaction() and remove test for BSD signal semantics. 28-Apr-2000: Bug fix: it seems if you started tftp and just pressed return, it would core... (Philipp Rumpf, prumpf@tux.org) 14-Dec-1999: netkit-tftp-0.16 is released. 14-Sep-1999: Log attempts to use ".." in filenames in tftpd. Also don't spew every request at LOG_ERR; use LOG_NOTICE. (Olaf Kirch, okir@caldera.de) 1-Aug-1999: Did complete y2k and y2038 audit. 31-Jul-1999: Redid makefiles/config stuff for new confgen version. 31-Jul-1999: Clean up file mode handling in tftpd. Fix tftp to not use gets(3). 23-Sep-1997: tftpd should now refuse to run as root and bail if it can't find "nobody". tftpd now opens with O_TRUNC when opening files for write. (Dick Porter, dick@cymru.net) 12-Jun-1997: netkit-tftp-0.10 released. 05-Apr-1997: Added configure script to generate MCONFIG. 08-Mar-1997: Split from full NetKit package. Generated this change log from NetKit's. 07-Mar-1997 Fixed tftpd to prevent using .. in a way that had been overlooked. (Marcus Better, Marcus.Better@abc.se) 29-Dec-1996 NetKit-0.09 released. Assorted alpha/glibc patches. (Erik Troan, ewt@redhat.com) Assorted bug fixes from Debian. (Peter Tobias, tobias@et-inf.fho-emden.de) Hardened programs against DNS h_length spoofing attacks. Use inet_aton() everywhere instead of inet_addr(). Fixes to tftpd. (Pekka Pietik{inen, pp@netppl.fi) 22-Aug-1996 NetKit-B-0.08 released. (almost) everything now compiles with lots of warnings turned on. Fixed possible DNS spoofing buffer overrun vulnerability in tftp. 25-Jul-1996 NetKit-B-0.07A released. 23-Jul-1996 NetKit-B-0.07 released. Integrated a collection of patches that had been lurking on the net, including the 256-ptys support for telnetd and passive mode ftp. Major security fixes, including to fingerd, lpr, rlogin, rsh, talkd, and telnetd. Do *not* use the sliplogin from earlier versions of this package, either. Much of the code builds without libbsd.a or bsd includes. Massive code cleanup. Almost everything compiles clean with gcc -Wall now. rusers and rusersd do not; patches to rpcgen to fix this would be appreciated if anyone feels like it. New maintainer: David A. Holland, dholland@hcs.harvard.edu date not known NetKit-B-0.06 released. date not known NetKit-B-0.05 released. date not known NetKit-B-0.04 released. date not known NetKit-B-0.03 released. netkit-tftp-0.17/MCONFIG.in100644 144 144 460 7136370262 14616 0ustar dhollandpeople# Dirs INSTALLROOT BINDIR MANDIR SBINDIR # Modes BINMODE DAEMONMODE MANMODE # Compiling ALLWARNINGS CC CFLAGS LDFLAGS LIBS # Features TYPE(socklen_t) # To run on Solaris we need to probe for -lsocket -lnsl # as well as whether we need to supply inet_aton() and herror() # (herror may be in -lresolv) netkit-tftp-0.17/MRULES100644 144 144 225 7136367406 14143 0ustar dhollandpeople# Standard compilation rules (don't use make builtins) %.o: %.c $(CC) $(CFLAGS) -I../include $< -c %.o: %.cc $(CC) $(CFLAGS) -I../include $< -c netkit-tftp-0.17/Makefile100644 144 144 745 7024720306 14644 0ustar dhollandpeople# You can do "make SUB=blah" to make only a few, or edit here, or both # You can also run make directly in the subdirs you want. SUB = tftp tftpd %.build: (cd $(patsubst %.build, %, $@) && $(MAKE)) %.install: (cd $(patsubst %.install, %, $@) && $(MAKE) install) %.clean: (cd $(patsubst %.clean, %, $@) && $(MAKE) clean) all: $(patsubst %, %.build, $(SUB)) install: $(patsubst %, %.install, $(SUB)) clean: $(patsubst %, %.clean, $(SUB)) distclean: clean rm -f MCONFIG netkit-tftp-0.17/README100644 144 144 6716 7141140001 14074 0ustar dhollandpeopleThis is netkit-tftp-0.17 for Linux. This package updates netkit-tftp-0.16. If you're reading this off a CD, go right away and check the net archives for later versions and security fixes. As of this writing the home site for NetKit is ftp://ftp.uk.linux.org/pub/linux/Networking/netkit Contents: tftp Trivial File Transfer Protocol client tftpd Trivial File Transfer Protocol daemon Requires: Working compiler, libc, and kernel. Security: This release contains no security fixes relative to netkit-tftp-0.16. However, versions of netkit-tftp prior to 0.10 should not be used in any circumstances. Installation: Do "./configure --help" and decide what options you want. The defaults should be suitable for most Linux systems. Then run the configure script. Do "make" to compile. Then (as root) do "make install". Save a backup copy of any mission-critical program in case the new one doesn't work, and so forth. We warned you. If you get gcc warnings from files in /usr/include, they are due to problems in your libc, not netkit. (You may only see them when compiling netkit because netkit turns on a lot of compiler warnings.) DEC CC: The DEC compiler for the Alpha is now freely available. This is a much better compiler with gcc, that is, it generates much better code. If you have the DEC compiler, you can explicitly use the DEC compiler instead of gcc by configuring like this: ./configure --with-c-compiler=ccc It is known to generate spurious warnings on some files. Also, some headers from some versions of glibc confuse it; that may prevent netkit from working. Other problems should be reported as bugs. Bugs: Please make sure the header files in /usr/include match the libc version installed in /lib and /usr/lib. If you have weird problems this is the most likely culprit. Also, before reporting a bug, be sure you're working with the latest version. If something doesn't compile for you, fix it and send diffs. If you can't, send the compiler's error output. If it compiles but doesn't work, send as complete a bug report as you can. Patches and fixes are welcome, as long as you describe adequately what they're supposed to fix. Please, one patch per distinct fix. Please do NOT send the whole archive back or reindent the source. Be sure to send all correspondence in e-mail to the netkit address. Postings to netnews or mailing lists will not be seen due to the enormous volume. Also, anything that doesn't get filed in the bug database is quite likely to end up forgotten. Please don't report known bugs (see the BUGS file(s)) unless you are including fixes. :-) Mail should be sent to: netbug@ftp.uk.linux.org Early in April 2000, a hacker broke into the machine that was hosting the netkit bug database for me and trashed it. Unfortunately, it seems backups hadn't gotten done for a while, so three months of mail (since mid-January) was lost. So, if you sent something and didn't hear back, or you sent something, heard back, but the changes failed to appear in this release (unlikely but possible) - please resend. Please see http://www.hcs.harvard.edu/~dholland/computers/netkit.html if you are curious why it was so long between the 0.10 and 0.16 releases. Future plans for netkit maintenance are still up in the air, but in the meantime new releases will still appear from time to time. I don't have a whole lot of cycles to spare to work on netkit, so things are likely to continue to be fairly slow. David A. Holland 23 July 2000 netkit-tftp-0.17/configure100755 144 144 12542 7136502701 15152 0ustar dhollandpeople#!/bin/sh # # This file was generated by confgen version 2. # Do not edit. # PREFIX='/usr' #EXECPREFIX='$PREFIX' INSTALLROOT='' BINMODE='755' #DAEMONMODE='$BINMODE' MANMODE='644' while [ x$1 != x ]; do case $1 in --help) cat < __conftest.c int main() { int class=0; return class; } EOF if [ x"$CC" = x ]; then echo -n 'Looking for a C compiler... ' for TRY in egcs gcc g++ CC c++ cc; do ( $TRY __conftest.c -o __conftest || exit 1; ./__conftest || exit 1; ) >/dev/null 2>&1 || continue; CC=$TRY break; done if [ x"$CC" = x ]; then echo 'failed.' echo 'Cannot find a C compiler. Run configure with --with-c-compiler.' rm -f __conftest* exit fi echo "$CC" else echo -n 'Checking if C compiler works... ' if ( $CC __conftest.c -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else echo 'no' echo 'Compiler '"$CC"' does not exist or cannot compile C; try another.' rm -f __conftest* exit fi fi echo -n "Checking if $CC accepts gcc warnings... " if ( $CC $WARNINGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' CC_WARNINGS=1 else echo 'no' fi if [ x$DEBUG = x ]; then echo -n "Checking if $CC accepts -O2... " if ( $CC -O2 __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -O2" else echo 'no' echo -n "Checking if $CC accepts -O... " if ( $CC -O __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -O" else echo 'no' fi fi else echo -n "Checking if $CC accepts -g... " if ( $CC -g __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -g" else echo 'no' fi fi LDFLAGS= LIBS= rm -f __conftest* ################################################## echo -n 'Checking for socklen_t... ' cat <__conftest.c #include #include #include int main() { struct sockaddr_in sn; socklen_t len = sizeof(sn); getpeername(0, (struct sockaddr *)&sn, &len); return 0; } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else if ( $CC $CFLAGS -Dsocklen_t=int __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'int' CFLAGS="$CFLAGS -Dsocklen_t=int" else if ( $CC $CFLAGS -Dsocklen_t=size_t __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'size_t' CFLAGS="$CFLAGS -Dsocklen_t=size_t" else echo 'no' echo 'Cannot work out what to use for socklen_t. Help...' rm -f __conftest* exit fi fi fi rm -f __conftest* ################################################## echo 'Generating MCONFIG...' ( echo -n '# Generated by configure (confgen version 2) on ' date echo '#' echo echo "BINDIR=$BINDIR" echo "SBINDIR=$SBINDIR" echo "MANDIR=$MANDIR" echo "BINMODE=$BINMODE" echo "DAEMONMODE=$DAEMONMODE" echo "MANMODE=$MANMODE" echo "PREFIX=$PREFIX" echo "EXECPREFIX=$EXECPREFIX" echo "INSTALLROOT=$INSTALLROOT" echo "CC=$CC" if [ x$CC_WARNINGS != x ]; then CFLAGS="$CFLAGS $WARNINGS" fi echo "CFLAGS=$CFLAGS" | sed 's/= */=/' echo "LDFLAGS=$LDFLAGS" | sed 's/= */=/' echo "LIBS=$LIBS" | sed 's/= */=/' ) > MCONFIG netkit-tftp-0.17/version.h100644 144 144 145 7141140326 15032 0ustar dhollandpeople/* * String to embed in binaries to identify package */ char pkg[]="$NetKit: netkit-tftp-0.17 $"; netkit-tftp-0.17/include/ 40700 144 144 0 7141142035 14527 5ustar dhollandpeoplenetkit-tftp-0.17/include/arpa/ 40700 144 144 0 7141142035 15452 5ustar dhollandpeoplenetkit-tftp-0.17/include/arpa/tftp.h100644 144 144 6072 7136367410 16726 0ustar dhollandpeople/* * 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. * * From: @(#)tftp.h 8.1 (Berkeley) 6/2/93 * $Id: tftp.h,v 1.1 2000/07/22 18:59:20 dholland Exp $ * * (Fixed for netkit to have well-defined field sizes) */ #ifndef _ARPA_TFTP_H #define _ARPA_TFTP_H 1 /* * Trivial File Transfer Protocol (IEN-133) */ #define SEGSIZE 512 /* data segment size */ /* * Packet types. */ #define RRQ 01 /* read request */ #define WRQ 02 /* write request */ #define DATA 03 /* data packet */ #define ACK 04 /* acknowledgement */ #define ERROR 05 /* error code */ struct tftphdr { u_int16_t th_opcode; /* packet type */ union { u_int16_t tu_block; /* block # */ u_int16_t tu_code; /* error code */ char tu_stuff[1]; /* request packet stuff */ } th_u; char th_data[1]; /* data or error string */ }; #define th_block th_u.tu_block #define th_code th_u.tu_code #define th_stuff th_u.tu_stuff #define th_msg th_data /* * Error codes. */ #define EUNDEF 0 /* not defined */ #define ENOTFOUND 1 /* file not found */ #define EACCESS 2 /* access violation */ #define ENOSPACE 3 /* disk full or allocation exceeded */ #define EBADOP 4 /* illegal TFTP operation */ #define EBADID 5 /* unknown transfer ID */ #define EEXISTS 6 /* file already exists */ #define ENOUSER 7 /* no such user */ #endif /* arpa/tftp.h */ netkit-tftp-0.17/tftpd/ 40700 144 144 0 7141142035 14225 5ustar dhollandpeoplenetkit-tftp-0.17/tftpd/.cvsignore100644 144 144 6 6774167006 16270 0ustar dhollandpeopletftpd netkit-tftp-0.17/tftpd/Makefile100644 144 144 1001 7024761722 15776 0ustar dhollandpeopleall: tftpd include ../MCONFIG include ../MRULES OBJS = tftpd.o tftpsubs.o tftpd: $(OBJS) $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ tftpsubs.c: ln -sf ../tftp/tftpsubs.c . tftpsubs.h: ln -sf ../tftp/tftpsubs.h . $(OBJS): tftpsubs.h tftpd.o: ../version.h install: tftpd install -s -m$(DAEMONMODE) tftpd $(INSTALLROOT)$(SBINDIR)/in.tftpd install -m$(MANMODE) tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.tftpd.8 ln -sf in.tftpd.8 $(INSTALLROOT)$(MANDIR)/man8/tftpd.8 clean: rm -f *.o tftpd tftpsubs.c tftpsubs.h netkit-tftp-0.17/tftpd/tftpd.8100644 144 144 10433 7141140326 15570 0ustar dhollandpeople.\" Copyright (c) 1983, 1991 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. .\" .\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91 .\" $Id: tftpd.8,v 1.14 2000/07/30 23:57:10 dholland Exp $ .\" .Dd July 29, 2000 .Dt TFTPD 8 .Os "Linux NetKit (0.17)" .Sh NAME .Nm tftpd .Nd .Tn DARPA Trivial File Transfer Protocol server .Sh SYNOPSIS .Nm tftpd .Op Ar directory ... .Sh DESCRIPTION .Nm Tftpd is a server which supports the .Tn DARPA Trivial File Transfer Protocol. The .Tn TFTP server operates at the port indicated in the .Ql tftp service description; see .Xr services 5 . The server is normally started by .Xr inetd 8 . .Pp The use of .Xr tftp 1 does not require an account or password on the remote system. Due to the lack of authentication information, .Nm tftpd will allow only publicly readable files to be accessed. Files may be written only if they already exist and are publicly writable. Note that this extends the concept of .Dq public to include all users on all hosts that can be reached through the network; this may not be appropriate on all systems, and its implications should be considered before enabling tftp service. The server should have the user ID with the lowest possible privilege. .Pp Access to files may be controlled by invoking .Nm tftpd with a list of directories by including pathnames as server program arguments in .Pa /etc/inetd.conf . In this case access is restricted to files whose names are prefixed by the one of the given directories. If no directories are supplied the default is .Pa /tftpboot . To give out access to the whole filesystem, should this be desired for some reason, supply .Pa / as an argument. .Pp Unfortunately, on multi-homed systems, it is impossible for .Nm tftpd to determine the address on which a packet was received. As a result, .Nm tftpd uses two different mechanisms to guess the best source address to use for replies. If the socket that .Xr inetd 8 passed to .Nm tftpd is bound to a particular address, .Nm tftpd uses that address for replies. Otherwise, .Nm tftpd uses ``UDP connect'' to let the kernel choose the reply address based on the destination of the replies and the routing tables. This means that most setups will work transparently, while in cases where the reply address must be fixed, the virtual hosting feature of .Xr inetd 8 can be used to ensure that replies go out from the correct address. These considerations are important, because most tftp clients will reject reply packets that appear to come from an unexpected address. .Sh SEE ALSO .Xr tftp 1 , .Xr inetd 8 .Sh HISTORY The .Nm command appeared in .Bx 4.2 . netkit-tftp-0.17/tftpd/tftpd.c100644 144 144 34464 7140622141 15654 0ustar dhollandpeople/* * Copyright (c) 1983 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. */ char copyright[] = "@(#) Copyright (c) 1983 Regents of the University of California.\n" "All rights reserved.\n"; /* * From: @(#)tftpd.c 5.13 (Berkeley) 2/26/91 */ char rcsid[] = "$Id: tftpd.c,v 1.20 2000/07/29 18:37:21 dholland Exp $"; /* * Trivial file transfer protocol server. * * This version includes many modifications by Jim Guyton */ #include #include #include #include #include #include #include /* #include <--- unused? */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tftpsubs.h" #include "../version.h" #define TIMEOUT 5 struct formats; static void tftp(struct tftphdr *tp, int size); static void nak(int error); static void sendfile(struct formats *pf); static void recvfile(struct formats *pf); static int validate_access(const char *, int); static int peer; static int rexmtval = TIMEOUT; static int maxtimeout = 5*TIMEOUT; static char buf[PKTSIZE]; static char ackbuf[PKTSIZE]; static struct sockaddr_in from; static socklen_t fromlen; #define MAXARG 4 static char *dirs[MAXARG+1]; int main(int ac, char **av) { struct sockaddr_in sn; socklen_t snsize; int dobind=1; register struct tftphdr *tp; register int n = 0; int on = 1; ac--; av++; if (ac==0) dirs[0] = "/tftpboot"; /* default directory */ while (ac-- > 0 && n < MAXARG) dirs[n++] = *av++; openlog("tftpd", LOG_PID, LOG_DAEMON); 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 * were to break before doing the above "recvfrom", inetd * would spawn endless instances, clogging the system. */ { int pid = -1; int i; socklen_t k; 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. */ k = sizeof(from); i = recvfrom(0, buf, sizeof (buf), 0, (struct sockaddr *)&from, &k); if (i > 0) { n = i; fromlen = k; } } else { break; } } if (pid < 0) { syslog(LOG_ERR, "fork: %m\n"); exit(1); } else if (pid != 0) { exit(0); } } if (!getuid() || !geteuid()) { struct passwd *pwd = getpwnam("nobody"); if (pwd) { initgroups(pwd->pw_name, pwd->pw_gid); setgid(pwd->pw_gid); setuid(pwd->pw_uid); } seteuid(0); /* this should fail */ if (!getuid() || !geteuid()) { syslog(LOG_CRIT, "can't drop root privileges"); exit(1); } } from.sin_family = AF_INET; alarm(0); /* * Get the local address of the port inetd is using, so we can * send from the same address. This will not make multihomed * usage work *transparently*, but it at least gives a chance * - you can have inetd bind a separate tftpd port for each * interface. */ snsize = sizeof(sn); if (getsockname(0, (struct sockaddr *)&sn, &snsize)<0 || sn.sin_addr.s_addr == INADDR_ANY) { dobind = 0; } sn.sin_port = 0; close(0); close(1); peer = socket(AF_INET, SOCK_DGRAM, 0); if (peer < 0) { syslog(LOG_ERR, "socket: %m\n"); exit(1); } if (dobind && bind(peer, (struct sockaddr *)&sn, snsize) < 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 { const char *f_mode; int (*f_validate)(const char *, int); void (*f_send)(struct formats *); void (*f_recv)(struct formats *); int f_convert; } formats[] = { { "netascii", validate_access, sendfile, recvfile, 1 }, { "octet", validate_access, sendfile, recvfile, 0 }, /* { "mail", validate_user, sendmail, recvmail, 1 }, */ { 0,0,0,0,0 } }; /* * Handle initial connection protocol. */ static void tftp(struct tftphdr *tp, int size) { register char *cp; int first = 1, ecode; register struct formats *pf; char *filename, *mode = NULL; 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 (ecode) { 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. */ static int validate_access(const char *filename, int mode) { struct stat stbuf; int fd; const char *cp; char **dirp; syslog(LOG_NOTICE, "tftpd: trying to get file: %s\n", filename); if (*filename != '/') { syslog(LOG_NOTICE, "tftpd: serving file from %s\n", dirs[0]); chdir(dirs[0]); } else { for (dirp = dirs; *dirp; dirp++) if (strncmp(filename, *dirp, strlen(*dirp)) == 0) break; if (*dirp==0 && dirp!=dirs) return (EACCESS); } /* * prevent tricksters from getting around the directory restrictions */ if (!strncmp(filename, "../", 3)) { syslog(LOG_WARNING, "tftpd: Blocked illegal request for %s\n", filename); return EACCESS; } for (cp = filename + 1; *cp; cp++) { if (*cp == '.' && strncmp(cp-1, "/../", 4) == 0) { syslog(LOG_WARNING, "tftpd: Blocked illegal request for %s\n", filename); return(EACCESS); } } if (stat(filename, &stbuf) < 0) return (errno == ENOENT ? ENOTFOUND : EACCESS); #if 0 /* * The idea is that symlinks are dangerous. However, a symlink * in the tftp area has to have been put there by root, and it's * not part of the philosophy of Unix to keep root from shooting * itself in the foot if it tries to. So basically we assume if * there are symlinks they're there on purpose and not pointing * to /etc/passwd or /tmp or other dangerous places. * * Note if this gets turned on the stat above needs to be made * an lstat, or the check is useless. */ /* symlinks prohibited */ if (S_ISLNK(stbuf.st_mode)) { return (EACCESS); } #endif if (mode == RRQ) { if ((stbuf.st_mode & S_IROTH) == 0) return (EACCESS); } else { if ((stbuf.st_mode & S_IWOTH) == 0) return (EACCESS); } fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); if (fd < 0) return (errno + 100); file = fdopen(fd, (mode == RRQ)? "r":"w"); if (file == NULL) { return errno+100; } return (0); } int timeout; sigjmp_buf timeoutbuf; static void timer(int signum) { (void)signum; timeout += rexmtval; if (timeout >= maxtimeout) exit(1); siglongjmp(timeoutbuf, 1); } /* * Send the requested file. */ static void sendfile(struct formats *pf) { struct tftphdr *dp; register struct tftphdr *ap; /* ack packet */ volatile u_int16_t block = 1; int size, n; mysignal(SIGALRM, timer); dp = r_init(); ap = (struct tftphdr *)ackbuf; 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) sigsetjmp(timeoutbuf, 1); send_data: if (send(peer, 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); } static void justquit(int signum) { (void)signum; exit(0); } /* * Receive a file. */ static void recvfile(struct formats *pf) { struct tftphdr *dp; struct tftphdr *ap; /* ack buffer */ volatile u_int16_t block = 0; int n, size; mysignal(SIGALRM, timer); dp = w_init(); ap = (struct tftphdr *)ackbuf; do { timeout = 0; ap->th_opcode = htons((u_short)ACK); ap->th_block = htons((u_short)block); block++; (void) sigsetjmp(timeoutbuf, 1); 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, 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); mysignal(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 } }; /* * 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(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"); }