📄 tftpd.c
字号:
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
exit(1);
}
/*
* Initialisation of thread_data structure.
*/
pthread_mutex_init(&new->opt_mutex, NULL);
pthread_mutex_init(&new->client_mutex, NULL);
new->sockfd = 0;
/* Allocate data buffer for tftp transfer. */
if ((new->data_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
exit(1);
}
new->data_buffer_size = SEGSIZE + 4;
/* Allocate ack buffer for tftp acknowledge. */
if ((new->ack_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
exit(1);
}
new->ack_buffer_size = SEGSIZE + 4;
/* Allocate memory for tftp option structure. */
if ((new->tftp_options =
malloc(sizeof(tftp_default_options))) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
exit(1);
}
/* Copy default options. */
memcpy(new->tftp_options, tftp_default_options,
sizeof(tftp_default_options));
/* default timeout */
new->timeout = retry_timeout;
/* Allocate memory for a client structure. */
if ((new->client_info =
calloc(1, sizeof(struct client_info))) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
exit(1);
}
new->client_info->master_client = 1;
new->client_info->next = NULL;
/* Start a new server thread. */
pthread_create(&new->tid, NULL, tftpd_receive_request,
(void *)new);
}
else
{
pthread_mutex_unlock(&stdin_mutex);
if (tftpd_list_num_of_thread() == 0)
{
run = 0;
if (tftpd_daemon || (tftpd_timeout == 0))
logger(LOG_NOTICE, "Terminating");
else
logger(LOG_NOTICE,
"Terminating after timeout of %d seconds",
tftpd_timeout);
}
}
}
close(0);
close(1);
close(2);
/* stop collecting stats and print them*/
stats_end();
stats_print();
/* some cleaning */
if (log_file)
free(log_file);
logger(LOG_NOTICE, "Main thread exiting");
close_logger();
exit(0);
}
/*
* This function handles the initial connection with a client. It reads
* the request from stdin and then release the stdin_mutex, so the main
* thread may listen for new clients. After that, we process options and
* call the sending or receiving function.
*
* arg is a thread_data structure pointer for that thread.
*/
void *tftpd_receive_request(void *arg)
{
struct thread_data *data = (struct thread_data *)arg;
int retval; /* hold return value for testing */
int data_size; /* returned size by recvfrom */
char string[MAXLEN]; /* hold the string we pass to the logger */
int num_of_threads;
int abort = 0; /* 1 if we need to abort because the maximum
number of threads have been reached*/
socklen_t len = sizeof(struct sockaddr);
/* Detach ourself. That way the main thread does not have to
* wait for us with pthread_join. */
pthread_detach(pthread_self());
/* Verify the number of threads */
if ((num_of_threads = tftpd_list_num_of_thread()) >= tftpd_max_thread)
abort = 1;
/* Read the first packet from stdin. */
data_size = data->data_buffer_size;
retval = tftp_get_packet(0, &data->client_info->client, data->timeout,
&data_size, data->data_buffer);
/* Add this new thread structure to the list. */
if (!abort)
stats_new_thread(tftpd_list_add(data));
/* now unlock stdin */
pthread_mutex_unlock(&stdin_mutex);
/* if the maximum number of thread is reached, too bad we abort. */
if (abort)
{
stats_abort_locked();
logger(LOG_INFO, "Maximum number of threads reached: %d",
num_of_threads);
}
else
{
/* open a socket for client communication */
data->sockfd = socket(PF_INET, SOCK_DGRAM, 0);
/* bind the socket to the interface */
getsockname(0, (struct sockaddr *)&(data->sa_in), &len);
data->sa_in.sin_port = 0;
bind(data->sockfd, (struct sockaddr *)&(data->sa_in), len);
getsockname(data->sockfd, (struct sockaddr *)&(data->sa_in), &len);
connect(data->sockfd, (struct sockaddr *)&data->client_info->client,
sizeof(data->client_info->client));
/* read options from request */
pthread_mutex_lock(&data->opt_mutex);
opt_parse_request(data->data_buffer, data_size,
data->tftp_options);
pthread_mutex_unlock(&data->opt_mutex);
opt_request_to_string(data->tftp_options, string, MAXLEN);
/* Analyse the request. */
switch (retval)
{
case GET_RRQ:
logger(LOG_NOTICE, "Serving %s to %s",
data->tftp_options[OPT_FILENAME].value,
inet_ntoa(data->client_info->client.sin_addr));
logger(LOG_DEBUG, "reveived RRQ <%s>", string);
if (tftpd_send_file(data) == OK)
stats_send_locked();
else
stats_err_locked();
break;
case GET_WRQ:
logger(LOG_NOTICE, "Fetching from %s to %s",
inet_ntoa(data->client_info->client.sin_addr),
data->tftp_options[OPT_FILENAME].value);
logger(LOG_DEBUG, "received WRQ <%s>", string);
if (tftpd_receive_file(data) == OK)
stats_recv_locked();
else
stats_err_locked();
break;
default:
logger(LOG_NOTICE, "Invalid request <%d> from %s",
retval, inet_ntoa(data->client_info->client.sin_addr));
tftp_send_error(data->sockfd, &data->client_info->client,
EBADOP);
logger(LOG_DEBUG, "sent ERROR ", EBADOP,
tftp_errmsg[EBADOP]);
stats_err_locked();
}
}
/* make sure all data is sent to the network */
if (data->sockfd)
{
fsync(data->sockfd);
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 : number of second of inactivity"
" before exiting\n"
" -r, --retry-timeout : time to wait a reply before"
" retransmition\n"
" -m, --maxthread : 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");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -