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

📄 tftpd.c

📁 TFTP Deamon Process code
💻 C
📖 第 1 页 / 共 3 页
字号:
                    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 + -