From: Gonzalo A. <ga...@us...> - 2006-09-08 14:31:02
|
Update of /cvsroot/mod-c/ehtml/src In directory sc8-pr-cvs7.sourceforge.net:/tmp/cvs-serv15294/src Modified Files: Session.cpp Added Files: DefaultSessionAddress.h DefaultSessionDriver.h DefaultSessionProtocol.h DefaultSessionAddress.cpp DefaultSessionDriver.cpp DefaultSessionIDDriver.cpp DefaultSessionServer.cpp DiskSessionDriver.cpp Log Message: Session handling shifted from mod_c to EHTML (more work on this to come). Session handling is divided into: - Session ID generation (SessionIDDriver class/Session.h) - Session storage (SessionDrive class/Session.h) - Session usage: o serialization & marshalling, o accessing/setting session variables, (Session classs/Session.h) --- NEW FILE: DefaultSessionAddress.h --- #ifndef __DEFAULT_SESSION_ADDRESS_H_ #define __DEFAULT_SESSION_ADDRESS_H_ #include <string> extern "C" { #include <sys/socket.h> // ipv4 sockets #include <netinet/in.h> #include <netinet/ip.h> /* superset of previous */ #include <arpa/inet.h> // unix sockets #include <sys/un.h> #include <unistd.h> }; class Address { public: int address_family, protocol_family; union { struct sockaddr _addr; struct sockaddr_un _unix; struct sockaddr_in _inet; } addr; size_t addr_len; Address(); ~Address(); bool SetArgs (const std::string& arg); }; #endif --- NEW FILE: DiskSessionDriver.cpp --- #include <Session.h> #include <Common.h> #include <unistd.h> #include <iostream> #include <sstream> #include <fstream> using namespace std; class DiskSessionDriver: public SessionDriver { string spool; bool connected; uint32_t collect_prob; time_t old_enough; string filename(const string& id); string locked_filename(const string& id); public: DiskSessionDriver(): connected(false), collect_prob(1), old_enough(86400) { ; } DiskSessionDriver(const char* _name): connected(false), collect_prob(1), old_enough(86400) { name(_name); } virtual ~DiskSessionDriver() { ; } virtual bool SetArgs(const std::string& arg); virtual bool Connect(); virtual bool Disconnect(); virtual Session* Get(const SessionID& id); virtual bool Save(Session*); virtual bool Remove(Session*); virtual bool Release(Session*); bool mayBeCollect(); }; string DiskSessionDriver::filename(const string& hexid) { return spool + "/" + hexid; } string DiskSessionDriver::locked_filename(const string& hexid) { return filename(hexid) + ",$$"; } bool DiskSessionDriver::mayBeCollect() { uint32_t i = random(); time_t now = time(NULL); if (i < collect_prob) { // find $spool -type f -mtime +old_enough DIR* dh = opendir(spool.c_str()); if (dh == NULL) return false; struct dirent* de; while (de = readdir(dh)) { string path = spool + " " + de->d_name; struct stat st; if (stat(path.c_str(), &st) < 0) return false; if (!S_ISREG(st.st_mode)) continue; if (st.st_mtime + old_enough < now && st.st_ctime + old_enough < now) { unlink(path.c_str()); continue; } //@TODO: we should check for expired sessions //old_enough should cover this. } closedir(dh); } return true; } bool DiskSessionDriver::SetArgs(const string& arg) { //@TODO: should check that no session is open. istringstream i(arg.c_str()); string _arg; while (i >> _arg) { if (!strncmp(_arg.c_str(),"spool=",6)) { spool = _arg.c_str() + 6; } else if (!strncmp(_arg.c_str(), "collect_prob=", 13)) { double p = xatod(_arg.c_str()+13); if (p > 1 || p < 0) return false; collect_prob = uint32_t(p * RAND_MAX); if (collect_prob == 0 && p != 0.0) collect_prob = 1; } else if (!strncmp(_arg.c_str(), "old_enough=", 11)) { char* end; old_enough = strtoul(_arg.c_str()+11, &end, 10); if (*end) return false; } else return false; } return true; } bool DiskSessionDriver::Connect() { if (connected) return false; return connected = is_directory(spool.c_str()); } bool DiskSessionDriver::Disconnect() { if (connected) { connected = false; return true; } return false; } Session* DiskSessionDriver::Get(const SessionID& id) { if (!connected) return NULL; mayBeCollect(); string name = filename(id.hex()); string lname = locked_filename(id.hex()); if (rename(name.c_str(), lname.c_str())) return NULL; Session* dev = new Session(); ifstream in(lname.c_str()); in >> *dev; if (!in.eof()) { delete dev; return NULL; } if (dev->Expires() < time(NULL)) { unlink(lname.c_str()); delete dev; dev = NULL; } //should check if !expired.... return dev; } bool DiskSessionDriver::Save(Session* s) { if (!connected) return false; string lfile = locked_filename(s->ID().hex()); ofstream out(lfile.c_str()); if (!out) return false; out << *s; return true; } bool DiskSessionDriver::Remove(Session* s) { if (!connected) return false; unlink(filename(s->ID().hex()).c_str()); unlink(locked_filename(s->ID().hex()).c_str()); } bool DiskSessionDriver::Release(Session* s) { if (!connected) return false; return rename(locked_filename(s->ID().hex()).c_str(), filename(s->ID().hex()).c_str()) == 0 ? true : false; } extern "C" SessionDriver* Disk() { return new DiskSessionDriver("Disk"); } --- NEW FILE: DefaultSessionDriver.h --- /*************************************************************************** * Copyright (C) 2005 by Matej Urbas * * mat...@gm... * * * * This program is free software; you can redistribute it and/or modify * * it 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef _DEF_SESSION_DRIVER_H_ #define _DEF_SESSION_DRIVER_H_ #include "Session.h" #include "DefaultSessionAddress.h" class DefaultSessionDriver: public SessionDriver { /** * File descriptor. */ Address addr; int fd; void init(); public: DefaultSessionDriver(); DefaultSessionDriver(const char* name); ~DefaultSessionDriver(); virtual bool SetArgs(const std::string& arg); virtual bool Connect(); virtual bool Disconnect(); virtual Session* Get(const SessionID& id); int Socket(); bool Bind(); virtual bool Save(Session*); virtual bool Remove(Session*); virtual bool Release(Session*); }; #endif --- NEW FILE: DefaultSessionAddress.cpp --- #include "DefaultSessionAddress.h" #include <sstream> using namespace std; Address::Address() { address_family = protocol_family = 0; addr_len = 0; } Address::~Address() { } bool Address::SetArgs (const string& arg) { istringstream s(arg); string opt; while (s >> opt) { if (!strncmp("addr=",opt.c_str(),5)) { string _addr(opt.c_str()+5); if (!strncmp("unix://",_addr.c_str(),7)) { address_family = AF_UNIX; protocol_family = PF_UNIX; addr_len = sizeof(sockaddr_un); addr._unix.sun_family = AF_UNIX; strncpy(addr._unix.sun_path,_addr.c_str()+7,sizeof(addr._unix.sun_path)); } else if (!_addr[0] == '/') { address_family = AF_UNIX; protocol_family = PF_UNIX; addr_len = sizeof(sockaddr_un); addr._unix.sun_family = AF_UNIX; strncpy(addr._unix.sun_path,_addr.c_str(),sizeof(addr._unix.sun_path)); } else if ("inet://",_addr.c_str(),7) { char host[16]; unsigned short port; if (sscanf(_addr.c_str()+7,"%16[.0-9]:%hu", host, &port) != 2) { errno = EINVAL; return false; } address_family = AF_INET; protocol_family = PF_INET; addr_len = sizeof(sockaddr_in); addr._inet.sin_family = AF_INET; addr._inet.sin_port = htons(port); if (!inet_aton(host, &addr._inet.sin_addr)) { errno = EINVAL; return false; } } else { errno = EINVAL; return false; } } else { errno = EINVAL; return false; } } return true; } Index: Session.cpp =================================================================== RCS file: /cvsroot/mod-c/ehtml/src/Session.cpp,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** Session.cpp 3 Mar 2006 14:45:17 -0000 1.11 --- Session.cpp 8 Sep 2006 14:30:59 -0000 1.12 *************** *** 20,408 **** #include <Common.h> ! #include <Session.h> ! #include <EHTMLApplication.h> #include <Request.h> ! ! #include <string> using namespace std; ! Session::Session() throw ( const char * ) ! { ! throw "The default constructor is disabled for sessions."; ! } ! ! Session::~Session() ! { ! CloseConnection(); ! DeleteData(); ! } ! Session::Session( EHTMLApplication & App ) ! : Application( &App ), ConnectionInited( false ), IsBroken( false ) ! { ! ReqCon = App.GetRequestContext(); ! mod_c_dir_config * dir = ReqCon->dir_config; ! // Set the ID, Data and Capacity to to NULL ! Data.Id = NULL; ! Data.Capacity = 0; ! Data.Data = NULL; ! Data.Duration = dir->session_duration; ! Data.IdSize = dir->key_size; ! // Get the session API ! SessionApi = dir->session_funcs; ! ! // Set the default deallocator ! Dealloc = NULL; } ! int Session::InitConnection() ! { ! if ( !ConnectionInited ) ! { ! int retVal = EHTML_OK; ! if ( SessionApi->InitConnection ) ! retVal = SessionApi->InitConnection( &Data, ReqCon, &DrData ); ! IsBroken = ( retVal != EHTML_OK ); ! ConnectionInited = !IsBroken; ! return retVal; ! } ! else ! return EHTML_OK; } ! int Session::CloseConnection() ! { ! if ( ConnectionInited && SessionApi->EndConnection ) ! { ! ConnectionInited = false; ! return SessionApi->EndConnection( &Data, ReqCon, DrData ); ! } ! else ! return EHTML_OK; } ! int Session::SetDuration( int Duration ) ! { ! if ( IsValid() && IsConnected() ) ! { ! // Save the old duration in case it didn't get updated ! int tmpDur = Data.Duration; ! // Set the new duration ! Data.Duration = Duration; ! int retVal = SessionApi->SetDuration( &Data, ReqCon, DrData ); ! // Change the duration back to the previous value if the call failed ! if ( retVal != EHTML_OK ) ! Data.Duration = tmpDur; ! return retVal; ! } ! else ! return EHTML_ERR; } ! int Session::Store() ! { ! if ( IsValid() && IsConnected() ) ! return SessionApi->SetSessionData( &Data, ReqCon, DrData ); ! else ! return EHTML_ERR; } ! int Session::Reload() ! { ! if ( IsValid() && IsConnected() ) ! { ! void * tmp = Data.Data; ! int tmpDataSize = Data.DataSize; ! int tmpCapacity = Data.Capacity; ! int retVal = SessionApi->GetSessionData( &Data, ReqCon, DrData ); ! ! // Free the data if it changed... ! if ( retVal == EHTML_OK && tmp != Data.Data && tmp != NULL && Dealloc ) ! { ! Dealloc( tmp ); ! Dealloc = NULL; ! } ! else if ( Data.Data != tmp ) ! { ! // If the call failed and the data got changed, don't update ! Data.Data = tmp; ! Data.DataSize = tmpDataSize; ! Data.Capacity= tmpCapacity; ! } ! ! return retVal; ! } ! else ! return EHTML_OK; } ! int Session::GetData( void ** Data ) ! { ! *Data = this->Data.Data; ! return this->Data.DataSize; } ! void Session::SetData( void * Data, int DataSize, DeallocatorFunc Deallocator ) ! { ! // Deallocate old data ! if ( Dealloc && this->Data.Data ) ! Dealloc( this->Data.Data ); ! // Set the new deallocator and new data ! Dealloc = Deallocator; ! this->Data.Data = Data; ! this->Data.Capacity = this->Data.DataSize = DataSize; } ! void Session::DeleteData() ! { ! if ( Dealloc && Data.Data ) ! Dealloc( Data.Data ); ! Data.Data = 0; ! Data.Capacity = 0; ! Data.DataSize = 0; } ! int Session::Unregister() ! { ! if ( IsValid() && IsConnected() ) ! { ! SessionApi->UnregisterID( &Data, ReqCon, DrData ); ! CloseConnection(); ! Data.Id = 0; ! } ! ! return EHTML_OK; } ! const std::string & Session::GetIdString() const ! { ! if ( EncodedId.empty() && IsValid() ) ! { ! int len = ( Data.IdSize << 1 ) + 1; ! char * tmp = new char[ len ]; ! int retVal = GetStringFromByte( tmp, len, (unsigned char*) Data.Id, Data.IdSize ); ! if ( retVal > 0 ) ! const_cast< Session* >( this )->EncodedId = tmp; ! delete tmp; ! } ! return EncodedId; } ! Session * Session::RestoreSession( EHTMLApplication & App, const void * Id, int IdSize ) ! { ! Session * tmp = new Session( App ); ! ! // Copy the Id... ! void * tmpId = NULL; ! if ( Id != NULL && IdSize > 0 ) ! { ! tmpId = apr_palloc( App.GetRequestContext()->r->pool, IdSize ); ! memcpy( tmpId, Id, IdSize ); ! } ! // Initialize the connection ! if ( tmp->InitConnection() == EHTML_OK ) ! // Register the session ! tmp->Restore( tmpId, IdSize ); ! // Was the session restored correctly? ! if ( tmp->IsValid() ) ! { ! App.OpenSessions.push_back( tmp ); ! return tmp; ! } ! else ! { ! delete tmp; ! return NULL; ! } } ! Session * Session::RestoreSession( EHTMLApplication & App, const std::string & Id ) ! { ! if ( Id.length() > 0 ) ! { ! Session * tmp = new Session( App ); ! // Initialize the connection ! if ( tmp->InitConnection() == EHTML_OK ) ! // Register the session ! tmp->Restore( Id ); ! // Was the session restored correctly? ! if ( tmp->IsValid() ) ! { ! App.OpenSessions.push_back( tmp ); ! return tmp; ! } ! else ! { ! delete tmp; ! return NULL; ! } ! } ! else ! return NULL; } - Session * Session::RestoreSession( EHTMLApplication & App, const char * Id ) - { - if ( Id && *Id ) - { - Session * tmp = new Session( App ); - // Initialize the connection - if ( tmp->InitConnection() == EHTML_OK ) - // Register the session - tmp->Restore( Id ); ! // Was the session restored correctly? ! if ( tmp->IsValid() ) ! { ! App.OpenSessions.push_back( tmp ); ! return tmp; ! } ! else ! { ! delete tmp; ! return NULL; ! } ! } ! else ! return NULL; } ! Session * Session::CreateSession( EHTMLApplication & App, int IdSize, int Duration ) ! { ! Session * tmp = new Session( App ); ! // Set the Id size and duration ! tmp->SetInitConfig( IdSize, Duration ); ! // Initialize the connection ! if ( tmp->InitConnection() == EHTML_OK ) ! // Register the session ! tmp->Register(); ! ! // Was the session registered correctly? ! if ( tmp->IsValid() ) ! { ! App.OpenSessions.push_back( tmp ); ! return tmp; ! } ! else ! { ! delete tmp; ! return NULL; ! } } ! int Session::Register() ! { ! // The session should be inited first ! if ( !IsConnected() ) ! return EHTML_ERR; ! // Register a new session ! int retVal = SessionApi->RegisterID( &Data, ReqCon, DrData ); ! if ( retVal != EHTML_OK ) ! Data.Id = NULL; ! return retVal; } ! int Session::Restore( const string & Id ) ! { ! // The session should be inited first ! if ( !IsConnected() ) ! return EHTML_ERR; ! if ( Id.length() > 0 ) ! { ! // We have found the Id ! EncodedId = Id; ! // Convert it into a more understandable format ! int tmp = ( Id.length() >> 1 ) + 1; ! Data.Id = apr_palloc( ReqCon->r->pool, tmp ); ! if ( ( Data.IdSize = GetByteFromString( (unsigned char*) Data.Id, tmp, EncodedId.c_str() ) ) > 0 ) ! { ! // We have the Id, get the data ! tmp = SessionApi->GetSessionData( &Data, ReqCon, DrData ); ! // If not successful, invalidate the session ! if ( tmp != EHTML_OK ) ! Data.Id = NULL; ! return tmp; ! } ! else ! // The conversion did not succeed ! Data.Id = NULL; ! } ! ! return EHTML_ERR; } ! int Session::Restore( const char * Id ) ! { ! if ( Id && *Id ) ! { ! // We have found the Id ! EncodedId = Id; ! return Restore( EncodedId ); ! } ! ! return EHTML_ERR; } ! int Session::Restore( void * Id, int IdSize ) ! { ! // The session should be inited first ! if ( !IsConnected() ) ! return EHTML_ERR; ! ! // The restore method should not be called after a session has already been ! // created. ! if ( IsValid() ) ! return EHTML_INVALID_CALL; ! if ( Id == NULL ) ! { ! // Search for the Id in the arguments (or in the cookie) ! if ( Application->GetRequestContext()->dir_config->cookieless ) ! { ! // Search in the query string ! const string * str = Application->GetRequest()->GetArgument( Session::SessionIdName ); ! if ( str ) ! return Restore( *str ); ! } ! else ! { ! // Search in the cookies ! // TODO: Search for the cookie ! } ! ! return EHTML_ERR; ! } ! else ! { ! // Set the desired Id ! Data.Id = Id; ! Data.IdSize = IdSize; ! // Get the data ! int retVal = SessionApi->GetSessionData( &Data, ReqCon, DrData ); ! if ( retVal != EHTML_OK ) ! Data.Id = NULL; ! // Return the return code ! return retVal; ! } } ! void Session::SetInitConfig( int IdSize, int Duration ) ! { ! if ( !IsValid() ) ! { ! Data.Duration = ( Duration > 0 ) ? Duration : ReqCon->dir_config->session_duration; ! Data.IdSize = ( IdSize > 0 ) ? IdSize : ReqCon->dir_config->key_size; ! } } - const string Session::SessionIdName( "ehtmlSID" ); --- 20,305 ---- #include <Common.h> ! #include "Session.h" #include <Request.h> ! #include <dlfcn.h> using namespace std; ! string Session::_session_id_name( "ehtmlSID" ); ! DECLARE_PLUGIN(SessionDriver); ! DECLARE_PLUGIN(SessionIDDriver); ! ////////////////////////////////////////////////////////////////////////////// ! // ! // Session ID ! // ! SessionID::SessionID() { ! _binary_set = 0; ! _base64_set = 0; ! _hex_set = 0; ! _url_set = 0; } ! SessionID::SessionID(const SessionID& id): _binary(id._binary), ! _base64(id._base64), _hex(id._hex), _urlencoded(id._urlencoded) { ! _binary_set = id._binary_set; ! _base64_set = id._base64_set; ! _hex_set = id._hex_set; ! _url_set = id._url_set; } ! SessionID::SessionID(void* b, size_t nbytes, bool dup): _binary(b,nbytes) { ! if (dup) ! _binary = MemBuf::Dup(b,nbytes); ! _binary_set = 1; ! _base64_set = 0; ! _hex_set = 0; ! _url_set = 0; } ! SessionID::~SessionID() { } ! MemBuf& SessionID::binary() throw (const char*) { ! if (!_binary_set) ! throw "binary session id is not set"; ! return _binary; } ! const MemBuf& SessionID::binary() const throw (const char*) { ! if (!_binary_set) ! throw "binary session id is not set"; ! return _binary; } ! bool SessionID::binary(MemBuf& mb) { ! _binary = mb; ! _binary_set = 1; ! _base64_set = 0; _base64.clear(); ! _hex_set = 0; _hex.clear(); ! _url_set = 0; _urlencoded.clear(); } ! string& SessionID::base64() throw (const char*) { ! if (!_base64_set) { ! _base64 = base64encode(MemBuf(binary().Buffer(), binary().Size())); ! _base64_set = 1; ! } ! return _base64; } ! const string& SessionID::base64() const throw (const char*) { ! if (!_base64_set) { ! //@todo we could avoid memdup here.... ! _base64 = base64encode(MemBuf::Dup(binary().Buffer(), binary().Size())); ! _base64_set = 1; ! } ! return _base64; } ! bool SessionID::base64(string& s) { ! _base64 = s; ! _binary = base64decode(s); ! _base64_set = 1; ! _hex_set = 0; _hex.clear(); ! _url_set = 0; _urlencoded.clear(); } ! string& SessionID::hex() throw (const char*) { ! if (!_hex_set) { ! _hex = hexencode(MemBuf::Dup(binary().Buffer(), binary().Size())); ! _hex_set = 1; ! } ! return _hex; } ! const string& SessionID::hex() const throw (const char*) { ! if (!_hex_set) { ! //@todo we could avoid memdup here.... ! _hex = hexencode(MemBuf::Dup(binary().Buffer(), binary().Size())); ! _hex_set = 1; ! } ! return _hex; ! } ! bool SessionID::hex(string& s) { ! _hex = s; ! _binary = hexdecode(s); ! _base64_set = 0; ! _hex_set = 1; _hex.clear(); ! _url_set = 0; _urlencoded.clear(); ! } ! string& SessionID::urlencoded() throw (const char*) { ! if (!_url_set) { ! _urlencoded = urlencode(_binary); ! _url_set = 1; ! } ! return _urlencoded; } ! const string& SessionID::urlencoded() const throw (const char*) { ! if (!_url_set) { ! _urlencoded = urlencode(_binary); ! _url_set = 1; ! } ! return _urlencoded; ! } ! bool SessionID::urlencoded(string& s) { ! _urlencoded = s; ! _binary = urldecode(s); ! _url_set = 1; ! _hex_set = 0; _hex.clear(); ! _base64_set = 0; _base64.clear(); } ! ! ////////////////////////////////////////////////////////////////////////////// ! // ! // Session Driver ! // ! ! int registerSessionDriver(const char* driver_name, const char* filename) { ! fprintf(stderr, "registerSessionDriver(%s,%s)\n",driver_name, filename); ! void* dlh = dlopen(filename, RTLD_LAZY); ! if (dlh == NULL) ! return -1; ! void* dls = dlsym(dlh, driver_name); ! if (dls == NULL) { ! errno = ENOENT; ! dlclose(dlh); ! return -1; ! } ! SessionDriver::Factory build = (SessionDriver::Factory)dls; ! SessionDriver* driver = build(); ! if (!SessionDriver::Register(driver)) { ! errno = EEXIST; ! return -1; ! } ! return 0; } ! int useSessionDriver(const char* name, const char* arg) { ! fprintf(stderr,"useSessionDriver(%s,%s)\n",name,arg); ! SessionDriver* selected = SessionDriver::getByName(name); ! if (selected == NULL) { ! errno = ENOENT; ! return -1; ! } ! selected = SessionDriver::Select(selected); ! if (selected == NULL) { ! errno = EFAULT; ! return -1; ! } ! if (!selected->SetArgs(arg)) ! return -1; ! return 0; } ! ////////////////////////////////////////////////////////////////////////////// ! // ! // Session ID Driver ! // ! int registerSessionIDDriver(const char* driver_name, const char* filename) { ! fprintf(stderr, "registerSessionIDDriver(%s,%s)\n",driver_name, filename); ! void* dlh = dlopen(filename, RTLD_LAZY); ! if (dlh == NULL) ! return -1; ! void* dls = dlsym(dlh, driver_name); ! if (dls == NULL) { ! errno = ENOENT; ! dlclose(dlh); ! return -1; ! } ! SessionIDDriver::Factory build = (SessionIDDriver::Factory)dls; ! SessionIDDriver* driver = build(); ! if (!SessionIDDriver::Register(driver)) { ! errno = EEXIST; ! return -1; ! } ! return 0; ! } ! int useSessionIDDriver(const char* name, const char* arg) { ! fprintf(stderr,"useSessionIDDriver(%s,%s)\n",name,arg); ! SessionIDDriver* selected = SessionIDDriver::getByName(name); ! if (selected == NULL) { ! errno = ENOENT; ! return -1; ! } ! selected = SessionIDDriver::Select(selected); ! if (selected == NULL) { ! errno = EFAULT; ! return -1; ! } ! if (arg && !selected->SetArgs(arg)) ! return -1; ! return 0; ! } ! SessionDriver::~SessionDriver() { ! //@todo what has to be done here?? ! //should check that no pending sessions are hanging. } ! ////////////////////////////////////////////////////////////////////////////// ! // ! // Session ! // ! Session* SessionDriver::Get() { ! assert(_app != NULL); ! Request* req = _app->GetRequest(); ! const string* id = Session::CookieLess() ? ! req->GetArgument(Session::SessionIDName()) : ! req->GetArgument(Session::SessionIDName()); ! /* @TODO req->GetCookie(Session::session_id_name()) */ ! //@todo TODO: MUST USE CONFIGURATION DIRECTIVE TO SEE WHICH ENCODING ! //IS TO BE USED. ! return id ? Get(SessionID((void*)id->c_str(), id->length())) : NULL; } ! string Session::Serialize() { ! char _e[11]; ! snprintf(_e, 11, "%lu", _expires); ! operator[]("$$__EXPIRES$$") = _e; ! string s(Dictionary::Serialize()); ! erase("$$__EXPIRES$$"); ! return s; } ! ostream& operator << (ostream& o, Session& s) { ! char _e[11]; ! snprintf(_e, 11, "%lu", s.Expires()); ! s["$$__EXPIRES$$"] = _e; ! o << *static_cast<Dictionary*>(&s); ! s.erase("$$__EXPIRES$$"); ! return o; ! } ! bool Session::Marshall(const string& s) { ! if (!Dictionary::Marshall(s)) ! return false; ! _expires = xatoul(operator[]("$$__EXPIRES$$").c_str()); ! erase("$$__EXPIRES$$"); ! return true; } ! istream& operator >> (istream& i, Session& s) { ! i >> *static_cast<Dictionary*>(&s); ! s.Expires(xatoul(s["$$__EXPIRES$$"].c_str())); ! s.erase("$$__EXPIRES$$"); ! return i; ! } ! ! Session::~Session() { ! //@TODO: would be nice to check selected is the same session driver which ! //created me.... ! SessionDriver::Selected()->Release(this); } --- NEW FILE: DefaultSessionIDDriver.cpp --- #include "Session.h" #include "Common.h" using namespace std; //@TODO: add librandom: r250 (http://www.taygeta.com/random.html) //@TODO: add librandom: mls //@todo define a custom HTTP encoding #if 0 char i2c[65] = "abcdefghijklmnopqrstuvwxyz" // 26 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // 26 => 52 "0123456789" // 10 => 62 "_-"; // 2 => 64 int c2i(char c) { if (c >= 'a' && c <= 'z') return c-'a'; if (c >= 'A' && c <= 'Z') return int(c)-'A'+26; if (c == '_') return 62; if (c == '-') return 63; } // in: char[3] // out: char[4] static void b64_ntoa(const char* in, char* out) { // 0000 0011 | 1111 2222 | 2233 3333 out[0] = i2c[(in[0] >> 2) & 0x3f]; out[1] = i2c[((in[0] << 4) | (in[1] >> 4)) & 0x3f]; out[2] = i2c[in[2] & 0x3f]; } // in: char[4] // out: char[3] static void b64_aton(const char* in, char* out) { // 0000 0011 | 1111 2222 | 2233 3333 uint32_t x[4]; x[0] = c2i(in[0]); x[1] = c2i(in[1]); x[2] = c2i(in[2]); x[3] = c2i(in[3]); out[0] = (x[0] << 2) | (x[1] >> 4); out[1] = (x[1] << 4) | (x[2] >> 2); out[3] = (x[2] << 6) | (x[3]); } #endif class DefaultSessionIDDriver: public SessionIDDriver { string priv_key; size_t extra_entropy; public: DefaultSessionIDDriver(const char* _name): extra_entropy(0) { name(_name); } DefaultSessionIDDriver(): extra_entropy(0) { ; } virtual bool SetArgs(const string& id); virtual bool ValidID(SessionID& id); virtual SessionID GenerateID(); }; bool DefaultSessionIDDriver::SetArgs(const string& arg) { bool dev = true; if (!strncmp(arg.c_str(), "extra_entropy=", 14)) extra_entropy = xatoul(arg.c_str()+14); else { errno = EINVAL; dev = false; } return dev; } bool DefaultSessionIDDriver::ValidID(SessionID& id) { if (id.binary().Size() != 4*(1+extra_entropy)) return false; struct timeval now, _id; uint32_t* uv = (uint32_t*)id.binary().Buffer(); gettimeofday(&now, NULL); _id.tv_sec = uv[0]; _id.tv_usec = uv[1]; return now.tv_sec+1 >= _id.tv_sec ? true : false; } struct sesid_t { uint32_t sec, usec, seq, rnd; }; SessionID DefaultSessionIDDriver::GenerateID() { static uint32_t seq = ehtml_random(); sesid_t* id = (sesid_t*)malloc(sizeof(sesid_t)); struct timeval now; gettimeofday(&now, NULL); id->sec = now.tv_sec; id->usec = (now.tv_usec << 12) | (ehtml_random() & 0x0fff); id->seq = ++seq; id->rnd = ehtml_random(); return SessionID(id, sizeof(*id)); } extern "C" SessionIDDriver* Default() { return new DefaultSessionIDDriver("Default"); } --- NEW FILE: DefaultSessionDriver.cpp --- /*************************************************************************** * Copyright (C) 2005 by Matej Urbas * * mat...@gm... * * * * This program is free software; you can redistribute it and/or modify * * it 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ //#include "DefaultSessionDriver.h" #include "Session.h" #include "DefaultSessionProtocol.h" #include <sstream> #include "DefaultSessionDriver.h" using namespace std; bool DefaultSessionDriver::SetArgs(const string& arg) { istringstream s(arg); string opt; while (s >> opt) { if (!strncmp("addr=",opt.c_str(),5)) { string _addr(opt.c_str()+5); if (!strncmp("unix://",_addr.c_str(),7)) { addr.address_family = AF_UNIX; addr.protocol_family = PF_UNIX; addr.addr_len = sizeof(sockaddr_un); addr.addr._unix.sun_family = AF_UNIX; strncpy(addr.addr._unix.sun_path,_addr.c_str()+7,sizeof(addr.addr._unix.sun_path)); } else if (!_addr[0] == '/') { addr.address_family = AF_UNIX; addr.protocol_family = PF_UNIX; addr.addr_len = sizeof(sockaddr_un); addr.addr._unix.sun_family = AF_UNIX; strncpy(addr.addr._unix.sun_path,_addr.c_str(),sizeof(addr.addr._unix.sun_path)); } else if (!strncmp("inet://",_addr.c_str(),7)) { char host[16]; unsigned short port; if (sscanf(_addr.c_str()+7, "%[.0-9]:%hu", host, &port) != 2) { errno = EINVAL; return false; } addr.address_family = AF_INET; addr.protocol_family = PF_INET; addr.addr_len = sizeof(sockaddr_in); addr.addr._inet.sin_family = AF_INET; addr.addr._inet.sin_port = htons(port); if (!inet_aton(host, &addr.addr._inet.sin_addr)) { errno = EINVAL; return false; } } else { errno = EINVAL; return false; } } else { errno = EINVAL; return false; } } return true; } void DefaultSessionDriver::init() { fd = -1; } DefaultSessionDriver::DefaultSessionDriver() { init(); } DefaultSessionDriver::DefaultSessionDriver(const char* _name) { init(); name(_name); } DefaultSessionDriver::~DefaultSessionDriver() { ; } Session* DefaultSessionDriver::Get (const SessionID& id) { // We are connected. Send the request and wait for the answer DSSGetData get_data; get_data.MsgType = DSS_CMD_GET_DATA; get_data.IdSize = id.binary().Size(); memcpy(get_data.Id, id.binary().Buffer(), get_data.IdSize); // Send the request if (send(fd, &get_data, sizeof(DSSGetData), 0) < 0) return NULL; // Receive the answer from the server - it should be data... DSSFetcherStruct buf; if (recv(fd, &buf, sizeof(DSSFetcherStruct), 0) < 0 || buf.MsgType != DSS_CMD_GET_DATA ) { if (buf.MsgType == DSS_CMD_STATUS && ((DSSStatusRespond*)&buf)->Type == DSS_STATUS_SESSION_OVER) errno = EHTML_TIMEOUT; else errno = EHTML_ERR; return NULL; } // Ok, we've got the data. Read the size and put it into the 'Parms' structure DSSGetData_Response * resp = ( DSSGetData_Response * ) &buf; Session* s = new Session(id); s->Expires(resp->LastAccess + resp->Duration); s->LastAccess(resp->LastAccess); s->Started(resp->SessionStarted); s->Modification(resp->Modified); // Two scenarios: a) one packet, or b) two packets if (resp->DataSize > MAX_GET_DATA_SIZE) { if (recv(fd, resp->Data + MAX_GET_DATA_SIZE, resp->DataSize - MAX_GET_DATA_SIZE, 0) < 0 || buf.MsgType != DSS_CMD_GET_DATA ) { delete s; return NULL; } } s->Marshall(string(resp->Data)); return s; } bool DefaultSessionDriver::Save(Session* s) { // We are connected. Send the request and wait for the answer bool dev = true; DSSFetcherStruct max_size; string data(s->Serialize()); DSSSetData * set_data = (DSSSetData*)&max_size; set_data->MsgType = DSS_CMD_SET_DATA; set_data->IdSize = s->ID().binary().Size(); set_data->DataSize = data.length(); set_data->Duration = s->Expires() - s->Started(); memcpy(set_data->Id, s->ID().binary().Buffer(), set_data->IdSize ); // We have 2 scenarios: // a) either all data fits into the first packet, or // b) the alternative ;) if (set_data->DataSize <= MAX_SET_DATA_SIZE) { memcpy(set_data->Data, data.c_str(), set_data->DataSize); if (send(fd, set_data, sizeof(DSSSetData) + set_data->DataSize, 0) < 0 ) dev = false; } else { memcpy(set_data->Data, data.c_str(), MAX_SET_DATA_SIZE ); if (send(fd, set_data, sizeof(DSSSetData) + MAX_SET_DATA_SIZE, 0) < 0 || send(fd, data.c_str() + MAX_SET_DATA_SIZE, set_data->DataSize - MAX_SET_DATA_SIZE, 0 ) < 0 ) dev = false; } // Receive the status answer... DSSStatusRespond response; if (recv(fd, &response, sizeof(DSSStatusRespond), 0 ) < 0 ) dev = false; else if ( response.MsgType == DSS_CMD_STATUS ) { int retVal = EHTML_OK; switch (response.Type ) { case DSS_STATUS_SESSION_OVER: errno = EHTML_TIMEOUT; dev = false; break; case DSS_STATUS_ERROR: errno = EHTML_ERR; dev = false; break; } return dev; } return dev; } bool DefaultSessionDriver::Remove (Session* s) { // Send the request and wait for the answer DSSUnregisterID unreg; unreg.MsgType = DSS_CMD_UNREGISTER_ID; unreg.IdSize = s->ID().binary().Size(); memcpy( unreg.Id, s->ID().binary().Buffer(), unreg.IdSize ); // Here goes our request if (send(fd, &unreg, sizeof(DSSUnregisterID), 0 ) < 0 ) return false; // Get the answer DSSStatusRespond response; if (recv(fd, &response, sizeof(DSSStatusRespond), 0) > 0 && response.MsgType == DSS_CMD_STATUS && response.Type == DSS_STATUS_OK) return true; errno = EHTML_ERR; return false; } bool DefaultSessionDriver::Release(Session* s) { //@todo we should implement this feature return true; } int DefaultSessionDriver::Socket() { if (fd < 0) fd = socket(addr.protocol_family, SOCK_STREAM, 0); return fd; } bool DefaultSessionDriver::Bind() { return bind(Socket(), &addr.addr._addr, addr.addr_len) == 0 ? true : false; } bool DefaultSessionDriver::Connect() { // Open a new connection to the server int fd = Socket(); if (fd < 0) return false; // Try to connect return connect(fd, &addr.addr._addr, addr.addr_len) >= 0 ? true : false; } bool DefaultSessionDriver::Disconnect() { // Post a quit message and close the connection DSSStandard rec; rec.MsgType = DSS_CMD_CLOSE; if(send(fd, &rec, sizeof( DSSStandard ), 0) < 0) { errno = EHTML_ERR; return false; } return close(fd) >= 0 ? true : false; } //@todo: register this driver extern "C" SessionDriver* Default() { return new DefaultSessionDriver("Default"); } --- NEW FILE: DefaultSessionServer.cpp --- /*************************************************************************** * Copyright (C) 2005 by Matej Urbas * * mat...@gm... * * * * This program is free software; you can redistribute it and/or modify * * it 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "DefaultSessionProtocol.h" #include "DefaultSessionAddress.h" #include "Session.h" #include <time.h> #include <sys/time.h> #include <map> #include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <stdlib.h> using namespace std; /** * Initialize the session server... Should be only called once (before child * init). * * @return 0 on success, otherwise it returns a pointer to a string explaining * the error... */ const char* DefSesSr_Init( mod_c_config* config ); /** * This method creates a new thread and returns instantly. * * @param config * additional configuration for the default session manager. */ void DefSesSr_RunServer( server_rec * s, mod_c_config* config, apr_pool_t *p ); /** * This number is used to generate an array that initializes the random number * generator to generate keys. The higher the number, the more secure the ID. */ #define RANDOM_KEY_SIZE 32 #define DEF_CLEANUP_INTERVAL 3600 /**< an hour */ /** * The interval (in seconds) in which the session map will be cleaned. */ static int CLEANUP_INTERVAL = DEF_CLEANUP_INTERVAL; /** * This one is for internal use... */ struct Id_Internal { char Id[MAX_ID_SIZE]; int IdSize; }; /** * This one is for internal use... */ struct DataEntry { void * Data; time_t SessionStarted; time_t LastTouched; time_t Modified; int Duration; int DataSize; int Capacity; }; /** * This one compares IDs for correct sorting in the map... */ struct IDComparator { bool operator()( const Id_Internal& __x, const Id_Internal& __y) const { return ( memcmp( __x.Id, __y.Id, MAX_ID_SIZE ) < 0 ) ? true : false; } }; /** * The map that stores session data... */ typedef map< Id_Internal, DataEntry, IDComparator > SessionDataMap; /** * This structure is used to pass the session server any configuration stuff it needs. */ struct __server_private_data { int socket; SessionDataMap * map; }; /** * The mutex that locks the map when reading/writing from/to it. */ pthread_mutex_t session_map_mutex = PTHREAD_MUTEX_INITIALIZER; /** * Key generation is a little ad hoc... * * TODO: Check for a better implementation... */ void GenerateId( char * Buf, int BufSize ); /** * The entry function - defined below... */ template < typename T > static void * RunServer ( void * ); /** * Starts the socket connection... */ void StartConnection( SessionDataMap * map, int socket ); /** * Cleans outdated sessions */ void* CleanerFunc( void* ); /** * The indicator which tells whether the server is still running or if it * stopped already. */ bool ServerRunning = false; /** * Removes a session entry from the map... */ inline void RemoveSessionEntry( SessionDataMap& TheMap, SessionDataMap::iterator& Entry ) { if ( Entry->second.Data ) delete [] (char*)Entry->second.Data; TheMap.erase( Entry ); } /** * Stores data for an existing session... */ inline void ProcessSetData( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { DSSSetData * data = ( DSSSetData * ) &Buf; int status = DSS_STATUS_SESSION_OVER; // Is the size OK? if ( data->IdSize < MIN_ID_SIZE || data->IdSize > MAX_ID_SIZE ) // Send an error message! status = DSS_STATUS_ERROR; else { // We can proceed with setting data Id_Internal tmp; tmp.IdSize = data->IdSize; memset( tmp.Id + data->IdSize, 0, MAX_ID_SIZE - data->IdSize ); memcpy( tmp.Id, data->Id, data->IdSize ); // Find the session in the map... pthread_mutex_lock( &session_map_mutex ); SessionDataMap::iterator _iter = TheMap.find( tmp ); if ( _iter != TheMap.end() ) { time_t now = time( 0 ); // Is the session outdated? if ( now - _iter->second.LastTouched > _iter->second.Duration * 60 ) { RemoveSessionEntry( TheMap, _iter ); status = DSS_STATUS_SESSION_OVER; } else { // Touch the session _iter->second.Modified = _iter->second.LastTouched = now; // Get the data size... if ( data->DataSize <= 0 ) { // Set the data size to zero... _iter->second.DataSize = 0; // We are finished - unlock the map... pthread_mutex_unlock( &session_map_mutex ); } else { // There is actually some data incoming... _iter->second.DataSize = data->DataSize; // If the current capacity of the data array is to small, // create a new one if ( _iter->second.Capacity < _iter->second.DataSize ) { _iter->second.Capacity = _iter->second.DataSize + 256; // Delete the old data array... if ( _iter->second.Data ) delete [] ((char*)_iter->second.Data); // Create a new one... _iter->second.Data = new char[ _iter->second.Capacity ]; } // Copy the data we already have int * data_size = &_iter->second.DataSize; char * new_data = (char*)_iter->second.Data; // WARNING: Unlocking for performance reasons - could cause segfaults // or incomplete session data - should be moved further below if // any problems... pthread_mutex_unlock( &session_map_mutex ); // Copying... if ( *data_size <= MAX_SET_DATA_SIZE ) { // We have only one packet memcpy( new_data, data->Data, *data_size ); } else { // We've got a big one comming // First store the first packet memcpy( new_data, data->Data, MAX_SET_DATA_SIZE ); // Now get the rest recv( connection, new_data + MAX_SET_DATA_SIZE, *data_size - MAX_SET_DATA_SIZE, 0 ); } } // Send an OK sign... DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_OK }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); return; } } else // The session does not exist... return an error code status = DSS_STATUS_ERROR; pthread_mutex_unlock( &session_map_mutex ); } DSSStatusRespond stResp = { DSS_CMD_STATUS, status }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } /** * Stores data for an existing session... */ inline void ProcessGetData( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { DSSGetData * data = ( DSSGetData * ) &Buf; // Is the size OK? if ( data->IdSize < MIN_ID_SIZE || data->IdSize > MAX_ID_SIZE ) { // Send an error message! DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_ERROR }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } else { // We can proceed with getting the data Id_Internal tmp; tmp.IdSize = data->IdSize; memset( tmp.Id + data->IdSize, 0, MAX_ID_SIZE - data->IdSize ); memcpy( tmp.Id, data->Id, data->IdSize ); int status = DSS_STATUS_OK; // Find the session in the map... pthread_mutex_lock( &session_map_mutex ); SessionDataMap::iterator _iter = TheMap.find( tmp ); if ( _iter != TheMap.end() ) { time_t now = time( 0 ); // Is the session outdated? if ( now - _iter->second.LastTouched > _iter->second.Duration * 60 ) { RemoveSessionEntry( TheMap, _iter ); status = DSS_STATUS_SESSION_OVER; } else { // The session has been found and is still valid... // Touch the session... DSSGetData_Response * response = ( DSSGetData_Response * ) &Buf; response->DataSize = _iter->second.DataSize; response->Duration = _iter->second.Duration; response->LastAccess = _iter->second.LastTouched = now; response->MsgType = DSS_CMD_GET_DATA; response->SessionStarted = _iter->second.SessionStarted; response->Modified = _iter->second.Modified; // Send the data! if ( response->DataSize > MAX_GET_DATA_SIZE ) { memcpy( response->Data, _iter->second.Data, MAX_GET_DATA_SIZE ); send( connection, response, sizeof( DSSGetData_Response ) + MAX_GET_DATA_SIZE, 0 ); send( connection, ((char *)_iter->second.Data) + MAX_GET_DATA_SIZE, response->DataSize - MAX_GET_DATA_SIZE, 0 ); } else { memcpy( response->Data, _iter->second.Data, response->DataSize ); send( connection, response, sizeof( DSSGetData_Response ) + response->DataSize, 0 ); } // Unlock the map... pthread_mutex_unlock( &session_map_mutex ); return; } } else status = DSS_STATUS_ERROR; pthread_mutex_unlock( &session_map_mutex ); // Answer according to the status... DSSStatusRespond stResp = { DSS_CMD_STATUS, status }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } } /** * Sets the duration of an existing session... */ inline void ProcessSetDuration( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { DSSSetDuration * data = ( DSSSetDuration * ) &Buf; // Are the session ID and the new duration invalid? if ( data->IdSize < MIN_ID_SIZE || data->IdSize > MAX_ID_SIZE || data->Duration < 1 || data->Duration > MAX_SESSION_DURATION ) { // Send an error message! DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_ERROR }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } else { // We can proceed with setting the duration Id_Internal tmp; tmp.IdSize = data->IdSize; memset( tmp.Id + data->IdSize, 0, MAX_ID_SIZE - data->IdSize ); memcpy( tmp.Id, data->Id, data->IdSize ); int status = DSS_STATUS_OK; // Find the session in the map... pthread_mutex_lock( &session_map_mutex ); SessionDataMap::iterator _iter = TheMap.find( tmp ); if ( _iter != TheMap.end() ) { time_t now = time( 0 ); // Is the session outdated? if ( now - _iter->second.LastTouched > _iter->second.Duration * 60 ) { RemoveSessionEntry( TheMap, _iter ); status = DSS_STATUS_SESSION_OVER; } else { // Set the new duration and touch it... _iter->second.Duration = data->Duration; _iter->second.LastTouched = now; } } else status = DSS_STATUS_ERROR; pthread_mutex_unlock( &session_map_mutex ); // Answer according to the status... DSSStatusRespond stResp = { DSS_CMD_STATUS, status }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } } /** * Unregisters an existing session. */ inline void ProcessUnregisterId( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { DSSUnregisterID * data = ( DSSUnregisterID * ) &Buf; if ( data->IdSize < MIN_ID_SIZE || data->IdSize > MAX_ID_SIZE ) { // Send an error message! DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_ERROR }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } else { Id_Internal tmp; tmp.IdSize = data->IdSize; memset( tmp.Id + data->IdSize, 0, MAX_ID_SIZE - data->IdSize ); memcpy( tmp.Id, data->Id, data->IdSize ); // Find the session in the map... pthread_mutex_lock( &session_map_mutex ); SessionDataMap::iterator _iter = TheMap.find( tmp ); if ( _iter != TheMap.end() ) // Remove it and free the data! RemoveSessionEntry( TheMap, _iter ); pthread_mutex_unlock( &session_map_mutex ); // Send an OK message... DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_OK }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } } int __i = 0; /** * Processes id session registration. */ inline void ProcessRegisterId( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { // Get the received data DSSRegisterID * data = ( DSSRegisterID * ) &Buf; // Are the size of the ID and the duration of the session within range? if ( data->IdSize < MIN_ID_SIZE || data->IdSize > MAX_ID_SIZE ) data->IdSize = DEF_ID_SIZE; if ( data->Duration < 1 || data->Duration > MAX_SESSION_DURATION ) data->Duration = DEF_SESSION_DURATION; // The size of the key is OK. Create one... pair< Id_Internal, DataEntry > tmp; tmp.first.IdSize = data->IdSize; memset( tmp.first.Id + data->IdSize, 0, MAX_ID_SIZE - data->IdSize ); GenerateId( tmp.first.Id, tmp.first.IdSize ); // Initialize other data... tmp.second.Capacity = 0; tmp.second.Data = 0; tmp.second.DataSize = 0; tmp.second.Duration = data->Duration; tmp.second.Modified = tmp.second.SessionStarted = time( &tmp.second.LastTouched ); // Store it into the map pthread_mutex_lock( &session_map_mutex ); pair< SessionDataMap::iterator, bool > retVal = TheMap.insert( tmp ); pthread_mutex_unlock( &session_map_mutex ); if ( retVal.second ) { // It was inserted correctly, return a success code DSSRegisterID_Respond * response = ( DSSRegisterID_Respond * ) &Buf; response->MsgType = DSS_CMD_REGISTER_ID; response->Duration = tmp.second.Duration; memcpy( response->Id, tmp.first.Id, response->IdSize = tmp.first.IdSize ); response->SessionStarted = response->LastAccess = tmp.second.SessionStarted; send( connection, response, sizeof( DSSRegisterID_Respond ), 0 ); } else { // It wasn't inserted... DSSStatusRespond stResp = { DSS_CMD_STATUS, DSS_STATUS_ERROR }; send( connection, &stResp, sizeof( DSSStatusRespond ), 0 ); } } inline void ProcessServerStatus( DSSFetcherStruct& Buf, SessionDataMap& TheMap, int connection ) { DSSServerStatus * status = ( DSSServerStatus * ) &Buf; time_t Time = time( 0 ); status->MsgType = DSS_SERVER_STATUS; status->OutdatedSessions = 0; status->TotalBytes = 0; // Get the desired data pthread_mutex_lock( &session_map_mutex ); // Get the total amount of sessions status->TotalSessions = TheMap.size(); // Go through the map... SessionDataMap::iterator _itr = TheMap.begin(); while( _itr != TheMap.end() ) { if ( Time - _itr->second.LastTouched > _itr->second.Duration * 60 ) // An outdated session ++status->OutdatedSessions; // Count the bytes status->TotalBytes += (unsigned int) _itr->second.DataSize; ++_itr; } pthread_mutex_unlock( &session_map_mutex ); // Send the results... send( connection, status, sizeof( DSSServerStatus ), 0 ); } /** * Removes sessions that have timed out... */ inline void Cleanup( SessionDataMap& TheMap, time_t Time ) { pthread_mutex_lock( &session_map_mutex ); // Go through the map... SessionDataMap::iterator _itr = TheMap.begin(); const SessionDataMap::const_iterator & end = TheMap.end(); while( _itr != end ) { if ( Time - _itr->second.LastTouched > _itr->second.Duration * 60 ) { // Delete it! SessionDataMap::iterator _tmp = _itr; ++_itr; RemoveSessionEntry( TheMap, _tmp ); } else ++_itr; } pthread_mutex_unlock( &session_map_mutex ); } /** * This one ensures that the shared segment is properly initialized. */ int DefSesSr_Init( ) { // Unlink the path for the RF_LOCAL sockets... unlink( UNIX_SOCKET_ADDRESS ); // You should always succeed... return 0; } /** * This function starts the session server. It ensures that only one server runs at any time. */ int DefSesSr_RunServer(Address& addr ) { // The server has started ServerRunning = true; umask( 0 ); // Create the map SessionDataMap * map = new SessionDataMap; // Prepare the thread structure pthread_t thread; void * returnThread = 0; int serverHandle = socket(addr.protocol_family, SOCK_STREAM, 0); if (serverHandle < 0) { printf ("socket(2) error: %s\n", strerror(errno)); return 0; } if (bind(serverHandle, &addr.addr._addr, addr.addr_len) < 0) { printf ("bind(2) error: %s\n", strerror(errno)); return 0; } // Create the cleaner thread (this one cleans outdated sessions from the map) pthread_t cleanerThread; pthread_attr_t thread_attr; pthread_attr_init( &thread_attr ); int retVal = pthread_create( &cleanerThread, &thread_attr, CleanerFunc, map ); if ( retVal != 0 ) { printf ("Could not create a thread in '%s' at line '%d': '%s'\n", __FILE__, __LINE__, strerror(errno)); goto finish; } // Has a server already been created? if ( retVal < 0 ) { printf ("Could not create a listening socket in '%s' at line '%d': '%s'\n", __FILE__, __LINE__, strerror(errno)); goto finish; } retVal = listen( serverHandle, MAX_QUEUE ); if ( retVal < 0 ) { printf ("Could not listen in '%s' at line '%d': '%s'\n", __FILE__, __LINE__, strerror(errno)); shutdown( serverHandle, SHUT_RDWR ); close( serverHandle ); goto finish; } else { // We have a server ;) Create a listening thread and buzz off... pthread_attr_init( &thread_attr ); // Create the parameters to pass to the server __server_private_data * parms = new __server_private_data; parms->socket = serverHandle; parms->map = map; // Create the listening thread int retVal = pthread_create( &thread, &thread_attr, RunServer< sockaddr_un >, parms ); if ( retVal != 0 ) { printf ("Could not create a thread that would listen in '%s' at line '%d': '%s'\n", __FILE__, __LINE__, strerror(errno)); delete parms; shutdown( serverHandle, SHUT_RDWR ); close( serverHandle ); goto finish; } } // TODO: Add the remote part of the server... pthread_join( thread, &returnThread ); // Shutdown and close shutdown( serverHandle, SHUT_RDWR ); close( serverHandle ); finish: // The server is definitely not running anymore ServerRunning = false; return 0; } /** * Starts listening... */ template < typename T > void * RunServer ( void * data ) { srand( time( 0 ) ); // Initialize the random number generator char * state = new char[RANDOM_KEY_SIZE]; FILE * rnd = fopen( "/dev/urandom", "r" ); fread( state, 1, RANDOM_KEY_SIZE, rnd ); fclose( rnd ); // Initialize the state of the random number generator initstate( rand(), state, RANDOM_KEY_SIZE ); __server_private_data * parms = ( __server_private_data* ) data; int serverHandle = parms->socket; // Start accepting connections... while( ServerRunning ) { T loc_addr; socklen_t loc_addr_size = sizeof( T ); // Accept the connection int retVal = accept( serverHandle, reinterpret_cast< sockaddr* >( &loc_addr ), &loc_addr_size ); if ( retVal >= 0 ) // The connection has been accepted... // Start the communication: create a new thread and echo/output recieved data... StartConnection( parms->map, retVal ); } return 0; } void GenerateId( char * Buf, int BufSize ) { // How many 'int' types fit in int _blocks = BufSize / sizeof( int ); int _tail = BufSize % sizeof( int ); int * piBuf = (int *)Buf; for( int i = 0; i < _blocks; i++ ) piBuf[ i ] += ( random() << 1 ) | ( rand() & 1 ); if ( _tail ) { Buf = Buf + BufSize - _tail; for ( int i = 0; i < _tail; i++ ) Buf[ i ] += rand(); } } /** * This structure contains info for the Communicate function. */ struct ConnectionArguments { SessionDataMap * map; int socket; }; /** * Starts receiving and sending data... */ void * Communicate( void * args ) { ConnectionArguments * parms = ( ConnectionArguments * ) args; DSSFetcherStruct buf; bool goOn = true; while ( goOn ) { // Get the request - figure out what type it is ssize_t tmp = recv( parms->socket, &buf, sizeof( DSSFetcherStruct ), 0 ); if ( tmp > 0 ) { // We have successfully read some data... now check the type switch ( buf.MsgType ) { case DSS_CMD_REGISTER_ID: ProcessRegisterId( buf,... [truncated message content] |