diff options
| author | Glenn Smith <glenn@vector35.com> | 2021-06-01 16:22:24 -0400 |
|---|---|---|
| committer | Glenn Smith <glenn@vector35.com> | 2021-08-17 16:50:09 -0400 |
| commit | 176cc79219cdf015cbcf7f90bb6eb5f6a8c59940 (patch) | |
| tree | f472869dadf12add3daf853f2dd73953d322ac6a | |
| parent | 401b73b242bc9447bd12215b17ea8ce215c7d6ed (diff) | |
Websocket provider
| -rw-r--r-- | binaryninjaapi.h | 152 | ||||
| -rw-r--r-- | binaryninjacore.h | 46 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/websocketprovider.py | 286 | ||||
| -rw-r--r-- | ui/uitypes.h | 2 | ||||
| -rw-r--r-- | websocketprovider.cpp | 209 |
6 files changed, 685 insertions, 11 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index e7358613..c2529942 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -5200,6 +5200,13 @@ __attribute__ ((format (printf, 1, 2))) class DownloadInstance: public CoreRefCountObject<BNDownloadInstance, BNNewDownloadInstanceReference, BNFreeDownloadInstance> { + public: + struct Response + { + uint16_t statusCode; + std::unordered_map<std::string, std::string> headers; + }; + protected: DownloadInstance(DownloadProvider* provider); DownloadInstance(BNDownloadInstance* instance); @@ -5208,25 +5215,52 @@ __attribute__ ((format (printf, 1, 2))) static int PerformRequestCallback(void* ctxt, const char* url); static int PerformCustomRequestCallback(void* ctxt, const char* method, const char* url, uint64_t headerCount, const char* const* headerKeys, const char* const* headerValues, BNDownloadInstanceResponse** response); static void PerformFreeResponse(void* ctxt, BNDownloadInstanceResponse* response); - + /*! + Cleanup any resources created by the instance + */ virtual void DestroyInstance(); - - public: - struct Response - { - uint16_t statusCode; - std::unordered_map<std::string, std::string> headers; - }; - + /*! + Virtual method to synchronously perform a GET request to a url, overridden by a subclass + \param url Full url to request + \return Zero or greater on successful request and HTTP 200 status + */ virtual int PerformRequest(const std::string& url) = 0; + /*! + Virtual method to synchronously perform a request to a url, overridden by a subclass + \param method Request method e.g. GET + \param url Full url to request + \param headers HTTP headers as keys/values + \param response Structure into which the response status code and headers should be stored + \return Zero or greater on success + */ virtual int PerformCustomRequest(const std::string& method, const std::string& url, const std::unordered_map<std::string, std::string>& headers, Response& response) = 0; - int PerformRequest(const std::string& url, BNDownloadInstanceOutputCallbacks* callbacks); - int PerformCustomRequest(const std::string& method, const std::string& url, const std::unordered_map<std::string, std::string>& headers, Response& response, BNDownloadInstanceInputOutputCallbacks* callbacks); int64_t ReadDataCallback(uint8_t* data, uint64_t len); uint64_t WriteDataCallback(uint8_t* data, uint64_t len); bool NotifyProgressCallback(uint64_t progress, uint64_t total); void SetError(const std::string& error); + + public: + /*! + Send a GET request to a url, synchronously + \param url Full url to request + \param callbacks Structure with callback functions for output data + \return Zero or greater on successful request and HTTP 200 status + */ + int PerformRequest(const std::string& url, BNDownloadInstanceOutputCallbacks* callbacks); + /*! + Send a request to a url, synchronously + \param method Request method e.g. GET + \param url Full url to request + \param headers HTTP headers as keys/values + \param response Structure into which the response status code and headers are stored + \param callbacks Structure with callback functions for input and output data + \return Zero or greater on success + */ + int PerformCustomRequest(const std::string& method, const std::string& url, const std::unordered_map<std::string, std::string>& headers, Response& response, BNDownloadInstanceInputOutputCallbacks* callbacks); + /*! + Retrieve the error from the last request sent by this instance + */ std::string GetError() const; }; @@ -5265,6 +5299,102 @@ __attribute__ ((format (printf, 1, 2))) virtual Ref<DownloadInstance> CreateNewInstance() override; }; + // WebsocketProvider + class WebsocketProvider; + + class WebsocketClient: public CoreRefCountObject<BNWebsocketClient, BNNewWebsocketClientReference, BNFreeWebsocketClient> + { + protected: + WebsocketClient(WebsocketProvider* provider); + WebsocketClient(BNWebsocketClient* instance); + + static void DestroyClientCallback(void* ctxt); + static bool ConnectCallback(void* ctxt, const char* host, uint64_t headerCount, const char* const* headerKeys, const char* const* headerValues); + static bool WriteCallback(const uint8_t* data, uint64_t len, void* ctxt); + static bool DisconnectCallback(void* ctxt); + static void ErrorCallback(const char* msg, void* ctxt); + bool ReadData(uint8_t* data, uint64_t len); + + /*! + Cleanup any resources created by the client + */ + virtual void DestroyClient(); + /*! + Virtual method for performing the connection, overridden by a subclass. + \param host Full url with scheme, domain, optionally port, and path + \param headers HTTP header keys and values + \return True if the connection has started, but not necessarily if it succeeded + */ + virtual bool Connect(const std::string& host, const std::unordered_map<std::string, std::string>& headers) = 0; + public: + /*! + Connect to a given url, asynchronously. The connection will be run in a separate thread managed by the websocket provider. + + Callbacks will be called **on the thread of the connection**, so be sure to ExecuteOnMainThread any long-running + or gui operations in the callbacks. + + If the connection succeeds, connectedCallback will be called. On normal termination, disconnectedCallback will be called. + If the connection succeeds, but later fails, disconnectedCallback will not be called, and errorCallback will be called instead. + If the connection fails, neither connectedCallback nor disconnectedCallback will be called, and errorCallback will be called instead. + + If connectedCallback or readCallback return false, the connection will be aborted. + + \param host Full url with scheme, domain, optionally port, and path + \param headers HTTP header keys and values + \param callbacks Structure with callbacks for various websocket events + \return True if the connection has started, but not necessarily if it succeeded + */ + bool Connect(const std::string& host, const std::unordered_map<std::string, std::string>& headers, BNWebsocketClientOutputCallbacks* callbacks); + + /*! + Write some data to the websocket + \param data Data to write + \return True if successful + */ + virtual bool Write(const std::vector<uint8_t>& data) = 0; + /*! + Disconnect the websocket + \return True if successful + */ + virtual bool Disconnect() = 0; + }; + + class CoreWebsocketClient: public WebsocketClient + { + public: + CoreWebsocketClient(BNWebsocketClient* instance); + virtual ~CoreWebsocketClient() {}; + + virtual bool Connect(const std::string& host, const std::unordered_map<std::string, std::string>& headers) override; + virtual bool Write(const std::vector<uint8_t>& data) override; + virtual bool Disconnect() override; + }; + + class WebsocketProvider: public StaticCoreRefCountObject<BNWebsocketProvider> + { + std::string m_nameForRegister; + + protected: + WebsocketProvider(const std::string& name); + WebsocketProvider(BNWebsocketProvider* provider); + + static BNWebsocketClient* CreateClientCallback(void* ctxt); + + public: + virtual Ref<WebsocketClient> CreateNewClient() = 0; + + static std::vector<Ref<WebsocketProvider>> GetList(); + static Ref<WebsocketProvider> GetByName(const std::string& name); + static void Register(WebsocketProvider* provider); + }; + + class CoreWebsocketProvider: public WebsocketProvider + { + public: + CoreWebsocketProvider(BNWebsocketProvider* provider); + virtual Ref<WebsocketClient> CreateNewClient() override; + }; + // Scripting Provider class ScriptingOutputListener { diff --git a/binaryninjacore.h b/binaryninjacore.h index 8cf76db6..08f2f483 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -166,6 +166,8 @@ extern "C" struct BNBasicBlock; struct BNDownloadProvider; struct BNDownloadInstance; + struct BNWebsocketProvider; + struct BNWebsocketClient; struct BNFlowGraph; struct BNFlowGraphNode; struct BNFlowGraphLayoutRequest; @@ -2237,6 +2239,30 @@ extern "C" BNDownloadInstance* (*createInstance)(void* ctxt); }; + struct BNWebsocketClientOutputCallbacks + { + void* context; + bool (*connectedCallback)(void* ctxt); + void (*disconnectedCallback)(void* ctxt); + void (*errorCallback)(const char* msg, void* ctxt); + bool (*readCallback)(uint8_t* data, uint64_t len, void* ctxt); + }; + + struct BNWebsocketClientCallbacks + { + void* context; + void (*destroyClient)(void* ctxt); + bool (*connect)(void* ctxt, const char* host, uint64_t headerCount, const char* const* headerKeys, const char* const* headerValues); + bool (*write)(const uint8_t* data, uint64_t len, void* ctxt); + bool (*disconnect)(void* ctxt); + }; + + struct BNWebsocketProviderCallbacks + { + void* context; + BNWebsocketClient* (*createClient)(void* ctxt); + }; + enum BNFindFlag { FindCaseSensitive = 0, @@ -5018,6 +5044,26 @@ __attribute__ ((format (printf, 1, 2))) BINARYNINJACOREAPI char* BNGetErrorForDownloadInstance(BNDownloadInstance* instance); BINARYNINJACOREAPI void BNSetErrorForDownloadInstance(BNDownloadInstance* instance, const char* error); + // Websocket providers + BINARYNINJACOREAPI BNWebsocketProvider* BNRegisterWebsocketProvider(const char* name, BNWebsocketProviderCallbacks* callbacks); + BINARYNINJACOREAPI BNWebsocketProvider** BNGetWebsocketProviderList(size_t* count); + BINARYNINJACOREAPI void BNFreeWebsocketProviderList(BNWebsocketProvider** providers); + BINARYNINJACOREAPI BNWebsocketProvider* BNGetWebsocketProviderByName(const char* name); + + BINARYNINJACOREAPI char* BNGetWebsocketProviderName(BNWebsocketProvider* provider); + BINARYNINJACOREAPI BNWebsocketClient* BNCreateWebsocketProviderClient(BNWebsocketProvider* provider); + + BINARYNINJACOREAPI BNWebsocketClient* BNInitWebsocketClient(BNWebsocketProvider* provider, BNWebsocketClientCallbacks* callbacks); + BINARYNINJACOREAPI BNWebsocketClient* BNNewWebsocketClientReference(BNWebsocketClient* client); + BINARYNINJACOREAPI void BNFreeWebsocketClient(BNWebsocketClient* client); + BINARYNINJACOREAPI bool BNConnectWebsocketClient(BNWebsocketClient* client, const char* url, uint64_t headerCount, const char* const* headerKeys, const char* const* headerValues, BNWebsocketClientOutputCallbacks* callbacks); + BINARYNINJACOREAPI bool BNNotifyWebsocketClientConnect(BNWebsocketClient* client); + BINARYNINJACOREAPI void BNNotifyWebsocketClientDisconnect(BNWebsocketClient* client); + BINARYNINJACOREAPI void BNNotifyWebsocketClientError(BNWebsocketClient* client, const char* msg); + BINARYNINJACOREAPI bool BNNotifyWebsocketClientReadData(BNWebsocketClient* client, uint8_t* data, uint64_t len); + BINARYNINJACOREAPI uint64_t BNWriteWebsocketClientData(BNWebsocketClient* client, const uint8_t* data, uint64_t len); + BINARYNINJACOREAPI bool BNDisconnectWebsocketClient(BNWebsocketClient* client); + // Scripting providers BINARYNINJACOREAPI BNScriptingProvider* BNRegisterScriptingProvider(const char* name, const char* apiName, BNScriptingProviderCallbacks* callbacks); diff --git a/python/__init__.py b/python/__init__.py index 0ed7b47c..8e63016d 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -64,6 +64,7 @@ from binaryninja.settings import * from binaryninja.metadata import * from binaryninja.flowgraph import * from binaryninja.datarender import * +from binaryninja.websocketprovider import * from binaryninja.workflow import * diff --git a/python/websocketprovider.py b/python/websocketprovider.py new file mode 100644 index 00000000..a1a6681b --- /dev/null +++ b/python/websocketprovider.py @@ -0,0 +1,286 @@ +# Copyright (c) 2015-2021 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +import abc +import ctypes +from json import loads, dumps +import sys +import traceback + +if sys.version_info >= (3, 0, 0): + from urllib.parse import urlencode +else: + from urllib import urlencode + +# Binary Ninja Components +import binaryninja._binaryninjacore as core + +import binaryninja +from binaryninja import settings +from binaryninja import with_metaclass +from binaryninja import startup +from binaryninja import log + +# 2-3 compatibility +from binaryninja import pyNativeStr +from binaryninja import range + + +def nop(*args, **kwargs): + pass + + +def to_bytes(field): + if type(field) == bytes: + return field + if type(field) == str: + return field.encode() + return str(field).encode() + + +class WebsocketClient(object): + _registered_clients = [] + + def __init__(self, provider, handle = None): + if handle is None: + self._cb = core.BNWebsocketClientCallbacks() + self._cb.context = 0 + self._cb.destroyClient = self._cb.destroyClient.__class__(self._destroy_client) + self._cb.connect = self._cb.connect.__class__(self._connect) + self._cb.write = self._cb.write.__class__(self._write) + self._cb.disconnect = self._cb.disconnect.__class__(self._disconnect) + self.handle = core.BNInitWebsocketClient(provider.handle, self._cb) + self.__class__._registered_clients.append(self) + else: + self.handle = core.handle_of_type(handle, core.BNWebsocketClient) + self._must_free = handle is not None + self.on_connected = nop + self.on_disconnected = nop + self.on_error = nop + self.on_data = nop + self._connected = False + + def __del__(self): + if self._must_free: + core.BNFreeWebsocketClient(self.handle) + + def _destroy_client(self, ctxt): + try: + if self in self.__class__._registered_clients: + self.__class__._registered_clients.remove(self) + self.perform_destroy_client() + except: + log.log_error(traceback.format_exc()) + + def _connect(self, ctxt, host, header_count, header_keys, header_values): + # Extract headers + keys_ptr = ctypes.cast(header_keys, ctypes.POINTER(ctypes.c_char_p)) + values_ptr = ctypes.cast(header_values, ctypes.POINTER(ctypes.c_char_p)) + header_key_array = (ctypes.c_char_p * header_count).from_address(ctypes.addressof(keys_ptr.contents)) + header_value_array = (ctypes.c_char_p * header_count).from_address(ctypes.addressof(values_ptr.contents)) + headers = {} + for i in range(header_count): + headers[header_key_array[i]] = header_value_array[i] + + return self.perform_connect(host, headers) + + def _write(self, data, len, ctxt): + try: + data_bytes = (ctypes.c_char * len).from_buffer(data) + self.perform_write(data_bytes) + return True + except: + log.log_error(traceback.format_exc()) + return False + + def _disconnect(self, ctxt): + return self.perform_disconnect() + + def _connected_callback(self, ctxt): + self.on_connected() + return True + + def _disconnected_callback(self, ctxt): + self.on_disconnected() + + def _error_callback(self, msg, ctxt): + self.on_error(msg) + + def _read_callback(self, data, len, ctxt): + c = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte)) + data_bytes = (ctypes.c_ubyte * len).from_address(ctypes.addressof(c.contents)) + self.on_data(bytes(data_bytes)) + return True + + @abc.abstractmethod + def perform_connect(self, host, headers): + raise NotImplementedError + + @abc.abstractmethod + def perform_destroy_client(self): + raise NotImplementedError + + @abc.abstractmethod + def perform_write(self, data): + raise NotImplementedError + + @abc.abstractmethod + def perform_disconnect(self): + raise NotImplementedError + + def connect(self, url, headers=None, on_connected=nop, on_disconnected=nop, on_error=nop, on_data=nop): + """ + Connect to a given url, asynchronously. The connection will be run in a separate thread managed by the websocket provider. + Client callbacks are set according to whichever on_ callback parameters you pass. + + Callbacks will be called **on the thread of the connection**, so be sure to execute_on_main_thread any long-running + or gui operations in the callbacks. + + If the connection succeeds, on_connected will be called. On normal termination, on_disconnected will be called. + If the connection succeeds, but later fails, on_disconnected will not be called, and on_error will be called instead. + If the connection fails, neither on_connected nor on_disconnected will be called, and on_error will be called instead. + + If on_connected or on_data return false, the connection will be aborted. + + :param str url: full url with scheme, domain, optionally port, and path + :param dict headers: dictionary of string header keys to string header values + :param function() -> bool on_connected: function to call when connection succeeds + :param function() -> void on_disconnected: function to call when connection is closed normally + :param function(str) -> void on_error: function to call when connection is closed with an error + :param function(bytes) -> bool on_data: function to call when data is read from the websocket + :return: if the connection has started, but not necessarily if it succeeded + :rtype: bool + """ + if self._connected: + raise RuntimeError("Cannot use connect() twice on the same WebsocketClient") + + self._connected = True + + header_keys = (ctypes.c_char_p * len(headers))() + header_values = (ctypes.c_char_p * len(headers))() + for (i, item) in enumerate(headers.items()): + key, value = item + header_keys[i] = to_bytes(key) + header_values[i] = to_bytes(value) + + # Store this so the callbacks are not GC'd + self.io_callbacks = core.BNWebsocketClientOutputCallbacks() + self.io_callbacks.context = 0 + self.io_callbacks.connectedCallback = self.io_callbacks.connectedCallback.__class__(self._connected_callback) + self.io_callbacks.disconnectedCallback = self.io_callbacks.disconnectedCallback.__class__(self._disconnected_callback) + self.io_callbacks.errorCallback = self.io_callbacks.errorCallback.__class__(self._error_callback) + self.io_callbacks.readCallback = self.io_callbacks.readCallback.__class__(self._read_callback) + + self.on_connected = on_connected + self.on_disconnected = on_disconnected + self.on_error = on_error + self.on_data = on_data + + return core.BNConnectWebsocketClient(self.handle, url, len(headers), header_keys, header_values, self.io_callbacks) + + def write(self, data): + """ + Send some data to the websocket + + :param bytes data: data to write + :return: true if successful + :rtype: bool + """ + return core.BNWriteWebsocketClientData(self.handle, (ctypes.c_ubyte * len(data)).from_buffer_copy(data), len(data)) + + def disconnect(self): + """ + Disconnect the websocket + + :return: true if successful + :rtype: bool + """ + return core.BNDisconnectWebsocketClient(self.handle) + +class _WebsocketProviderMetaclass(type): + @property + def list(self): + """List all WebsocketProvider types (read-only)""" + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetWebsocketProviderList(count) + result = [] + for i in range(0, count.value): + result.append(WebsocketProvider(types[i])) + core.BNFreeWebsocketProviderList(types) + return result + + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetWebsocketProviderList(count) + try: + for i in range(0, count.value): + yield WebsocketProvider(types[i]) + finally: + core.BNFreeWebsocketProviderList(types) + + def __getitem__(self, value): + binaryninja._init_plugins() + provider = core.BNGetWebsocketProviderByName(str(value)) + if provider is None: + raise KeyError("'%s' is not a valid websocket provider" % str(value)) + return WebsocketProvider(provider) + + def __setattr__(self, name, value): + try: + type.__setattr__(self, name, value) + except AttributeError: + raise AttributeError("attribute '%s' is read only" % name) + + +class WebsocketProvider(with_metaclass(_WebsocketProviderMetaclass, object)): + name = None + instance_class = None + _registered_providers = [] + + def __init__(self, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNWebsocketProvider) + self.__dict__["name"] = core.BNGetWebsocketProviderName(handle) + + def register(self): + self._cb = core.BNWebsocketProviderCallbacks() + self._cb.context = 0 + self._cb.createInstance = self._cb.createInstance.__class__(self._create_instance) + self.handle = core.BNRegisterWebsocketProvider(self.__class__.name, self._cb) + self.__class__._registered_providers.append(self) + + def _create_instance(self, ctxt): + try: + result = self.__class__.instance_class(self) + if result is None: + return None + return ctypes.cast(core.BNNewWebsocketClientReference(result.handle), ctypes.c_void_p).value + except: + log.log_error(traceback.format_exc()) + return None + + def create_instance(self): + result = core.BNCreateWebsocketProviderClient(self.handle) + if result is None: + return None + return WebsocketClient(self, handle=result) diff --git a/ui/uitypes.h b/ui/uitypes.h index 7fd92298..7bcaa560 100644 --- a/ui/uitypes.h +++ b/ui/uitypes.h @@ -96,6 +96,8 @@ typedef BinaryNinja::Ref<BinaryNinja::TagType> TagTypeRef; typedef BinaryNinja::Ref<BinaryNinja::TemporaryFile> TemporaryFileRef; typedef BinaryNinja::Ref<BinaryNinja::Transform> TransformRef; typedef BinaryNinja::Ref<BinaryNinja::Type> TypeRef; +typedef BinaryNinja::Ref<BinaryNinja::WebsocketClient> WebsocketClientRef; +typedef BinaryNinja::Ref<BinaryNinja::WebsocketProvider> WebsocketProviderRef; typedef BinaryNinja::Ref<BinaryNinja::RepoPlugin> RepoPluginRef; typedef BinaryNinja::Ref<BinaryNinja::Repository> RepositoryRef; typedef BinaryNinja::Ref<BinaryNinja::RepositoryManager> RepositoryManagerRef; diff --git a/websocketprovider.cpp b/websocketprovider.cpp new file mode 100644 index 00000000..a23416b7 --- /dev/null +++ b/websocketprovider.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2015-2021 Vector 35 Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +WebsocketClient::WebsocketClient(WebsocketProvider* provider) +{ + BNWebsocketClientCallbacks cb; + cb.context = this; + cb.destroyClient = DestroyClientCallback; + cb.connect = ConnectCallback; + cb.write = WriteCallback; + cb.disconnect = DisconnectCallback; + AddRefForRegistration(); + m_object = BNInitWebsocketClient(provider->GetObject(), &cb); +} + + +WebsocketClient::WebsocketClient(BNWebsocketClient* client) +{ + m_object = client; +} + + +void WebsocketClient::DestroyClientCallback(void* ctxt) +{ + WebsocketClient* client = (WebsocketClient*)ctxt; + client->DestroyClient(); +} + + +bool WebsocketClient::ConnectCallback(void* ctxt, const char* host, uint64_t headerCount, + const char* const* headerKeys, const char* const* headerValues) +{ + unordered_map<string, string> headers; + for (uint64_t i = 0; i < headerCount; i ++) + { + headers[headerKeys[i]] = headerValues[i]; + } + + WebsocketClient* client = (WebsocketClient*)ctxt; + return client->Connect(host, headers); +} + + +bool WebsocketClient::DisconnectCallback(void* ctxt) +{ + WebsocketClient* client = (WebsocketClient*)ctxt; + return client->Disconnect(); +} + + +void WebsocketClient::ErrorCallback(const char* msg, void* ctxt) +{ + WebsocketClient* client = (WebsocketClient*)ctxt; + BNNotifyWebsocketClientError(client->m_object, msg); +} + + +bool WebsocketClient::WriteCallback(const uint8_t* data, uint64_t len, void* ctxt) +{ + WebsocketClient* client = (WebsocketClient*)ctxt; + return client->Write(vector<uint8_t>(data, data + len)); +} + + +bool WebsocketClient::ReadData(uint8_t* data, uint64_t len) +{ + return BNNotifyWebsocketClientReadData(m_object, data, len); +} + + +bool WebsocketClient::Connect(const std::string& host, + const std::unordered_map<std::string, std::string>& headers, BNWebsocketClientOutputCallbacks* callbacks) +{ + const char** headerKeys = new const char*[headers.size()]; + const char** headerValues = new const char*[headers.size()]; + + uint64_t i = 0; + for (auto it = headers.begin(); it != headers.end(); ++it) + { + headerKeys[i] = it->first.c_str(); + headerValues[i] = it->second.c_str(); + i ++; + } + + bool result = BNConnectWebsocketClient(m_object, host.c_str(), headers.size(), headerKeys, headerValues, callbacks); + + delete [] headerKeys; + delete [] headerValues; + + return result; +} + + +void WebsocketClient::DestroyClient() +{ + ReleaseForRegistration(); +} + + +CoreWebsocketClient::CoreWebsocketClient(BNWebsocketClient* client): WebsocketClient(client) +{ +} + + +bool CoreWebsocketClient::Connect(const std::string& host, const std::unordered_map<std::string, std::string>& headers) +{ + (void)host; + (void)headers; + return false; +} + + +bool CoreWebsocketClient::Write(const std::vector<uint8_t>& data) +{ + return BNWriteWebsocketClientData(m_object, data.data(), data.size()); +} + + +bool CoreWebsocketClient::Disconnect() +{ + return BNDisconnectWebsocketClient(m_object); +} + + +WebsocketProvider::WebsocketProvider(const string& name): m_nameForRegister(name) +{ +} + + +WebsocketProvider::WebsocketProvider(BNWebsocketProvider* provider) +{ + m_object = provider; +} + + +BNWebsocketClient* WebsocketProvider::CreateClientCallback(void* ctxt) +{ + WebsocketProvider* provider = (WebsocketProvider*)ctxt; + Ref<WebsocketClient> client = provider->CreateNewClient(); + return client ? BNNewWebsocketClientReference(client->GetObject()) : nullptr; +} + + +vector<Ref<WebsocketProvider>> WebsocketProvider::GetList() +{ + size_t count; + BNWebsocketProvider** list = BNGetWebsocketProviderList(&count); + vector<Ref<WebsocketProvider>> result; + for (size_t i = 0; i < count; i++) + result.push_back(new CoreWebsocketProvider(list[i])); + BNFreeWebsocketProviderList(list); + return result; +} + + +Ref<WebsocketProvider> WebsocketProvider::GetByName(const string& name) +{ + BNWebsocketProvider* result = BNGetWebsocketProviderByName(name.c_str()); + if (!result) + return nullptr; + return new CoreWebsocketProvider(result); +} + + +void WebsocketProvider::Register(WebsocketProvider* provider) +{ + BNWebsocketProviderCallbacks cb; + cb.context = provider; + cb.createClient = CreateClientCallback; + provider->m_object = BNRegisterWebsocketProvider(provider->m_nameForRegister.c_str(), &cb); +} + + +CoreWebsocketProvider::CoreWebsocketProvider(BNWebsocketProvider* provider): WebsocketProvider(provider) +{ +} + + +Ref<WebsocketClient> CoreWebsocketProvider::CreateNewClient() +{ + BNWebsocketClient* result = BNCreateWebsocketProviderClient(m_object); + + if (!result) + return nullptr; + return new CoreWebsocketClient(result); +} |
