mirror of
https://github.com/frida/frida-core.git
synced 2026-11-09 10:09:38 -10:00
Compare commits
No files matched your search
@@ -18,7 +18,7 @@ jobs:
|
||||
include:
|
||||
- { id: windows-x86_64, runner: '"windows-latest"' }
|
||||
- { id: windows-x86, runner: '"windows-latest"' }
|
||||
- { id: macos-x86_64, runner: '"macos-11"' }
|
||||
- { id: macos-x86_64, runner: '"macos-latest"' }
|
||||
- { id: macos-arm64, runner: '"macos-latest"' }
|
||||
- { id: linux-x86_64, runner: '"ubuntu-latest"' }
|
||||
- { id: linux-x86, runner: '"ubuntu-latest"' }
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
name: core-devkit-${{ matrix.id }}
|
||||
path: build/src/devkit/
|
||||
- name: Test
|
||||
if: matrix.id != 'macos-arm64'
|
||||
if: ${{ !startsWith(matrix.id, 'macos-') }}
|
||||
run: make test
|
||||
|
||||
cross:
|
||||
|
||||
+135
-9
@@ -428,21 +428,28 @@ namespace Frida {
|
||||
return *((double *) &bits);
|
||||
}
|
||||
|
||||
public string read_string (size_t offset) {
|
||||
public string read_string (size_t offset) throws Error {
|
||||
string * start = (string *) get_pointer (offset, sizeof (char));
|
||||
size_t max_length = size - offset;
|
||||
string * val = (string *) get_pointer (offset, sizeof (char));
|
||||
string * end = memchr (val, 0, max_length);
|
||||
assert (end != null);
|
||||
size_t size = end - val;
|
||||
return val->substring (0, (long) size);
|
||||
string * end = memchr (start, 0, max_length);
|
||||
if (end == null)
|
||||
throw new Error.PROTOCOL ("Missing null character");
|
||||
size_t size = end - start;
|
||||
string val = start->substring (0, (long) size);
|
||||
if (!val.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid UTF-8 string");
|
||||
return val;
|
||||
}
|
||||
|
||||
[CCode (cname = "memchr", cheader_filename = "string.h")]
|
||||
private extern static string * memchr (string * s, int c, size_t n);
|
||||
|
||||
public string read_fixed_string (size_t offset, size_t size) {
|
||||
string * val = (string *) get_pointer (offset, size);
|
||||
return val->substring (0, (long) size);
|
||||
public string read_fixed_string (size_t offset, size_t size) throws Error {
|
||||
string * start = (string *) get_pointer (offset, size);
|
||||
string val = start->substring (0, (long) size);
|
||||
if (!val.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid UTF-8 string");
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned Buffer write_string (size_t offset, string val) {
|
||||
@@ -458,4 +465,123 @@ namespace Frida {
|
||||
return data + offset;
|
||||
}
|
||||
}
|
||||
|
||||
public class BufferReader {
|
||||
public size_t available {
|
||||
get {
|
||||
return buffer.bytes.get_size () - offset;
|
||||
}
|
||||
}
|
||||
|
||||
private Buffer buffer;
|
||||
private size_t offset = 0;
|
||||
|
||||
public BufferReader (Buffer buf) {
|
||||
buffer = buf;
|
||||
}
|
||||
|
||||
public uint64 read_pointer (size_t offset) throws Error {
|
||||
var pointer_size = buffer.pointer_size;
|
||||
check_available (pointer_size);
|
||||
var ptr = buffer.read_pointer (offset);
|
||||
offset += pointer_size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public int8 read_int8 () throws Error {
|
||||
check_available (sizeof (int8));
|
||||
var val = buffer.read_int8 (offset);
|
||||
offset += sizeof (int8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint8 read_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
var val = buffer.read_uint8 (offset);
|
||||
offset += sizeof (uint8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int16 read_int16 () throws Error {
|
||||
check_available (sizeof (int16));
|
||||
var val = buffer.read_int16 (offset);
|
||||
offset += sizeof (int16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint16 read_uint16 () throws Error {
|
||||
check_available (sizeof (uint16));
|
||||
var val = buffer.read_uint16 (offset);
|
||||
offset += sizeof (uint16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int32 read_int32 () throws Error {
|
||||
check_available (sizeof (int32));
|
||||
var val = buffer.read_int32 (offset);
|
||||
offset += sizeof (int32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint32 read_uint32 () throws Error {
|
||||
check_available (sizeof (uint32));
|
||||
var val = buffer.read_uint32 (offset);
|
||||
offset += sizeof (uint32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int64 read_int64 () throws Error {
|
||||
check_available (sizeof (int64));
|
||||
var val = buffer.read_int64 (offset);
|
||||
offset += sizeof (int64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint64 read_uint64 () throws Error {
|
||||
check_available (sizeof (uint64));
|
||||
var val = buffer.read_uint64 (offset);
|
||||
offset += sizeof (uint64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public float read_float () throws Error {
|
||||
check_available (sizeof (float));
|
||||
var val = buffer.read_float (offset);
|
||||
offset += sizeof (float);
|
||||
return val;
|
||||
}
|
||||
|
||||
public double read_double () throws Error {
|
||||
check_available (sizeof (double));
|
||||
var val = buffer.read_double (offset);
|
||||
offset += sizeof (double);
|
||||
return val;
|
||||
}
|
||||
|
||||
public string read_string () throws Error {
|
||||
check_available (1);
|
||||
var val = buffer.read_string (offset);
|
||||
offset += val.length + 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
public string read_fixed_string (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = buffer.read_fixed_string (offset, size);
|
||||
offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
public Bytes read_bytes (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = buffer.bytes[offset:offset + size];
|
||||
offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
private void check_available (size_t n) throws Error {
|
||||
if (available < n)
|
||||
throw new Error.PROTOCOL ("Malformed buffer: truncated");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,209 +35,232 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public class LinuxPairingBrowser : Object, PairingBrowser {
|
||||
private DBusConnection connection;
|
||||
private AvahiServer server;
|
||||
private AvahiServiceBrowser browser;
|
||||
|
||||
private Gee.List<PairingServiceDetails> current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
private Resolved.Manager? resolved;
|
||||
private Source? timer;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
start.begin ();
|
||||
}
|
||||
private const size_t IF_NAMESIZE = 16;
|
||||
|
||||
private async void start () {
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
connection = yield GLib.Bus.get (BusType.SYSTEM, io_cancellable);
|
||||
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
|
||||
|
||||
server = yield connection.get_proxy (AVAHI_SERVICE_NAME, "/", DO_NOT_LOAD_PROPERTIES, io_cancellable);
|
||||
resolved = yield connection.get_proxy (Resolved.SERVICE_NAME, Resolved.SERVICE_PATH, DO_NOT_LOAD_PROPERTIES,
|
||||
cancellable);
|
||||
|
||||
GLib.ObjectPath browser_path = yield server.service_browser_prepare (-1, INET6, PAIRING_REGTYPE,
|
||||
PAIRING_DOMAIN, 0, io_cancellable);
|
||||
browser = yield connection.get_proxy (AVAHI_SERVICE_NAME, browser_path, DO_NOT_LOAD_PROPERTIES,
|
||||
io_cancellable);
|
||||
browser.item_new.connect (on_item_new);
|
||||
browser.all_for_now.connect (on_all_for_now);
|
||||
yield browser.start (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("Oopsie: %s\n", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain, uint flags) {
|
||||
char raw_interface_name[Linux.Network.INTERFACE_NAME_SIZE];
|
||||
Linux.Network.if_indextoname (interface_index, (string) raw_interface_name);
|
||||
unowned string interface_name = (string) raw_interface_name;
|
||||
current_batch.add (new LinuxPairingServiceDetails (name, interface_index, interface_name, protocol, server));
|
||||
}
|
||||
|
||||
private void on_all_for_now () {
|
||||
services_discovered (current_batch.to_array ());
|
||||
current_batch.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
private class LinuxPairingServiceDetails : Object, PairingServiceDetails {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint interface_index {
|
||||
get { return _interface_index; }
|
||||
}
|
||||
|
||||
public string interface_name {
|
||||
get { return _interface_name; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint _interface_index;
|
||||
private string _interface_name;
|
||||
private AvahiProtocol protocol;
|
||||
|
||||
private AvahiServer server;
|
||||
|
||||
internal LinuxPairingServiceDetails (string name, uint interface_index, string interface_name, AvahiProtocol protocol,
|
||||
AvahiServer server) {
|
||||
_name = name;
|
||||
_interface_index = interface_index;
|
||||
_interface_name = interface_name;
|
||||
this.protocol = protocol;
|
||||
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
AvahiServiceResolver resolver;
|
||||
try {
|
||||
GLib.ObjectPath path = yield server.service_resolver_prepare ((int) interface_index, protocol, name,
|
||||
PAIRING_REGTYPE, PAIRING_DOMAIN, INET6, 0, cancellable);
|
||||
DBusConnection connection = ((DBusProxy) server).get_connection ();
|
||||
resolver = yield connection.get_proxy (AVAHI_SERVICE_NAME, path, DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
var promise = new Promise<Gee.List<PairingServiceHost>> ();
|
||||
var hosts = new Gee.ArrayList<PairingServiceHost> ();
|
||||
resolver.found.connect ((interface_index, protocol, name, type, domain, host, address_protocol, address, port, txt,
|
||||
flags) => {
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
var iter = new VariantIter (txt);
|
||||
Variant? cur;
|
||||
while ((cur = iter.next_value ()) != null) {
|
||||
unowned string raw_val = (string) cur.get_data ();
|
||||
string val = raw_val.make_valid ((ssize_t) cur.get_size ());
|
||||
txt_record.add (val);
|
||||
try {
|
||||
yield resolve_services (MDNS_IPV6 | NO_NETWORK, cancellable);
|
||||
schedule_next_poll ();
|
||||
} catch (Error e) {
|
||||
handle_poll_timer_tick.begin ();
|
||||
}
|
||||
|
||||
hosts.add (new LinuxPairingServiceHost (
|
||||
host,
|
||||
new InetSocketAddress.from_string (address, port),
|
||||
port,
|
||||
txt_record));
|
||||
|
||||
if (!promise.future.ready)
|
||||
promise.resolve (hosts);
|
||||
});
|
||||
resolver.failure.connect (error => {
|
||||
if (!promise.future.ready)
|
||||
promise.reject (new Error.NOT_SUPPORTED ("%s", error));
|
||||
});
|
||||
|
||||
try {
|
||||
yield resolver.start (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
if (timer != null) {
|
||||
timer.destroy ();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
return yield promise.future.wait_async (cancellable);
|
||||
io_cancellable.cancel ();
|
||||
}
|
||||
|
||||
private void schedule_next_poll () {
|
||||
timer = new TimeoutSource.seconds (5);
|
||||
timer.set_callback (() => {
|
||||
timer = null;
|
||||
handle_poll_timer_tick.begin ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timer.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
private async void handle_poll_timer_tick () {
|
||||
try {
|
||||
yield resolve_services (MDNS_IPV6, io_cancellable);
|
||||
schedule_next_poll ();
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.CANCELLED))
|
||||
schedule_next_poll ();
|
||||
}
|
||||
}
|
||||
|
||||
private async void resolve_services (Resolved.Flags flags, Cancellable? cancellable) throws Error, IOError {
|
||||
Resolved.RRItem[] items;
|
||||
uint64 ptr_flags;
|
||||
try {
|
||||
yield resolved.resolve_record (Resolved.ANY_INTERFACE, PairingService.DNS_SD_NAME, DnsRecordClass.IN,
|
||||
DnsRecordType.PTR, flags, cancellable, out items, out ptr_flags);
|
||||
} catch (GLib.Error e) {
|
||||
throw (Error) parse_error (e);
|
||||
}
|
||||
|
||||
var promises = new Gee.ArrayQueue<Promise<PairingServiceDetails?>> ();
|
||||
foreach (var item in items) {
|
||||
var pr = new DnsPacketReader (new Bytes (item.data));
|
||||
DnsPtrRecord ptr = pr.read_ptr ();
|
||||
var promise = new Promise<PairingServiceDetails> ();
|
||||
resolve_service.begin (ptr, item.ifindex, flags, cancellable, promise);
|
||||
promises.offer (promise);
|
||||
}
|
||||
|
||||
Promise<PairingServiceDetails?>? p;
|
||||
while ((p = promises.poll ()) != null) {
|
||||
try {
|
||||
yield p.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags, Cancellable? cancellable,
|
||||
Promise<PairingServiceDetails?> promise) {
|
||||
try {
|
||||
PairingServiceDetails? service = yield do_resolve_service (ptr, ifindex, flags, cancellable);
|
||||
promise.resolve (service);
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async PairingServiceDetails? do_resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Resolved.SrvItem[] srv_items;
|
||||
Variant txt_items;
|
||||
string canonical_name;
|
||||
string canonical_type;
|
||||
string canonical_domain;
|
||||
uint64 srv_flags;
|
||||
try {
|
||||
yield resolved.resolve_service (ifindex, "", "", ptr.name, Posix.AF_INET6, flags, cancellable,
|
||||
out srv_items, out txt_items, out canonical_name, out canonical_type, out canonical_domain,
|
||||
out srv_flags);
|
||||
} catch (GLib.Error e) {
|
||||
throw (Error) parse_error (e);
|
||||
}
|
||||
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
foreach (var raw_item in txt_items) {
|
||||
string item = ((string *) raw_item.get_data ())->substring (0, (long) raw_item.get_size ());
|
||||
if (!item.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid TXT item");
|
||||
txt_record.add (item);
|
||||
}
|
||||
|
||||
var meta = PairingServiceMetadata.from_txt_record (txt_record);
|
||||
var ip = new InetAddress.from_bytes (srv_items[0].addresses[0].ip, IPV6);
|
||||
var service = new PairingServiceDetails () {
|
||||
identifier = meta.identifier,
|
||||
auth_tag = meta.auth_tag,
|
||||
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: ip,
|
||||
port: srv_items[0].port,
|
||||
scope_id: ip.get_is_link_local () ? ifindex : 0
|
||||
),
|
||||
interface_address = resolve_interface_address (ifindex),
|
||||
};
|
||||
|
||||
service_discovered (service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
private static InetSocketAddress resolve_interface_address (int32 ifindex) throws Error {
|
||||
char ifname_buf[IF_NAMESIZE];
|
||||
unowned string? ifname = Linux.Network.if_indextoname (ifindex, (string) ifname_buf);
|
||||
if (ifname == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to resolve interface name");
|
||||
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
if (candidate.ifa_name != ifname)
|
||||
continue;
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
return (InetSocketAddress)
|
||||
SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6));
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve interface address");
|
||||
}
|
||||
|
||||
private static GLib.Error parse_error (GLib.Error e) {
|
||||
if (e is Error || e is IOError.CANCELLED)
|
||||
return e;
|
||||
return new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingServiceHost : Object, PairingServiceHost {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
namespace Resolved {
|
||||
public const string SERVICE_NAME = "org.freedesktop.resolve1";
|
||||
public const string SERVICE_PATH = "/org/freedesktop/resolve1";
|
||||
|
||||
public const int32 ANY_INTERFACE = 0;
|
||||
|
||||
[DBus (name = "org.freedesktop.resolve1.Manager")]
|
||||
public interface Manager : Object {
|
||||
public abstract async void resolve_record (int32 ifindex, string name, uint16 klass, uint16 type, uint64 flags,
|
||||
Cancellable? cancellable, out RRItem[] items, out uint64 result_flags) throws GLib.Error;
|
||||
public abstract async void resolve_service (int32 ifindex, string name, string type, string domain, int32 family,
|
||||
uint64 flags, Cancellable? cancellable, out SrvItem[] srv_items,
|
||||
[DBus (signature = "aay")] out Variant txt_items, out string canonical_name, out string canonical_type,
|
||||
out string canonical_domain, out uint64 result_flags) throws GLib.Error;
|
||||
}
|
||||
|
||||
public InetSocketAddress address {
|
||||
get { return _address; }
|
||||
[Flags]
|
||||
public enum Flags {
|
||||
DNS = 1 << 0,
|
||||
LLMNR_IPV4 = 1 << 1,
|
||||
LLMNR_IPV6 = 1 << 2,
|
||||
MDNS_IPV4 = 1 << 3,
|
||||
MDNS_IPV6 = 1 << 4,
|
||||
NO_CNAME = 1 << 5,
|
||||
NO_TXT = 1 << 6,
|
||||
NO_ADDRESS = 1 << 7,
|
||||
NO_SEARCH = 1 << 8,
|
||||
AUTHENTICATED = 1 << 9,
|
||||
NO_VALIDATE = 1 << 10,
|
||||
NO_SYNTHESIZE = 1 << 11,
|
||||
NO_CACHE = 1 << 12,
|
||||
NO_ZONE = 1 << 13,
|
||||
NO_TRUST_ANCHOR = 1 << 14,
|
||||
NO_NETWORK = 1 << 15,
|
||||
REQUIRE_PRIMARY = 1 << 16,
|
||||
CLAMP_TTL = 1 << 17,
|
||||
CONFIDENTIAL = 1 << 18,
|
||||
SYNTHETIC = 1 << 19,
|
||||
FROM_CACHE = 1 << 20,
|
||||
FROM_ZONE = 1 << 21,
|
||||
FROM_TRUST_ANCHOR = 1 << 22,
|
||||
FROM_NETWORK = 1 << 23,
|
||||
NO_STALE = 1 << 24,
|
||||
RELAX_SINGLE_LABEL = 1 << 25,
|
||||
}
|
||||
|
||||
public uint16 port {
|
||||
get { return _port; }
|
||||
public struct RRItem {
|
||||
public int32 ifindex;
|
||||
public uint16 klass;
|
||||
public uint16 type;
|
||||
public uint8[] data;
|
||||
}
|
||||
|
||||
public Gee.List<string> txt_record {
|
||||
get { return _txt_record; }
|
||||
public struct SrvItem {
|
||||
public uint16 priority;
|
||||
public uint16 weight;
|
||||
public uint16 port;
|
||||
public string name;
|
||||
public SrvAddress[] addresses;
|
||||
public string canonical_name;
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private InetSocketAddress _address;
|
||||
private uint16 _port;
|
||||
private Gee.List<string> _txt_record;
|
||||
|
||||
internal LinuxPairingServiceHost (string name, InetSocketAddress address, uint16 port, Gee.List<string> txt_record) {
|
||||
_name = name;
|
||||
_address = address;
|
||||
_port = port;
|
||||
_txt_record = txt_record;
|
||||
public struct SrvAddress {
|
||||
public int32 ifindex;
|
||||
public int32 family;
|
||||
public uint8[] ip;
|
||||
}
|
||||
|
||||
public async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var result = new Gee.ArrayList<InetSocketAddress> ();
|
||||
result.add (address);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private const string AVAHI_SERVICE_NAME = "org.freedesktop.Avahi";
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.Server2")]
|
||||
private interface AvahiServer : Object {
|
||||
public abstract async GLib.ObjectPath service_browser_prepare (int interface_index, AvahiProtocol protocol, string type,
|
||||
string domain, uint flags, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async GLib.ObjectPath service_resolver_prepare (int interface_index, AvahiProtocol protocol, string name,
|
||||
string type, string domain, AvahiProtocol aprotocol, AvahiLookupFlags flags, Cancellable? cancellable)
|
||||
throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.ServiceBrowser")]
|
||||
private interface AvahiServiceBrowser : Object {
|
||||
public signal void item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
|
||||
uint flags);
|
||||
public signal void item_remove (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
|
||||
uint flags);
|
||||
public signal void failure (string error);
|
||||
public signal void all_for_now ();
|
||||
public signal void cache_exhausted ();
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.ServiceResolver")]
|
||||
private interface AvahiServiceResolver : Object {
|
||||
public signal void found (int interface_index, AvahiProtocol protocol, string name, string type, string domain, string host,
|
||||
AvahiProtocol address_protocol, string address, uint16 port, [DBus (signature = "aay")] Variant txt, uint flags);
|
||||
public signal void failure (string error);
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
private enum AvahiProtocol {
|
||||
INET,
|
||||
INET6,
|
||||
UNSPEC = -1,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum AvahiLookupFlags {
|
||||
USE_WIDE_AREA = 1,
|
||||
USE_MULTICAST = 2,
|
||||
NO_TXT = 4,
|
||||
NO_ADDRESS = 8,
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
#include "../windows/icon-helpers.h"
|
||||
|
||||
#include <setupapi.h>
|
||||
#include <devguid.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <setupapi.h>
|
||||
#include <windns.h>
|
||||
|
||||
typedef struct _FridaPairingBrowserBackend FridaPairingBrowserBackend;
|
||||
|
||||
typedef struct _FridaMobileDeviceInfo FridaMobileDeviceInfo;
|
||||
typedef struct _FridaImageDeviceInfo FridaImageDeviceInfo;
|
||||
@@ -13,8 +17,18 @@ typedef struct _FridaFindImageDeviceContext FridaFindImageDeviceContext;
|
||||
|
||||
typedef struct _FridaDeviceInfo FridaDeviceInfo;
|
||||
|
||||
typedef struct _FridaDnsApi FridaDnsApi;
|
||||
|
||||
typedef gboolean (* FridaEnumerateDeviceFunc) (const FridaDeviceInfo * device_info, gpointer user_data);
|
||||
|
||||
struct _FridaPairingBrowserBackend
|
||||
{
|
||||
FridaFruityWindowsPairingBrowserResultCallback callback;
|
||||
gpointer callback_target;
|
||||
|
||||
DNS_SERVICE_CANCEL browse_handle;
|
||||
};
|
||||
|
||||
struct _FridaMobileDeviceInfo
|
||||
{
|
||||
WCHAR * location;
|
||||
@@ -49,6 +63,14 @@ struct _FridaDeviceInfo
|
||||
PSP_DEVINFO_DATA device_info_data;
|
||||
};
|
||||
|
||||
struct _FridaDnsApi
|
||||
{
|
||||
DNS_STATUS (WINAPI * browse) (DNS_SERVICE_BROWSE_REQUEST * request, DNS_SERVICE_CANCEL * cancel);
|
||||
DNS_STATUS (WINAPI * browse_cancel) (DNS_SERVICE_CANCEL * cancel_handle);
|
||||
};
|
||||
|
||||
static void WINAPI frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results);
|
||||
|
||||
static FridaMobileDeviceInfo * find_mobile_device_by_udid (const WCHAR * udid);
|
||||
static FridaImageDeviceInfo * find_image_device_by_location (const WCHAR * location);
|
||||
|
||||
@@ -68,8 +90,104 @@ static WCHAR * frida_read_registry_string (HKEY key, WCHAR * value_name);
|
||||
static WCHAR * frida_read_registry_multi_string (HKEY key, WCHAR * value_name);
|
||||
static gpointer frida_read_registry_value (HKEY key, WCHAR * value_name, DWORD expected_type);
|
||||
|
||||
static FridaDnsApi * frida_try_get_dns_api (void);
|
||||
|
||||
static GUID GUID_APPLE_USB = { 0xF0B32BE3, 0x6678, 0x4879, { 0x92, 0x30, 0x0E4, 0x38, 0x45, 0xD8, 0x05, 0xEE } };
|
||||
|
||||
void
|
||||
_frida_fruity_windows_pairing_browser_enumerate_network_interfaces (FridaFruityWindowsPairingBrowserNetifFoundFunc func,
|
||||
gpointer func_target)
|
||||
{
|
||||
IP_ADAPTER_ADDRESSES * adapters;
|
||||
ULONG buffer_size, result;
|
||||
IP_ADAPTER_ADDRESSES_LH * adapter;
|
||||
|
||||
if (frida_try_get_dns_api () == NULL)
|
||||
return;
|
||||
|
||||
buffer_size = 32768;
|
||||
adapters = g_malloc (buffer_size);
|
||||
|
||||
do
|
||||
{
|
||||
result = GetAdaptersAddresses (AF_INET6, 0, NULL, adapters, &buffer_size);
|
||||
if (result != ERROR_BUFFER_OVERFLOW)
|
||||
break;
|
||||
adapters = g_realloc (adapters, buffer_size);
|
||||
}
|
||||
while (result == ERROR_BUFFER_OVERFLOW);
|
||||
if (result != ERROR_SUCCESS)
|
||||
goto beach;
|
||||
|
||||
for (adapter = adapters; adapter != NULL; adapter = adapter->Next)
|
||||
{
|
||||
SOCKET_ADDRESS * raw_addr = &adapter->FirstUnicastAddress->Address;
|
||||
GInetSocketAddress * addr;
|
||||
|
||||
if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
|
||||
continue;
|
||||
|
||||
addr = G_INET_SOCKET_ADDRESS (g_socket_address_new_from_native (raw_addr->lpSockaddr, raw_addr->iSockaddrLength));
|
||||
|
||||
func (adapter->IfIndex, adapter->AdapterName, addr, func_target);
|
||||
}
|
||||
|
||||
beach:
|
||||
g_free (adapters);
|
||||
}
|
||||
|
||||
void *
|
||||
_frida_fruity_windows_pairing_browser_monitor_create_backend (gulong interface_index,
|
||||
FridaFruityWindowsPairingBrowserResultCallback callback, gpointer callback_target)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend;
|
||||
FridaDnsApi * api;
|
||||
DNS_SERVICE_BROWSE_REQUEST r = { 0, };
|
||||
|
||||
api = frida_try_get_dns_api ();
|
||||
if (api == NULL)
|
||||
return NULL;
|
||||
|
||||
backend = g_slice_new0 (FridaPairingBrowserBackend);
|
||||
backend->callback = callback;
|
||||
backend->callback_target = callback_target;
|
||||
|
||||
r.Version = DNS_QUERY_REQUEST_VERSION2;
|
||||
r.InterfaceIndex = interface_index;
|
||||
r.QueryName = G_PASTE (L, FRIDA_FRUITY_PAIRING_SERVICE_DNS_SD_NAME);
|
||||
r.pBrowseCallbackV2 = frida_fruity_windows_pairing_browser_on_browse_results;
|
||||
r.pQueryContext = backend;
|
||||
|
||||
if (api->browse (&r, &backend->browse_handle) != DNS_REQUEST_PENDING)
|
||||
{
|
||||
g_slice_free (FridaPairingBrowserBackend, backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
void
|
||||
_frida_fruity_windows_pairing_browser_monitor_destroy_backend (void * opaque_backend)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend = opaque_backend;
|
||||
|
||||
if (backend == NULL)
|
||||
return;
|
||||
|
||||
frida_try_get_dns_api ()->browse_cancel (&backend->browse_handle);
|
||||
|
||||
g_slice_free (FridaPairingBrowserBackend, backend);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend = query_context;
|
||||
|
||||
backend->callback (query_results, backend->callback_target);
|
||||
}
|
||||
|
||||
void
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
@@ -426,3 +544,30 @@ beach:
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static FridaDnsApi *
|
||||
frida_try_get_dns_api (void)
|
||||
{
|
||||
static gsize api_value = 0;
|
||||
|
||||
if (g_once_init_enter (&api_value))
|
||||
{
|
||||
HMODULE mod;
|
||||
FARPROC browse;
|
||||
FridaDnsApi * api = NULL;
|
||||
|
||||
mod = GetModuleHandleW (L"dnsapi.dll");
|
||||
|
||||
browse = GetProcAddress (mod, "DnsServiceBrowse");
|
||||
if (browse != NULL)
|
||||
{
|
||||
api = g_slice_new (FridaDnsApi);
|
||||
api->browse = (gpointer) browse;
|
||||
api->browse_cancel = (gpointer) GetProcAddress (mod, "DnsServiceBrowseCancel");
|
||||
}
|
||||
|
||||
g_once_init_leave (&api_value, GPOINTER_TO_SIZE (api) + 1);
|
||||
}
|
||||
|
||||
return GSIZE_TO_POINTER (api_value - 1);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class WindowsPairingBrowser : Object, PairingBrowser {
|
||||
private Gee.Map<string, Monitor> monitors = new Gee.HashMap<string, Monitor> ();
|
||||
|
||||
private MainContext main_context = MainContext.ref_thread_default ();
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
_enumerate_network_interfaces ((index, name, address) => {
|
||||
var monitor = new Monitor (this, index, address);
|
||||
monitors[name] = monitor;
|
||||
});
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
monitors.clear ();
|
||||
}
|
||||
|
||||
private void on_result (WinDns.QueryResult * result, InetSocketAddress interface_address) {
|
||||
try {
|
||||
PairingServiceDetails service = parse_result (result, interface_address);
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
service_discovered (service);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static PairingServiceDetails parse_result (WinDns.QueryResult * res,
|
||||
InetSocketAddress interface_address) throws GLib.Error {
|
||||
PairingServiceMetadata? meta = null;
|
||||
InetAddress? ip = null;
|
||||
uint16 port = 0;
|
||||
for (WinDns.Record * r = res->query_records; r != null; r = r->next) {
|
||||
switch (r->type) {
|
||||
case TEXT:
|
||||
WinDns.TxtData * txt = &r->txt;
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
foreach (unowned string16 str in txt->strings)
|
||||
txt_record.add (str.to_utf8 ());
|
||||
meta = PairingServiceMetadata.from_txt_record (txt_record);
|
||||
break;
|
||||
case AAAA:
|
||||
ip = new InetAddress.from_bytes (r->aaaa.ip.data, IPV6);
|
||||
break;
|
||||
case SRV:
|
||||
port = r->srv.port;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (meta == null || ip == null || port == 0)
|
||||
throw new Error.PROTOCOL ("Incomplete result");
|
||||
|
||||
return new PairingServiceDetails () {
|
||||
identifier = meta.identifier,
|
||||
auth_tag = meta.auth_tag,
|
||||
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: ip,
|
||||
port: port,
|
||||
scope_id: ip.get_is_link_local () ? interface_address.get_scope_id () : 0
|
||||
),
|
||||
interface_address = interface_address,
|
||||
};
|
||||
}
|
||||
|
||||
private class Monitor {
|
||||
private weak WindowsPairingBrowser parent;
|
||||
private InetSocketAddress interface_address;
|
||||
|
||||
private void * backend;
|
||||
|
||||
public Monitor (WindowsPairingBrowser parent, ulong interface_index, InetSocketAddress interface_address) {
|
||||
this.parent = parent;
|
||||
this.interface_address = interface_address;
|
||||
|
||||
backend = _create_backend (interface_index, on_result);
|
||||
}
|
||||
|
||||
~Monitor () {
|
||||
_destroy_backend (backend);
|
||||
}
|
||||
|
||||
private void on_result (void * result) {
|
||||
parent.on_result (result, interface_address);
|
||||
}
|
||||
|
||||
public extern static void * _create_backend (ulong interface_index, ResultCallback callback);
|
||||
public extern static void _destroy_backend (void * backend);
|
||||
}
|
||||
|
||||
public extern static void _enumerate_network_interfaces (NetifFoundFunc func);
|
||||
|
||||
public delegate void NetifFoundFunc (ulong index, string identifier, owned InetSocketAddress address);
|
||||
public delegate void ResultCallback (void * result);
|
||||
}
|
||||
}
|
||||
+301
-90
@@ -708,8 +708,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private State state = CREATED;
|
||||
private Gee.Set<PortableCoreDeviceTransport> transports = new Gee.HashSet<PortableCoreDeviceTransport> ();
|
||||
private Promise<bool> started = new Promise<bool> ();
|
||||
|
||||
private Gee.Set<PortableCoreDeviceUsbTransport> usb_transports = new Gee.HashSet<PortableCoreDeviceUsbTransport> ();
|
||||
private Promise<bool> usb_started = new Promise<bool> ();
|
||||
private bool modeswitch_allowed = false;
|
||||
private Promise<bool>? modeswitch_activated;
|
||||
private Gee.Set<string>? modeswitch_udids_pending;
|
||||
@@ -718,10 +719,16 @@ namespace Frida.Fruity {
|
||||
private LibUSB.Context? usb_context;
|
||||
private LibUSB.HotCallbackHandle iphone_callback;
|
||||
private LibUSB.HotCallbackHandle ipad_callback;
|
||||
private uint pending_device_arrivals = 0;
|
||||
private Gee.Map<uint32, LibUSB.Device> polled_devices = new Gee.HashMap<uint32, LibUSB.Device> ();
|
||||
private Source? polled_timer;
|
||||
private uint polled_outdated = 0;
|
||||
private uint pending_usb_device_arrivals = 0;
|
||||
private Gee.Map<uint32, LibUSB.Device> polled_usb_devices = new Gee.HashMap<uint32, LibUSB.Device> ();
|
||||
private Source? polled_usb_timer;
|
||||
private uint polled_usb_outdated = 0;
|
||||
|
||||
private PairingBrowser network_browser = PairingBrowser.make_default ();
|
||||
private Gee.Map<string, PortableCoreDeviceNetworkTransport> network_transports =
|
||||
new Gee.HashMap<string, PortableCoreDeviceNetworkTransport> ();
|
||||
|
||||
private Gee.List<PairingIdentity.Peer> peers = PairingIdentity.load_peers ();
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
@@ -741,6 +748,8 @@ namespace Frida.Fruity {
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
network_browser.service_discovered.connect (on_network_pairing_service_discovered);
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
@@ -748,8 +757,10 @@ namespace Frida.Fruity {
|
||||
|
||||
usb_worker = new Thread<void> ("frida-core-device-usb", perform_usb_work);
|
||||
|
||||
yield network_browser.start (cancellable);
|
||||
|
||||
try {
|
||||
yield started.future.wait_async (cancellable);
|
||||
yield usb_started.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
@@ -765,17 +776,23 @@ namespace Frida.Fruity {
|
||||
if (usb_context != null)
|
||||
usb_context.interrupt_event_handler ();
|
||||
|
||||
yield network_browser.stop (cancellable);
|
||||
|
||||
foreach (var transport in network_transports.values.to_array ())
|
||||
yield transport.close (cancellable);
|
||||
network_transports.clear ();
|
||||
|
||||
usb_worker.join ();
|
||||
usb_worker = null;
|
||||
|
||||
if (polled_timer != null) {
|
||||
polled_timer.destroy ();
|
||||
polled_timer = null;
|
||||
if (polled_usb_timer != null) {
|
||||
polled_usb_timer.destroy ();
|
||||
polled_usb_timer = null;
|
||||
}
|
||||
|
||||
foreach (var transport in transports)
|
||||
foreach (var transport in usb_transports.to_array ())
|
||||
yield transport.close (cancellable);
|
||||
transports.clear ();
|
||||
usb_transports.clear ();
|
||||
|
||||
usb_context = null;
|
||||
|
||||
@@ -787,7 +804,7 @@ namespace Frida.Fruity {
|
||||
|
||||
modeswitch_activated = new Promise<bool> ();
|
||||
modeswitch_udids_pending = new Gee.HashSet<string> ();
|
||||
foreach (var transport in transports.to_array ()) {
|
||||
foreach (var transport in usb_transports.to_array ()) {
|
||||
var usb_device = transport.usb_device;
|
||||
try {
|
||||
if (yield usb_device.maybe_modeswitch (cancellable))
|
||||
@@ -805,32 +822,37 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void perform_usb_work () {
|
||||
if (LibUSB.Context.init (out usb_context) != SUCCESS)
|
||||
if (LibUSB.Context.init (out usb_context) != SUCCESS) {
|
||||
schedule_on_frida_thread (() => {
|
||||
usb_started.resolve (true);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
AtomicUint.inc (ref pending_device_arrivals);
|
||||
AtomicUint.inc (ref pending_usb_device_arrivals);
|
||||
|
||||
if (LibUSB.has_capability (HAS_HOTPLUG) != 0) {
|
||||
usb_context.hotplug_register_callback (DEVICE_ARRIVED | DEVICE_LEFT, ENUMERATE, VENDOR_ID_APPLE,
|
||||
PRODUCT_ID_IPHONE, LibUSB.HotPlugEvent.MATCH_ANY, on_hotplug_event, out iphone_callback);
|
||||
PRODUCT_ID_IPHONE, LibUSB.HotPlugEvent.MATCH_ANY, on_usb_hotplug_event, out iphone_callback);
|
||||
usb_context.hotplug_register_callback (DEVICE_ARRIVED | DEVICE_LEFT, ENUMERATE, VENDOR_ID_APPLE,
|
||||
PRODUCT_ID_IPAD, LibUSB.HotPlugEvent.MATCH_ANY, on_hotplug_event, out ipad_callback);
|
||||
PRODUCT_ID_IPAD, LibUSB.HotPlugEvent.MATCH_ANY, on_usb_hotplug_event, out ipad_callback);
|
||||
} else {
|
||||
refresh_polled_devices ();
|
||||
refresh_polled_usb_devices ();
|
||||
|
||||
var source = new TimeoutSource.seconds (2);
|
||||
source.set_callback (() => {
|
||||
AtomicUint.set (ref polled_outdated, 1);
|
||||
AtomicUint.set (ref polled_usb_outdated, 1);
|
||||
usb_context.interrupt_event_handler ();
|
||||
return Source.CONTINUE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
polled_timer = source;
|
||||
polled_usb_timer = source;
|
||||
}
|
||||
|
||||
if (AtomicUint.dec_and_test (ref pending_device_arrivals)) {
|
||||
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals)) {
|
||||
schedule_on_frida_thread (() => {
|
||||
started.resolve (true);
|
||||
usb_started.resolve (true);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
@@ -839,35 +861,35 @@ namespace Frida.Fruity {
|
||||
int completed = 0;
|
||||
usb_context.handle_events_completed (out completed);
|
||||
|
||||
if (AtomicUint.compare_and_exchange (ref polled_outdated, 1, 0))
|
||||
refresh_polled_devices ();
|
||||
if (AtomicUint.compare_and_exchange (ref polled_usb_outdated, 1, 0))
|
||||
refresh_polled_usb_devices ();
|
||||
}
|
||||
}
|
||||
|
||||
private int on_hotplug_event (LibUSB.Context ctx, LibUSB.Device device, LibUSB.HotPlugEvent event) {
|
||||
private int on_usb_hotplug_event (LibUSB.Context ctx, LibUSB.Device device, LibUSB.HotPlugEvent event) {
|
||||
if (event == DEVICE_ARRIVED)
|
||||
on_device_arrived (device);
|
||||
on_usb_device_arrived (device);
|
||||
else
|
||||
on_device_left (device);
|
||||
on_usb_device_left (device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void on_device_arrived (LibUSB.Device device) {
|
||||
AtomicUint.inc (ref pending_device_arrivals);
|
||||
private void on_usb_device_arrived (LibUSB.Device device) {
|
||||
AtomicUint.inc (ref pending_usb_device_arrivals);
|
||||
schedule_on_frida_thread (() => {
|
||||
handle_device_arrival.begin (device);
|
||||
handle_usb_device_arrival.begin (device);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void on_device_left (LibUSB.Device device) {
|
||||
private void on_usb_device_left (LibUSB.Device device) {
|
||||
schedule_on_frida_thread (() => {
|
||||
handle_device_departure.begin (device);
|
||||
handle_usb_device_departure.begin (device);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private async void handle_device_arrival (LibUSB.Device raw_device) {
|
||||
private async void handle_usb_device_arrival (LibUSB.Device raw_device) {
|
||||
UsbDevice? device = null;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
@@ -875,11 +897,11 @@ namespace Frida.Fruity {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (handle_device_arrival.callback);
|
||||
timeout_source.set_callback (handle_usb_device_arrival.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (io_cancellable);
|
||||
cancel_source.set_callback (handle_device_arrival.callback);
|
||||
cancel_source.set_callback (handle_usb_device_arrival.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
@@ -897,8 +919,8 @@ namespace Frida.Fruity {
|
||||
if (modeswitch_allowed && yield device.maybe_modeswitch (io_cancellable))
|
||||
break;
|
||||
|
||||
var transport = new PortableCoreDeviceTransport (device);
|
||||
transports.add (transport);
|
||||
var transport = new PortableCoreDeviceUsbTransport (device);
|
||||
usb_transports.add (transport);
|
||||
transport_attached (transport);
|
||||
|
||||
break;
|
||||
@@ -909,8 +931,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
if (AtomicUint.dec_and_test (ref pending_device_arrivals) && state == STARTING)
|
||||
started.resolve (true);
|
||||
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING)
|
||||
usb_started.resolve (true);
|
||||
|
||||
if (device != null && modeswitch_udids_pending != null) {
|
||||
modeswitch_udids_pending.remove (device.udid);
|
||||
@@ -919,13 +941,13 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private async void handle_device_departure (LibUSB.Device raw_device) {
|
||||
var transport = transports.first_match (t => t.usb_device.raw_device == raw_device);
|
||||
private async void handle_usb_device_departure (LibUSB.Device raw_device) {
|
||||
var transport = usb_transports.first_match (t => t.usb_device.raw_device == raw_device);
|
||||
if (transport == null)
|
||||
return;
|
||||
|
||||
transport_detached (transport);
|
||||
transports.remove (transport);
|
||||
usb_transports.remove (transport);
|
||||
|
||||
try {
|
||||
yield transport.close (io_cancellable);
|
||||
@@ -933,7 +955,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh_polled_devices () {
|
||||
private void refresh_polled_usb_devices () {
|
||||
var current_devices = new Gee.HashMap<uint32, LibUSB.Device> ();
|
||||
foreach (var device in usb_context.get_device_list ()) {
|
||||
var desc = LibUSB.DeviceDescriptor (device);
|
||||
@@ -944,27 +966,73 @@ namespace Frida.Fruity {
|
||||
if (desc.idProduct != PRODUCT_ID_IPHONE && desc.idProduct != PRODUCT_ID_IPAD)
|
||||
continue;
|
||||
|
||||
uint id = make_device_id (device, desc);
|
||||
uint id = make_usb_device_id (device, desc);
|
||||
current_devices[id] = device;
|
||||
|
||||
if (!polled_devices.has_key (id))
|
||||
on_device_arrived (device);
|
||||
if (!polled_usb_devices.has_key (id))
|
||||
on_usb_device_arrived (device);
|
||||
}
|
||||
|
||||
foreach (var e in polled_devices.entries) {
|
||||
foreach (var e in polled_usb_devices.entries) {
|
||||
if (!current_devices.has_key (e.key))
|
||||
on_device_left (e.value);
|
||||
on_usb_device_left (e.value);
|
||||
}
|
||||
|
||||
polled_devices = current_devices;
|
||||
polled_usb_devices = current_devices;
|
||||
}
|
||||
|
||||
private static uint32 make_device_id (LibUSB.Device device, LibUSB.DeviceDescriptor desc) {
|
||||
private static uint32 make_usb_device_id (LibUSB.Device device, LibUSB.DeviceDescriptor desc) {
|
||||
return ((uint32) device.get_port_number () << 24) |
|
||||
((uint32) device.get_device_address () << 16) |
|
||||
(uint32) desc.idProduct;
|
||||
}
|
||||
|
||||
private void on_network_pairing_service_discovered (PairingServiceDetails service) {
|
||||
var peer = find_peer (service);
|
||||
if (peer == null)
|
||||
return;
|
||||
|
||||
var transport = network_transports[peer.udid];
|
||||
if (transport == null) {
|
||||
transport = new PortableCoreDeviceNetworkTransport (peer, service.endpoint, service.interface_address);
|
||||
network_transports[peer.udid] = transport;
|
||||
transport_attached (transport);
|
||||
} else {
|
||||
transport.endpoint = service.endpoint;
|
||||
transport.interface_address = service.interface_address;
|
||||
}
|
||||
}
|
||||
|
||||
private PairingIdentity.Peer? find_peer (PairingServiceDetails service) {
|
||||
var mac = OpenSSL.Envelope.MessageAuthCode.fetch (null, OpenSSL.ShortName.siphash);
|
||||
|
||||
size_t hash_size = 8;
|
||||
size_t return_size = OpenSSL.ParamReturnSize.UNMODIFIED;
|
||||
OpenSSL.Param mac_params[] = {
|
||||
{ OpenSSL.Envelope.MessageAuthParameter.SIZE, UNSIGNED_INTEGER,
|
||||
(uint8[]) &hash_size, return_size },
|
||||
{ null, INTEGER, null, return_size },
|
||||
};
|
||||
|
||||
foreach (var peer in peers) {
|
||||
var ctx = new OpenSSL.Envelope.MessageAuthCodeContext (mac);
|
||||
ctx.init (peer.irk.get_data (), mac_params);
|
||||
ctx.update (service.identifier.data);
|
||||
uint8 output[8];
|
||||
size_t outlen = 0;
|
||||
ctx.final (output, out outlen);
|
||||
|
||||
uint8 tag[6];
|
||||
for (uint i = 0; i != 6; i++)
|
||||
tag[i] = output[5 - i];
|
||||
|
||||
if (Memory.cmp (tag, service.auth_tag.get_data (), service.auth_tag.get_size ()) == 0)
|
||||
return peer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
@@ -972,7 +1040,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableCoreDeviceTransport : Object, Transport {
|
||||
private sealed class PortableCoreDeviceUsbTransport : Object, Transport {
|
||||
public UsbDevice usb_device {
|
||||
get;
|
||||
construct;
|
||||
@@ -1010,7 +1078,7 @@ namespace Frida.Fruity {
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
public PortableCoreDeviceTransport (UsbDevice usb_device) {
|
||||
public PortableCoreDeviceUsbTransport (UsbDevice usb_device) {
|
||||
Object (usb_device: usb_device);
|
||||
}
|
||||
|
||||
@@ -1041,7 +1109,7 @@ namespace Frida.Fruity {
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new PortableTunnel (usb_device);
|
||||
var tunnel = new PortableUsbTunnel (usb_device);
|
||||
yield tunnel.open (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
@@ -1056,7 +1124,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableTunnel : Object, Tunnel {
|
||||
private sealed class PortableUsbTunnel : Object, Tunnel {
|
||||
public UsbDevice usb_device {
|
||||
get;
|
||||
construct;
|
||||
@@ -1072,7 +1140,7 @@ namespace Frida.Fruity {
|
||||
private TunnelConnection? tunnel_connection;
|
||||
private DiscoveryService? _discovery_service;
|
||||
|
||||
public PortableTunnel (UsbDevice usb_device) {
|
||||
public PortableUsbTunnel (UsbDevice usb_device) {
|
||||
Object (usb_device: usb_device);
|
||||
}
|
||||
|
||||
@@ -1332,6 +1400,165 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableCoreDeviceNetworkTransport : Object, Transport {
|
||||
public PairingIdentity.Peer peer {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public InetSocketAddress endpoint {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public InetSocketAddress interface_address {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
get {
|
||||
return NETWORK;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return peer.udid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? name {
|
||||
get {
|
||||
return peer.name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbmuxDevice? usbmux_device {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
public PortableCoreDeviceNetworkTransport (PairingIdentity.Peer peer, InetSocketAddress endpoint,
|
||||
InetSocketAddress interface_address) {
|
||||
Object (
|
||||
peer: peer,
|
||||
endpoint: endpoint,
|
||||
interface_address: interface_address
|
||||
);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (tunnel_request != null) {
|
||||
try {
|
||||
var tunnel = yield tunnel_request.future.wait_async (cancellable);
|
||||
yield tunnel.close (cancellable);
|
||||
} catch (Error e) {
|
||||
} finally {
|
||||
tunnel_request = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new PortableNetworkTunnel (endpoint, interface_address);
|
||||
yield tunnel.open (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
tunnel_request.reject (e);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableNetworkTunnel : Object, Tunnel {
|
||||
public InetSocketAddress endpoint {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public InetSocketAddress interface_address {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery_service;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelConnection? tunnel_connection;
|
||||
private DiscoveryService? _discovery_service;
|
||||
|
||||
public PortableNetworkTunnel (InetSocketAddress endpoint, InetSocketAddress interface_address) {
|
||||
Object (endpoint: endpoint, interface_address: interface_address);
|
||||
}
|
||||
|
||||
public async void open (Cancellable? cancellable) throws Error, IOError {
|
||||
var netstack = new SystemNetworkStack (interface_address.get_address (), interface_address.scope_id);
|
||||
|
||||
var pairing_connection = yield netstack.open_tcp_connection (endpoint, cancellable);
|
||||
var pairing_transport = new PlainPairingTransport (pairing_connection);
|
||||
var pairing_service = yield PairingService.open (pairing_transport, cancellable);
|
||||
|
||||
TunnelConnection tc = yield pairing_service.open_tunnel (endpoint.get_address (), netstack, cancellable);
|
||||
|
||||
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tc.remote_address,
|
||||
port: tc.remote_rsd_port,
|
||||
scope_id: tc.tunnel_netstack.scope_id
|
||||
);
|
||||
var rsd_connection = yield tc.tunnel_netstack.open_tcp_connection (rsd_endpoint, cancellable);
|
||||
var disco = yield DiscoveryService.open (rsd_connection, cancellable);
|
||||
|
||||
tunnel_connection = tc;
|
||||
_discovery_service = disco;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
_discovery_service.close ();
|
||||
|
||||
tunnel_connection.cancel ();
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
var netstack = tunnel_connection.tunnel_netstack;
|
||||
var endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tunnel_connection.remote_address,
|
||||
port: port,
|
||||
scope_id: netstack.scope_id
|
||||
);
|
||||
return yield netstack.open_tcp_connection (endpoint, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public interface FruitFinder : Object {
|
||||
public static FruitFinder make_default () {
|
||||
#if LINUX && !ANDROID
|
||||
@@ -1350,53 +1577,37 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
internal const string PAIRING_REGTYPE = "_remoted._tcp";
|
||||
internal const string PAIRING_DOMAIN = "local.";
|
||||
|
||||
public interface PairingBrowser : Object {
|
||||
public const string SERVICE_NAME = "_remotepairing._tcp.local";
|
||||
|
||||
public signal void service_discovered (PairingServiceDetails service);
|
||||
|
||||
public static PairingBrowser make_default () {
|
||||
#if LINUX && !ANDROID
|
||||
#if WINDOWS
|
||||
return new WindowsPairingBrowser ();
|
||||
#elif LINUX && !ANDROID
|
||||
return new LinuxPairingBrowser ();
|
||||
#else
|
||||
return new NullPairingBrowser ();
|
||||
#endif
|
||||
}
|
||||
|
||||
public signal void services_discovered (PairingServiceDetails[] services);
|
||||
public abstract async void start (Cancellable? cancellable) throws IOError;
|
||||
public abstract async void stop (Cancellable? cancellable) throws IOError;
|
||||
}
|
||||
|
||||
public class NullPairingBrowser : Object, PairingBrowser {
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
}
|
||||
|
||||
public interface PairingServiceDetails : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint interface_index {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string interface_name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface PairingServiceHost : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint16 port {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract Gee.List<string> txt_record {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
public class PairingServiceDetails {
|
||||
public string identifier;
|
||||
public Bytes auth_tag;
|
||||
public InetSocketAddress endpoint;
|
||||
public InetSocketAddress interface_address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class DnsPacketReader {
|
||||
private BufferReader reader;
|
||||
|
||||
public DnsPacketReader (Bytes packet) {
|
||||
reader = new BufferReader (new Buffer (packet, BIG_ENDIAN));
|
||||
}
|
||||
|
||||
public DnsPtrRecord read_ptr () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != PTR)
|
||||
throw new Error.PROTOCOL ("Expected a PTR record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a PTR record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var name = subreader.read_name ();
|
||||
return new DnsPtrRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
name = name,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsTxtRecord read_txt () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != TXT)
|
||||
throw new Error.PROTOCOL ("Expected a TXT record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a TXT record of class IN");
|
||||
var entries = new Gee.ArrayList<string> ();
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
while (subreader.reader.available != 0) {
|
||||
string text = subreader.read_string ();
|
||||
entries.add (text);
|
||||
}
|
||||
return new DnsTxtRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
entries = entries.to_array (),
|
||||
};
|
||||
}
|
||||
|
||||
public DnsAaaaRecord read_aaaa () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != AAAA)
|
||||
throw new Error.PROTOCOL ("Expected an AAAA record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected an AAAA record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var raw_address = subreader.reader.read_bytes (16);
|
||||
var address = new InetAddress.from_bytes (raw_address.get_data (), IPV6);
|
||||
return new DnsAaaaRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
address = address,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsSrvRecord read_srv () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != SRV)
|
||||
throw new Error.PROTOCOL ("Expected a SRV record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a SRV record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var priority = subreader.reader.read_uint16 ();
|
||||
var weight = subreader.reader.read_uint16 ();
|
||||
var port = subreader.reader.read_uint16 ();
|
||||
var name = subreader.read_name ();
|
||||
return new DnsSrvRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
priority = priority,
|
||||
weight = weight,
|
||||
port = port,
|
||||
name = name,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsResourceRecord read_record () throws Error {
|
||||
var key = read_key ();
|
||||
var ttl = reader.read_uint32 ();
|
||||
var size = reader.read_uint16 ();
|
||||
var data = reader.read_bytes (size);
|
||||
return new DnsResourceRecord () {
|
||||
key = key,
|
||||
ttl = ttl,
|
||||
data = data,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsResourceKey read_key () throws Error {
|
||||
var name = read_name ();
|
||||
var type = reader.read_uint16 ();
|
||||
var klass = reader.read_uint16 ();
|
||||
return new DnsResourceKey () {
|
||||
name = name,
|
||||
type = type,
|
||||
klass = klass,
|
||||
};
|
||||
}
|
||||
|
||||
public string read_name () throws Error {
|
||||
var name = new StringBuilder.sized (256);
|
||||
|
||||
while (true) {
|
||||
size_t size = reader.read_uint8 ();
|
||||
if (size == 0)
|
||||
break;
|
||||
if (size > 63)
|
||||
throw new Error.PROTOCOL ("Invalid DNS name length");
|
||||
|
||||
var label = reader.read_fixed_string (size);
|
||||
if (name.len != 0)
|
||||
name.append_c ('.');
|
||||
name.append (label);
|
||||
}
|
||||
|
||||
return name.str;
|
||||
}
|
||||
|
||||
public string read_string () throws Error {
|
||||
size_t size = reader.read_uint8 ();
|
||||
return reader.read_fixed_string (size);
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsPtrRecord : DnsResourceRecord {
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class DnsTxtRecord : DnsResourceRecord {
|
||||
public string[] entries;
|
||||
}
|
||||
|
||||
public class DnsAaaaRecord : DnsResourceRecord {
|
||||
public InetAddress address;
|
||||
}
|
||||
|
||||
public class DnsSrvRecord : DnsResourceRecord {
|
||||
public uint16 priority;
|
||||
public uint16 weight;
|
||||
public uint16 port;
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class DnsResourceRecord {
|
||||
public DnsResourceKey key;
|
||||
public uint32 ttl;
|
||||
public Bytes data;
|
||||
}
|
||||
|
||||
public class DnsResourceKey {
|
||||
public string name;
|
||||
public DnsRecordType type;
|
||||
public DnsRecordClass klass;
|
||||
}
|
||||
|
||||
public enum DnsRecordType {
|
||||
PTR = 12,
|
||||
TXT = 16,
|
||||
AAAA = 28,
|
||||
SRV = 33,
|
||||
}
|
||||
|
||||
public enum DnsRecordClass {
|
||||
IN = 1,
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -504,6 +504,14 @@ namespace Frida.Fruity {
|
||||
process_incoming_fragments.begin ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
foreach (var channel in channels.values)
|
||||
channel.transport = null;
|
||||
channels.clear ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
private async void close (Cancellable? cancellable) throws IOError {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
@@ -878,9 +886,9 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak DTXTransport transport {
|
||||
public weak DTXTransport? transport {
|
||||
get;
|
||||
construct;
|
||||
set;
|
||||
}
|
||||
|
||||
public State state {
|
||||
@@ -903,7 +911,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
transport.remove_channel (this);
|
||||
close ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
@@ -915,6 +923,11 @@ namespace Frida.Fruity {
|
||||
var error = new Error.TRANSPORT ("Channel closed");
|
||||
foreach (var request in pending_responses.values.to_array ())
|
||||
request.reject (error);
|
||||
|
||||
if (transport != null) {
|
||||
transport.remove_channel (this);
|
||||
transport = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async NSObject? invoke (string method_name, DTXArgumentListBuilder? args, Cancellable? cancellable)
|
||||
|
||||
+40
-24
@@ -71,41 +71,55 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
unowned LibUSB.Device raw_device = device.raw_device;
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (device.raw_device.get_active_config_descriptor (out config),
|
||||
"Failed to get active USB config descriptor");
|
||||
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
|
||||
|
||||
int current_config;
|
||||
Usb.check (handle.get_configuration (out current_config), "Failed to get current configuration");
|
||||
|
||||
int desired_config = -1;
|
||||
bool found_cdc_header = false;
|
||||
bool found_data_interface = false;
|
||||
uint8 mac_address_index = 0;
|
||||
foreach (var iface in config.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config),
|
||||
"Failed to get config descriptor");
|
||||
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
foreach (var iface in config.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) == LibUSB.EndpointDirection.IN)
|
||||
rx_address = ep.bEndpointAddress;
|
||||
else
|
||||
tx_address = ep.bEndpointAddress;
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) == LibUSB.EndpointDirection.IN)
|
||||
rx_address = ep.bEndpointAddress;
|
||||
else
|
||||
tx_address = ep.bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_cdc_header || found_data_interface) {
|
||||
desired_config = config_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_cdc_header || !found_data_interface)
|
||||
throw_user_error ("No USB CDC-NCM interface found");
|
||||
@@ -122,6 +136,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
handle.detach_kernel_driver (data_iface);
|
||||
if (current_config != desired_config)
|
||||
Usb.check (handle.set_configuration (desired_config), "Failed to set configuration");
|
||||
try {
|
||||
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
|
||||
} catch (Error e) {
|
||||
|
||||
@@ -154,8 +154,6 @@ namespace Frida.Fruity {
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
private DataOutputStream? pcap;
|
||||
|
||||
private enum State {
|
||||
STARTED,
|
||||
STOPPED
|
||||
@@ -249,13 +247,11 @@ namespace Frida.Fruity {
|
||||
public void handle_incoming_datagram (Bytes datagram) throws Error {
|
||||
check_started ();
|
||||
|
||||
log_datagram (datagram);
|
||||
|
||||
check (perform_on_lwip_thread (() => {
|
||||
var pbuf = LWIP.PacketBuffer.alloc (RAW, (uint16) datagram.get_size (), POOL);
|
||||
pbuf.take (datagram.get_data ());
|
||||
|
||||
var err = handle.input (pbuf, handle);
|
||||
var err = handle.input (pbuf, ref handle);
|
||||
if (err == OK)
|
||||
*((void **) &pbuf) = null;
|
||||
|
||||
@@ -282,10 +278,8 @@ namespace Frida.Fruity {
|
||||
var datagram = new Bytes (packet[:pbuf.tot_len]);
|
||||
|
||||
schedule_on_frida_thread (() => {
|
||||
if (state == STARTED) {
|
||||
log_datagram (datagram);
|
||||
if (state == STARTED)
|
||||
outgoing_datagram (datagram);
|
||||
}
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
@@ -352,45 +346,6 @@ namespace Frida.Fruity {
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private void log_datagram (Bytes datagram) {
|
||||
lock (pcap) {
|
||||
try {
|
||||
if (pcap == null) {
|
||||
char buf[40];
|
||||
unowned string addr = raw_ipv6_address.to_string (buf);
|
||||
var f = File.new_build_filename (Environment.get_user_special_dir (DESKTOP),
|
||||
"vns-%s.pcap".printf (addr.replace (":", "")));
|
||||
try {
|
||||
f.delete ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
pcap = new DataOutputStream (f.create (REPLACE_DESTINATION));
|
||||
pcap.set_byte_order (HOST_ENDIAN);
|
||||
pcap.put_uint32 (0xa1b2c3d4U);
|
||||
pcap.put_uint16 (2);
|
||||
pcap.put_uint16 (4);
|
||||
pcap.put_uint32 (0);
|
||||
pcap.put_uint32 (0);
|
||||
pcap.put_uint32 (16384);
|
||||
pcap.put_uint32 ((ethernet_address != null) ? 1 : 229); // Ethernet or IPv6
|
||||
pcap.flush ();
|
||||
}
|
||||
|
||||
int64 timestamp = get_real_time ();
|
||||
pcap.put_uint32 ((uint32) (timestamp / 1000000));
|
||||
pcap.put_uint32 ((uint32) (timestamp % 1000000));
|
||||
pcap.put_uint32 (datagram.length);
|
||||
pcap.put_uint32 (datagram.length);
|
||||
size_t written;
|
||||
pcap.write_all (datagram.get_data (), out written);
|
||||
pcap.flush ();
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("%s\n", e.message);
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpConnection : IOStream, AsyncInitable {
|
||||
public VirtualNetworkStack netstack {
|
||||
get;
|
||||
@@ -516,7 +471,7 @@ namespace Frida.Fruity {
|
||||
self->on_error (err);
|
||||
});
|
||||
pcb.nagle_disable ();
|
||||
pcb.bind_netif (netstack.handle);
|
||||
pcb.bind_netif (&netstack.handle);
|
||||
|
||||
var err = pcb.connect (ip6_address_from_inet_socket_address (address), address.get_port (), (user_data, pcb, err) => {
|
||||
TcpConnection * self = user_data;
|
||||
@@ -544,6 +499,11 @@ namespace Frida.Fruity {
|
||||
});
|
||||
}
|
||||
|
||||
private void detach_from_pcb () {
|
||||
pcb.set_user_data (null);
|
||||
pcb = null;
|
||||
}
|
||||
|
||||
private void on_connect () {
|
||||
lock (state)
|
||||
tx_space_available = pcb.query_available_send_buffer_space ();
|
||||
@@ -561,7 +521,7 @@ namespace Frida.Fruity {
|
||||
|
||||
private void on_recv (owned LWIP.PacketBuffer? pbuf, LWIP.ErrorCode err) {
|
||||
if (pbuf == null) {
|
||||
pcb = null;
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
@@ -584,7 +544,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void on_error (LWIP.ErrorCode err) {
|
||||
pcb = null;
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
@@ -957,7 +917,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
//private VirtualNetworkStack? _netstack;
|
||||
private unowned LWIP.UdpPcb? pcb;
|
||||
private IOCondition events = OUT;
|
||||
private Gee.Queue<Packet> rx_queue = new Gee.ArrayQueue<Packet> ();
|
||||
@@ -972,7 +931,7 @@ namespace Frida.Fruity {
|
||||
netstack.perform_on_lwip_thread (() => {
|
||||
pcb = LWIP.UdpPcb.make (V6);
|
||||
pcb.set_recv_callback (on_recv);
|
||||
pcb.bind_netif (netstack.handle);
|
||||
pcb.bind_netif (&netstack.handle);
|
||||
return OK;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class OpackBuilder {
|
||||
protected BufferBuilder builder = new BufferBuilder (LITTLE_ENDIAN);
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public OpackBuilder () {
|
||||
push_scope (new Scope (ROOT));
|
||||
}
|
||||
|
||||
public unowned OpackBuilder begin_dictionary () {
|
||||
begin_value ();
|
||||
|
||||
size_t type_offset = builder.offset;
|
||||
builder.append_uint8 (0x00);
|
||||
|
||||
push_scope (new CollectionScope (type_offset));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder set_member_name (string name) {
|
||||
return add_string_value (name);
|
||||
}
|
||||
|
||||
public unowned OpackBuilder end_dictionary () {
|
||||
CollectionScope scope = pop_scope ();
|
||||
|
||||
size_t n = scope.num_values / 2;
|
||||
if (n < 0xf) {
|
||||
builder.write_uint8 (scope.type_offset, 0xe0 | n);
|
||||
} else {
|
||||
builder
|
||||
.write_uint8 (scope.type_offset, 0xef)
|
||||
.append_uint8 (0x03);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_string_value (string val) {
|
||||
begin_value ();
|
||||
|
||||
size_t len = val.length;
|
||||
|
||||
if (len > uint32.MAX) {
|
||||
builder
|
||||
.append_uint8 (0x6f)
|
||||
.append_string (val, StringTerminator.NUL);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (len <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x40 + len));
|
||||
else if (len <= uint8.MAX)
|
||||
builder.append_uint8 (0x61).append_uint8 ((uint8) len);
|
||||
else if (len <= uint16.MAX)
|
||||
builder.append_uint8 (0x62).append_uint16 ((uint16) len);
|
||||
else if (len <= 0xffffff)
|
||||
builder.append_uint8 (0x63).append_uint8 ((uint8) (len & 0xff)).append_uint16 ((uint16) (len >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x64).append_uint32 ((uint32) len);
|
||||
|
||||
builder.append_string (val, StringTerminator.NONE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_data_value (Bytes val) {
|
||||
begin_value ();
|
||||
|
||||
size_t size = val.get_size ();
|
||||
if (size <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x70 + size));
|
||||
else if (size <= uint8.MAX)
|
||||
builder.append_uint8 (0x91).append_uint8 ((uint8) size);
|
||||
else if (size <= uint16.MAX)
|
||||
builder.append_uint8 (0x92).append_uint16 ((uint16) size);
|
||||
else if (size <= 0xffffff)
|
||||
builder.append_uint8 (0x93).append_uint8 ((uint8) (size & 0xff)).append_uint16 ((uint16) (size >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x94).append_uint32 ((uint32) size);
|
||||
|
||||
builder.append_bytes (val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private unowned OpackBuilder begin_value () {
|
||||
peek_scope ().num_values++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bytes build () {
|
||||
return builder.build ();
|
||||
}
|
||||
|
||||
private void push_scope (Scope scope) {
|
||||
scopes.offer_tail (scope);
|
||||
}
|
||||
|
||||
private Scope peek_scope () {
|
||||
return scopes.peek_tail ();
|
||||
}
|
||||
|
||||
private T pop_scope<T> () {
|
||||
return (T) scopes.poll_tail ();
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Kind kind;
|
||||
public size_t num_values = 0;
|
||||
|
||||
public enum Kind {
|
||||
ROOT,
|
||||
COLLECTION,
|
||||
}
|
||||
|
||||
public Scope (Kind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
private class CollectionScope : Scope {
|
||||
public size_t type_offset;
|
||||
|
||||
public CollectionScope (size_t type_offset) {
|
||||
base (COLLECTION);
|
||||
this.type_offset = type_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OpackParser {
|
||||
private BufferReader reader;
|
||||
|
||||
[Flags]
|
||||
private enum ValueFlags {
|
||||
ALLOW_TERMINATOR = 1 << 0,
|
||||
}
|
||||
|
||||
public static Variant parse (Bytes opack) throws Error {
|
||||
var parser = new OpackParser (opack);
|
||||
return parser.read_value ();
|
||||
}
|
||||
|
||||
private OpackParser (Bytes opack) {
|
||||
reader = new BufferReader (new Buffer (opack, LITTLE_ENDIAN));
|
||||
}
|
||||
|
||||
private Variant? read_value (ValueFlags flags = 0) throws Error {
|
||||
uint8 v = reader.read_uint8 ();
|
||||
uint8 top = v >> 4;
|
||||
uint8 bottom = v & 0b1111;
|
||||
switch (top) {
|
||||
case 0:
|
||||
switch (bottom) {
|
||||
case 3:
|
||||
if ((flags & ValueFlags.ALLOW_TERMINATOR) == 0)
|
||||
throw new Error.PROTOCOL ("Unexpected OPACK terminator");
|
||||
return null;
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
case 3:
|
||||
switch (bottom) {
|
||||
case 0: return (int64) reader.read_int8 ();
|
||||
case 1: return (int64) reader.read_int16 ();
|
||||
case 2: return (int64) reader.read_int32 ();
|
||||
case 3: return (int64) reader.read_int64 ();
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
return read_string (v - 0x40);
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
return read_data (v - 0x70);
|
||||
case 0xe:
|
||||
return read_dictionary (bottom);
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
}
|
||||
}
|
||||
|
||||
private Variant read_string (size_t len) throws Error {
|
||||
if (len == 0x2f)
|
||||
return reader.read_string ();
|
||||
len = read_variable_length (len);
|
||||
return reader.read_fixed_string (len);
|
||||
}
|
||||
|
||||
private Variant read_data (size_t len) throws Error {
|
||||
len = read_variable_length (len);
|
||||
var bytes = reader.read_bytes (len);
|
||||
return Variant.new_from_data (new VariantType.array (VariantType.BYTE), bytes.get_data (), true, bytes);
|
||||
}
|
||||
|
||||
private size_t read_variable_length (size_t len) throws Error {
|
||||
if (len <= 0x20)
|
||||
return len;
|
||||
|
||||
switch (len) {
|
||||
case 0x21:
|
||||
return reader.read_uint8 ();
|
||||
case 0x22:
|
||||
return reader.read_uint16 ();
|
||||
case 0x23:
|
||||
uint32 bottom = reader.read_uint8 ();
|
||||
uint32 top = reader.read_uint16 ();
|
||||
return top << 8 | bottom;
|
||||
case 0x24:
|
||||
return reader.read_uint32 ();
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK length: 0x%zx", len);
|
||||
}
|
||||
}
|
||||
|
||||
private Variant read_dictionary (size_t n) throws Error {
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
|
||||
bool has_terminator = n == 0xf;
|
||||
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
if (!has_terminator && i == n)
|
||||
break;
|
||||
|
||||
var key = read_value (has_terminator ? ValueFlags.ALLOW_TERMINATOR : 0);
|
||||
if (key == null)
|
||||
break;
|
||||
if (!key.is_of_type (VariantType.STRING))
|
||||
throw new Error.PROTOCOL ("Unsupported OPACK dictionary key type");
|
||||
var val = read_value ();
|
||||
builder.add_value (new Variant.dict_entry (key, new Variant.variant (val)));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return builder.end ();
|
||||
}
|
||||
}
|
||||
}
|
||||
+191
-217
@@ -99,6 +99,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public class PairingService : Object, AsyncInitable {
|
||||
public const string DNS_SD_NAME = "_remotepairing._tcp.local";
|
||||
|
||||
public PairingTransport transport {
|
||||
get;
|
||||
construct;
|
||||
@@ -119,7 +121,6 @@ namespace Frida.Fruity {
|
||||
private uint64 next_control_sequence_number = 0;
|
||||
private uint64 next_encrypted_sequence_number = 0;
|
||||
|
||||
private File config_file;
|
||||
private string host_identifier;
|
||||
private Key pair_record_key;
|
||||
private ChaCha20Poly1305? client_cipher;
|
||||
@@ -146,29 +147,15 @@ namespace Frida.Fruity {
|
||||
transport.close.connect (on_close);
|
||||
transport.message.connect (on_message);
|
||||
|
||||
#if DARWIN
|
||||
config_file = File.new_for_path ("/var/db/lockdown/RemotePairing/user_%u/selfIdentity.plist".printf (
|
||||
(uint) Posix.getuid ()));
|
||||
#else
|
||||
config_file = File.new_build_filename (Environment.get_user_config_dir (), "frida", "remote-xpc.plist");
|
||||
#endif
|
||||
|
||||
Bytes? key = null;
|
||||
try {
|
||||
uint8[] raw_identity;
|
||||
FileUtils.get_data (config_file.get_path (), out raw_identity);
|
||||
Plist identity = new Plist.from_data (raw_identity);
|
||||
|
||||
unowned string identifier = identity.get_string ("identifier");
|
||||
key = identity.get_bytes ("privateKey");
|
||||
|
||||
host_identifier = identifier;
|
||||
} catch (GLib.Error e) {
|
||||
host_identifier = make_host_identifier ();
|
||||
}
|
||||
if (key == null) {
|
||||
Bytes? key;
|
||||
var identity = PairingIdentity.try_load ();
|
||||
if (identity != null) {
|
||||
key = identity.private_key;
|
||||
host_identifier = identity.identifier;
|
||||
} else {
|
||||
uint8 dummy_key[32] = { 0, };
|
||||
key = new Bytes (dummy_key);
|
||||
host_identifier = make_host_identifier ();
|
||||
}
|
||||
|
||||
pair_record_key = new Key.from_raw_private_key (ED25519, null, key.get_data ());
|
||||
@@ -389,7 +376,7 @@ namespace Frida.Fruity {
|
||||
// TODO: Verify signature using peer's public key.
|
||||
/* var start_inner_response = */ new VariantReader (PairingParamsParser.parse (cipher.decrypt (
|
||||
new Bytes.static ("\x00\x00\x00\x00PV-Msg02".data[:12]),
|
||||
start_response.read_member ("encrypted-data").get_data_value ()).get_data ()));
|
||||
start_response.read_member ("encrypted-data").get_data_value ())));
|
||||
|
||||
var message = new ByteArray.sized (100);
|
||||
message.append (raw_host_pubkey);
|
||||
@@ -524,7 +511,7 @@ namespace Frida.Fruity {
|
||||
message.append (new_pair_record_pubkey.get_data ());
|
||||
Bytes signature = compute_message_signature (ByteArray.free_to_bytes ((owned) message), new_pair_record_key);
|
||||
|
||||
Bytes info = new OpackBuilder ()
|
||||
Bytes self_info = new OpackBuilder ()
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("name")
|
||||
.add_string_value (Environment.get_host_name ())
|
||||
@@ -547,7 +534,7 @@ namespace Frida.Fruity {
|
||||
.add_identifier (host_identifier)
|
||||
.add_raw_public_key (new_pair_record_pubkey)
|
||||
.add_signature (signature)
|
||||
.add_info (info)
|
||||
.add_info (self_info)
|
||||
.build ();
|
||||
|
||||
Bytes outer_params = new PairingParamsBuilder ()
|
||||
@@ -571,27 +558,26 @@ namespace Frida.Fruity {
|
||||
.end_dictionary ()
|
||||
.build ();
|
||||
|
||||
var finish_response = yield request_pairing_data (finish_payload, cancellable);
|
||||
var outer_finish_response = yield request_pairing_data (finish_payload, cancellable);
|
||||
var inner_finish_response = new VariantReader (PairingParamsParser.parse (
|
||||
cipher.decrypt (new Bytes.static ("\x00\x00\x00\x00PS-Msg06".data[:12]),
|
||||
outer_finish_response.read_member ("encrypted-data").get_data_value ())));
|
||||
|
||||
Bytes encrypted_response = finish_response.read_member ("encrypted-data").get_data_value ();
|
||||
Bytes raw_response = cipher.decrypt (new Bytes.static ("\x00\x00\x00\x00PS-Msg06".data[:12]), encrypted_response);
|
||||
// TODO: Wire up if/when needed.
|
||||
/* Variant response = */ PairingParamsParser.parse (raw_response.get_data ());
|
||||
string peer_identifier = inner_finish_response.read_member ("identifier").get_string_value ();
|
||||
inner_finish_response.end_member ();
|
||||
Bytes peer_pubkey = inner_finish_response.read_member ("public-key").get_data_value ();
|
||||
inner_finish_response.end_member ();
|
||||
Bytes peer_info = inner_finish_response.read_member ("info").get_data_value ();
|
||||
inner_finish_response.end_member ();
|
||||
PairingIdentity.save_peer (peer_identifier, peer_pubkey, peer_info);
|
||||
|
||||
var config = new Plist ();
|
||||
config.set_string ("identifier", host_identifier);
|
||||
config.set_bytes ("publicKey", new_pair_record_pubkey);
|
||||
config.set_bytes ("privateKey", get_raw_private_key (new_pair_record_key));
|
||||
config.set_bytes ("irk", irk);
|
||||
try {
|
||||
config_file.get_parent ().make_directory_with_parents (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
FileUtils.set_contents (config_file.get_path (), config.to_xml ());
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
var identity = new PairingIdentity () {
|
||||
identifier = host_identifier,
|
||||
public_key = new_pair_record_pubkey,
|
||||
private_key = get_raw_private_key (new_pair_record_key),
|
||||
irk = irk
|
||||
};
|
||||
identity.save ();
|
||||
|
||||
pair_record_key = (owned) new_pair_record_key;
|
||||
|
||||
@@ -636,7 +622,7 @@ namespace Frida.Fruity {
|
||||
.read_member ("_0")
|
||||
.read_member ("data")
|
||||
.get_data_value ();
|
||||
Variant data = PairingParamsParser.parse (raw_data.get_data ());
|
||||
Variant data = PairingParamsParser.parse (raw_data);
|
||||
return new VariantReader (data);
|
||||
}
|
||||
|
||||
@@ -1345,6 +1331,148 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingIdentity {
|
||||
public string identifier;
|
||||
public Bytes public_key;
|
||||
public Bytes private_key;
|
||||
public Bytes irk;
|
||||
|
||||
public static PairingIdentity? try_load () {
|
||||
try {
|
||||
Bytes raw_identity = query_self_identity_location ().load_bytes ();
|
||||
Plist identity = new Plist.from_data (raw_identity.get_data ());
|
||||
return new PairingIdentity () {
|
||||
identifier = identity.get_string ("identifier"),
|
||||
public_key = identity.get_bytes ("publicKey"),
|
||||
private_key = identity.get_bytes ("privateKey"),
|
||||
irk = identity.get_bytes ("irk")
|
||||
};
|
||||
} catch (GLib.Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void save () throws Error {
|
||||
var plist = new Plist ();
|
||||
plist.set_string ("identifier", identifier);
|
||||
plist.set_bytes ("publicKey", public_key);
|
||||
plist.set_bytes ("privateKey", private_key);
|
||||
plist.set_bytes ("irk", irk);
|
||||
save_plist (plist, query_self_identity_location ());
|
||||
}
|
||||
|
||||
public static Gee.List<Peer> load_peers () {
|
||||
var peers = new Gee.ArrayList<Peer> ();
|
||||
|
||||
try {
|
||||
var enumerator = query_peers_location ().enumerate_children (FileAttribute.STANDARD_NAME, FileQueryInfoFlags.NONE);
|
||||
File? child;
|
||||
while (enumerator.iterate (null, out child) && child != null) {
|
||||
Plist plist = new Plist.from_data (child.load_bytes ().get_data ());
|
||||
|
||||
var reader = new VariantReader (OpackParser.parse (plist.get_bytes ("info")));
|
||||
unowned string udid = reader.read_member ("remotepairing_udid").get_string_value ();
|
||||
|
||||
peers.add (new Peer () {
|
||||
identifier = plist.get_string ("identifier"),
|
||||
public_key = plist.get_bytes ("publicKey"),
|
||||
irk = plist.get_bytes ("irk"),
|
||||
name = plist.get_string ("name"),
|
||||
model = plist.get_string ("model"),
|
||||
udid = udid,
|
||||
});
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
public static void save_peer (string identifier, Bytes public_key, Bytes info) throws Error {
|
||||
var r = new VariantReader (OpackParser.parse (info));
|
||||
|
||||
Bytes irk = r.read_member ("altIRK").get_data_value ();
|
||||
r.end_member ();
|
||||
|
||||
unowned string name = r.read_member ("name").get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
unowned string model = r.read_member ("model").get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
var plist = new Plist ();
|
||||
plist.set_string ("identifier", identifier);
|
||||
plist.set_bytes ("publicKey", public_key);
|
||||
plist.set_bytes ("irk", irk);
|
||||
plist.set_string ("name", name);
|
||||
plist.set_string ("model", model);
|
||||
plist.set_bytes ("info", info);
|
||||
save_plist (plist, query_peers_location ().get_child (identifier + ".plist"));
|
||||
}
|
||||
|
||||
public static File query_self_identity_location () {
|
||||
return query_base_location ().get_child ("self-identity.plist");
|
||||
}
|
||||
|
||||
public static File query_peers_location () {
|
||||
return query_base_location ().get_child ("peers");
|
||||
}
|
||||
|
||||
private static File query_base_location () {
|
||||
return File.new_build_filename (Environment.get_user_config_dir (), "frida");
|
||||
}
|
||||
|
||||
private static void save_plist (Plist plist, File location) throws Error {
|
||||
try {
|
||||
location.get_parent ().make_directory_with_parents ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
location.replace_contents (plist.to_xml ().data, null, false, PRIVATE | REPLACE_DESTINATION, null);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public class Peer {
|
||||
public string identifier;
|
||||
public Bytes public_key;
|
||||
public Bytes irk;
|
||||
public string name;
|
||||
public string model;
|
||||
public string udid;
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingServiceMetadata {
|
||||
public string identifier;
|
||||
public Bytes auth_tag;
|
||||
|
||||
public static PairingServiceMetadata from_txt_record (Gee.Iterable<string> record) throws Error {
|
||||
string? identifier = null;
|
||||
Bytes? auth_tag = null;
|
||||
foreach (string item in record) {
|
||||
string[] tokens = item.split ("=", 2);
|
||||
if (tokens.length != 2)
|
||||
continue;
|
||||
|
||||
unowned string key = tokens[0];
|
||||
unowned string val = tokens[1];
|
||||
if (key == "identifier")
|
||||
identifier = val;
|
||||
else if (key == "authTag")
|
||||
auth_tag = new Bytes (Base64.decode (val));
|
||||
}
|
||||
if (identifier == null || auth_tag == null)
|
||||
throw new Error.PROTOCOL ("Missing TXT metadata");
|
||||
|
||||
return new PairingServiceMetadata () {
|
||||
identifier = identifier,
|
||||
auth_tag = auth_tag,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceOptions {
|
||||
public bool allows_pair_setup;
|
||||
public bool allows_pinless_pairing;
|
||||
@@ -1435,54 +1563,51 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private class PairingParamsParser {
|
||||
private Buffer buf;
|
||||
private size_t cursor = 0;
|
||||
private BufferReader reader;
|
||||
private EnumClass param_type_class;
|
||||
|
||||
public static Variant parse (uint8[] data) throws Error {
|
||||
var parser = new PairingParamsParser (new Bytes.static (data));
|
||||
public static Variant parse (Bytes pairing_params) throws Error {
|
||||
var parser = new PairingParamsParser (pairing_params);
|
||||
return parser.read_params ();
|
||||
}
|
||||
|
||||
private PairingParamsParser (Bytes bytes) {
|
||||
this.buf = new Buffer (bytes, LITTLE_ENDIAN);
|
||||
this.param_type_class = (EnumClass) typeof (PairingParamType).class_ref ();
|
||||
reader = new BufferReader (new Buffer (bytes, LITTLE_ENDIAN));
|
||||
param_type_class = (EnumClass) typeof (PairingParamType).class_ref ();
|
||||
}
|
||||
|
||||
private Variant read_params () throws Error {
|
||||
var byte_array = new VariantType.array (VariantType.BYTE);
|
||||
|
||||
var parameters = new Gee.HashMap<string, Variant> ();
|
||||
size_t size = buf.bytes.get_size ();
|
||||
while (cursor != size) {
|
||||
var raw_type = read_raw_uint8 ();
|
||||
while (reader.available != 0) {
|
||||
var raw_type = reader.read_uint8 ();
|
||||
unowned EnumValue? type_enum_val = param_type_class.get_value (raw_type);
|
||||
if (type_enum_val == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported pairing parameter type (0x%x)", raw_type);
|
||||
var type = (PairingParamType) raw_type;
|
||||
unowned string key = type_enum_val.value_nick;
|
||||
|
||||
var val_size = read_raw_uint8 ();
|
||||
Bytes val_bytes = read_raw_bytes (val_size);
|
||||
|
||||
var val_size = reader.read_uint8 ();
|
||||
Variant val;
|
||||
switch (type) {
|
||||
case IDENTIFIER:
|
||||
val = new Variant.string (reader.read_fixed_string (val_size));
|
||||
break;
|
||||
case STATE:
|
||||
case ERROR:
|
||||
if (val_bytes.length != 1) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for '%s': length=%d",
|
||||
key, val_bytes.length);
|
||||
}
|
||||
val = new Variant.byte (val_bytes[0]);
|
||||
if (val_size != 1)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for '%s': size=%u", key, val_size);
|
||||
val = new Variant.byte (reader.read_uint8 ());
|
||||
break;
|
||||
case RETRY_DELAY: {
|
||||
uint16 delay;
|
||||
switch (val_bytes.length) {
|
||||
switch (val_size) {
|
||||
case 1:
|
||||
delay = val_bytes[0];
|
||||
delay = reader.read_uint8 ();
|
||||
break;
|
||||
case 2:
|
||||
delay = new Buffer (val_bytes, LITTLE_ENDIAN).read_uint16 (0);
|
||||
delay = reader.read_uint16 ();
|
||||
break;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for 'retry-delay'");
|
||||
@@ -1491,6 +1616,7 @@ namespace Frida.Fruity {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Bytes val_bytes = reader.read_bytes (val_size);
|
||||
var val_bytes_copy = new Bytes (val_bytes.get_data ());
|
||||
val = Variant.new_from_data (byte_array, val_bytes_copy.get_data (), true, val_bytes_copy);
|
||||
break;
|
||||
@@ -1517,26 +1643,6 @@ namespace Frida.Fruity {
|
||||
builder.add ("{sv}", e.key, e.value);
|
||||
return builder.end ();
|
||||
}
|
||||
|
||||
private uint8 read_raw_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
var result = buf.read_uint8 (cursor);
|
||||
cursor += sizeof (uint8);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Bytes read_raw_bytes (size_t n) throws Error {
|
||||
check_available (n);
|
||||
Bytes result = buf.bytes[cursor:cursor + n];
|
||||
cursor += n;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void check_available (size_t required) throws Error {
|
||||
size_t available = buf.bytes.get_size () - cursor;
|
||||
if (available < required)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid pairing parameters: truncated");
|
||||
}
|
||||
}
|
||||
|
||||
private enum PairingParamType {
|
||||
@@ -1553,138 +1659,6 @@ namespace Frida.Fruity {
|
||||
INFO = 17,
|
||||
}
|
||||
|
||||
public class OpackBuilder {
|
||||
protected BufferBuilder builder = new BufferBuilder (LITTLE_ENDIAN);
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public OpackBuilder () {
|
||||
push_scope (new Scope (ROOT));
|
||||
}
|
||||
|
||||
public unowned OpackBuilder begin_dictionary () {
|
||||
begin_value ();
|
||||
|
||||
size_t type_offset = builder.offset;
|
||||
builder.append_uint8 (0x00);
|
||||
|
||||
push_scope (new CollectionScope (type_offset));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder set_member_name (string name) {
|
||||
return add_string_value (name);
|
||||
}
|
||||
|
||||
public unowned OpackBuilder end_dictionary () {
|
||||
CollectionScope scope = pop_scope ();
|
||||
|
||||
size_t n = scope.num_values / 2;
|
||||
if (n < 0xf) {
|
||||
builder.write_uint8 (scope.type_offset, 0xe0 | n);
|
||||
} else {
|
||||
builder
|
||||
.write_uint8 (scope.type_offset, 0xef)
|
||||
.append_uint8 (0x03);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_string_value (string val) {
|
||||
begin_value ();
|
||||
|
||||
size_t len = val.length;
|
||||
|
||||
if (len > uint32.MAX) {
|
||||
builder
|
||||
.append_uint8 (0x6f)
|
||||
.append_string (val, StringTerminator.NUL);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (len <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x40 + len));
|
||||
else if (len <= uint8.MAX)
|
||||
builder.append_uint8 (0x61).append_uint8 ((uint8) len);
|
||||
else if (len <= uint16.MAX)
|
||||
builder.append_uint8 (0x62).append_uint16 ((uint16) len);
|
||||
else if (len <= 0xffffff)
|
||||
builder.append_uint8 (0x63).append_uint8 ((uint8) (len & 0xff)).append_uint16 ((uint16) (len >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x64).append_uint32 ((uint32) len);
|
||||
|
||||
builder.append_string (val, StringTerminator.NONE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_data_value (Bytes val) {
|
||||
begin_value ();
|
||||
|
||||
size_t size = val.get_size ();
|
||||
if (size <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x70 + size));
|
||||
else if (size <= uint8.MAX)
|
||||
builder.append_uint8 (0x91).append_uint8 ((uint8) size);
|
||||
else if (size <= uint16.MAX)
|
||||
builder.append_uint8 (0x92).append_uint16 ((uint16) size);
|
||||
else if (size <= 0xffffff)
|
||||
builder.append_uint8 (0x93).append_uint8 ((uint8) (size & 0xff)).append_uint16 ((uint16) (size >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x94).append_uint32 ((uint32) size);
|
||||
|
||||
builder.append_bytes (val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private unowned OpackBuilder begin_value () {
|
||||
peek_scope ().num_values++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bytes build () {
|
||||
return builder.build ();
|
||||
}
|
||||
|
||||
private void push_scope (Scope scope) {
|
||||
scopes.offer_tail (scope);
|
||||
}
|
||||
|
||||
private Scope peek_scope () {
|
||||
return scopes.peek_tail ();
|
||||
}
|
||||
|
||||
private T pop_scope<T> () {
|
||||
return (T) scopes.poll_tail ();
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Kind kind;
|
||||
public size_t num_values = 0;
|
||||
|
||||
public enum Kind {
|
||||
ROOT,
|
||||
COLLECTION,
|
||||
}
|
||||
|
||||
public Scope (Kind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
private class CollectionScope : Scope {
|
||||
public size_t type_offset;
|
||||
|
||||
public CollectionScope (size_t type_offset) {
|
||||
base (COLLECTION);
|
||||
this.type_offset = type_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TunnelConnection : Object, AsyncInitable {
|
||||
public InetSocketAddress address {
|
||||
get;
|
||||
|
||||
+8
-2
@@ -490,15 +490,19 @@ if have_fruity_backend
|
||||
'fruity' / 'lldb.vala',
|
||||
'fruity' / 'injector.vala',
|
||||
'fruity' / 'usbmux.vala',
|
||||
'fruity' / 'dns.vala',
|
||||
'fruity' / 'keyed-archive.vala',
|
||||
'fruity' / 'plist.vala',
|
||||
'fruity' / 'plist-service.vala',
|
||||
'fruity' / 'opack.vala',
|
||||
'fruity' / 'network-stack.vala',
|
||||
'fruity' / 'usb.vala',
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
|
||||
if host_os == 'macos'
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.vala'
|
||||
elif host_os == 'macos'
|
||||
backend_sources += 'fruity' / 'device-monitor-macos.vala'
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'device-monitor-linux.vala'
|
||||
@@ -521,7 +525,9 @@ if have_fruity_backend
|
||||
'--pkg=lwip',
|
||||
'--pkg=openssl',
|
||||
]
|
||||
if host_os_family == 'darwin'
|
||||
if host_os_family == 'windows'
|
||||
backend_vala_args += '--pkg=windows-windns'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_vala_args += [
|
||||
'--pkg=darwin-xnu',
|
||||
'--pkg=darwin-xpc',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.4.2
|
||||
revision = 16.4.3
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
+8
-8
@@ -33,7 +33,7 @@ namespace LWIP {
|
||||
public void ip6_addr_set_state (int8 addr_index, IP6AddressState state);
|
||||
|
||||
[CCode (cname = "netif_input")]
|
||||
public static ErrorCode default_input_handler (PacketBuffer pbuf, NetworkInterface netif);
|
||||
public static ErrorCode default_input_handler (PacketBuffer pbuf, ref NetworkInterface netif);
|
||||
|
||||
public IP6Address ip6_addr[IPV6_NUM_ADDRESSES];
|
||||
|
||||
@@ -56,16 +56,16 @@ namespace LWIP {
|
||||
}
|
||||
|
||||
[CCode (cname = "netif_init_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceInitFunc (NetworkInterface netif);
|
||||
public delegate ErrorCode NetworkInterfaceInitFunc (ref NetworkInterface netif);
|
||||
|
||||
[CCode (cname = "netif_input_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceInputFunc (PacketBuffer pbuf, NetworkInterface netif);
|
||||
public delegate ErrorCode NetworkInterfaceInputFunc (PacketBuffer pbuf, ref NetworkInterface netif);
|
||||
|
||||
[CCode (cname = "netif_linkoutput_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceLinkOutputFunc (NetworkInterface netif, PacketBuffer pbuf);
|
||||
public delegate ErrorCode NetworkInterfaceLinkOutputFunc (ref NetworkInterface netif, PacketBuffer pbuf);
|
||||
|
||||
[CCode (cname = "netif_output_ip6_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceOutputIP6Func (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
public delegate ErrorCode NetworkInterfaceOutputIP6Func (ref NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
|
||||
[Flags]
|
||||
[CCode (cheader_filename = "lwip/netif.h", cprefix = "NETIF_FLAG_")]
|
||||
@@ -85,7 +85,7 @@ namespace LWIP {
|
||||
|
||||
namespace IPv6 {
|
||||
[CCode (cheader_filename = "lwip/ethip6.h", cname = "ethip6_output")]
|
||||
public ErrorCode output (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
public ErrorCode output (ref NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace LWIP {
|
||||
public ErrorCode close ();
|
||||
public ErrorCode shutdown (bool shut_rx, bool shut_tx);
|
||||
|
||||
public void bind_netif (NetworkInterface? netif);
|
||||
public void bind_netif (NetworkInterface * netif);
|
||||
|
||||
public ErrorCode connect (IP6Address address, uint16 port, ConnectedFunc connected);
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace LWIP {
|
||||
public void set_recv_callback (RecvFunc f);
|
||||
|
||||
public ErrorCode bind (IP6Address? ipaddr = null, uint16 port = 0);
|
||||
public void bind_netif (NetworkInterface? netif);
|
||||
public void bind_netif (NetworkInterface * netif);
|
||||
|
||||
public ErrorCode connect (IP6Address ipaddr, uint16 port);
|
||||
|
||||
|
||||
@@ -384,6 +384,44 @@ namespace OpenSSL {
|
||||
public class MessageDigest {
|
||||
public static MessageDigest fetch (LibraryContext? ctx, string algorithm, string? properties = null);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "EVP_MAC_CTX", cprefix = "EVP_MAC_CTX_")]
|
||||
public class MessageAuthCodeContext {
|
||||
public MessageAuthCodeContext (MessageAuthCode mac);
|
||||
|
||||
[CCode (cname = "EVP_MAC_init")]
|
||||
public int init (uint8[] key, [CCode (array_length = false)] Param[]? params = null);
|
||||
[CCode (cname = "EVP_MAC_update")]
|
||||
public int update (uint8[] data);
|
||||
[CCode (cname = "EVP_MAC_final")]
|
||||
public int final ([CCode (array_length_pos = 2)] uint8[]? outdata, out size_t outlen);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "EVP_MAC", cprefix = "EVP_MAC_")]
|
||||
public class MessageAuthCode {
|
||||
public static MessageAuthCode fetch (LibraryContext? ctx, string algorithm, string? properties = null);
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "openssl/core_names.h", lower_case_cprefix = "OSSL_MAC_PARAM_")]
|
||||
namespace MessageAuthParameter {
|
||||
public const string KEY;
|
||||
public const string IV;
|
||||
public const string CUSTOM;
|
||||
public const string SALT;
|
||||
public const string XOF;
|
||||
public const string DIGEST_NOINIT;
|
||||
public const string DIGEST_ONESHOT;
|
||||
public const string C_ROUNDS;
|
||||
public const string D_ROUNDS;
|
||||
public const string CIPHER;
|
||||
public const string DIGEST;
|
||||
public const string PROPERTIES;
|
||||
public const string SIZE;
|
||||
public const string BLOCK_SIZE;
|
||||
public const string TLS_DATA_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
[Compact]
|
||||
@@ -426,6 +464,7 @@ namespace OpenSSL {
|
||||
public const string sha256;
|
||||
public const string sha384;
|
||||
public const string sha512;
|
||||
public const string siphash;
|
||||
public const string chacha20_poly1305;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
[CCode (cheader_filename = "windns.h", gir_namespace = "Windows", gir_version = "1.0")]
|
||||
namespace WinDns {
|
||||
[CCode (cname = "DNS_QUERY_RESULT")]
|
||||
public struct QueryResult {
|
||||
[CCode (cname = "Version")]
|
||||
public ulong version;
|
||||
[CCode (cname = "QueryStatus")]
|
||||
public long query_status;
|
||||
[CCode (cname = "QueryOptions")]
|
||||
public uint64 query_options;
|
||||
[CCode (cname = "pQueryRecords")]
|
||||
public Record * query_records;
|
||||
[CCode (cname = "Reserved")]
|
||||
public void * reserved;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_RECORDW")]
|
||||
public struct Record {
|
||||
[CCode (cname = "pNext")]
|
||||
public Record * next;
|
||||
[CCode (cname = "pName")]
|
||||
public unowned string16 name;
|
||||
[CCode (cname = "wType")]
|
||||
public RecordType type;
|
||||
[CCode (cname = "wDataLength")]
|
||||
public uint16 data_length;
|
||||
[CCode (cname = "Flags.DW")]
|
||||
public uint32 flags;
|
||||
[CCode (cname = "dwTtl")]
|
||||
public uint32 ttl;
|
||||
[CCode (cname = "dwReserved")]
|
||||
public uint32 reserved;
|
||||
|
||||
[CCode (cname = "Data.A")]
|
||||
public AData a;
|
||||
[CCode (cname = "Data.PTR")]
|
||||
public PtrData ptr;
|
||||
[CCode (cname = "Data.TXT")]
|
||||
public TxtData txt;
|
||||
[CCode (cname = "Data.AAAA")]
|
||||
public AaaaData aaaa;
|
||||
[CCode (cname = "Data.SRV")]
|
||||
public SrvData srv;
|
||||
}
|
||||
|
||||
[CCode (cname = "WORD", cprefix = "DNS_TYPE_", has_type_id = false)]
|
||||
public enum RecordType {
|
||||
A,
|
||||
PTR,
|
||||
TEXT,
|
||||
AAAA,
|
||||
SRV,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_A_DATA")]
|
||||
public struct AData {
|
||||
[CCode (cname = "IpAddress")]
|
||||
public uint32 ip;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_PTR_DATAW")]
|
||||
public struct PtrData {
|
||||
[CCode (cname = "pNameHost")]
|
||||
public unowned string16 name;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_TXT_DATAW")]
|
||||
public struct TxtData {
|
||||
[CCode (cname = "pStringArray", array_length_cname = "dwStringCount")]
|
||||
public unowned string16[] strings;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_AAAA_DATA")]
|
||||
public struct AaaaData {
|
||||
[CCode (cname = "Ip6Address")]
|
||||
public IP6Address ip;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_SRV_DATAW")]
|
||||
public struct SrvData {
|
||||
[CCode (cname = "pNameTarget")]
|
||||
public unowned string16 name_target;
|
||||
[CCode (cname = "wPriority")]
|
||||
public uint16 priority;
|
||||
[CCode (cname = "wWeight")]
|
||||
public uint16 weight;
|
||||
[CCode (cname = "wPort")]
|
||||
public uint16 port;
|
||||
}
|
||||
|
||||
[CCode (cname = "IP6_ADDRESS")]
|
||||
public struct IP6Address {
|
||||
[CCode (cname = "IP6Byte")]
|
||||
public uint8 data[16];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user