📄 tftpd.c
字号:
close(data->sockfd);
}
/* update stats */
stats_thread_usage_locked();
/* Remove the thread_data structure from the list, if it as been
added. */
if (!abort)
tftpd_list_remove(data);
/* Free memory. */
free(data->data_buffer);
free(data->ack_buffer);
free(data->tftp_options);
/* Three things may append with client_info:
1- We have only one client (probably not multicast)
2- We do not have any client anymore, we transfered it to an other
server
3- We do not have any client, they are all done.
*/
tftpd_clientlist_free(data->client_info);
free(data);
logger(LOG_INFO, "Server thread exiting");
pthread_exit(NULL);
}
/*
* If we receive signals, we must exit in a clean way. This means
* sending an ERROR packet to all clients to terminate the connection.
*/
void signal_handler(int signal)
{
switch (signal)
{
case SIGINT:
case SIGTERM:
logger(LOG_ERR, "SIGINT received, stopping threads and exiting.");
/* Send cancellation message */
tftpd_cancel = 1;
break;
case SIGKILL:
logger(LOG_ERR, "SIGKILL received, aborting.");
exit(ERR);
break;
default:
logger(LOG_WARNING, "Signal %d received, ignoring.", signal);
break;
}
}
/*
* Parse the command line using the standard getopt function.
*/
int tftpd_cmd_line_options(int argc, char **argv)
{
int c;
static struct option options[] = {
{ "help", 0, NULL, 'h' },
{ "tftpd-timeout", 1, NULL, 't' },
{ "retry-timeout", 1, NULL, 'r' },
{ "verbose", 2, NULL, 'v' },
{ "maxthread", 1, NULL, 'm' },
{ "no-timeout", 0, NULL, 'T' },
{ "no-tsize", 0, NULL, 'S' },
{ "no-blksize", 0, NULL, 'B' },
{ "no-multicast", 0, NULL, 'M' },
{ "logfile", 1, NULL, 'L' },
{ "daemon", 0, NULL, 'D' },
{ "port", 1, NULL, 'P' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ 0, 0, 0, 0 }
};
while ((c = getopt_long(argc, argv, "Vht:r:v::m:",
options, NULL)) != EOF)
{
switch (c)
{
case 't':
tftpd_timeout = atoi(optarg);
break;
case 'r':
retry_timeout = atoi(optarg);
break;
case 'm':
tftpd_max_thread = atoi(optarg);
break;
case 'v':
if (optarg)
logging_level = atoi(optarg);
else
logging_level++;
break;
case 'T':
tftp_default_options[OPT_TIMEOUT].enabled = 0;
break;
case 'S':
tftp_default_options[OPT_TSIZE].enabled = 0;
break;
case 'B':
tftp_default_options[OPT_BLKSIZE].enabled = 0;
break;
case 'M':
tftp_default_options[OPT_MULTICAST].enabled = 0;
break;
case 'L':
log_file = strdup(optarg);
break;
case 'D':
tftpd_daemon = 1;
break;
case 'P':
tftpd_port = (short)atoi(optarg);
break;
case 'V':
printf("atftp-%s (server)\n", VERSION);
exit(0);
case 'h':
tftpd_usage();
exit(0);
case '?':
exit(1);
break;
}
}
/* verify that only one arguement is left */
if (optind < argc)
strncpy(directory, argv[optind], MAXLEN);
/* make sure the last caracter is a / */
if (directory[strlen(directory)] != '/')
strcat(directory, "/");
return OK;
}
/*
* Output option to the syslog.
*/
void tftpd_log_options(void)
{
if (tftpd_daemon == 1)
logger(LOG_INFO, " running in daemon mode on port %d", tftpd_port);
else
logger(LOG_INFO, " started by inetd");
logger(LOG_INFO, " logging level: %d", logging_level);
logger(LOG_INFO, " directory: %s", directory);
logger(LOG_INFO, " log file: %s", (log_file==NULL) ? "syslog":log_file);
if (tftpd_daemon == 1)
logger(LOG_INFO, " server timeout: Not used");
else
logger(LOG_INFO, " server timeout: %d", tftpd_timeout);
logger(LOG_INFO, " tftp retry timeout: %d", retry_timeout);
logger(LOG_INFO, " maximum number of thread: %d", tftpd_max_thread);
logger(LOG_INFO, " option timeout: %s",
tftp_default_options[OPT_TIMEOUT].enabled ? "enabled":"disabled");
logger(LOG_INFO, " option tzise: %s",
tftp_default_options[OPT_TSIZE].enabled ? "enabled":"disabled");
logger(LOG_INFO, " option blksize: %s",
tftp_default_options[OPT_BLKSIZE].enabled ? "enabled":"disabled");
logger(LOG_INFO, " option multicast: %s",
tftp_default_options[OPT_MULTICAST].enabled ? "enabled":"disabled");
}
/*
* Show a nice usage...
*/
void tftpd_usage(void)
{
printf("*** tftpd must be called by inetd ***\n"
"Usage: tftpd [options] [directory]\n"
" [options] may be:\n"
" -t, --tftpd-timeout <VALUE>: number of second of inactivity"
" before exiting\n"
" -r, --retry-timeout <VALUE>: time to wait a reply before"
" retransmition\n"
" -m, --maxthread <VALUE> : number of concurrent thread"
" allowed\n"
" -v, --verbose [value] : increase or set the level of"
" output messages\n"
" --no-timeout : disable 'timeout' from RFC2349\n"
" --no-tisize : disable 'tsize' from RFC2349\n"
" --no-blksize : disable 'blksize' from RFC2348\n"
" --no-multicast : disable 'multicast' from RFC2090\n"
" --logfile : logfile to log logs to ;-)\n"
" --daemon : run atftpd standalone (no inetd)\n"
" --port : port the server will listen\n"
" -h, --help : print this help\n"
" -V, --version : print version information\n"
"\n"
" [directories] must be a world readable/writable directories.\n"
" By default /tftpboot is assumed."
"\n");
}
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd.c
* main server file
*
* $Id: tftpd.c,v 1.25 2001/07/08 17:45:46 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre
* and Remi Lefebvre
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tftpd.h"
#include "tftp_io.h"
#include "tftp_def.h"
#include "logger.h"
#include "options.h"
#include "stats.h"
/*
* Global variables set by main when starting. Read-only for threads
*/
int tftpd_max_thread = 100; /* number of concurent thread allowed */
int tftpd_timeout = 300; /* number of second of inactivity
before exiting */
char directory[MAXLEN] = "/tftpboot/";
int retry_timeout = S_TIMEOUT;
int tftpd_daemon = 0; /* By default we are started by inetd */
short tftpd_port = 0;
int tftpd_cancel = 0; /* When true, thread must exit. */
/*
* "logging level" is the maximum error level that will get logged.
* This can be increased with the -v switch.
*/
int logging_level = LOG_NOTICE;
char *log_file = NULL;
/*
* We need a lock on stdin from the time we notice fresh data coming from
* stdin to the time the freshly created server thread as read it.
*/
pthread_mutex_t stdin_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Function defined in this file
*/
void *tftpd_receive_request(void *);
void signal_handler(int signal);
int tftpd_cmd_line_options(int argc, char **argv);
void tftpd_log_options(void);
void tftpd_usage(void);
/*
* Main thread. Do required initialisation and then go through a loop
* listening for client requests. When a request arrives, we allocate
* memory for a thread data structure and start a thread to serve the
* new client. If theres no activity for more than 'tftpd_timeout'
* seconds, we exit and tftpd must be respawned by inetd.
*/
int main(int argc, char **argv)
{
fd_set rfds; /* for select */
struct timeval tv; /* for select */
int run = 1; /* while (run) loop */
struct thread_data *new; /* for allocation of new thread_data */
int sockfd; /* used in daemon mode */
struct sockaddr_in sa; /* used in daemon mode */
struct servent *serv;
struct passwd *user;
struct group *group;
/*
* Parse command line options. We parse before verifying
* if we are running on a tty or not to make it possible to
* verify the command line arguments
*/
if (tftpd_cmd_line_options(argc, argv) == ERR)
exit(1);
/*
* Can't be started from the prompt without explicitely specifying
* the --daemon option.
*/
if (isatty(0) && !(tftpd_daemon))
{
tftpd_usage();
exit(1);
}
/*
* In tftpd is run in daemon mode ...
*/
if (tftpd_daemon)
{
/* daemonize here */
if (daemon(0, 0) == -1)
exit(2);
/* find the port */
if (tftpd_port == 0)
{
if ((serv = getservbyname("tftp", "udp")) == NULL)
{
logger(LOG_ERR, "atftpd: udp/tftp, unknown service");
exit(1);
}
tftpd_port = ntohs(serv->s_port);
}
/* initialise sockaddr_in structure */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(tftpd_port);
/* open the socket */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
{
logger(LOG_ERR, "atftpd: can't open socket");
exit(1);
}
/* bind the socket to the tftp port */
if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
{
logger(LOG_ERR, "atftpd: can't bind port");
exit(1);
}
/* dup sockfd on 0,1,2 */
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
close(sockfd);
/* release priviliedge */
user = getpwnam("nobody");
group = getgrnam("nogroup");
setgid(group->gr_gid);
setuid(user->pw_uid);
}
/* Register signal handler. */
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGKILL, signal_handler);
/* Using syslog facilties through a wrapper. This call divert logs
* to a file as specified or do nothing
*/
open_logger("tftpd", log_file, logging_level);
logger(LOG_NOTICE, "Trivial FTP server started (%s)", VERSION);
/* print summary of options */
tftpd_log_options();
/* start collecting stats */
stats_start();
/* Wait for read or write request and exit if timeout. */
while (run)
{
/*
* inetd dups the socket file descriptor to 0, 1 and 2 so we can
* use any of those as the socket fd. We use 0. stdout and stderr
* may not be used to print messages.
*/
FD_ZERO(&rfds);
FD_SET(0, &rfds);
tv.tv_sec = tftpd_timeout;
tv.tv_usec = 0;
/* We need to lock stdin, and release it when the thread
is done reading the request. */
pthread_mutex_lock(&stdin_mutex);
/* A timeout of 0 is interpreted as infinity */
if (!tftpd_cancel)
{
if ((tftpd_timeout == 0) || (tftpd_daemon))
select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
else
select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
}
if (FD_ISSET(0, &rfds) && (!tftpd_cancel))
{
/* Allocate memory for thread_data structure. */
if ((new = malloc(sizeof(struct thread_data))) == NULL)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -