?? tftpd.c.test
字號:
/* tftp-hpa: $Id: tftpd.c.test,v 1.1.1.1 2004/05/06 19:43:41 hjia Exp $ */
/* $OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $ */
/*
* 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.
*/
#ifndef lint
static const char *copyright =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
All rights reserved.\n";
/*static char sccsid[] = "from: @(#)tftpd.c 5.13 (Berkeley) 2/26/91";*/
/*static char rcsid[] = "$OpenBSD: tftpd.c,v 1.13 1999/06/23 17:01:36 deraadt Exp $: tftpd.c,v 1.6 1997/02/16 23:49:21 deraadt Exp $";*/
static const char *rcsid = "tftp-hpa $Id: tftpd.c.test,v 1.1.1.1 2004/05/06 19:43:41 hjia Exp $";
#endif /* not lint */
/*
* Trivial file transfer protocol server.
*
* This version includes many modifications by Jim Guyton <guyton@rand-unix>
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/tftp.h>
#include <netdb.h>
#include <setjmp.h>
#include <syslog.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#define __USE_GNU /* Necessary for basename() on glibc systems */
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <unistd.h>
#include <limits.h>
#include <sys/wait.h>
#include "../config.h"
#include "tftpsubs.h"
#include "recvfrom.h"
#include "../../html/util.h"
#ifdef HAVE_TCPWRAPPERS
#include <tcpd.h>
int deny_severity = LOG_WARNING;
int allow_severity = -1; /* Don't log at all */
struct request_info wrap_request;
#endif
void bsd_signal(int, void (*)(int));
#ifndef HAVE_SIGSETJMP
#define sigsetjmp(x,y) setjmp(x)
#define siglongjmp(x,y) longjmp(x,y)
#define sigjmp_buf jmp_buf
#endif
#define TIMEOUT 5 /* Default timeout (seconds) */
#define TRIES 4 /* Number of attempts to send each packet */
#define TIMEOUT_LIMIT (TRIES*(TRIES+1)/2)
#ifndef OACK
#define OACK 6
#endif
#ifndef EOPTNEG
#define EOPTNEG 8
#endif
extern char *__progname;
int peer;
int timeout = TIMEOUT;
int rexmtval = TIMEOUT;
int maxtimeout = TIMEOUT_LIMIT*TIMEOUT;
#define PKTSIZE MAX_SEGSIZE+4
char buf[PKTSIZE];
char ackbuf[PKTSIZE];
struct sockaddr_in from;
int fromlen;
off_t tsize;
int tsize_ok;
int ndirs;
char **dirs;
int secure = 0;
int cancreate = 0;
struct formats;
int tftp(struct tftphdr *, int);
void nak(int);
void timer(int);
void justquit(int);
void do_opt(char *, char *, char **);
int set_blksize(char *, char **);
int set_blksize2(char *, char **);
int set_tsize(char *, char **);
int set_timeout(char *, char **);
struct options {
char *o_opt;
int (*o_fnc)(char *, char **);
} options[] = {
{ "blksize", set_blksize },
{ "blksize2", set_blksize2 },
{ "tsize", set_tsize },
{ "timeout", set_timeout },
{ NULL, NULL }
};
static void
usage(void)
{
syslog(LOG_ERR, "Usage: %s [-c] [-u user] [-t timeout] [-r option...] [-s] [directory ...]",
__progname);
exit(1);
}
/*
* program starting, what need to initialize
*/
static void
doInit()
{
}
void signal_handler(int signum)
{
// printf("signum=%d\n",signum);
if(signum == SIGCHLD)
{
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */
}
// don't comment out this line, other wise program will exit
else if(signum == SIGHUP)
doInit();
else
{
while(waitpid(-1,NULL,WNOHANG) > 0);
exit(0);
}
}
int
main(int argc, char **argv)
{
struct tftphdr *tp;
struct passwd *pw;
struct options *opt;
struct sockaddr_in myaddr;
struct sockaddr_in servaddr;
int n = 0;
int on = 1;
int fd = 0;
int pid, xx;
int c;
int setrv;
int timeout = 900; /* Default timeout */
// char *user = "nobody"; /* Default user */
char *user = "root"; /* Default user */
__progname = basename(argv[0]);
openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
while ((c = getopt(argc, argv, "csu:r:t:")) != -1)
switch (c) {
case 'c':
cancreate = 1;
break;
case 's':
secure = 1;
break;
case 't':
timeout = atoi(optarg);
break;
case 'u':
user = optarg;
break;
case 'r':
for ( opt = options ; opt->o_opt ; opt++ ) {
if ( !strcasecmp(optarg, opt->o_opt) ) {
opt->o_opt = ""; /* Don't support this option */
break;
}
}
if ( !opt->o_opt ) {
syslog(LOG_ERR, "Unknown option: %s", optarg);
exit(1);
}
break;
default:
usage();
break;
}
for (; optind != argc; optind++) {
if (dirs)
dirs = realloc(dirs, (ndirs+2) * sizeof (char *));
else
dirs = calloc(ndirs+2, sizeof(char *));
if (dirs == NULL) {
syslog(LOG_ERR, "malloc: %m");
exit(1);
}
dirs[n++] = argv[optind];
dirs[n] = NULL;
ndirs++;
}
if (secure) {
if (ndirs == 0) {
syslog(LOG_ERR, "no -s directory");
exit(1);
}
if (ndirs > 1) {
syslog(LOG_ERR, "too many -s directories");
exit(1);
}
if (chdir(dirs[0])) {
syslog(LOG_ERR, "%s: %m", dirs[0]);
exit(1);
}
}
pw = getpwnam(user);
if (!pw) {
syslog(LOG_ERR, "no user %s: %m", user);
exit(1);
}
#if KAM_LATER // not needed if not by inetd
if (ioctl(fd, FIONBIO, &on) < 0) {
syslog(LOG_ERR, "ioctl(FIONBIO): %m");
exit(1);
}
#else
// bind to default tftp port
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
kd_Log("tftpd:socket err");
exit(1);
}
//printf("fd=%d\n",fd);
on = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on));
bzero(&servaddr, sizeof(servaddr)); /* zero the struct */
servaddr.sin_family = AF_INET; /* host byte order */
servaddr.sin_port = htons(IPPORT_TFTP);/* short, network byte order */
servaddr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
if (bind(fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) == -1)
{
kd_Log("tftpd:bind err");
close(fd);
exit(1);
}
#endif
/* This means we don't want to wait() for children */
bsd_signal(SIGCHLD, SIG_IGN);
// Set up signal handlers so we can clear up our child processes
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGCHLD, signal_handler);
signal(SIGKILL, signal_handler);
xx = fork();
if(xx==-1)
{
kd_Log("tftpd:FATAL ERROR, Could not fork() tftpd\n");
syslog(LOG_CRIT, "Could not fork() tftpd into background\n");
closelog();
close(fd);
exit(1);
}
if(xx != 0) { // Parent...
printf("tftp in background pid=%d\n",xx);
exit(0);
}
do {
fromlen = sizeof (from);
// printf("recvfrom %d: ",fd);
#if KAM_LATER
n = myrecvfrom(fd, buf, sizeof (buf), 0,
(struct sockaddr *)&from, &fromlen,
&myaddr);
#else
bzero(&myaddr, sizeof(struct sockaddr_in));
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(IPPORT_TFTP);
n = recvfrom(fd, buf, sizeof (buf), 0,
(struct sockaddr *)&from, &fromlen);
#endif
if (n < 0) {
syslog(LOG_ERR, "recvfrom: %m");
exit(1);
}
//printf("%d bytes ",n);
#if KAM_LATER
//#ifdef HAVE_TCPWRAPPERS
/* Verify if this was a legal request for us. This has to be
done before the chroot, while /etc is still accessible. */
request_init(&wrap_request,
RQ_DAEMON, __progname,
RQ_FILE, fd,
RQ_CLIENT_SIN, &from,
RQ_SERVER_SIN, &myaddr,
0);
sock_methods(&wrap_request);
if ( hosts_access(&wrap_request) == 0 ) {
if ( deny_severity != -1 )
syslog(deny_severity, "connection refused from %s",
inet_ntoa(from.sin_addr));
exit(1); /* Access denied */
} else if ( allow_severity != -1 ) {
syslog(allow_severity, "connect from %s",
inet_ntoa(from.sin_addr));
}
#endif
/*
* Now that we have read the message out of the UDP
* socket, we fork and go back to listening to the
* socket.
*/
pid = fork();
if (pid < 0) {
kd_Log("tftpd:fork fail\n");
syslog(LOG_ERR, "fork: %m");
exit(1); /* Return to inetd, just in case */
}
} while ( pid > 0 ); /* Parent process continues... */
/* Child process: handle the actual request here */
/* Chroot and drop privileges */
if (secure && chroot(".")) {
syslog(LOG_ERR, "chroot: %m");
exit(1);
}
#ifdef HAVE_SETREGID
setrv = setregid(pw->pw_gid, pw->pw_gid);
#else
setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid);
#endif
#ifdef HAVE_SETREUID
setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid);
#else
/* Important: setuid() must come first */
setrv = setrv || setuid(pw->pw_uid) ||
(geteuid() != pw->pw_uid && seteuid(pw->pw_uid));
#endif
if ( setrv ) {
syslog(LOG_ERR, "cannot drop privileges: %m");
exit(1);
}
/* Close file descriptors we don't need */
from.sin_family = AF_INET;
alarm(0);
#if KAM_LATER
close(fd);
close(1);
#endif
/* Process the request... */
peer = socket(AF_INET, SOCK_DGRAM, 0);
if (peer < 0) {
syslog(LOG_ERR, "socket: %m");
exit(1);
}
myaddr.sin_port = htons(0); /* We want a new local port */
if (bind(peer, (struct sockaddr *)&myaddr, sizeof (myaddr)) < 0) {
syslog(LOG_ERR, "bind: %m");
exit(1);
}
if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
syslog(LOG_ERR, "connect: %m");
exit(1);
}
tp = (struct tftphdr *)buf;
tp->th_opcode = ntohs(tp->th_opcode);
if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) {
kd_Log("tftp:%s %d %s",inet_ntoa(from.sin_addr),tp->th_opcode,
tp->th_stuff);
tftp(tp, n);
}
//printf("opcode %d peer=%d\n",tp->th_opcode, peer);
exit(0);
}
int validate_access(char *, int, struct formats *);
void sendfile(struct formats *, struct tftphdr *, int);
void recvfile(struct formats *, struct tftphdr *, int);
struct formats {
char *f_mode;
int (*f_validate)(char *, int, struct formats *);
void (*f_send)(struct formats *, struct tftphdr *, int);
void (*f_recv)(struct formats *, struct tftphdr *, int);
int f_convert;
char filename[128];
} formats[] = {
{ "netascii", validate_access, sendfile, recvfile, 1, "\0" },
{ "octet", validate_access, sendfile, recvfile, 0, "\0" },
{ NULL, NULL, NULL, NULL, 0, "\0" }
};
/*
* Handle initial connection protocol.
*/
int
tftp(struct tftphdr *tp, int size)
{
char *cp;
int argn, ecode;
struct formats *pf = NULL;
char *filename, *mode = NULL;
char *val = NULL, *opt = NULL;
char *ap = ackbuf + 2;
((struct tftphdr *)ackbuf)->th_opcode = ntohs(OACK);
filename = cp = tp->th_stuff;
argn = 0;
while ( cp < buf + size && *cp ) {
do {
cp++;
} while (cp < buf + size && *cp);
if ( *cp ) {
nak(EBADOP); /* Corrupt packet - no final NULL */
exit(0);
}
argn++;
if (argn == 1) {
mode = ++cp;
} else if (argn == 2) {
for (cp = mode; *cp; cp++)
*cp = tolower(*cp);
for (pf = formats; pf->f_mode; pf++) {
if (!strcmp(pf->f_mode, mode))
break;
}
if (!pf->f_mode) {
nak(EBADOP);
exit(0);
}
ecode = (*pf->f_validate)(filename, tp->th_opcode, pf);
if (ecode) {
nak(ecode);
exit(0);
}
opt = ++cp;
} else if ( argn & 1 ) {
val = ++cp;
} else {
do_opt(opt, val, &ap);
opt = ++cp;
}
}
if (!pf) {
nak(EBADOP);
exit(0);
}
if (strlen(filename) >= 128) {
nak(EBADOP);
exit(0);
}
strcpy(pf->filename, filename);
printf("tftp filename=%s\n",filename);
if ( ap != (ackbuf+2) ) {
// doesn't come here
if ( tp->th_opcode == WRQ )
(*pf->f_recv)(pf, ackbuf, ap-ackbuf);
else
(*pf->f_send)(pf, ackbuf, ap-ackbuf);
} else {
if (tp->th_opcode == WRQ)
(*pf->f_recv)(pf, NULL, 0);
else
(*pf->f_send)(pf, NULL, 0);
}
exit(1);
}
static int blksize_set;
/*
* Set a non-standard block size (c.f. RFC2348)
*/
int
set_blksize(char *val, char **ret)
{
static char b_ret[6];
unsigned int sz = atoi(val);
if ( blksize_set )
return 0;
if (sz < 8)
return(0);
else if (sz > MAX_SEGSIZE)
sz = MAX_SEGSIZE;
segsize = sz;
sprintf(*ret = b_ret, "%u", sz);
blksize_set = 1;
return(1);
}
/*
* Set a power-of-two block size (nonstandard)
*/
int
set_blksize2(char *val, char **ret)
{
static char b_ret[6];
unsigned int sz = atoi(val);
if ( blksize_set )
return 0;
if (sz < 8)
return(0);
else if (sz > MAX_SEGSIZE)
sz = MAX_SEGSIZE;
/* Convert to a power of two */
if ( sz & (sz-1) ) {
unsigned int sz1 = 1;
/* Not a power of two - need to convert */
while ( sz >>= 1 )
sz1 <<= 1;
sz = sz1;
}
segsize = sz;
sprintf(*ret = b_ret, "%u", sz);
blksize_set = 1;
return(1);
}
/*
* Return a file size (c.f. RFC2349)
* For netascii mode, we don't know the size ahead of time;
* so reject the option.
*/
int
set_tsize(char *val, char **ret)
{
static char b_ret[sizeof(off_t)*CHAR_BIT/3+2];
off_t sz = atol(val);
if ( !tsize_ok )
return 0;
if (sz == 0)
sz = tsize;
sprintf(*ret = b_ret, "%lu", sz);
return(1);
}
/*
* Set the timeout (c.f. RFC2349)
*/
int
set_timeout(char *val, char **ret)
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -