|
|
Power 6/32 Unix version 1.21
#ifndef lint
static char sccsid[] = "@(#)main.c 4.7 (Berkeley) 8/11/83";
#endif
/*
* TFTP User Program -- Command Interface.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <setjmp.h>
#include <ctype.h>
#include <netdb.h>
#define MAXPATHLEN 1024 /* from <include/param.h> */
#define TIMEOUT 5 /* secs between rexmt's */
struct sockaddr_in sin; /* daemon address */
int f = -1; /* socket talking to daemon */
int trace;
int verbose;
int connected;
char mode[32];
char line[200];
int margc;
char *margv[20];
char *prompt = "tftp";
jmp_buf toplevel;
int intr();
struct servent *sp;
int quit(), help(), setverbose(), settrace(), status();
int get(), put(), setpeer(), setmode(), setrexmt(), settimeout();
#define HELPINDENT (sizeof("connect"))
struct cmd {
char *name;
char *help;
int (*handler)();
};
char vhelp[] = "to toggle verbose mode";
char thelp[] = "to toggle packet tracing";
char chelp[] = "to connect to remote tftp";
char qhelp[] = "to exit tftp";
char hhelp[] = "to print help information";
char shelp[] = "to send file";
char rhelp[] = "to receive file";
char mhelp[] = "to set file transfer mode";
char sthelp[] = "to show current status";
char xhelp[] = "to set per-packet retransmission timeout";
char ihelp[] = "to set total retransmission timeout";
struct cmd cmdtab[] = {
{ "connect", chelp, setpeer },
{ "mode", mhelp, setmode },
{ "put", shelp, put },
{ "get", rhelp, get },
{ "quit", qhelp, quit },
/*{ "verbose", vhelp, setverbose },*/
{ "trace", thelp, settrace },
{ "status", sthelp, status },
{ "rexmt", xhelp, setrexmt },
{ "timeout", ihelp, settimeout },
{ "?", hhelp, help },
0
};
struct cmd *getcmd();
char *tail();
char *index();
char *rindex();
/*-------------------------------------------------------------------
Changes:
. fix declaration in the tftphdr structure to be host
independent.
. initialize the port #; otherwise, if the user does not
specify the host name as argument to tftp, the command
does not work.
. create files with mode 0666 so that umask set by user works.
. fix get() so that destination could be a directory.
also, for multiple file transfer, print message per file.
. fix put() to print message per file transfer.
-------------------------------------------------------------------*/
main(argc, argv)
char *argv[];
{
int top;
getsock();
/* use sin as remote address */
sp = getservbyname("tftp", "udp");
if (sp == 0) {
fprintf(stderr, "tftp: udp/tftp: unknown service\n");
exit(1);
}
sin.sin_family = AF_INET;
sin.sin_port = sp->s_port;
strcpy(mode, "netascii");
signal(SIGINT, intr);
if (argc > 1) {
if (setjmp(toplevel) != 0)
exit(0);
setpeer(argc, argv);
}
top = 1;
if (setjmp(toplevel))
top = 0;
for (;;) {
command(top);
}
}
char *hostname;
char hnamebuf[32];
setpeer(argc, argv)
int argc;
char *argv[];
{
register int c;
struct hostent *host;
if (argc < 2) {
strcpy(line, "Connect ");
printf("(to) ");
gets(&line[strlen(line)]);
makeargv();
argc = margc;
argv = margv;
}
if (argc > 3) {
printf("usage: %s host-name [port]\n", argv[0]);
return;
}
host = gethostbyname(argv[1]);
if (host) {
sin.sin_family = host->h_addrtype;
bcopy(host->h_addr, &sin.sin_addr, host->h_length);
hostname = host->h_name;
} else {
u_long t;
sin.sin_family = AF_INET;
t = inet_addr(argv[1]);
if (t == -1) {
printf("%s: unknown host\n", argv[1]);
return;
}
sin.sin_addr.s_addr = t;
strcpy(hnamebuf, argv[1]);
hostname = hnamebuf;
}
if (argc == 3) {
u_short t;
t = atoi(argv[2]);
if (t < 0) {
printf("%s: bad port number\n", argv[2]);
return;
}
sin.sin_port = htons((u_short)t);
}
connected = 1;
}
struct modes {
char *m_name;
char *m_mode;
} modes[] = {
{ "ascii", "netascii" },
/*
{ "binary", "octect" },
{ "mail", "mail" },
*/
{ 0, 0 }
};
setmode(argc, argv)
char *argv[];
{
register struct modes *p;
if (argc != 2) {
char *sep;
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;
}
/*
if (argc < 2) {
printf("Using %s mode to transfer files.\n", mode);
return;
}
*/
for (p = modes; p->m_name; p++)
if (strcmp(argv[1], p->m_name) == 0)
break;
if (p->m_name)
strcpy(mode, p->m_mode);
else
printf("%s: unknown mode\n", argv[1]);
}
/*
* Send file(s).
*/
put(argc, argv)
char *argv[];
{
FILE *fd;
register int n, addr;
register char *cp, *targ;
char destname[MAXPATHLEN];
if (argc < 2) {
strcpy(line, "send ");
printf("(file) ");
gets(&line[strlen(line)]);
makeargv();
argc = margc;
argv = margv;
}
if (argc < 2) {
putusage(argv[0]);
return;
}
targ = argv[argc - 1];
if (index(argv[argc - 1], ':')) {
char *cp;
struct hostent *hp;
for (n = 1; n < argc - 1; n++)
if (index(argv[n], ':')) {
putusage(argv[0]);
return;
}
cp = argv[argc - 1];
targ = index(cp, ':');
*targ++ = 0;
hp = gethostbyname(cp);
if (hp == 0) {
printf("%s: Unknown host.\n", cp);
return;
}
bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
sin.sin_family = hp->h_addrtype;
connected = 1;
hostname = hp->h_name;
}
if (!connected) {
printf("No target machine specified.\n");
return;
}
if (argc < 4) {
/* cp = argc == 2 ? tail(targ) : argv[1]; */
cp = argv[1];
fd = fopen(cp, "r");
if (!fd ) {
fprintf(stderr, "tftp: "); perror(cp);
return;
}
sendfile(fd, targ);
return;
}
cp = index(targ, '\0');
*cp++ = '/';
for (n = 1; n < argc - 1; n++) {
strcpy(cp, tail(argv[n]));
fd = fopen(argv[n], "r");
if (!fd) {
fprintf(stderr, "tftp: "); perror(argv[n]);
continue;
}
if (argc > 3)
printf("\nput %s %s\n",argv[n], targ);
sendfile(fd, targ);
}
}
putusage(s)
char *s;
{
printf("usage: %s file ... host:target, or\n", s);
printf(" %s file ... target (when already connected)\n", s);
if (!connected)
printf("+++no connection has been made yet.\n");
}
/*
* Receive file(s).
*/
get(argc, argv)
char *argv[];
{
FILE *fd;
register int n, addr;
register char *cp;
char *src;
char destname[MAXPATHLEN];
if (argc < 2) {
strcpy(line, "get ");
printf("(files) ");
gets(&line[strlen(line)]);
makeargv();
argc = margc;
argv = margv;
}
if (argc < 2) {
getusage(argv[0]);
return;
}
if (!connected)
for (n = 1; n < argc - 1; n++)
if (index(argv[n], ':') == 0) {
getusage(argv[0]);
return;
}
for (n = 1; argc == 2 || n < argc - 1; n++) {
src = index(argv[n], ':');
if (src == NULL)
src = argv[n];
else {
struct hostent *hp;
char c;
c = *src;
*src = 0;
hp = gethostbyname(argv[n]);
*src++ = c;
if (hp == 0) {
printf("%s: Unknown host.\n", argv[n]);
continue;
}
bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
sin.sin_family = hp->h_addrtype;
connected = 1;
hostname = hp->h_name;
}
if (argc<4) {
cp = argc == 3 ? argv[2] : tail(src);
fd = fopen(cp, "w");
if (!fd) {
fprintf(stderr, "tftp: "); perror(cp);
return;
}
recvfile(fd, src);
break;
}
destdir:
/* destination is a directory */
makedestname(destname, sizeof(destname),argv[argc-1], src);
fd = fopen(destname, "w");
if (!fd) {
fprintf(stderr, "tftp: "); perror(destname);
continue;
}
if (argc>3)
printf("\nget %s %s\n",argv[n], destname);
recvfile(fd, src);
}
}
getusage(s)
{
printf("usage: %s host:file host:file ... file, or\n", s);
printf(" %s file file ... file if connected\n", s);
if (!connected)
printf("+++no connection has been made yet.\n");
}
int rexmtval = TIMEOUT;
setrexmt(argc, argv)
char *argv[];
{
int t;
if (argc < 2) {
strcpy(line, "Rexmt-timeout ");
printf("(value) ");
gets(&line[strlen(line)]);
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", t);
else
rexmtval = t;
}
int maxtimeout = 5 * TIMEOUT;
settimeout(argc, argv)
char *argv[];
{
int t;
if (argc < 2) {
strcpy(line, "Maximum-timeout ");
printf("(value) ");
gets(&line[strlen(line)]);
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", t);
else
maxtimeout = t;
}
status(argc, argv)
char *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);
}
intr()
{
longjmp(toplevel, 1);
}
char *
tail(filename)
char *filename;
{
register char *s;
while (*filename) {
s = rindex(filename, '/');
if (s == NULL)
break;
if (s[1])
return (s + 1);
*s = '\0';
}
return (filename);
}
/*
* Command parser.
*/
command(top)
int top;
{
register struct cmd *c;
if (!top)
putchar('\n');
for (;;) {
printf("%s> ", prompt);
if (gets(line) == 0)
quit();
if (line[0] == 0)
goto next;
makeargv();
c = getcmd(margv[0]);
if (c == (struct cmd *)-1) {
printf("?Ambiguous command\n");
goto next;
}
if (c == 0) {
printf("?Invalid command\n");
goto next;
}
(*c->handler)(margc, margv);
next:
;
}
}
struct cmd *
getcmd(name)
register char *name;
{
register char *p, *q;
register struct cmd *c, *found;
register int nmatches, longest;
longest = 0;
nmatches = 0;
found = 0;
for (c = cmdtab; p = c->name; 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.
*/
makeargv()
{
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;
}
/*VARARGS*/
quit()
{
exit(0);
}
/*
* Help command.
*/
help(argc, argv)
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);
}
}
/*
* Call routine with argc, argv set from args (terminated by 0).
*/
/* VARARGS2 */
call(routine, args)
int (*routine)();
int args;
{
register int *argp;
register int argc;
for (argc = 0, argp = &args; *argp++ != 0; argc++)
;
(*routine)(argc, &args);
}
/*VARARGS*/
settrace()
{
trace = !trace;
printf("Packet tracing %s.\n", trace ? "on" : "off");
}
/*VARARGS*/
setverbose()
{
verbose = !verbose;
printf("Verbose mode %s.\n", verbose ? "on" : "off");
}
/*----------------------
get local address
---------------------*/
getsock()
{
struct sockaddr_in tsin;
/* in case some function forgot to close */
if (f >=0)
close(f);
f = socket(AF_INET, SOCK_DGRAM, 0, 0);
if (f < 0) {
perror("tftp: socket fails");
exit(3);
}
bzero((char *)&tsin, sizeof (tsin));
/* use tsin for binding to local address */
tsin.sin_family = AF_INET;
if (bind(f, &tsin, sizeof (tsin)) < 0) {
perror("tftp: bind fails");
exit(1);
}
}
makedestname(destname, len, path, base)
char *destname; /* is "path" + "/" + tail(base) */
int len;
char *path;
char *base;
{
if (strlen(path)+strlen(tail(base))+2 > len) {
fprintf(stderr,"tftp: file name is too long");
exit(2);
}
strcpy(destname, path);
strcat(destname,"/");
strcat(destname,tail(base));
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.