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(); }
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); } }
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(); }