⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tftpd.c

📁 TFTP Deamon Process code
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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 <HELIX@STEP.POLYMTL.CA> 
 *                and Remi Lefebvre <REMI@DEBIAN.ORG> 
 * 
 * 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 <CONFIG.H>   
  
#include <STDIO.H>   
#include <STDLIB.H>   
#include <SYS time.h>   
#include <SYS types.h>   
#include <SYS socket.h>   
#include <ARPA inet.h>   
#include <NETDB.H>   
#include <GETOPT.H>   
#include <UNISTD.H>   
#include <SIGNAL.H>    
#include <PTHREAD.H>   
#include <STRING.H>   
#include <PWD.H>   
#include <GRP.H>   
#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)  
               {  
                    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 <CODE: %s msg: %d,>", EBADOP,  
                      tftp_errmsg[EBADOP]);  
               stats_err_locked();  
          }  
     }    
  
     /* make sure all data is sent to the network */  
     if (data->sockfd)  
     {  
          fsync(data->sockfd);  

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -