summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2021-06-01 16:22:24 -0400
committerGlenn Smith <glenn@vector35.com>2021-08-17 16:50:09 -0400
commit176cc79219cdf015cbcf7f90bb6eb5f6a8c59940 (patch)
treef472869dadf12add3daf853f2dd73953d322ac6a
parent401b73b242bc9447bd12215b17ea8ce215c7d6ed (diff)
Websocket provider
-rw-r--r--binaryninjaapi.h152
-rw-r--r--binaryninjacore.h46
-rw-r--r--python/__init__.py1
-rw-r--r--python/websocketprovider.py286
-rw-r--r--ui/uitypes.h2
-rw-r--r--websocketprovider.cpp209
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);
+}