/* Oscar - Multi-Threaded Implementation of OSCAR * Copyright (C) 2002-2005 Jay Freeman (saurik) */ /* * Redistribution and use in source and binary * forms, with or without modification, are permitted * provided that the following conditions are met: * * 1. Redistributions of source code must retain the * above copyright notice, this list of conditions * and the following disclaimer. * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions * and the following disclaimer in the documentation * and/or other materials provided with the * distribution. * 3. The name of the author may not be used to endorse * or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MENES_NET_OSCAR_FLAP_HPP #define MENES_NET_OSCAR_FLAP_HPP #include "cxx/platform.hpp" #include "net/linkage.hpp" #ifdef MENES_PRAGMA_ONCE #pragma once #endif #include "api/locks.hpp" #include "api/socket.hpp" #include "ext/listset.hpp" #include "ext/map.hpp" #include "ext/redblackset.hpp" #include "ios/buffer.hpp" #include "ios/getput.hpp" #include "net/oscar/capability.hpp" #include #include namespace net { namespace Oscar { class Session; class LvTuple { protected: _S data_; public: LvTuple() { } LvTuple(ios::Reader &stream) : data_(stream.GetIterator(), ios::GetBig(stream)) { } LvTuple(const char *data, uint16_t length) : data_(data, length) { } operator cse::String() const { return data_; } operator _S() const { return data_; } operator uint32_t() const { uint32_t value; for (ios::Buffer::ConstIterator byte(data_.Begin()); byte != data_.End(); ++byte) value *= 256 + *byte; return value; } operator Capabilities() const { // XXX: this is broken (duh) return Capabilities(); } bool IsEmpty() const { return data_.IsEmpty(); } void WriteTo(ios::Writer &stream) const { ios::PutBig(stream, data_.GetSize()); data_.WriteTo(stream); } LvTuple &operator =(uint16_t rhs) { data_.Clear(); ios::PutBig(data_, rhs); return *this; } LvTuple &operator =(uint32_t rhs) { data_.Clear(); ios::PutBig(data_, rhs); return *this; } LvTuple &operator =(const Capabilities &rhs) { data_.Clear(); for (Capabilities::ConstIterator capability(rhs.Begin()); capability != rhs.End(); ++capability) // XXX: this might need to take into account endian-ness... no? data_.Write(reinterpret_cast(capability->GetData()), sizeof(Capability)); return *this; } LvTuple &operator =(const ext::Buffer &rhs) { data_ = rhs; return *this; } void Clear() { data_.Clear(); } }; class TlvParser { friend ios::PrintWriter &operator <<(ios::PrintWriter &os, const TlvParser &tlv); private: typedef ext::RedBlackMap LvMap_; public: typedef LvMap_::Iterator Iterator; typedef LvMap_::ConstIterator ConstIterator; private: LvMap_ lvs_; public: TlvParser() { } TlvParser(const LvTuple &lv) { // XXX: this makes me sad _S segment(lv.operator _S()); ReadFrom(segment); } TlvParser(ios::Buffer &segment) { ReadFrom(segment); } TlvParser(ios::Reader &stream, uint16_t number) { ReadFrom(stream, number); } void ReadFrom(ios::Reader &stream, uint16_t number) { Clear(); for (uint16_t i(0); i != number; ++i) { uint16_t type(ios::GetBig(stream)); bool inserted(lvs_.Insert(type, LvTuple(stream)).Second()); _assert(inserted); } } void ReadFrom(ios::Buffer &segment) { Clear(); while (!segment.IsEmpty()) { uint16_t type(ios::GetBig(segment)); bool inserted(lvs_.Insert(type, LvTuple(segment)).Second()); _assert(inserted); } } void WriteTo(ios::Writer &stream) const { for (LvMap_::ConstIterator lv(lvs_.Begin()); lv != lvs_.End(); ++lv) { ios::PutBig(stream, lv->First()); lv->Second().WriteTo(stream); } } const LvTuple &operator [](uint16_t type) const { return lvs_[type]; } LvTuple &operator [](uint16_t type) { return lvs_[type]; } Iterator Begin() { return lvs_.Begin(); } Iterator End() { return lvs_.End(); } void Clear() { lvs_.Clear(); } operator _S() const { _S value; WriteTo(value); return value; } }; ios::PrintWriter &operator <<(ios::PrintWriter &os, const TlvParser &tlv); struct SnacHeader { uint16_t family; uint16_t subtype; uint16_t flags; uint32_t request; static uint32_t lastrequest_; static const size_t Size = sizeof(uint16_t) * 3 + sizeof(uint32_t); SnacHeader() : family(0), subtype(0), flags(0), request(++lastrequest_) { } SnacHeader(uint16_t family, uint16_t subtype) : family(family), subtype(subtype), flags(0), request(lastrequest_++) { } SnacHeader(uint16_t family, uint16_t subtype, uint32_t request) : family(family), subtype(subtype), flags(0), request(request) { } SnacHeader(ios::Reader &stream) : family(ios::GetBig(stream)), subtype(ios::GetBig(stream)), flags(ios::GetBig(stream)), request(ios::GetBig(stream)) { } void WriteTo(ios::Writer &stream) const { ios::PutBig(stream, family); ios::PutBig(stream, subtype); ios::PutBig(stream, flags); ios::PutBig(stream, request); } }; struct SnacPacket { SnacHeader snac; _S data; SnacPacket() { } SnacPacket(const SnacHeader &snac, const ios::Buffer &data = _S()) : snac(snac), data(data) { } SnacPacket(uint16_t family, uint16_t subtype, const ios::Buffer &data = _S()) : snac(family, subtype), data(data) { } SnacPacket(ios::Buffer &packet) : snac(packet), data(packet) { } }; typedef ext::ListSet FamilySet; struct FlapHeader { uint8_t start_; uint8_t channel_; uint16_t sequence_; uint16_t length_; FlapHeader() : start_(0x2a) { } FlapHeader(uint8_t channel, uint16_t sequence, uint16_t length) : start_(0x2a), channel_(channel), sequence_(sequence), length_(length) { } FlapHeader(ios::Reader &stream) { Read(stream); } void Read(ios::Reader &stream) { start_ = ios::GetBig(stream); _assert(start_ == 0x2a); channel_ = ios::GetBig(stream); sequence_ = ios::GetBig(stream); length_ = ios::GetBig(stream); } void WriteTo(ios::Writer &stream) { ios::PutBig(stream, start_); ios::PutBig(stream, channel_); ios::PutBig(stream, sequence_); ios::PutBig(stream, length_); } }; class FlapStream { public: static const uint32_t Family = 0x0001; protected: _S socket_; uint16_t sequence_; Session &session_; cse::String address_; _S cookie_; struct RateClass_ { uint32_t windowsize; uint32_t clear; uint32_t alert; uint32_t limit; uint32_t disconnect; uint32_t current; uint32_t max; char unknown[5]; //struct snacpair *members; }; typedef ext::ListMap RateMap_; RateMap_ rates_; FamilySet families_; api::ThreadMutex lock_; void WriteHeader(uint8_t channel, uint16_t length) { FlapHeader(channel, sequence_++, length).WriteTo(socket_); } uint8_t Read(ios::Buffer &segment) { FlapHeader header(socket_); segment.Assign(socket_.GetIterator(), header.length_); return header.channel_; } public: FlapStream(Session &session, const cse::String &address, const ios::Buffer &cookie); const FamilySet &GetFamilies() const; const cse::String &GetAddress() const; void Handle(); void Write(uint8_t channel, ios::Buffer &segment) { ext::ScopeLock guard_(lock_); WriteHeader(channel, segment.GetSize()); segment.WriteTo(socket_); socket_.Flush(); } // XXX: this should check for socket error and return appropriately bool Write(const SnacPacket &packet) { ext::ScopeLock guard_(lock_); uint16_t length(static_cast(packet.data.GetSize())); WriteHeader(2, SnacHeader::Size + length); packet.snac.WriteTo(socket_); socket_.Write(packet.data.Begin(), length); socket_.Flush(); return true; } }; } } #endif//MENES_NET_OSCAR_FLAP_HPP