Example #1
0
 void Server::disconnectSignals(const TransportSocketPtr& socket, const SocketSubscriber& subscriber)
 {
   socket->connected.disconnectAll();
   socket->disconnected.disconnect(subscriber.disconnected);
   socket->messageReady.disconnect(subscriber.messageReady);
   socket->disconnect();
 }
void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  ConnectionMap map;
  std::list<TransportSocketPtr> pending;
  {
    boost::mutex::scoped_lock lock(_socketMutex);
    _dying = true;
    std::swap(map, _connections);
    std::swap(pending, _allPendingConnections);
  }
  for (ConnectionMap::iterator mIt = map.begin(), mEnd = map.end(); mIt != mEnd; ++mIt)
  {
    for (std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.begin(), uEnd = mIt->second.end();
         uIt != uEnd;
         ++uIt)
    {
      TransportSocketPtr endpoint = uIt->second->endpoint;

      // Disconnect any valid socket we were holding.
      if (endpoint)
      {
        endpoint->disconnect();
      }
      else
      {
        uIt->second->state = State_Error;
        uIt->second->promise.setError("TransportSocketCache is closing.");
      }
    }
  }
  for (std::list<TransportSocketPtr>::iterator it = pending.begin(), end = pending.end(); it != end; ++it)
    (*it)->disconnect();
}
Example #3
0
  void Server::onTransportServerNewConnection(TransportSocketPtr socket, bool startReading)
  {
    boost::recursive_mutex::scoped_lock sl(_socketsMutex);
    if (!socket)
      return;
    if (_dying)
    {
      qiLogDebug() << "Incoming connection while closing, dropping...";
      socket->disconnect().async();
      return;
    }

    auto inserted = _subscribers.insert(std::make_pair(socket, SocketSubscriber{}));
    QI_ASSERT(inserted.second && "Socket insertion failed. Socket already exists.");

    auto& subscriber = inserted.first->second;

    QI_ASSERT(subscriber.disconnected == qi::SignalBase::invalidSignalLink && "Connecting a signal that already exists.");
    subscriber.disconnected =
        socket->disconnected.connect(&Server::onSocketDisconnected, this, socket, _1);

    // If false : the socket is only being registered, and has already been authenticated. The connection
    // was made elsewhere.
    // If true, it's an actual connection to this server.
    if (startReading)
    {
      SignalSubscriberPtr sub(new SignalSubscriber);
      boost::shared_ptr<bool> first = boost::make_shared<bool>(true);
      // We are reading on the socket for the first time : the first message has to be the capabilities
      *sub = socket->messageReady.connect(&Server::onMessageReadyNotAuthenticated, this, _1, socket, _authProviderFactory->newProvider(), first, sub).setCallType(MetaCallType_Direct);
      socket->startReading();
    }
    else
    {
      QI_ASSERT(subscriber.messageReady == qi::SignalBase::invalidSignalLink &&
             "Connecting a signal that already exists.");
      subscriber.messageReady =
          socket->messageReady.connect(&Server::onMessageReady, this, _1, socket).setCallType(MetaCallType_Direct);
    }
  }
Example #4
0
  void Server::onMessageReadyNotAuthenticated(const Message &msg, TransportSocketPtr socket, AuthProviderPtr auth,
                                              boost::shared_ptr<bool> first, SignalSubscriberPtr oldSignal)
  {
    qiLogVerbose() << "Starting auth message" << msg.address();
    int id = msg.id();
    int service = msg.service();
    int function = msg.action();
    int type = msg.type();
    Message reply;

    reply.setId(id);
    reply.setService(service);
    if (service != Message::Service_Server
        || type != Message::Type_Call
        || function != Message::ServerFunction_Authenticate)
    {
      socket->messageReady.disconnect(*oldSignal);
      if (_enforceAuth)
      {
        std::stringstream err;

        err << "Expected authentication (service #" << Message::Service_Server <<
               ", type #" << Message::Type_Call <<
               ", action #" << Message::ServerFunction_Authenticate <<
               "), received service #" << service << ", type #" << type << ", action #" << function;
        reply.setType(Message::Type_Error);
        reply.setError(err.str());
        socket->send(reply);
        socket->disconnect();
        qiLogVerbose() << err.str();
      }
      else
      {
        server_private::sendCapabilities(socket);
        qiLogVerbose() << "Authentication is not enforced. Skipping...";

        connectMessageReady(socket);
        onMessageReady(msg, socket);
      }
      return;
    }
    // the socket now contains the remote capabilities in socket->remoteCapabilities()
    qiLogVerbose() << "Authenticating client " << socket->remoteEndpoint().str() << "...";

    AnyReference cmref = msg.value(typeOf<CapabilityMap>()->signature(), socket);
    CapabilityMap authData = cmref.to<CapabilityMap>();
    cmref.destroy();
    CapabilityMap authResult = auth->processAuth(authData);
    unsigned int state = authResult[AuthProvider::State_Key].to<unsigned int>();
    std::string cmsig = typeOf<CapabilityMap>()->signature().toString();
    reply.setFunction(function);
    switch (state)
    {
    case AuthProvider::State_Done:
      qiLogVerbose() << "Client " << socket->remoteEndpoint().str() << " successfully authenticated.";
      socket->messageReady.disconnect(*oldSignal);
      connectMessageReady(socket);
      // no break, we know that authentication is done, send the response to the remote end
    case AuthProvider::State_Cont:
      if (*first)
      {
        authResult.insert(socket->localCapabilities().begin(), socket->localCapabilities().end());
        *first = false;
      }
      reply.setValue(authResult, cmsig);
      reply.setType(Message::Type_Reply);
      socket->send(reply);
      break;
    case AuthProvider::State_Error:
    default:{
      std::stringstream builder;
      builder << "Authentication failed";
      if (authResult.find(AuthProvider::Error_Reason_Key) != authResult.end())
      {
        builder << ": " << authResult[AuthProvider::Error_Reason_Key].to<std::string>();
        builder << " [" << _authProviderFactory->authVersionMajor() << "." << _authProviderFactory->authVersionMinor() << "]";
      }
      reply.setType(Message::Type_Error);
      reply.setError(builder.str());
      qiLogVerbose() << builder.str();
      socket->send(reply);
      socket->disconnect();
      }
    }
    qiLogVerbose() << "Auth ends";
  }
/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             TransportSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint answered.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}