Logo Search packages:      
Sourcecode: dbbalancer version File versions

DBPostgresFrontend.cc

#include "DBPostgresFrontend.hh"
#include "../config/DBBalancerConfig.hh"

// Constructor: Copy the attributes, and init the backend connection.

DBPostgresFrontend::DBPostgresFrontend(DBBalancerConfig *config) {

  // Store backend connection data, just in case it's needed.

  _user = config->getDaemonUser();
  _dbname = config->getDaemonDbName();
  _password = config->getDaemonPassword();

  if (config->getDaemonAuthMethod() == "trust") 
    _authMode = DBPostgresFrontend::TRUSTED_AUTH_MODE;
  else if (config->getDaemonAuthMethod() == "password")
    _authMode = DBPostgresFrontend::PLAINTEXT_PASSWORD_AUTH_MODE;
  else {
    ACE_DEBUG((LM_WARNING,"(%t,%T) DBPostgresFrontend: WARNING: %s isn't recognized as an auth mode. Default is 'password'\n",config->getDaemonAuthMethod().c_str()));
    _authMode = DBPostgresFrontend::PLAINTEXT_PASSWORD_AUTH_MODE;
  }

  memset(_buffer,0,sizeof(_buffer));
  memset(_secret_key_response,0,sizeof(_secret_key_response));

}


DBPostgresFrontend::~DBPostgresFrontend() {

  // Destructor
  ACE_DEBUG((LM_DEBUG,"(%t,%T) Destroying and closing DBPostgresFrontend().\n"));

}

bool DBPostgresFrontend::receiveStartup(int fe_socket) {

  char startup[296];
  char user[32];
  char dbname[64];
  char ready_for_query[1];

  // Let's read a Startup client packet

  ACE_DEBUG((LM_DEBUG,"(%t, %T) Starting client connection process.\n"));
 
  memset(startup,0,sizeof(startup));
  if (recv(fe_socket,startup,sizeof(startup),RECV_FLAGS)!=sizeof(startup)) {

    ACE_DEBUG((LM_ERROR,"(%t) Different size startup packet - probably protocol 3 (not yet supported)\n"));
    writeMessage(fe_socket,"Unsupported authentication mode: ", string(1,_authMode));
    return false;
  }


  // Handle attempt to startup with SSL connection, which we can't do yet
  if(startup[0]==0 && startup[1]==0 && startup[2]==0x1 && startup[3]==0x28
          && startup[4]==0x4 && ((unsigned char) startup[5])==0xd2 && startup[6]==0x16 && startup[7]==0x2f) {

    send(fe_socket,"N",1,SEND_FLAGS);

    ACE_DEBUG((LM_DEBUG,"SSL Startup request was refused (not supported)\n"));

    if (recv(fe_socket,startup,sizeof(startup),RECV_FLAGS)!=sizeof(startup)) {

      ACE_DEBUG((LM_ERROR,"(%t) Different size startup packet - probably protocol 3 (not yet supported)\n"));
      writeMessage(fe_socket,"Unsupported authentication mode: ", string(1,_authMode));
      return false;

    }
  }

  ACE_DEBUG((LM_DEBUG,"Startup packet read\n"));
 
  // Packet size (must be 296): Int32(296)
  // Protocol version (must be 2.0): Int32
  while(startup[0]!=0 || startup[1]!=0 || startup[2]!=0x1 || startup[3]!=0x28 || startup[4]!=0 || startup[5]!=2 || startup[6]!=0 || startup[7]!=0) {

    ACE_DEBUG((LM_ERROR,"(%t) Wrong startup packet read (bad size or protocol version (must be 2.0). Hexdump follows.\n"));
    ACE_LOG_MSG->log_hexdump(LM_ERROR,startup,sizeof(startup));
    return false;

  }

  // Now we'll read the user and dbname from the client. They will be checked against daemon.user
  // and daemon.dbname.

  memcpy(user,startup+72,32);
  memcpy(dbname,startup+8,64);
                 

  ACE_DEBUG((LM_DEBUG,"(%t, %T) Requested connection from user \"%s\" trying db \"%s\".\n",user, dbname));
  if (strncmp(dbname,_dbname.c_str(),_dbname.length())!=0) {
    ACE_DEBUG((LM_ERROR,"(%t, %T) Received dbname \"%s\". The served one is \"%s\".\n",dbname, _dbname.c_str()));
    writeMessage(fe_socket,"This dbname is not served by this DBBalancer instance: ",dbname);
    return false;
  }


  // Here we'll fork depending on the authentication type ******************************************
  // a) Trusted: If the user is OK, then let's go.
  // b) Unencrypted password: Ask for the password. If user and password match, open up.

  switch (_authMode) {


  case TRUSTED_AUTH_MODE:
   // We'll check if the user supplied is OK.
    if (!handleTrustedAuth(fe_socket,user))
      return false;
    else
      break;

  case PLAINTEXT_PASSWORD_AUTH_MODE:
   // We'll ask for a plain password, without encryption. Then check user/password.
    if (!handlePlaintextPasswordAuth(fe_socket,user))
      return false;
    else
      break;
    
  default:
    writeMessage(fe_socket,"Unsupported authentication mode: ", string(1,_authMode));
    return false;

  }

  // End authentication *****************************************************

  // .. the key ...

 //ACE_LOG_MSG->log_hexdump(LM_DEBUG,_secret_key_response,sizeof(_secret_key_response));

 if (send(fe_socket,_secret_key_response,sizeof(_secret_key_response),SEND_FLAGS)==-1) {

   ACE_DEBUG((LM_ERROR,"(%t, %T) Error writing secret key: %m\n"));
   return false;

 }

  // ... and the ready for query.

 ready_for_query[0]='Z';
 if (send(fe_socket,ready_for_query,1,SEND_FLAGS)==-1) {

   ACE_DEBUG((LM_ERROR,"(%t, %T) Error writing ready for query: %m\n"));
   return false;

 }

 ACE_DEBUG((LM_DEBUG,"(%t, %T) Correctly finishing receiveStartup method.\n"));

 return true;
  
  

}


// This method will return three different things:
// BUFF_MORE: Buffer filled with data. We presume that there's more data in the socket.
// BUFF_DATA: Buffer not filled. We presume that there's NOT more data in the socket.
// CONN_CLOSED: Buffer not filled. The data is a "close connection" command.
// and "true" if has to be continued.
// If there's any problem, it will throw DBConnectionException.

unsigned int DBPostgresFrontend::read(int fe_socket) {

  unsigned int result;

  _buffer_filling=recv(fe_socket,_buffer,sizeof(_buffer),RECV_FLAGS);

  if (_buffer_filling!=sizeof(_buffer) && !interceptFrontEndMessage(_buffer_filling)) {
    
    result = CONN_CLOSED;

  } else {

    if (_buffer_filling==sizeof(_buffer)) {

      result = BUFF_MORE;

    } else {

      if (_buffer_filling<(int)sizeof(_buffer) && _buffer_filling>=0) {
      
      result = BUFF_DATA;

      } else {

      _buffer_filling = 0;
      // Throw Exception !!!!!
      ACE_DEBUG ((LM_DEBUG," (%t, %T) Problem reading from FE socket: %m.\n "));
      return DBPostgresFrontend::READ_EXCEPTION;
      }

    }

  }
    
  ACE_DEBUG ((LM_DEBUG," (%t, %T) Received %d bytes from FE socket.\n ",_buffer_filling));
  ACE_LOG_MSG->log_hexdump(LM_DEBUG,_buffer,_buffer_filling);
  ACE_DEBUG ((LM_DEBUG,"\n"));

  return result;

}

// This method will write in another socket data from the local buffer, without
// deleting data from it;

unsigned int DBPostgresFrontend::writeBack(int be_socket) {

  int result = send(be_socket,_buffer,_buffer_filling,SEND_FLAGS);
  if (result!=_buffer_filling) {
    // Throw exception
    ACE_DEBUG ((LM_DEBUG," (%t, %T) Problem writing from FE to BE socket: %m.\n "));
    return DBPostgresFrontend::WRITE_EXCEPTION;
  }
  return DBPostgresFrontend::BUFF_DATA;

}


// Checks the things we want to intercept like ends of connection (always).

bool DBPostgresFrontend::interceptFrontEndMessage(int size) {

  if ((size==1 &&_buffer[0]=='X') || (size==2 &&_buffer[0]=='X' && _buffer[1]==0)) {

    ACE_DEBUG((LM_DEBUG," (%t) End of connection intercepted.\n"));
    //ACE_LOG_MSG->log_hexdump(LM_DEBUG,_buffer,size);
    return false;

  }
  
  return true;
  
}


bool DBPostgresFrontend::handleTrustedAuth(int fe_socket, char *user) {

  char auth_response[5];
  ACE_DEBUG((LM_DEBUG,"(%t, %T) Handling TRUST auth for user: %s\n",user));

  if (_user==user) {

    // We give back the auth confirmation ...
  
    memset(auth_response,0,sizeof(auth_response));
    auth_response[0]='R';
    
    if (send(fe_socket,auth_response,sizeof(auth_response),SEND_FLAGS)==-1) {
      
      ACE_DEBUG((LM_ERROR,"(%t) Error writing access authorized response: %m\n"));
      return false;
      
    }
    
  } else {

    // .. or an error message.

    writeMessage(fe_socket,"Trusted authentication failed for user: ",user);
    return false;
  }

  return true;
  


}

/**
 * Handles unencrypted password auth for the user supplied by the frontend.
 * If the frontend doesn't support this method it will close the connection.
 *
 */

bool DBPostgresFrontend::handlePlaintextPasswordAuth(int fe_socket, char *user) {

  char auth_response[5];
  ACE_DEBUG((LM_DEBUG,"(%t, %T) Handling PASSWORD auth for user: %s\n",user));

  memset(auth_response,0,sizeof(auth_response));
  auth_response[0]='R';
  auth_response[4]=0x3;
  
  if (send(fe_socket,auth_response,sizeof(auth_response),SEND_FLAGS)==-1) {
    
    ACE_DEBUG((LM_ERROR,"(%t) Error writing password request: %m\n"));
    return false;
    
  }
  
  // Two cases here:
  // The client doesn't support this auth method. It sends 'X\0' to close connection.
  // The client supports it: Then it will answer with the size of password and the password.  
  // So we read the first two characters just to figure out.

  char size[4];

  // Read the first 2 chars.
  if (recv(fe_socket,size,sizeof(size),RECV_FLAGS)==-1) {
    
    ACE_DEBUG((LM_ERROR,"(%t, %T) Error reading password size: %m.\n"));
    return false;
    
  }
  
  // If not supported... return false
  if (size[0]=='X' && size[1]==0) {

    ACE_DEBUG((LM_ERROR,"(%t, %T) Client doesn't support unencrypted password authentication.\n"));
    return false;

  }
    
  int pass_size = 4096*size[0]+256*size[1]+16*size[2]+size[3];
  ACE_DEBUG((LM_DEBUG,"(%t, %T) Password size is %d. \n",pass_size));
  
  char password[pass_size];
  
  if (recv(fe_socket,password,pass_size,RECV_FLAGS)==-1) {
    
    ACE_DEBUG((LM_ERROR,"(%t) Error reading password: %m\n"));    
    return false;
    
  }
  
  ACE_DEBUG((LM_DEBUG,"(%t, %T) Password is #%s#\n",password));
   


  if (_password==password && _user==user) {

    // We give back the auth confirmation ...
    
    memset(auth_response,0,sizeof(auth_response));
    auth_response[0]='R';
      
      if (send(fe_socket,auth_response,sizeof(auth_response),SEND_FLAGS)==-1) {

      ACE_DEBUG((LM_ERROR,"(%t) Error writing access authorized response: %m\n"));
      return false;

      }
         
  } else {

      // .. or an error message.
    
    writeMessage(fe_socket,"Password authentication failed for user:",user);
    return false;
  }

  return true;

}

void DBPostgresFrontend::writeMessage(int fe_socket, string message1, string message2) {

    char message[256];
    memset(message,0,sizeof(message));
    snprintf(message+1,254," DBBalancer: %s %s",message1.c_str(),message2.c_str());

    ACE_DEBUG((LM_WARNING,"(%t) DBPostgresFrontend: Message to FrontEnd: %s\n", message+1));
    message[0]='E';
   
    if (send(fe_socket,message,strlen(message)+1,SEND_FLAGS)==-1) {
      ACE_DEBUG((LM_ERROR,"(%t) DBPostgresFrontend: Error writing error message: %m\n"));
    }

    ACE_LOG_MSG->log_hexdump(LM_DEBUG,message,strlen(message)+1);

}



Generated by  Doxygen 1.6.0   Back to index