void OutboundMessageFragments::timerCallback(const boost::system::error_code& e, PeerState ps, uint32_t const msgId)
        {
            if(!e) {
                std::lock_guard<std::mutex> lock(m_mutex);

                auto itr = m_states.find(msgId);
                if(itr != m_states.end()) {
                    OutboundMessageState& oms = itr->second;

                    if(oms.getTries() > 5) {
                        m_states.erase(msgId);
                        return;
                    }

                    PacketBuilder::FragmentPtr frag = oms.getNextUnackdFragment();
                    if(frag) {
                        std::vector<PacketBuilder::FragmentPtr> fragList;
                        fragList.push_back(frag);

                        oms.markFragmentSent(fragList[0]->fragNum);

                        PacketPtr p = PacketBuilder::buildData(ps.getEndpoint(), false, CompleteAckList(), PartialAckList(), fragList);
                        p->encrypt(ps.getCurrentSessionKey(), ps.getCurrentMacKey());
                        m_context.sendPacket(p);

                        oms.incrementTries();

                        oms.getTimer().expires_at(oms.getTimer().expires_at() + boost::posix_time::time_duration(0, 0, 2));
                        oms.getTimer().async_wait(boost::bind(&OutboundMessageFragments::timerCallback, this, boost::asio::placeholders::error, ps, msgId));
                    }
                }
            }
        }
        void OutboundMessageFragments::sendDataCallback(PeerState ps, uint32_t const msgId)
        {
            std::lock_guard<std::mutex> lock(m_mutex);

            auto itr = m_states.find(msgId);
            if(itr != m_states.end()) {
                OutboundMessageState& oms = itr->second;

                std::vector<PacketBuilder::FragmentPtr> fragList;
                auto fragment = oms.getNextFragment();

                if (fragment == nullptr) {
                    throw std::runtime_error("no ssu fragment when sending");
                }

                fragList.push_back(fragment);

                oms.markFragmentSent(fragList[0]->fragNum);

                PacketPtr p = PacketBuilder::buildData(ps.getEndpoint(), false, CompleteAckList(), PartialAckList(), fragList);
                p->encrypt(ps.getCurrentSessionKey(), ps.getCurrentMacKey());
                m_context.sendPacket(p);

                if(!oms.allFragmentsSent())
                    m_context.ios.post(boost::bind(&OutboundMessageFragments::sendDataCallback, this, ps, msgId));
            }
        }
        void EstablishmentManager::processCreated(EstablishmentStatePtr const &state)
        {
            state->calculateDHSecret();

            if(!state->verifyCreationSignature()) {
                I2P_LOG(m_log, warning) << "creation signature verification failed";
                state->setState(EstablishmentState::State::FAILURE);
                return;
            }

            const ByteArray& dhSecret = state->getDHSecret();
            SessionKey newKey(toSessionKey(dhSecret)), newMacKey;

            state->setSessionKey(newKey);

            copy(dhSecret.begin() + 32, dhSecret.begin() + 32 + 32, newMacKey.begin());
            state->setMacKey(newMacKey);

            Endpoint ep = state->getTheirEndpoint();
            PeerState ps(ep, state->getTheirIdentity().getHash());
            ps.setCurrentSessionKey(state->getSessionKey());
            ps.setCurrentMacKey(state->getMacKey());

            std::lock_guard<std::mutex> lock(m_context.peers.getMutex());
            m_context.peers.addPeer(std::move(ps));

            PacketPtr p = PacketBuilder::buildSessionConfirmed(state);
            p->encrypt(state->getSessionKey(), state->getMacKey());

            m_context.sendPacket(p);

            state->setState(EstablishmentState::State::CONFIRMED_SENT);
            post(state);
        }
        void EstablishmentManager::sendRequest(EstablishmentStatePtr const &state)
        {
            PacketPtr p = PacketBuilder::buildSessionRequest(state);
            p->encrypt(state->getSessionKey(), state->getMacKey());

            m_context.sendPacket(p);

            state->setState(EstablishmentState::State::REQUEST_SENT);
            post(state);
        }
        void EstablishmentManager::processRequest(EstablishmentStatePtr const &state)
        {
            state->calculateDHSecret();

            PacketPtr p = PacketBuilder::buildSessionCreated(state);
            p->encrypt(state->getIV(), state->getSessionKey(), state->getMacKey());

            const ByteArray& dhSecret = state->getDHSecret();
            SessionKey newKey(toSessionKey(dhSecret)), newMacKey;

            state->setSessionKey(newKey);

            copy(dhSecret.begin() + 32, dhSecret.begin() + 32 + 32, newMacKey.begin());
            state->setMacKey(newMacKey);

            m_context.sendPacket(p);

            state->setState(EstablishmentState::State::CREATED_SENT);
            post(state);
        }