aboutsummaryrefslogtreecommitdiff
path: root/plugins/ice
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/ice')
-rw-r--r--plugins/ice/CMakeLists.txt36
-rw-r--r--plugins/ice/src/dtls_srtp.vala356
-rw-r--r--plugins/ice/src/module.vala55
-rw-r--r--plugins/ice/src/plugin.vala71
-rw-r--r--plugins/ice/src/register_plugin.vala3
-rw-r--r--plugins/ice/src/transport_parameters.vala345
-rw-r--r--plugins/ice/src/util.vala18
-rw-r--r--plugins/ice/vapi/gnutls.vapi419
-rw-r--r--plugins/ice/vapi/metadata/Nice-0.1.metadata11
-rw-r--r--plugins/ice/vapi/nice.vapi386
10 files changed, 1700 insertions, 0 deletions
diff --git a/plugins/ice/CMakeLists.txt b/plugins/ice/CMakeLists.txt
new file mode 100644
index 00000000..4783cea6
--- /dev/null
+++ b/plugins/ice/CMakeLists.txt
@@ -0,0 +1,36 @@
+find_package(Nice 0.1.15 REQUIRED)
+find_package(GnuTLS REQUIRED)
+find_packages(ICE_PACKAGES REQUIRED
+ Gee
+ GLib
+ GModule
+ GObject
+ GTK3
+)
+
+vala_precompile(ICE_VALA_C
+SOURCES
+ src/dtls_srtp.vala
+ src/module.vala
+ src/plugin.vala
+ src/transport_parameters.vala
+ src/util.vala
+ src/register_plugin.vala
+CUSTOM_VAPIS
+ ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
+ ${CMAKE_BINARY_DIR}/exports/dino.vapi
+ ${CMAKE_BINARY_DIR}/exports/qlite.vapi
+ ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi
+ ${CMAKE_CURRENT_SOURCE_DIR}/vapi/nice.vapi
+ ${CMAKE_CURRENT_SOURCE_DIR}/vapi/gnutls.vapi
+PACKAGES
+ ${ICE_PACKAGES}
+)
+
+add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="ice")
+add_library(ice SHARED ${ICE_VALA_C})
+target_link_libraries(ice libdino crypto-vala ${ICE_PACKAGES} nice gnutls)
+set_target_properties(ice PROPERTIES PREFIX "")
+set_target_properties(ice PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/)
+
+install(TARGETS ice ${PLUGIN_INSTALL})
diff --git a/plugins/ice/src/dtls_srtp.vala b/plugins/ice/src/dtls_srtp.vala
new file mode 100644
index 00000000..0254351d
--- /dev/null
+++ b/plugins/ice/src/dtls_srtp.vala
@@ -0,0 +1,356 @@
+using GnuTLS;
+
+namespace Dino.Plugins.Ice.DtlsSrtp {
+
+public class CredentialsCapsule {
+ public uint8[] own_fingerprint;
+ public X509.Certificate[] own_cert;
+ public X509.PrivateKey private_key;
+}
+
+public class Handler {
+
+ public signal void send_data(uint8[] data);
+
+ public bool ready { get {
+ return srtp_session.has_encrypt && srtp_session.has_decrypt;
+ }}
+
+ public Mode mode { get; set; default = Mode.CLIENT; }
+ public uint8[] own_fingerprint { get; private set; }
+ public uint8[] peer_fingerprint { get; set; }
+ public string peer_fp_algo { get; set; }
+
+ private CredentialsCapsule credentials;
+ private Cond buffer_cond = Cond();
+ private Mutex buffer_mutex = Mutex();
+ private Gee.LinkedList<Bytes> buffer_queue = new Gee.LinkedList<Bytes>();
+
+ private bool running = false;
+ private bool stop = false;
+ private bool restart = false;
+
+ private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session();
+
+ public Handler.with_cert(CredentialsCapsule creds) {
+ this.credentials = creds;
+ this.own_fingerprint = creds.own_fingerprint;
+ }
+
+ public uint8[]? process_incoming_data(uint component_id, uint8[] data) {
+ if (srtp_session.has_decrypt) {
+ try {
+ if (component_id == 1) {
+ if (data.length >= 2 && data[1] >= 192 && data[1] < 224) {
+ return srtp_session.decrypt_rtcp(data);
+ }
+ return srtp_session.decrypt_rtp(data);
+ }
+ if (component_id == 2) return srtp_session.decrypt_rtcp(data);
+ } catch (Error e) {
+ warning("%s (%d)", e.message, e.code);
+ return null;
+ }
+ } else if (component_id == 1) {
+ on_data_rec(data);
+ }
+ return null;
+ }
+
+ public uint8[]? process_outgoing_data(uint component_id, uint8[] data) {
+ if (srtp_session.has_encrypt) {
+ try {
+ if (component_id == 1) {
+ if (data.length >= 2 && data[1] >= 192 && data[1] < 224) {
+ return srtp_session.encrypt_rtcp(data);
+ }
+ return srtp_session.encrypt_rtp(data);
+ }
+ if (component_id == 2) return srtp_session.encrypt_rtcp(data);
+ } catch (Error e) {
+ warning("%s (%d)", e.message, e.code);
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public void on_data_rec(owned uint8[] data) {
+ buffer_mutex.lock();
+ buffer_queue.add(new Bytes.take(data));
+ buffer_cond.signal();
+ buffer_mutex.unlock();
+ }
+
+ internal static CredentialsCapsule generate_credentials() throws GLib.Error {
+ int err = 0;
+
+ X509.PrivateKey private_key = X509.PrivateKey.create();
+ err = private_key.generate(PKAlgorithm.RSA, 2048);
+ throw_if_error(err);
+
+ var start_time = new DateTime.now_local().add_days(1);
+ var end_time = start_time.add_days(2);
+
+ X509.Certificate cert = X509.Certificate.create();
+ cert.set_key(private_key);
+ cert.set_version(1);
+ cert.set_activation_time ((time_t) start_time.to_unix ());
+ cert.set_expiration_time ((time_t) end_time.to_unix ());
+
+ uint32 serial = 1;
+ cert.set_serial(&serial, sizeof(uint32));
+
+ cert.sign(cert, private_key);
+
+ uint8[] own_fingerprint = get_fingerprint(cert, DigestAlgorithm.SHA256);
+ X509.Certificate[] own_cert = new X509.Certificate[] { (owned)cert };
+
+ var creds = new CredentialsCapsule();
+ creds.own_fingerprint = own_fingerprint;
+ creds.own_cert = (owned) own_cert;
+ creds.private_key = (owned) private_key;
+
+ return creds;
+ }
+
+ public void stop_dtls_connection() {
+ buffer_mutex.lock();
+ stop = true;
+ buffer_cond.signal();
+ buffer_mutex.unlock();
+ }
+
+ public async Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection() {
+ buffer_mutex.lock();
+ if (stop) {
+ restart = true;
+ buffer_mutex.unlock();
+ return null;
+ }
+ if (running || ready) {
+ buffer_mutex.unlock();
+ return null;
+ }
+ running = true;
+ restart = false;
+ buffer_mutex.unlock();
+
+ InitFlags server_or_client = mode == Mode.SERVER ? InitFlags.SERVER : InitFlags.CLIENT;
+ debug("Setting up DTLS connection. We're %s", mode.to_string());
+
+ CertificateCredentials cert_cred = CertificateCredentials.create();
+ int err = cert_cred.set_x509_key(credentials.own_cert, credentials.private_key);
+ throw_if_error(err);
+
+ Session? session = Session.create(server_or_client | InitFlags.DATAGRAM);
+ session.enable_heartbeat(1);
+ session.set_srtp_profile_direct("SRTP_AES128_CM_HMAC_SHA1_80");
+ session.set_credentials(GnuTLS.CredentialsType.CERTIFICATE, cert_cred);
+ session.server_set_request(CertificateRequest.REQUEST);
+ session.set_priority_from_string("NORMAL:!VERS-TLS-ALL:+VERS-DTLS-ALL:+CTYPE-CLI-X509");
+
+ session.set_transport_pointer(this);
+ session.set_pull_function(pull_function);
+ session.set_pull_timeout_function(pull_timeout_function);
+ session.set_push_function(push_function);
+ session.set_verify_function(verify_function);
+
+ Thread<int> thread = new Thread<int> (null, () => {
+ DateTime maximum_time = new DateTime.now_utc().add_seconds(20);
+ do {
+ err = session.handshake();
+
+ DateTime current_time = new DateTime.now_utc();
+ if (maximum_time.compare(current_time) < 0) {
+ warning("DTLS handshake timeouted");
+ err = ErrorCode.APPLICATION_ERROR_MIN + 1;
+ break;
+ }
+ if (stop) {
+ debug("DTLS handshake stopped");
+ err = ErrorCode.APPLICATION_ERROR_MIN + 2;
+ break;
+ }
+ } while (err < 0 && !((ErrorCode)err).is_fatal());
+ Idle.add(setup_dtls_connection.callback);
+ return err;
+ });
+ yield;
+ err = thread.join();
+ buffer_mutex.lock();
+ if (stop) {
+ stop = false;
+ running = false;
+ bool restart = restart;
+ buffer_mutex.unlock();
+ if (restart) {
+ debug("Restarting DTLS handshake");
+ return yield setup_dtls_connection();
+ }
+ return null;
+ }
+ buffer_mutex.unlock();
+ if (err != ErrorCode.SUCCESS) {
+ warning("DTLS handshake failed: %s", ((ErrorCode)err).to_string());
+ return null;
+ }
+
+ uint8[] km = new uint8[150];
+ Datum? client_key, client_salt, server_key, server_salt;
+ session.get_srtp_keys(km, km.length, out client_key, out client_salt, out server_key, out server_salt);
+ if (client_key == null || client_salt == null || server_key == null || server_salt == null) {
+ warning("SRTP client/server key/salt null");
+ }
+
+ debug("Finished DTLS connection. We're %s", mode.to_string());
+ if (mode == Mode.SERVER) {
+ srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract());
+ srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract());
+ } else {
+ srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract());
+ srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract());
+ }
+ return new Xmpp.Xep.Jingle.ContentEncryption() { encryption_ns=Xmpp.Xep.JingleIceUdp.DTLS_NS_URI, encryption_name = "DTLS-SRTP", our_key=credentials.own_fingerprint, peer_key=peer_fingerprint };
+ }
+
+ private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) {
+ Handler self = transport_ptr as Handler;
+
+ self.buffer_mutex.lock();
+ while (self.buffer_queue.size == 0) {
+ self.buffer_cond.wait(self.buffer_mutex);
+ if (self.stop) {
+ self.buffer_mutex.unlock();
+ debug("DTLS handshake pull_function stopped");
+ return -1;
+ }
+ }
+ Bytes data = self.buffer_queue.remove_at(0);
+ self.buffer_mutex.unlock();
+
+ uint8[] data_uint8 = Bytes.unref_to_data((owned) data);
+ Memory.copy(buffer, data_uint8, data_uint8.length);
+
+ // The callback should return 0 on connection termination, a positive number indicating the number of bytes received, and -1 on error.
+ return (ssize_t)data_uint8.length;
+ }
+
+ private static int pull_timeout_function(void* transport_ptr, uint ms) {
+ Handler self = transport_ptr as Handler;
+
+ int64 end_time = get_monotonic_time() + ms * 1000;
+
+ self.buffer_mutex.lock();
+ while (self.buffer_queue.size == 0) {
+ self.buffer_cond.wait_until(self.buffer_mutex, end_time);
+ if (self.stop) {
+ self.buffer_mutex.unlock();
+ debug("DTLS handshake pull_timeout_function stopped");
+ return -1;
+ }
+
+ if (get_monotonic_time() > end_time) {
+ self.buffer_mutex.unlock();
+ return 0;
+ }
+ }
+ self.buffer_mutex.unlock();
+
+ // The callback should return 0 on timeout, a positive number if data can be received, and -1 on error.
+ return 1;
+ }
+
+ private static ssize_t push_function(void* transport_ptr, uint8[] buffer) {
+ Handler self = transport_ptr as Handler;
+ self.send_data(buffer);
+
+ // The callback should return a positive number indicating the bytes sent, and -1 on error.
+ return (ssize_t)buffer.length;
+ }
+
+ private static int verify_function(Session session) {
+ Handler self = session.get_transport_pointer() as Handler;
+ try {
+ bool valid = self.verify_peer_cert(session);
+ if (!valid) {
+ warning("DTLS certificate invalid. Aborting handshake.");
+ return 1;
+ }
+ } catch (Error e) {
+ warning("Error during DTLS certificate validation: %s. Aborting handshake.", e.message);
+ return 1;
+ }
+
+ // The callback function should return 0 for the handshake to continue or non-zero to terminate.
+ return 0;
+ }
+
+ private bool verify_peer_cert(Session session) throws GLib.Error {
+ unowned Datum[] cert_datums = session.get_peer_certificates();
+ if (cert_datums.length == 0) {
+ warning("No peer certs");
+ return false;
+ }
+ if (cert_datums.length > 1) warning("More than one peer cert");
+
+ X509.Certificate peer_cert = X509.Certificate.create();
+ peer_cert.import(ref cert_datums[0], CertificateFormat.DER);
+
+ DigestAlgorithm algo;
+ switch (peer_fp_algo) {
+ case "sha-256":
+ algo = DigestAlgorithm.SHA256;
+ break;
+ default:
+ warning("Unkown peer fingerprint algorithm: %s", peer_fp_algo);
+ return false;
+ }
+
+ uint8[] real_peer_fp = get_fingerprint(peer_cert, algo);
+
+ if (real_peer_fp.length != this.peer_fingerprint.length) {
+ warning("Fingerprint lengths not equal %i vs %i", real_peer_fp.length, peer_fingerprint.length);
+ return false;
+ }
+
+ for (int i = 0; i < real_peer_fp.length; i++) {
+ if (real_peer_fp[i] != this.peer_fingerprint[i]) {
+ warning("First cert in peer cert list doesn't equal advertised one: %s vs %s", format_fingerprint(real_peer_fp), format_fingerprint(peer_fingerprint));
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) {
+ uint8[] buf = new uint8[512];
+ size_t buf_out_size = 512;
+ certificate.get_fingerprint(digest_algo, buf, ref buf_out_size);
+
+ uint8[] ret = new uint8[buf_out_size];
+ for (int i = 0; i < buf_out_size; i++) {
+ ret[i] = buf[i];
+ }
+ return ret;
+}
+
+private string format_fingerprint(uint8[] fingerprint) {
+ var sb = new StringBuilder();
+ for (int i = 0; i < fingerprint.length; i++) {
+ sb.append("%02x".printf(fingerprint[i]));
+ if (i < fingerprint.length - 1) {
+ sb.append(":");
+ }
+ }
+ return sb.str;
+}
+
+
+public enum Mode {
+ CLIENT, SERVER
+}
+
+}
diff --git a/plugins/ice/src/module.vala b/plugins/ice/src/module.vala
new file mode 100644
index 00000000..2645d7dc
--- /dev/null
+++ b/plugins/ice/src/module.vala
@@ -0,0 +1,55 @@
+using Gee;
+using Xmpp;
+using Xmpp.Xep;
+
+public class Dino.Plugins.Ice.Module : JingleIceUdp.Module {
+
+ public string? stun_ip = null;
+ public uint stun_port = 0;
+ public string? turn_ip = null;
+ public Xep.ExternalServiceDiscovery.Service? turn_service = null;
+
+ private weak Nice.Agent? agent;
+ private HashMap<string, DtlsSrtp.CredentialsCapsule> cerds = new HashMap<string, DtlsSrtp.CredentialsCapsule>();
+
+ private Nice.Agent get_agent() {
+ Nice.Agent? agent = this.agent;
+ if (agent == null) {
+ agent = new Nice.Agent(MainContext.@default(), Nice.Compatibility.RFC5245);
+ if (stun_ip != null) {
+ agent.stun_server = stun_ip;
+ agent.stun_server_port = stun_port;
+ }
+ agent.ice_tcp = false;
+ agent.set_software("Dino");
+ agent.weak_ref(agent_unweak);
+ this.agent = agent;
+ debug("STUN server for libnice %s %u", agent.stun_server, agent.stun_server_port);
+ }
+ return agent;
+ }
+
+ public override Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid) {
+ DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid);
+ return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid);
+ }
+
+ public override Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError {
+ DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid);
+ return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid, transport);
+ }
+
+ private DtlsSrtp.CredentialsCapsule? get_create_credentials(Jid local_full_jid, Jid peer_full_jid) {
+ string from_to_id = local_full_jid.to_string() + peer_full_jid.to_string();
+ try {
+ if (!cerds.has_key(from_to_id)) cerds[from_to_id] = DtlsSrtp.Handler.generate_credentials();
+ } catch (Error e) {
+ warning("Error creating dtls credentials: %s", e.message);
+ }
+ return cerds[from_to_id];
+ }
+
+ private void agent_unweak() {
+ this.agent = null;
+ }
+} \ No newline at end of file
diff --git a/plugins/ice/src/plugin.vala b/plugins/ice/src/plugin.vala
new file mode 100644
index 00000000..3ee8a72a
--- /dev/null
+++ b/plugins/ice/src/plugin.vala
@@ -0,0 +1,71 @@
+using Gee;
+using Dino.Entities;
+using Xmpp;
+using Xmpp.Xep;
+
+private extern const size_t NICE_ADDRESS_STRING_LEN;
+
+public class Dino.Plugins.Ice.Plugin : RootInterface, Object {
+ public Dino.Application app;
+
+ public void registered(Dino.Application app) {
+ Nice.debug_enable(true);
+ this.app = app;
+ app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => {
+ list.add(new Module());
+ });
+ app.stream_interactor.stream_attached_modules.connect((account, stream) => {
+ stream.get_module(Socks5Bytestreams.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses);
+ });
+ app.stream_interactor.stream_negotiated.connect(on_stream_negotiated);
+ }
+
+ private async void on_stream_negotiated(Account account, XmppStream stream) {
+ Module? ice_udp_module = stream.get_module(JingleIceUdp.Module.IDENTITY) as Module;
+ if (ice_udp_module == null) return;
+ Gee.List<Xep.ExternalServiceDiscovery.Service> services = yield ExternalServiceDiscovery.request_services(stream);
+ foreach (Xep.ExternalServiceDiscovery.Service service in services) {
+ if (service.transport == "udp" && (service.ty == "stun" || service.ty == "turn")) {
+ InetAddress ip = yield lookup_ipv4_addess(service.host);
+ if (ip == null) continue;
+
+ if (service.ty == "stun") {
+ debug("Server offers STUN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string());
+ ice_udp_module.stun_ip = ip.to_string();
+ ice_udp_module.stun_port = service.port;
+ } else if (service.ty == "turn") {
+ debug("Server offers TURN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string());
+ ice_udp_module.turn_ip = ip.to_string();
+ ice_udp_module.turn_service = service;
+ }
+ }
+ }
+ if (ice_udp_module.stun_ip == null) {
+ InetAddress ip = yield lookup_ipv4_addess("stun.l.google.com");
+ if (ip == null) return;
+
+ debug("Using fallback STUN server: stun.l.google.com:19302, resolved to %s", ip.to_string());
+
+ ice_udp_module.stun_ip = ip.to_string();
+ ice_udp_module.stun_port = 19302;
+ }
+ }
+
+ public void shutdown() {
+ // Nothing to do
+ }
+
+ private async InetAddress? lookup_ipv4_addess(string host) {
+ try {
+ Resolver resolver = Resolver.get_default();
+ GLib.List<GLib.InetAddress>? ips = yield resolver.lookup_by_name_async(host);
+ foreach (GLib.InetAddress ina in ips) {
+ if (ina.get_family() != SocketFamily.IPV4) continue;
+ return ina;
+ }
+ } catch (Error e) {
+ warning("Failed looking up IP address of %s", host);
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/plugins/ice/src/register_plugin.vala b/plugins/ice/src/register_plugin.vala
new file mode 100644
index 00000000..b2ed56c1
--- /dev/null
+++ b/plugins/ice/src/register_plugin.vala
@@ -0,0 +1,3 @@
+public Type register_plugin(Module module) {
+ return typeof (Dino.Plugins.Ice.Plugin);
+}
diff --git a/plugins/ice/src/transport_parameters.vala b/plugins/ice/src/transport_parameters.vala
new file mode 100644
index 00000000..62c04906
--- /dev/null
+++ b/plugins/ice/src/transport_parameters.vala
@@ -0,0 +1,345 @@
+using Gee;
+using Xmpp;
+using Xmpp.Xep;
+
+
+public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransportParameters {
+ private Nice.Agent agent;
+ private uint stream_id;
+ private bool we_want_connection;
+ private bool remote_credentials_set;
+ private Map<uint8, DatagramConnection> connections = new HashMap<uint8, DatagramConnection>();
+ private DtlsSrtp.Handler? dtls_srtp_handler;
+
+ private class DatagramConnection : Jingle.DatagramConnection {
+ private Nice.Agent agent;
+ private DtlsSrtp.Handler? dtls_srtp_handler;
+ private uint stream_id;
+ private string? error;
+ private ulong sent;
+ private ulong sent_reported;
+ private ulong recv;
+ private ulong recv_reported;
+ private ulong datagram_received_id;
+
+ public DatagramConnection(Nice.Agent agent, DtlsSrtp.Handler? dtls_srtp_handler, uint stream_id, uint8 component_id) {
+ this.agent = agent;
+ this.dtls_srtp_handler = dtls_srtp_handler;
+ this.stream_id = stream_id;
+ this.component_id = component_id;
+ this.datagram_received_id = this.datagram_received.connect((datagram) => {
+ recv += datagram.length;
+ if (recv > recv_reported + 100000) {
+ debug("Received %lu bytes via stream %u component %u", recv, stream_id, component_id);
+ recv_reported = recv;
+ }
+ });
+ }
+
+ public override async void terminate(bool we_terminated, string? reason_string = null, string? reason_text = null) {
+ yield base.terminate(we_terminated, reason_string, reason_text);
+ this.disconnect(datagram_received_id);
+ agent = null;
+ dtls_srtp_handler = null;
+ }
+
+ public override void send_datagram(Bytes datagram) {
+ if (this.agent != null && is_component_ready(agent, stream_id, component_id)) {
+ uint8[] encrypted_data = null;
+ if (dtls_srtp_handler != null) {
+ encrypted_data = dtls_srtp_handler.process_outgoing_data(component_id, datagram.get_data());
+ if (encrypted_data == null) return;
+ }
+ agent.send(stream_id, component_id, encrypted_data ?? datagram.get_data());
+ sent += datagram.length;
+ if (sent > sent_reported + 100000) {
+ debug("Sent %lu bytes via stream %u component %u", sent, stream_id, component_id);
+ sent_reported = sent;
+ }
+ }
+ }
+ }
+
+ public TransportParameters(Nice.Agent agent, DtlsSrtp.CredentialsCapsule? credentials, Xep.ExternalServiceDiscovery.Service? turn_service, string? turn_ip, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) {
+ base(components, local_full_jid, peer_full_jid, node);
+ this.we_want_connection = (node == null);
+ this.agent = agent;
+
+ if (this.peer_fingerprint != null || !incoming) {
+ dtls_srtp_handler = setup_dtls(this, credentials);
+ own_fingerprint = dtls_srtp_handler.own_fingerprint;
+ if (incoming) {
+ own_setup = "active";
+ dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT;
+ dtls_srtp_handler.peer_fingerprint = peer_fingerprint;
+ dtls_srtp_handler.peer_fp_algo = peer_fp_algo;
+ } else {
+ own_setup = "actpass";
+ dtls_srtp_handler.mode = DtlsSrtp.Mode.SERVER;
+ dtls_srtp_handler.setup_dtls_connection.begin((_, res) => {
+ var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res);
+ if (content_encryption != null) {
+ this.content.encryptions[content_encryption.encryption_ns] = content_encryption;
+ }
+ });
+ }
+ }
+
+ agent.candidate_gathering_done.connect(on_candidate_gathering_done);
+ agent.initial_binding_request_received.connect(on_initial_binding_request_received);
+ agent.component_state_changed.connect(on_component_state_changed);
+ agent.new_selected_pair_full.connect(on_new_selected_pair_full);
+ agent.new_candidate_full.connect(on_new_candidate);
+
+ agent.controlling_mode = !incoming;
+ stream_id = agent.add_stream(components);
+
+ if (turn_ip != null) {
+ for (uint8 component_id = 1; component_id <= components; component_id++) {
+ agent.set_relay_info(stream_id, component_id, turn_ip, turn_service.port, turn_service.username, turn_service.password, Nice.RelayType.UDP);
+ debug("TURN info (component %i) %s:%u", component_id, turn_ip, turn_service.port);
+ }
+ }
+ string ufrag;
+ string pwd;
+ agent.get_local_credentials(stream_id, out ufrag, out pwd);
+ init(ufrag, pwd);
+
+ for (uint8 component_id = 1; component_id <= components; component_id++) {
+ // We don't properly get local candidates before this call
+ agent.attach_recv(stream_id, component_id, MainContext.@default(), on_recv);
+ }
+
+ agent.gather_candidates(stream_id);
+ }
+
+ private static DtlsSrtp.Handler setup_dtls(TransportParameters tp, DtlsSrtp.CredentialsCapsule credentials) {
+ var weak_self = WeakRef(tp);
+ DtlsSrtp.Handler dtls_srtp = new DtlsSrtp.Handler.with_cert(credentials);
+ dtls_srtp.send_data.connect((data) => {
+ TransportParameters self = (TransportParameters) weak_self.get();
+ if (self != null) self.agent.send(self.stream_id, 1, data);
+ });
+ return dtls_srtp;
+ }
+
+ private void on_candidate_gathering_done(uint stream_id) {
+ if (stream_id != this.stream_id) return;
+ debug("on_candidate_gathering_done in %u", stream_id);
+
+ for (uint8 i = 1; i <= components; i++) {
+ foreach (unowned Nice.Candidate nc in agent.get_local_candidates(stream_id, i)) {
+ if (nc.transport == Nice.CandidateTransport.UDP) {
+ JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc);
+ if (candidate == null) continue;
+ debug("Local candidate summary: %s", agent.generate_local_candidate_sdp(nc));
+ }
+ }
+ }
+ }
+
+ private void on_new_candidate(Nice.Candidate nc) {
+ if (nc.stream_id != stream_id) return;
+ JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc);
+ if (candidate == null) return;
+
+ if (nc.transport == Nice.CandidateTransport.UDP) {
+ // Execution was in the agent thread before
+ add_local_candidate_threadsafe(candidate);
+ }
+ }
+
+ public override void handle_transport_accept(StanzaNode transport) throws Jingle.IqError {
+ debug("on_transport_accept from %s", peer_full_jid.to_string());
+ base.handle_transport_accept(transport);
+
+ if (dtls_srtp_handler != null && peer_fingerprint != null) {
+ dtls_srtp_handler.peer_fingerprint = peer_fingerprint;
+ dtls_srtp_handler.peer_fp_algo = peer_fp_algo;
+ if (peer_setup == "passive") {
+ dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT;
+ dtls_srtp_handler.stop_dtls_connection();
+ dtls_srtp_handler.setup_dtls_connection.begin((_, res) => {
+ var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res);
+ if (content_encryption != null) {
+ this.content.encryptions[content_encryption.encryption_ns] = content_encryption;
+ }
+ });
+ }
+ } else {
+ dtls_srtp_handler = null;
+ }
+ }
+
+ public override void handle_transport_info(StanzaNode transport) throws Jingle.IqError {
+ debug("on_transport_info from %s", peer_full_jid.to_string());
+ base.handle_transport_info(transport);
+
+ if (!we_want_connection) return;
+
+ if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) {
+ agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd);
+ remote_credentials_set = true;
+ }
+ for (uint8 i = 1; i <= components; i++) {
+ SList<Nice.Candidate> candidates = new SList<Nice.Candidate>();
+ foreach (JingleIceUdp.Candidate candidate in remote_candidates) {
+ if (candidate.component == i) {
+ candidates.append(candidate_to_nice(candidate));
+ }
+ }
+ int new_candidates = agent.set_remote_candidates(stream_id, i, candidates);
+ debug("Updated to %i remote candidates for candidate %u via transport info", new_candidates, i);
+ }
+ }
+
+ public override void create_transport_connection(XmppStream stream, Jingle.Content content) {
+ debug("create_transport_connection: %s", content.session.sid);
+ debug("local_credentials: %s %s", local_ufrag, local_pwd);
+ debug("remote_credentials: %s %s", remote_ufrag, remote_pwd);
+ debug("expected incoming credentials: %s %s", local_ufrag + ":" + remote_ufrag, local_pwd);
+ debug("expected outgoing credentials: %s %s", remote_ufrag + ":" + local_ufrag, remote_pwd);
+
+ we_want_connection = true;
+
+ if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) {
+ agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd);
+ remote_credentials_set = true;
+ }
+ for (uint8 i = 1; i <= components; i++) {
+ SList<Nice.Candidate> candidates = new SList<Nice.Candidate>();
+ foreach (JingleIceUdp.Candidate candidate in remote_candidates) {
+ if (candidate.ip.has_prefix("fe80::")) continue;
+ if (candidate.component == i) {
+ candidates.append(candidate_to_nice(candidate));
+ debug("remote candidate: %s", agent.generate_local_candidate_sdp(candidate_to_nice(candidate)));
+ }
+ }
+ int new_candidates = agent.set_remote_candidates(stream_id, i, candidates);
+ debug("Initiated component %u with %i remote candidates", i, new_candidates);
+
+ connections[i] = new DatagramConnection(agent, dtls_srtp_handler, stream_id, i);
+ content.set_transport_connection(connections[i], i);
+ }
+
+ base.create_transport_connection(stream, content);
+ }
+
+ private void on_component_state_changed(uint stream_id, uint component_id, uint state) {
+ if (stream_id != this.stream_id) return;
+ debug("stream %u component %u state changed to %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string());
+ may_consider_ready(stream_id, component_id);
+ if (incoming && dtls_srtp_handler != null && !dtls_srtp_handler.ready && is_component_ready(agent, stream_id, component_id) && dtls_srtp_handler.mode == DtlsSrtp.Mode.CLIENT) {
+ dtls_srtp_handler.setup_dtls_connection.begin((_, res) => {
+ Jingle.ContentEncryption? encryption = dtls_srtp_handler.setup_dtls_connection.end(res);
+ if (encryption != null) {
+ this.content.encryptions[encryption.encryption_ns] = encryption;
+ }
+ });
+ }
+ }
+
+ private void may_consider_ready(uint stream_id, uint component_id) {
+ if (stream_id != this.stream_id) return;
+ if (connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready && is_component_ready(agent, stream_id, component_id) && (dtls_srtp_handler == null || dtls_srtp_handler.ready)) {
+ connections[(uint8)component_id].ready = true;
+ }
+ }
+
+ private void on_initial_binding_request_received(uint stream_id) {
+ if (stream_id != this.stream_id) return;
+ debug("initial_binding_request_received");
+ }
+
+ private void on_new_selected_pair_full(uint stream_id, uint component_id, Nice.Candidate p1, Nice.Candidate p2) {
+ if (stream_id != this.stream_id) return;
+ debug("new_selected_pair_full %u [%s, %s]", component_id, agent.generate_local_candidate_sdp(p1), agent.generate_local_candidate_sdp(p2));
+ }
+
+ private void on_recv(Nice.Agent agent, uint stream_id, uint component_id, uint8[] data) {
+ if (stream_id != this.stream_id) return;
+ uint8[] decrypt_data = null;
+ if (dtls_srtp_handler != null) {
+ decrypt_data = dtls_srtp_handler.process_incoming_data(component_id, data);
+ if (decrypt_data == null) return;
+ }
+ may_consider_ready(stream_id, component_id);
+ if (connections.has_key((uint8) component_id)) {
+ if (!connections[(uint8) component_id].ready) {
+ debug("on_recv stream %u component %u when state %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string());
+ }
+ connections[(uint8) component_id].datagram_received(new Bytes(decrypt_data ?? data));
+ } else {
+ debug("on_recv stream %u component %u length %u", stream_id, component_id, data.length);
+ }
+ }
+
+ private static Nice.Candidate candidate_to_nice(JingleIceUdp.Candidate c) {
+ Nice.CandidateType type;
+ switch (c.type_) {
+ case JingleIceUdp.Candidate.Type.HOST: type = Nice.CandidateType.HOST; break;
+ case JingleIceUdp.Candidate.Type.PRFLX: type = Nice.CandidateType.PEER_REFLEXIVE; break;
+ case JingleIceUdp.Candidate.Type.RELAY: type = Nice.CandidateType.RELAYED; break;
+ case JingleIceUdp.Candidate.Type.SRFLX: type = Nice.CandidateType.SERVER_REFLEXIVE; break;
+ default: assert_not_reached();
+ }
+
+ Nice.Candidate candidate = new Nice.Candidate(type);
+ candidate.component_id = c.component;
+ char[] foundation = new char[Nice.CANDIDATE_MAX_FOUNDATION];
+ Memory.copy(foundation, c.foundation.data, size_t.min(c.foundation.length, Nice.CANDIDATE_MAX_FOUNDATION - 1));
+ candidate.foundation = foundation;
+ candidate.addr = Nice.Address();
+ candidate.addr.init();
+ candidate.addr.set_from_string(c.ip);
+ candidate.addr.set_port(c.port);
+ candidate.priority = c.priority;
+ if (c.rel_addr != null) {
+ candidate.base_addr = Nice.Address();
+ candidate.base_addr.init();
+ candidate.base_addr.set_from_string(c.rel_addr);
+ candidate.base_addr.set_port(c.rel_port);
+ }
+ candidate.transport = Nice.CandidateTransport.UDP;
+ return candidate;
+ }
+
+ private static JingleIceUdp.Candidate? candidate_to_jingle(Nice.Candidate nc) {
+ JingleIceUdp.Candidate candidate = new JingleIceUdp.Candidate();
+ switch (nc.type) {
+ case Nice.CandidateType.HOST: candidate.type_ = JingleIceUdp.Candidate.Type.HOST; break;
+ case Nice.CandidateType.PEER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.PRFLX; break;
+ case Nice.CandidateType.RELAYED: candidate.type_ = JingleIceUdp.Candidate.Type.RELAY; break;
+ case Nice.CandidateType.SERVER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.SRFLX; break;
+ default: assert_not_reached();
+ }
+ candidate.component = (uint8) nc.component_id;
+ candidate.foundation = ((string)nc.foundation).dup();
+ candidate.generation = 0;
+ candidate.id = Random.next_int().to_string("%08x"); // TODO
+
+ char[] res = new char[NICE_ADDRESS_STRING_LEN];
+ nc.addr.to_string(res);
+ candidate.ip = (string) res;
+ candidate.network = 0; // TODO
+ candidate.port = (uint16) nc.addr.get_port();
+ candidate.priority = nc.priority;
+ candidate.protocol = "udp";
+ if (nc.base_addr.is_valid() && !nc.base_addr.equal(nc.addr)) {
+ res = new char[NICE_ADDRESS_STRING_LEN];
+ nc.base_addr.to_string(res);
+ candidate.rel_addr = (string) res;
+ candidate.rel_port = (uint16) nc.base_addr.get_port();
+ }
+ if (candidate.ip.has_prefix("fe80::")) return null;
+
+ return candidate;
+ }
+
+ public override void dispose() {
+ base.dispose();
+ agent = null;
+ dtls_srtp_handler = null;
+ connections.clear();
+ }
+}
diff --git a/plugins/ice/src/util.vala b/plugins/ice/src/util.vala
new file mode 100644
index 00000000..dd89d2f4
--- /dev/null
+++ b/plugins/ice/src/util.vala
@@ -0,0 +1,18 @@
+using Gee;
+
+namespace Dino.Plugins.Ice {
+
+internal static bool is_component_ready(Nice.Agent agent, uint stream_id, uint component_id) {
+ var state = agent.get_component_state(stream_id, component_id);
+ return state == Nice.ComponentState.CONNECTED || state == Nice.ComponentState.READY;
+}
+
+internal Gee.List<string> get_local_ip_addresses() {
+ Gee.List<string> result = new ArrayList<string>();
+ foreach (string ip_address in Nice.interfaces_get_local_ips(false)) {
+ result.add(ip_address);
+ }
+ return result;
+}
+
+} \ No newline at end of file
diff --git a/plugins/ice/vapi/gnutls.vapi b/plugins/ice/vapi/gnutls.vapi
new file mode 100644
index 00000000..bc3f13d0
--- /dev/null
+++ b/plugins/ice/vapi/gnutls.vapi
@@ -0,0 +1,419 @@
+[CCode (cprefix = "gnutls_", lower_case_cprefix = "gnutls_", cheader_filename = "gnutls/gnutls.h")]
+namespace GnuTLS {
+
+ public int global_init();
+
+ [CCode (cname = "gnutls_pull_func", has_target = false)]
+ public delegate ssize_t PullFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array);
+
+ [CCode (cname = "gnutls_pull_timeout_func", has_target = false)]
+ public delegate int PullTimeoutFunc(void* transport_ptr, uint ms);
+
+ [CCode (cname = "gnutls_push_func", has_target = false)]
+ public delegate ssize_t PushFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array);
+
+ [CCode (cname = "gnutls_certificate_verify_function", has_target = false)]
+ public delegate int VerifyFunc(Session session);
+
+ [Compact]
+ [CCode (cname = "struct gnutls_session_int", free_function = "gnutls_deinit")]
+ public class Session {
+
+ public static Session? create(int con_end) throws GLib.Error {
+ Session result;
+ var ret = init(out result, con_end);
+ throw_if_error(ret);
+ return result;
+ }
+
+ [CCode (cname = "gnutls_init")]
+ private static int init(out Session session, int con_end);
+
+ [CCode (cname = "gnutls_transport_set_push_function")]
+ public void set_push_function(PushFunc func);
+
+ [CCode (cname = "gnutls_transport_set_pull_function")]
+ public void set_pull_function(PullFunc func);
+
+ [CCode (cname = "gnutls_transport_set_pull_timeout_function")]
+ public void set_pull_timeout_function(PullTimeoutFunc func);
+
+ [CCode (cname = "gnutls_transport_set_ptr")]
+ public void set_transport_pointer(void* ptr);
+
+ [CCode (cname = "gnutls_transport_get_ptr")]
+ public void* get_transport_pointer();
+
+ [CCode (cname = "gnutls_heartbeat_enable")]
+ public int enable_heartbeat(uint type);
+
+ [CCode (cname = "gnutls_certificate_server_set_request")]
+ public void server_set_request(CertificateRequest req);
+
+ [CCode (cname = "gnutls_credentials_set")]
+ public int set_credentials_(CredentialsType type, void* cred);
+ [CCode (cname = "gnutls_credentials_set_")]
+ public void set_credentials(CredentialsType type, void* cred) throws GLib.Error {
+ int err = set_credentials_(type, cred);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_priority_set_direct")]
+ public int set_priority_from_string_(string priority, out unowned string err_pos = null);
+ [CCode (cname = "gnutls_priority_set_direct_")]
+ public void set_priority_from_string(string priority, out unowned string err_pos = null) throws GLib.Error {
+ int err = set_priority_from_string_(priority, out err_pos);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_srtp_set_profile_direct")]
+ public int set_srtp_profile_direct_(string profiles, out unowned string err_pos = null);
+ [CCode (cname = "gnutls_srtp_set_profile_direct_")]
+ public void set_srtp_profile_direct(string profiles, out unowned string err_pos = null) throws GLib.Error {
+ int err = set_srtp_profile_direct_(profiles, out err_pos);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_transport_set_int")]
+ public void transport_set_int(int fd);
+
+ [CCode (cname = "gnutls_handshake")]
+ public int handshake();
+
+ [CCode (cname = "gnutls_srtp_get_keys")]
+ public int get_srtp_keys_(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt);
+ [CCode (cname = "gnutls_srtp_get_keys_")]
+ public void get_srtp_keys(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt) throws GLib.Error {
+ get_srtp_keys_(key_material, key_material_size, out client_key, out client_salt, out server_key, out server_salt);
+ }
+
+ [CCode (cname = "gnutls_certificate_get_peers", array_length_type = "unsigned int")]
+ public unowned Datum[]? get_peer_certificates();
+
+ [CCode (cname = "gnutls_session_set_verify_function")]
+ public void set_verify_function(VerifyFunc func);
+ }
+
+ [Compact]
+ [CCode (cname = "struct gnutls_certificate_credentials_st", free_function = "gnutls_certificate_free_credentials", cprefix = "gnutls_certificate_")]
+ public class CertificateCredentials {
+
+ [CCode (cname = "gnutls_certificate_allocate_credentials")]
+ private static int allocate(out CertificateCredentials credentials);
+
+ public static CertificateCredentials create() throws GLib.Error {
+ CertificateCredentials result;
+ var ret = allocate (out result);
+ throw_if_error(ret);
+ return result;
+ }
+
+ public void get_x509_crt(uint index, [CCode (array_length_type = "unsigned int")] out unowned X509.Certificate[] x509_ca_list);
+
+ public int set_x509_key(X509.Certificate[] cert_list, X509.PrivateKey key);
+ }
+
+ [CCode (cheader_filename = "gnutls/x509.h", cprefix = "GNUTLS_")]
+ namespace X509 {
+
+ [Compact]
+ [CCode (cname = "struct gnutls_x509_crt_int", cprefix = "gnutls_x509_crt_", free_function = "gnutls_x509_crt_deinit")]
+ public class Certificate {
+
+ [CCode (cname = "gnutls_x509_crt_init")]
+ private static int init (out Certificate cert);
+ public static Certificate create() throws GLib.Error {
+ Certificate result;
+ var ret = init (out result);
+ throw_if_error(ret);
+ return result;
+ }
+
+ [CCode (cname = "gnutls_x509_crt_import")]
+ public int import_(ref Datum data, CertificateFormat format);
+ [CCode (cname = "gnutls_x509_crt_import_")]
+ public void import(ref Datum data, CertificateFormat format) throws GLib.Error {
+ int err = import_(ref data, format);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_set_version")]
+ public int set_version_(uint version);
+ [CCode (cname = "gnutls_x509_crt_set_version_")]
+ public void set_version(uint version) throws GLib.Error {
+ int err = set_version_(version);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_set_key")]
+ public int set_key_(PrivateKey key);
+ [CCode (cname = "gnutls_x509_crt_set_key_")]
+ public void set_key(PrivateKey key) throws GLib.Error {
+ int err = set_key_(key);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_set_activation_time")]
+ public int set_activation_time_(time_t act_time);
+ [CCode (cname = "gnutls_x509_crt_set_activation_time_")]
+ public void set_activation_time(time_t act_time) throws GLib.Error {
+ int err = set_activation_time_(act_time);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_set_expiration_time")]
+ public int set_expiration_time_(time_t exp_time);
+ [CCode (cname = "gnutls_x509_crt_set_expiration_time_")]
+ public void set_expiration_time(time_t exp_time) throws GLib.Error {
+ int err = set_expiration_time_(exp_time);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_set_serial")]
+ public int set_serial_(void* serial, size_t serial_size);
+ [CCode (cname = "gnutls_x509_crt_set_serial_")]
+ public void set_serial(void* serial, size_t serial_size) throws GLib.Error {
+ int err = set_serial_(serial, serial_size);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_sign")]
+ public int sign_(Certificate issuer, PrivateKey issuer_key);
+ [CCode (cname = "gnutls_x509_crt_sign_")]
+ public void sign(Certificate issuer, PrivateKey issuer_key) throws GLib.Error {
+ int err = sign_(issuer, issuer_key);
+ throw_if_error(err);
+ }
+
+ [CCode (cname = "gnutls_x509_crt_get_fingerprint")]
+ public int get_fingerprint_(DigestAlgorithm algo, void* buf, ref size_t buf_size);
+ [CCode (cname = "gnutls_x509_crt_get_fingerprint_")]
+ public void get_fingerprint(DigestAlgorithm algo, void* buf, ref size_t buf_size) throws GLib.Error {
+ int err = get_fingerprint_(algo, buf, ref buf_size);
+ throw_if_error(err);
+ }
+ }
+
+ [Compact]
+ [CCode (cname = "struct gnutls_x509_privkey_int", cprefix = "gnutls_x509_privkey_", free_function = "gnutls_x509_privkey_deinit")]
+ public class PrivateKey {
+ private static int init (out PrivateKey key);
+ public static PrivateKey create () throws GLib.Error {
+ PrivateKey result;
+ var ret = init (out result);
+ throw_if_error(ret);
+ return result;
+ }
+
+ public int generate(PKAlgorithm algo, uint bits, uint flags = 0);
+ }
+
+ }
+
+ [CCode (cname = "gnutls_certificate_request_t", cprefix = "GNUTLS_CERT_", has_type_id = false)]
+ public enum CertificateRequest {
+ IGNORE,
+ REQUEST,
+ REQUIRE
+ }
+
+ [CCode (cname = "gnutls_pk_algorithm_t", cprefix = "GNUTLS_PK_", has_type_id = false)]
+ public enum PKAlgorithm {
+ UNKNOWN,
+ RSA,
+ DSA;
+ }
+
+ [CCode (cname = "gnutls_digest_algorithm_t", cprefix = "GNUTLS_DIG_", has_type_id = false)]
+ public enum DigestAlgorithm {
+ NULL,
+ MD5,
+ SHA1,
+ RMD160,
+ MD2,
+ SHA224,
+ SHA256,
+ SHA384,
+ SHA512;
+ }
+
+ [Flags]
+ [CCode (cname = "gnutls_init_flags_t", cprefix = "GNUTLS_", has_type_id = false)]
+ public enum InitFlags {
+ SERVER,
+ CLIENT,
+ DATAGRAM
+ }
+
+ [CCode (cname = "gnutls_credentials_type_t", cprefix = "GNUTLS_CRD_", has_type_id = false)]
+ public enum CredentialsType {
+ CERTIFICATE,
+ ANON,
+ SRP,
+ PSK,
+ IA
+ }
+
+ [CCode (cname = "gnutls_x509_crt_fmt_t", cprefix = "GNUTLS_X509_FMT_", has_type_id = false)]
+ public enum CertificateFormat {
+ DER,
+ PEM
+ }
+
+ [Flags]
+ [CCode (cname = "gnutls_certificate_status_t", cprefix = "GNUTLS_CERT_", has_type_id = false)]
+ public enum CertificateStatus {
+ INVALID, // will be set if the certificate was not verified.
+ REVOKED, // in X.509 this will be set only if CRLs are checked
+ SIGNER_NOT_FOUND,
+ SIGNER_NOT_CA,
+ INSECURE_ALGORITHM
+ }
+
+ [SimpleType]
+ [CCode (cname = "gnutls_datum_t", has_type_id = false)]
+ public struct Datum {
+ public uint8* data;
+ public uint size;
+
+ public uint8[] extract() {
+ uint8[] ret = new uint8[size];
+ for (int i = 0; i < size; i++) {
+ ret[i] = data[i];
+ }
+ return ret;
+ }
+ }
+
+ // Gnutls error codes. The mapping to a TLS alert is also shown in comments.
+ [CCode (cname = "int", cprefix = "GNUTLS_E_", lower_case_cprefix = "gnutls_error_", has_type_id = false)]
+ public enum ErrorCode {
+ SUCCESS,
+ UNKNOWN_COMPRESSION_ALGORITHM,
+ UNKNOWN_CIPHER_TYPE,
+ LARGE_PACKET,
+ UNSUPPORTED_VERSION_PACKET, // GNUTLS_A_PROTOCOL_VERSION
+ UNEXPECTED_PACKET_LENGTH, // GNUTLS_A_RECORD_OVERFLOW
+ INVALID_SESSION,
+ FATAL_ALERT_RECEIVED,
+ UNEXPECTED_PACKET, // GNUTLS_A_UNEXPECTED_MESSAGE
+ WARNING_ALERT_RECEIVED,
+ ERROR_IN_FINISHED_PACKET,
+ UNEXPECTED_HANDSHAKE_PACKET,
+ UNKNOWN_CIPHER_SUITE, // GNUTLS_A_HANDSHAKE_FAILURE
+ UNWANTED_ALGORITHM,
+ MPI_SCAN_FAILED,
+ DECRYPTION_FAILED, // GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_BAD_RECORD_MAC
+ MEMORY_ERROR,
+ DECOMPRESSION_FAILED, // GNUTLS_A_DECOMPRESSION_FAILURE
+ COMPRESSION_FAILED,
+ AGAIN,
+ EXPIRED,
+ DB_ERROR,
+ SRP_PWD_ERROR,
+ INSUFFICIENT_CREDENTIALS,
+ HASH_FAILED,
+ BASE64_DECODING_ERROR,
+ MPI_PRINT_FAILED,
+ REHANDSHAKE, // GNUTLS_A_NO_RENEGOTIATION
+ GOT_APPLICATION_DATA,
+ RECORD_LIMIT_REACHED,
+ ENCRYPTION_FAILED,
+ PK_ENCRYPTION_FAILED,
+ PK_DECRYPTION_FAILED,
+ PK_SIGN_FAILED,
+ X509_UNSUPPORTED_CRITICAL_EXTENSION,
+ KEY_USAGE_VIOLATION,
+ NO_CERTIFICATE_FOUND, // GNUTLS_A_BAD_CERTIFICATE
+ INVALID_REQUEST,
+ SHORT_MEMORY_BUFFER,
+ INTERRUPTED,
+ PUSH_ERROR,
+ PULL_ERROR,
+ RECEIVED_ILLEGAL_PARAMETER, // GNUTLS_A_ILLEGAL_PARAMETER
+ REQUESTED_DATA_NOT_AVAILABLE,
+ PKCS1_WRONG_PAD,
+ RECEIVED_ILLEGAL_EXTENSION,
+ INTERNAL_ERROR,
+ DH_PRIME_UNACCEPTABLE,
+ FILE_ERROR,
+ TOO_MANY_EMPTY_PACKETS,
+ UNKNOWN_PK_ALGORITHM,
+ // returned if libextra functionality was requested but
+ // gnutls_global_init_extra() was not called.
+
+ INIT_LIBEXTRA,
+ LIBRARY_VERSION_MISMATCH,
+ // returned if you need to generate temporary RSA
+ // parameters. These are needed for export cipher suites.
+
+ NO_TEMPORARY_RSA_PARAMS,
+ LZO_INIT_FAILED,
+ NO_COMPRESSION_ALGORITHMS,
+ NO_CIPHER_SUITES,
+ OPENPGP_GETKEY_FAILED,
+ PK_SIG_VERIFY_FAILED,
+ ILLEGAL_SRP_USERNAME,
+ SRP_PWD_PARSING_ERROR,
+ NO_TEMPORARY_DH_PARAMS,
+ // For certificate and key stuff
+
+ ASN1_ELEMENT_NOT_FOUND,
+ ASN1_IDENTIFIER_NOT_FOUND,
+ ASN1_DER_ERROR,
+ ASN1_VALUE_NOT_FOUND,
+ ASN1_GENERIC_ERROR,
+ ASN1_VALUE_NOT_VALID,
+ ASN1_TAG_ERROR,
+ ASN1_TAG_IMPLICIT,
+ ASN1_TYPE_ANY_ERROR,
+ ASN1_SYNTAX_ERROR,
+ ASN1_DER_OVERFLOW,
+ OPENPGP_UID_REVOKED,
+ CERTIFICATE_ERROR,
+ CERTIFICATE_KEY_MISMATCH,
+ UNSUPPORTED_CERTIFICATE_TYPE, // GNUTLS_A_UNSUPPORTED_CERTIFICATE
+ X509_UNKNOWN_SAN,
+ OPENPGP_FINGERPRINT_UNSUPPORTED,
+ X509_UNSUPPORTED_ATTRIBUTE,
+ UNKNOWN_HASH_ALGORITHM,
+ UNKNOWN_PKCS_CONTENT_TYPE,
+ UNKNOWN_PKCS_BAG_TYPE,
+ INVALID_PASSWORD,
+ MAC_VERIFY_FAILED, // for PKCS #12 MAC
+ CONSTRAINT_ERROR,
+ WARNING_IA_IPHF_RECEIVED,
+ WARNING_IA_FPHF_RECEIVED,
+ IA_VERIFY_FAILED,
+ UNKNOWN_ALGORITHM,
+ BASE64_ENCODING_ERROR,
+ INCOMPATIBLE_CRYPTO_LIBRARY,
+ INCOMPATIBLE_LIBTASN1_LIBRARY,
+ OPENPGP_KEYRING_ERROR,
+ X509_UNSUPPORTED_OID,
+ RANDOM_FAILED,
+ BASE64_UNEXPECTED_HEADER_ERROR,
+ OPENPGP_SUBKEY_ERROR,
+ CRYPTO_ALREADY_REGISTERED,
+ HANDSHAKE_TOO_LARGE,
+ UNIMPLEMENTED_FEATURE,
+ APPLICATION_ERROR_MAX, // -65000
+ APPLICATION_ERROR_MIN; // -65500
+
+ [CCode (cname = "gnutls_error_is_fatal")]
+ public bool is_fatal();
+
+ [CCode (cname = "gnutls_perror")]
+ public void print();
+
+ [CCode (cname = "gnutls_strerror")]
+ public unowned string to_string();
+ }
+
+ public void throw_if_error(int err_int) throws GLib.Error {
+ ErrorCode error = (ErrorCode)err_int;
+ if (error != ErrorCode.SUCCESS) {
+ throw new GLib.Error(-1, error, "%s%s", error.to_string(), error.is_fatal() ? " fatal" : "");
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/ice/vapi/metadata/Nice-0.1.metadata b/plugins/ice/vapi/metadata/Nice-0.1.metadata
new file mode 100644
index 00000000..7fcf046a
--- /dev/null
+++ b/plugins/ice/vapi/metadata/Nice-0.1.metadata
@@ -0,0 +1,11 @@
+Nice cheader_filename="nice.h"
+Address.to_string.dst type="char[]"
+Agent.new_reliable#constructor name="create_reliable"
+Agent.attach_recv skip=false
+Agent.send.buf type="uint8[]" array_length_idx=2
+AgentRecvFunc.buf type="uint8[]" array_length_idx=3
+PseudoTcpCallbacks#record skip
+PseudoTcpSocket#class skip
+
+# Not yet supported by vapigen
+# Candidate copy_function="nice_candidate_copy" free_function="nice_candidate_free" type_id=""
diff --git a/plugins/ice/vapi/nice.vapi b/plugins/ice/vapi/nice.vapi
new file mode 100644
index 00000000..540e2b4e
--- /dev/null
+++ b/plugins/ice/vapi/nice.vapi
@@ -0,0 +1,386 @@
+/* nice.vapi generated by vapigen, do not modify. */
+
+[CCode (cprefix = "Nice", gir_namespace = "Nice", gir_version = "0.1", lower_case_cprefix = "nice_")]
+namespace Nice {
+ [CCode (cheader_filename = "nice.h", type_id = "nice_agent_get_type ()")]
+ public class Agent : GLib.Object {
+ [CCode (has_construct_function = false)]
+ public Agent (GLib.MainContext ctx, Nice.Compatibility compat);
+ public bool add_local_address (Nice.Address addr);
+ public uint add_stream (uint n_components);
+ public bool attach_recv (uint stream_id, uint component_id, GLib.MainContext ctx, Nice.AgentRecvFunc func);
+ [Version (since = "0.1.16")]
+ public async void close_async ();
+ [CCode (cname = "nice_agent_new_reliable", has_construct_function = false)]
+ [Version (since = "0.0.11")]
+ public Agent.create_reliable (GLib.MainContext ctx, Nice.Compatibility compat);
+ [Version (since = "0.1.6")]
+ public bool forget_relays (uint stream_id, uint component_id);
+ [CCode (has_construct_function = false)]
+ [Version (since = "0.1.15")]
+ public Agent.full (GLib.MainContext ctx, Nice.Compatibility compat, Nice.AgentOption flags);
+ public bool gather_candidates (uint stream_id);
+ [Version (since = "0.1.4")]
+ public string generate_local_candidate_sdp (Nice.Candidate candidate);
+ [Version (since = "0.1.4")]
+ public string generate_local_sdp ();
+ [Version (since = "0.1.4")]
+ public string generate_local_stream_sdp (uint stream_id, bool include_non_ice);
+ [Version (since = "0.1.8")]
+ public Nice.ComponentState get_component_state (uint stream_id, uint component_id);
+ public Nice.Candidate get_default_local_candidate (uint stream_id, uint component_id);
+ [Version (since = "0.1.5")]
+ public GLib.IOStream get_io_stream (uint stream_id, uint component_id);
+ public GLib.SList<Nice.Candidate> get_local_candidates (uint stream_id, uint component_id);
+ public bool get_local_credentials (uint stream_id, out string ufrag, out string pwd);
+ public GLib.SList<Nice.Candidate> get_remote_candidates (uint stream_id, uint component_id);
+ public bool get_selected_pair (uint stream_id, uint component_id, Nice.Candidate local, Nice.Candidate remote);
+ [Version (since = "0.1.5")]
+ public GLib.Socket? get_selected_socket (uint stream_id, uint component_id);
+ [Version (since = "0.1.4")]
+ public unowned string get_stream_name (uint stream_id);
+ [Version (since = "0.1.4")]
+ public Nice.Candidate parse_remote_candidate_sdp (uint stream_id, string sdp);
+ [Version (since = "0.1.4")]
+ public int parse_remote_sdp (string sdp);
+ [Version (since = "0.1.4")]
+ public GLib.SList<Nice.Candidate> parse_remote_stream_sdp (uint stream_id, string sdp, string ufrag, string pwd);
+ [Version (since = "0.1.16")]
+ public bool peer_candidate_gathering_done (uint stream_id);
+ [Version (since = "0.1.5")]
+ public ssize_t recv (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error;
+ [Version (since = "0.1.5")]
+ public int recv_messages (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error;
+ [Version (since = "0.1.5")]
+ public int recv_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error;
+ [Version (since = "0.1.5")]
+ public ssize_t recv_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error;
+ public void remove_stream (uint stream_id);
+ public bool restart ();
+ [Version (since = "0.1.6")]
+ public bool restart_stream (uint stream_id);
+ public int send (uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 2.5, array_length_type = "guint", type = "const gchar*")] uint8[] buf);
+ [Version (since = "0.1.5")]
+ public int send_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] Nice.OutputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error;
+ public bool set_local_credentials (uint stream_id, string ufrag, string pwd);
+ public void set_port_range (uint stream_id, uint component_id, uint min_port, uint max_port);
+ public bool set_relay_info (uint stream_id, uint component_id, string server_ip, uint server_port, string username, string password, Nice.RelayType type);
+ public int set_remote_candidates (uint stream_id, uint component_id, GLib.SList<Nice.Candidate> candidates);
+ public bool set_remote_credentials (uint stream_id, string ufrag, string pwd);
+ public bool set_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation);
+ public bool set_selected_remote_candidate (uint stream_id, uint component_id, Nice.Candidate candidate);
+ [Version (since = "0.0.10")]
+ public void set_software (string software);
+ [Version (since = "0.1.4")]
+ public bool set_stream_name (uint stream_id, string name);
+ [Version (since = "0.0.9")]
+ public void set_stream_tos (uint stream_id, int tos);
+ [NoAccessorMethod]
+ [Version (since = "0.1.8")]
+ public bool bytestream_tcp { get; }
+ [NoAccessorMethod]
+ public uint compatibility { get; construct; }
+ [NoAccessorMethod]
+ public bool controlling_mode { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.14")]
+ public bool force_relay { get; set; }
+ [NoAccessorMethod]
+ public bool full_mode { get; construct; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.8")]
+ public bool ice_tcp { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.16")]
+ public bool ice_trickle { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.8")]
+ public bool ice_udp { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.8")]
+ public bool keepalive_conncheck { get; set; }
+ [NoAccessorMethod]
+ public void* main_context { get; construct; }
+ [NoAccessorMethod]
+ public uint max_connectivity_checks { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.4")]
+ public string proxy_ip { owned get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.4")]
+ public string proxy_password { owned get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.4")]
+ public uint proxy_port { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.4")]
+ public uint proxy_type { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.4")]
+ public string proxy_username { owned get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.11")]
+ public bool reliable { get; construct; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.15")]
+ public uint stun_initial_timeout { get; set construct; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.15")]
+ public uint stun_max_retransmissions { get; set construct; }
+ [NoAccessorMethod]
+ public uint stun_pacing_timer { get; set construct; }
+ [NoAccessorMethod]
+ [Version (since = "0.1.15")]
+ public uint stun_reliable_timeout { get; set construct; }
+ [NoAccessorMethod]
+ public string stun_server { owned get; set; }
+ [NoAccessorMethod]
+ public uint stun_server_port { get; set; }
+ [NoAccessorMethod]
+ public bool support_renomination { get; set; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.7")]
+ public bool upnp { get; set construct; }
+ [NoAccessorMethod]
+ [Version (since = "0.0.7")]
+ public uint upnp_timeout { get; set construct; }
+ public signal void candidate_gathering_done (uint stream_id);
+ public signal void component_state_changed (uint stream_id, uint component_id, uint state);
+ public signal void initial_binding_request_received (uint stream_id);
+ [Version (deprecated = true, deprecated_since = "0.1.8")]
+ public signal void new_candidate (uint stream_id, uint component_id, string foundation);
+ [Version (since = "0.1.8")]
+ public signal void new_candidate_full (Nice.Candidate candidate);
+ [Version (deprecated = true, deprecated_since = "0.1.8")]
+ public signal void new_remote_candidate (uint stream_id, uint component_id, string foundation);
+ [Version (since = "0.1.8")]
+ public signal void new_remote_candidate_full (Nice.Candidate candidate);
+ [Version (deprecated = true, deprecated_since = "0.1.8")]
+ public signal void new_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation);
+ [Version (since = "0.1.8")]
+ public signal void new_selected_pair_full (uint stream_id, uint component_id, Nice.Candidate lcandidate, Nice.Candidate rcandidate);
+ [Version (since = "0.0.11")]
+ public signal void reliable_transport_writable (uint stream_id, uint component_id);
+ [Version (since = "0.1.5")]
+ public signal void streams_removed ([CCode (array_length = false, array_null_terminated = true)] uint[] stream_ids);
+ }
+ [CCode (cheader_filename = "nice.h", copy_function = "nice_candidate_copy", free_function = "nice_candidate_free")]
+ [Compact]
+ public class Candidate {
+ public Nice.Address addr;
+ public Nice.Address base_addr;
+ public uint component_id;
+ [CCode (array_length = false)]
+ public weak char foundation[33];
+ public weak string password;
+ public uint32 priority;
+ public void* sockptr;
+ public uint stream_id;
+ public Nice.CandidateTransport transport;
+ public Nice.TurnServer turn;
+ public Nice.CandidateType type;
+ public weak string username;
+ [CCode (has_construct_function = false)]
+ public Candidate (Nice.CandidateType type);
+ public Nice.Candidate copy ();
+ [Version (since = "0.1.15")]
+ public bool equal_target (Nice.Candidate candidate2);
+ public void free ();
+ }
+ [CCode (cheader_filename = "nice.h", has_type_id = false)]
+ public struct Address {
+ [CCode (cname = "s.addr")]
+ public void* s_addr;
+ [CCode (cname = "s.ip4")]
+ public void* s_ip4;
+ [CCode (cname = "s.ip6")]
+ public void* s_ip6;
+ public void copy_to_sockaddr (void* sin);
+ public bool equal (Nice.Address b);
+ [Version (since = "0.1.8")]
+ public bool equal_no_port (Nice.Address b);
+ public void free ();
+ public uint get_port ();
+ public void init ();
+ public int ip_version ();
+ public bool is_private ();
+ public bool is_valid ();
+ public void set_from_sockaddr (void* sin);
+ public bool set_from_string (string str);
+ public void set_ipv4 (uint32 addr_ipv4);
+ public void set_ipv6 (uint8 addr_ipv6);
+ public void set_port (uint port);
+ public void to_string ([CCode (array_length = false, type = "gchar*")] char[] dst);
+ }
+ [CCode (cheader_filename = "nice.h", has_type_id = false)]
+ [Version (since = "0.1.5")]
+ public struct InputMessage {
+ [CCode (array_length_cname = "n_buffers")]
+ public weak GLib.InputVector[] buffers;
+ public int n_buffers;
+ public Nice.Address from;
+ public size_t length;
+ }
+ [CCode (cheader_filename = "nice.h", has_type_id = false)]
+ [Version (since = "0.1.5")]
+ public struct OutputMessage {
+ [CCode (array_length_cname = "n_buffers")]
+ public weak GLib.OutputVector[] buffers;
+ public int n_buffers;
+ }
+ [CCode (cheader_filename = "nice.h", cname = "TurnServer", has_type_id = false)]
+ public struct TurnServer {
+ public int ref_count;
+ public Nice.Address server;
+ public weak string username;
+ public weak string password;
+ public Nice.RelayType type;
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_AGENT_OPTION_", has_type_id = false)]
+ [Flags]
+ [Version (since = "0.1.15")]
+ public enum AgentOption {
+ REGULAR_NOMINATION,
+ RELIABLE,
+ LITE_MODE,
+ ICE_TRICKLE,
+ SUPPORT_RENOMINATION
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TRANSPORT_", has_type_id = false)]
+ public enum CandidateTransport {
+ UDP,
+ TCP_ACTIVE,
+ TCP_PASSIVE,
+ TCP_SO
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TYPE_", has_type_id = false)]
+ public enum CandidateType {
+ HOST,
+ SERVER_REFLEXIVE,
+ PEER_REFLEXIVE,
+ RELAYED
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPATIBILITY_", has_type_id = false)]
+ public enum Compatibility {
+ RFC5245,
+ DRAFT19,
+ GOOGLE,
+ MSN,
+ WLM2009,
+ OC2007,
+ OC2007R2,
+ LAST
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_STATE_", has_type_id = false)]
+ public enum ComponentState {
+ DISCONNECTED,
+ GATHERING,
+ CONNECTING,
+ CONNECTED,
+ READY,
+ FAILED,
+ LAST;
+ [Version (since = "0.1.6")]
+ public unowned string to_string ();
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_TYPE_", has_type_id = false)]
+ public enum ComponentType {
+ RTP,
+ RTCP
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_NOMINATION_MODE_", has_type_id = false)]
+ [Version (since = "0.1.15")]
+ public enum NominationMode {
+ REGULAR,
+ AGGRESSIVE
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_PROXY_TYPE_", has_type_id = false)]
+ [Version (since = "0.0.4")]
+ public enum ProxyType {
+ NONE,
+ SOCKS5,
+ HTTP,
+ LAST
+ }
+ [CCode (cheader_filename = "nice.h", cname = "PseudoTcpDebugLevel", cprefix = "PSEUDO_TCP_DEBUG_", has_type_id = false)]
+ [Version (since = "0.0.11")]
+ public enum PseudoTcpDebugLevel {
+ NONE,
+ NORMAL,
+ VERBOSE
+ }
+ [CCode (cheader_filename = "nice.h", cname = "PseudoTcpShutdown", cprefix = "PSEUDO_TCP_SHUTDOWN_", has_type_id = false)]
+ [Version (since = "0.1.8")]
+ public enum PseudoTcpShutdown {
+ RD,
+ WR,
+ RDWR
+ }
+ [CCode (cheader_filename = "nice.h", cname = "PseudoTcpState", cprefix = "PSEUDO_TCP_", has_type_id = false)]
+ [Version (since = "0.0.11")]
+ public enum PseudoTcpState {
+ LISTEN,
+ SYN_SENT,
+ SYN_RECEIVED,
+ ESTABLISHED,
+ CLOSED,
+ FIN_WAIT_1,
+ FIN_WAIT_2,
+ CLOSING,
+ TIME_WAIT,
+ CLOSE_WAIT,
+ LAST_ACK
+ }
+ [CCode (cheader_filename = "nice.h", cname = "PseudoTcpWriteResult", cprefix = "WR_", has_type_id = false)]
+ [Version (since = "0.0.11")]
+ public enum PseudoTcpWriteResult {
+ SUCCESS,
+ TOO_LARGE,
+ FAIL
+ }
+ [CCode (cheader_filename = "nice.h", cprefix = "NICE_RELAY_TYPE_TURN_", has_type_id = false)]
+ public enum RelayType {
+ UDP,
+ TCP,
+ TLS
+ }
+ [CCode (cheader_filename = "nice.h", instance_pos = 4.9)]
+ public delegate void AgentRecvFunc (Nice.Agent agent, uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 3.5, array_length_type = "guint", type = "gchar*")] uint8[] buf);
+ [CCode (cheader_filename = "nice.h", cname = "NICE_AGENT_MAX_REMOTE_CANDIDATES")]
+ public const int AGENT_MAX_REMOTE_CANDIDATES;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_ACTIVE")]
+ public const int CANDIDATE_DIRECTION_MS_PREF_ACTIVE;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_PASSIVE")]
+ public const int CANDIDATE_DIRECTION_MS_PREF_PASSIVE;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_MAX_FOUNDATION")]
+ public const int CANDIDATE_MAX_FOUNDATION;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_TCP")]
+ public const int CANDIDATE_TRANSPORT_MS_PREF_TCP;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP")]
+ public const int CANDIDATE_TRANSPORT_MS_PREF_UDP;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_HOST")]
+ public const int CANDIDATE_TYPE_PREF_HOST;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED")]
+ public const int CANDIDATE_TYPE_PREF_NAT_ASSISTED;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE")]
+ public const int CANDIDATE_TYPE_PREF_PEER_REFLEXIVE;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED")]
+ public const int CANDIDATE_TYPE_PREF_RELAYED;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP")]
+ public const int CANDIDATE_TYPE_PREF_RELAYED_UDP;
+ [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE")]
+ public const int CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE;
+ [CCode (cheader_filename = "nice.h")]
+ public static void debug_disable (bool with_stun);
+ [CCode (cheader_filename = "nice.h")]
+ public static void debug_enable (bool with_stun);
+ [CCode (cheader_filename = "nice.h")]
+ public static string? interfaces_get_ip_for_interface (string interface_name);
+ [CCode (cheader_filename = "nice.h")]
+ public static GLib.List<string> interfaces_get_local_interfaces ();
+ [CCode (cheader_filename = "nice.h")]
+ public static GLib.List<string> interfaces_get_local_ips (bool include_loopback);
+ [CCode (cheader_filename = "nice.h", cname = "pseudo_tcp_set_debug_level")]
+ [Version (since = "0.0.11")]
+ public static void pseudo_tcp_set_debug_level (Nice.PseudoTcpDebugLevel level);
+}