Documentation

Generated on Thu Aug 31 00:02:27 2006

 

babyftpd.cpp

Go to the documentation of this file.
00001 
00002 //  $Id: babyftpd.cpp,v 1.138 2005/08/23 19:34:02 klas Exp $
00003 //
00004 // Usage: main function, commandline parsing and function that
00005 //        on the main socket.
00006 //
00007 //  A part of babyftpd, licensed under the GNU GPL, see the file
00008 //    LICENSE for complete information.
00010 
00011 #define BABYFTPD_CPP
00012 #define NETINCLS
00013 #define BABY_IO
00014 #define BABY_CORE
00015 #include "babyftpd.h"
00016 
00017 int main(int argc, char **argv)
00018 {
00019   pid_t pid;
00020 
00021   logging = new class Log;
00022   config = new class Configuration;
00023   config->parse_command_line(argc, argv);
00024   // we send the start message for logging here so that starts our log.
00025   logging->log_this(3, TYPE_INFO, config->general.welcome_msg + " started.");
00026   // the config file variable exists in the Configuration class :p
00027   if(config->parse_config_file() == false)
00028     cout << "Failed to open the config file (" << config->conf_file <<
00029       "), check if it exists + permissions." << endl;
00030   // if the conffile changes variables from commandline we reset them.
00031   config->parse_command_line(argc, argv);
00032 
00033   if(config->general.no_daemon == false)
00034     pid = fork();
00035   else
00036     pid = 0;
00037   
00038   switch(pid)
00039   {
00040     case -1:
00041       // error
00042       exit(1);
00043       break;
00044       
00045     case 0:
00046       // child
00047       int new_pid;
00048       setsid();
00049       if(config->general.no_daemon == false)
00050         new_pid = fork();
00051       else
00052         new_pid = 0;
00053 
00054       switch(new_pid)
00055       {
00056         case -1:
00057           exit(0);
00058           break;
00059 
00060         case 0:
00061           break;
00062 
00063         default:
00064           _exit(0);
00065       }
00066 
00067       chdir("/");
00068       umask(0);
00069 
00070       close(STDIN_FILENO);
00071       close(STDOUT_FILENO);
00072       close(STDERR_FILENO);
00073 
00074       if(!config->general.pid_file.empty())
00075       {
00076         ofstream pidstream;
00077         pidstream.open(config->general.pid_file.c_str(), ofstream::out);
00078         if(!pidstream)
00079           logging->log_this(2, TYPE_INFO, "Failed to open pid_file " +
00080               config->general.pid_file + " for writing. continuing.");
00081         else
00082         {
00083           pidstream << getpid();
00084           pidstream.close();
00085         }
00086       }
00087 
00088       // we can't spawn the logging thread until after we are daemonized
00089       pthread_create(&logging_thread, NULL, log_wrapper, NULL);
00090 
00091       pthread_key_create(&my_user, kill_user);
00092       pthread_key_create(&my_data, kill_data);
00093 
00094       pthread_rwlock_init(&user_list_lock, NULL);
00095       sem_init(&cleanup_list_lock, 0, 1);
00096       sem_init(&comm.pam_lock, 0, 1);
00097 
00098       signal(SIGTERM, sig_handler);
00099       signal(SIGINT, sig_handler);
00100       signal(SIGHUP, sig_handler);
00101       signal(SIGPIPE, sig_handler);
00102 
00103       main_thread = pthread_self();
00104 
00105 #ifdef USE_TLS
00106 #ifndef OLD_GCRYPT
00107       gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
00108 #endif // OLD_GCRYPT
00109       int ret;
00110       pthread_rwlock_init(&cache_lock, NULL);
00111       gnutls_global_init();
00112 
00113       gnutls_certificate_allocate_credentials(&x509_cred);
00114       ret = gnutls_certificate_set_x509_trust_file(x509_cred,
00115           config->general.tls_ca.c_str(), GNUTLS_X509_FMT_PEM);
00116       if(ret < 0)
00117         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
00118 
00119       ret = gnutls_certificate_set_x509_crl_file(x509_cred,
00120           config->general.tls_crl.c_str(), GNUTLS_X509_FMT_PEM);
00121       if(ret < 0)
00122         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
00123 
00124       ret = gnutls_certificate_set_x509_key_file(x509_cred,
00125           config->general.tls_cert.c_str(), config->general.tls_key.c_str(),
00126           GNUTLS_X509_FMT_PEM);
00127       if(ret < 0)
00128         logging->log_this(2, TYPE_INFO, gnutls_strerror(ret));
00129 
00130       gnutls_dh_params_init(&dh_params);
00131 
00132       gnutls_dh_params_generate2(dh_params, 1024);
00133 
00134       gnutls_certificate_set_dh_params(x509_cred, dh_params);
00135       dh_time = time(NULL);
00136 
00137 #endif // USE_TLS
00138       handler = new Handler;
00139       listener();
00140       break;
00141 
00142     default:
00143       // parent
00144       _exit(0);
00145       break;
00146   }
00147 }
00148 
00149 bool listener(void)
00150 {
00151   struct sockaddr_in address;
00152   struct timeval tv = {config->general.lifebeat_tick,0};
00153 
00154   int first_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00155   address.sin_family = AF_INET;
00156   address.sin_addr.s_addr = INADDR_ANY;
00157   address.sin_port = htons(config->general.port);
00158   if(bind(first_socket, reinterpret_cast<struct sockaddr*>(&address),
00159         sizeof(address)) < 0)
00160   {
00161     logging->log_this(2, TYPE_INFO, "Port " +
00162         util.itos(config->general.port) + " already in use.");
00163     exit_baby(1, EXIT_FULL);
00164   }
00165   seteuid(config->general.user);
00166   setgid(config->general.group);
00167   listen(first_socket, LISTEN_LIMIT);
00168   while(true)
00169   {
00170     pthread_t thread_tmp;
00171     socklen_t socketlen = sizeof(struct sockaddr_in);
00172     fd_set sock_set;
00173     time_t before = time(NULL) - (config->general.lifebeat_tick - tv.tv_sec);
00174 
00175     FD_ZERO(&sock_set);
00176     FD_SET(first_socket, &sock_set);
00177     if(!select(first_socket + 1, &sock_set, NULL, NULL, &tv))
00178     {
00179       lifebeat();
00180       tv.tv_sec = config->general.lifebeat_tick; // important to reset lifebeat
00181       continue;
00182     }
00183 
00184     int *final_socket = new int;
00185     *final_socket = accept(first_socket,
00186         reinterpret_cast<struct sockaddr*>(&address), &socketlen);
00187     if(*final_socket != -1)
00188       pthread_create(&thread_tmp, NULL, user_wrapper, (final_socket));
00189     else
00190     {
00191       logging->log_this(3, TYPE_INFO, "Failed to accept new caller.");
00192       // if the socket is accepted and the thread created the pointer will
00193       // get deleted there after it is stored.
00194       delete final_socket;
00195     }
00196 
00197     tv.tv_sec = config->general.lifebeat_tick - (time(NULL) - before);
00198     if(tv.tv_sec < 0 || tv.tv_sec > config->general.lifebeat_tick)
00199       tv.tv_sec = 0;
00200   }
00201   close(first_socket);
00202 
00203   return(true);
00204 }
00205 
00206 void lifebeat(void)
00207 {
00208   logging->log_this(4, TYPE_DEBUG, "lifebeat reached.");
00209 
00210   sem_wait(&cleanup_list_lock);
00211   while(!cleanup_list.empty())
00212   {
00213     logging->log_this(4, TYPE_DEBUG, "joining thread #" +
00214         util.itos(cleanup_list.back()));
00215     pthread_join(cleanup_list.back(), NULL);
00216     cleanup_list.pop_back();
00217   }
00218   sem_post(&cleanup_list_lock);
00219 
00220 #ifdef USE_TLS
00221   if((time(NULL) - dh_time) > config->general.dh_regenerate)
00222   {
00223     gnutls_dh_params temp_dh_params;
00224     gnutls_dh_params_init(&temp_dh_params);
00225     gnutls_dh_params_generate2(temp_dh_params, 1024);
00226     gnutls_dh_params_cpy(dh_params, temp_dh_params);
00227     gnutls_dh_params_deinit(temp_dh_params);
00228     gnutls_certificate_set_dh_params(x509_cred, dh_params);
00229     dh_time = time(NULL);
00230   }
00231 #endif // USE_TLS
00232 }
00233 
00234 static void *user_wrapper(void *socket_id)
00235 {
00236   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
00237 
00238   class User *user = handler->initializer((*(static_cast<int*>(socket_id))));
00239   delete (static_cast<int*>(socket_id));
00240   pthread_setspecific(my_user, static_cast<void*>(user));
00241 
00242   handler->init(user);
00243 
00244   logging->log_this(3, TYPE_DEBUG, "closed connection from: " +
00245       user->host_name());
00246   pthread_exit(NULL); // the thread exits..., and takes the user with it..
00247 }
00248 
00249 static void *log_wrapper(void *not_used)
00250 {
00251   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
00252   logging->log_main();
00253 
00254   pthread_exit(NULL);
00255 }
00256 
00257 
00258 void sig_handler(int signal)
00259 {
00260   switch(signal)
00261   {
00262     case SIGTERM:
00263     case SIGINT:
00264       logging->log_this(3, TYPE_INFO, "SIGTERM received.");
00265 
00266       logging->log_this(4, TYPE_DEBUG, "main thread. users: " +
00267           util.itos(user_list.size()));
00268 
00269       for(vector<User*>::iterator user_it = user_list.begin();
00270           user_it != user_list.end(); user_it++)
00271         pthread_cancel((*user_it)->thread_id());
00272 
00273       struct timeval times;
00274       times.tv_sec = 1;
00275       times.tv_usec = 0;
00276       select(0, NULL, NULL, NULL, &times);
00277 
00278       logging->log_this(3, TYPE_INFO, "main thread exiting.");
00279       exit_baby(1, EXIT_FULL);
00280       break;
00281 
00282     case SIGHUP:
00283       logging->log_this(3, TYPE_INFO,
00284           "SIGHUP received, reloading configuration");
00285       config->parse_config_file();
00286       break;
00287 
00288     case SIGPIPE:
00289       logging->log_this(3, TYPE_INFO,
00290           "SIGPIPE received in thread: " + util.itos(pthread_self()));
00291       if(!pthread_equal(pthread_self(), main_thread) ||
00292           pthread_equal(pthread_self(), logging_thread))
00293         pthread_exit(NULL); // if this is a user or data thread, bail.
00294       break;
00295   }
00296 }
00297 
00298 void exit_baby(int num, enum exits how)
00299 {
00300   if(how == EXIT_FULL)
00301   {
00302     if(!config->general.pid_file.empty())
00303       unlink(config->general.pid_file.c_str());
00304 
00305     while(!cleanup_list.empty())
00306     {
00307       logging->log_this(4, TYPE_DEBUG, "joining thread #" +
00308           util.itos(cleanup_list.back()));
00309       pthread_join(cleanup_list.back(), NULL);
00310       cleanup_list.pop_back();
00311     }
00312 
00313     delete handler;
00314     pthread_key_delete(my_user);
00315     pthread_key_delete(my_data);
00316 
00317 #ifdef USE_TLS
00318     // emptying and freeing session cache.
00319     for(vector<cache*>::iterator cache_it = cache_db.begin();
00320         cache_it != cache_db.end(); cache_it++)
00321     {
00322       gnutls_free((*cache_it)->key.data);
00323       gnutls_free((*cache_it)->data.data);
00324       delete(*cache_it);
00325       cache_db.erase(cache_it);
00326     }
00327 
00328     gnutls_dh_params_deinit(dh_params);
00329     gnutls_certificate_free_credentials(x509_cred);
00330     gnutls_global_deinit();
00331     pthread_rwlock_destroy(&cache_lock);
00332 #endif // USE_TLS 
00333     sem_destroy(&comm.pam_lock);
00334     sem_destroy(&cleanup_list_lock);
00335     pthread_rwlock_destroy(&user_list_lock);
00336     pthread_cancel(logging_thread);
00337     pthread_join(logging_thread, NULL);
00338   }
00339   delete logging;
00340 
00341   delete config;
00342   exit(num);
00343 }
00344 
00345 #ifdef USE_TLS // code for session resuming
00346 
00347 int tls_db_store(void *dbf, gnutls_datum key, gnutls_datum data)
00348 {
00349   struct cache *temp_cache = new cache;
00350 
00351   temp_cache->key.data = static_cast<unsigned char*>(gnutls_malloc(key.size));
00352   temp_cache->key.size = key.size;
00353   memcpy(temp_cache->key.data, key.data, key.size);
00354 
00355   temp_cache->data.data =
00356     static_cast<unsigned char*>(gnutls_malloc(data.size));
00357   temp_cache->data.size = data.size;
00358   memcpy(temp_cache->data.data, data.data, data.size);
00359 
00360   pthread_rwlock_wrlock(&cache_lock);
00361   cache_db.push_back(temp_cache);
00362   pthread_rwlock_unlock(&cache_lock);
00363   return(0);
00364 }
00365 
00366 gnutls_datum tls_db_fetch(void *dbf, gnutls_datum key)
00367 {
00368   gnutls_datum data = { NULL, 0 };
00369 
00370   pthread_rwlock_rdlock(&cache_lock);
00371   for(vector<cache*>::iterator cache_it = cache_db.begin();
00372       cache_it != cache_db.end(); cache_it++)
00373     if(memcmp(key.data, (*cache_it)->key.data, (*cache_it)->key.size) == 0)
00374     {
00375       data.size = (*cache_it)->data.size;
00376       data.data = static_cast<unsigned char*>(gnutls_malloc(data.size));
00377       memcpy(data.data, (*cache_it)->data.data, data.size);
00378       break;
00379     }
00380   pthread_rwlock_unlock(&cache_lock);
00381 
00382   return(data);
00383 }
00384 
00385 int tls_db_delete(void *dbf, gnutls_datum key)
00386 {
00387   int ret = -1;
00388   pthread_rwlock_rdlock(&cache_lock);
00389   for(vector<cache*>::iterator cache_it = cache_db.begin();
00390       cache_it != cache_db.end(); cache_it++)
00391     if(memcmp(key.data, (*cache_it)->key.data, (*cache_it)->key.size) == 0)
00392     {
00393       pthread_rwlock_unlock(&cache_lock);
00394       pthread_rwlock_wrlock(&cache_lock);
00395       gnutls_free((*cache_it)->key.data);
00396       gnutls_free((*cache_it)->data.data);
00397       delete(*cache_it);
00398       cache_db.erase(cache_it);
00399       ret = 0;
00400       break;
00401     }
00402   pthread_rwlock_unlock(&cache_lock);
00403 
00404   return(ret);
00405 }
00406 
00407 #endif // USE_TLS