diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 3c0491a7b..02c461fc2 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -3747,6 +3747,8 @@ g_tls_connection_get_require_close_notify GTlsRehandshakeMode g_tls_connection_set_rehandshake_mode g_tls_connection_get_rehandshake_mode +g_tls_connection_set_advertised_protocols +g_tls_connection_get_negotiated_protocol g_tls_connection_set_use_system_certdb g_tls_connection_get_use_system_certdb g_tls_connection_get_database @@ -3938,6 +3940,8 @@ g_dtls_connection_set_require_close_notify g_dtls_connection_get_require_close_notify g_dtls_connection_set_rehandshake_mode g_dtls_connection_get_rehandshake_mode +g_dtls_connection_set_advertised_protocols +g_dtls_connection_get_negotiated_protocol g_dtls_connection_get_database g_dtls_connection_set_database g_dtls_connection_get_interaction diff --git a/gio/gdtlsconnection.c b/gio/gdtlsconnection.c index 4177fb4aa..254537198 100644 --- a/gio/gdtlsconnection.c +++ b/gio/gdtlsconnection.c @@ -232,6 +232,37 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface) 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GDtlsConnection:advertised-protocols: + * + * The list of application-layer protocols that the connection + * advertises that it is willing to speak. See + * g_dtls_connection_set_advertised_protocols(). + * + * Since: 2.60 + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("advertised-protocols", + P_("Advertised Protocols"), + P_("Application-layer protocols available on this connection"), + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * GDtlsConnection:negotiated-protocol: + * + * The application-layer protocol negotiated during the TLS + * handshake. See g_dtls_connection_get_negotiated_protocol(). + * + * Since: 2.60 + */ + g_object_interface_install_property (iface, + g_param_spec_string ("negotiated-protocol", + P_("Negotiated Protocol"), + P_("Application-layer protocol negotiated for this connection"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GDtlsConnection::accept-certificate: @@ -989,3 +1020,63 @@ g_dtls_connection_emit_accept_certificate (GDtlsConnection *conn, peer_cert, errors, &accept); return accept; } + +/** + * g_dtls_connection_set_advertised_protocols: + * @conn: a #GDtlsConnection + * @protocols: (array zero-terminated=1) (nullable): a %NULL-terminated + * array of ALPN protocol names (eg, "http/1.1", "h2"), or %NULL + * + * Sets the list of application-layer protocols to advertise that the + * caller is willing to speak on this connection. The + * Application-Layer Protocol Negotiation (ALPN) extension will be + * used to negotiate a compatible protocol with the peer; use + * g_dtls_connection_get_negotiated_protocol() to find the negotiated + * protocol after the handshake. Specifying %NULL for the the value + * of @protocols will disable ALPN negotiation. + * + * See [IANA TLS ALPN Protocol IDs](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids) + * for a list of registered protocol IDs. + * + * Since: 2.60 + */ +void +g_dtls_connection_set_advertised_protocols (GDtlsConnection *conn, + const gchar * const *protocols) +{ + GDtlsConnectionInterface *iface; + + iface = G_DTLS_CONNECTION_GET_INTERFACE (conn); + if (iface->set_advertised_protocols == NULL) + return; + + return iface->set_advertised_protocols (conn, protocols); +} + +/** + * g_dtls_connection_get_negotiated_protocol: + * @conn: a #GDtlsConnection + * + * Gets the name of the application-layer protocol negotiated during + * the handshake. + * + * If the peer did not use the ALPN extension, or did not advertise a + * protocol that matched one of @conn's protocols, or the TLS backend + * does not support ALPN, then this will be %NULL. See + * g_dtls_connection_set_advertised_protocols(). + * + * Returns: (nullable): the negotiated protocol, or %NULL + * + * Since: 2.60 + */ +const gchar * +g_dtls_connection_get_negotiated_protocol (GDtlsConnection *conn) +{ + GDtlsConnectionInterface *iface; + + iface = G_DTLS_CONNECTION_GET_INTERFACE (conn); + if (iface->set_advertised_protocols == NULL) + return NULL; + + return iface->get_negotiated_protocol (conn); +} diff --git a/gio/gdtlsconnection.h b/gio/gdtlsconnection.h index 3cf6cb31c..364be935e 100644 --- a/gio/gdtlsconnection.h +++ b/gio/gdtlsconnection.h @@ -45,6 +45,8 @@ typedef struct _GDtlsConnectionInterface GDtlsConnectionInterface; * @shutdown: Shut down one or both directions of the connection. * @shutdown_async: Start an asynchronous shutdown operation. * @shutdown_finish: Finish an asynchronous shutdown operation. + * @set_advertised_protocols: Set APLN protocol list + * @get_negotiated_protocol: Retrieve ALPN-negotiated protocol * * Virtual method table for a #GDtlsConnection implementation. * @@ -89,6 +91,10 @@ struct _GDtlsConnectionInterface gboolean (*shutdown_finish) (GDtlsConnection *conn, GAsyncResult *result, GError **error); + + void (*set_advertised_protocols) (GDtlsConnection *conn, + const gchar * const *protocols); + const gchar *(*get_negotiated_protocol) (GDtlsConnection *conn); }; GLIB_AVAILABLE_IN_2_48 @@ -186,6 +192,13 @@ GLIB_AVAILABLE_IN_2_48 gboolean g_dtls_connection_emit_accept_certificate (GDtlsConnection *conn, GTlsCertificate *peer_cert, GTlsCertificateFlags errors); +GLIB_AVAILABLE_IN_2_60 +void g_dtls_connection_set_advertised_protocols (GDtlsConnection *conn, + const gchar * const *protocols); + +GLIB_AVAILABLE_IN_2_60 +const gchar * g_dtls_connection_get_negotiated_protocol (GDtlsConnection *conn); + G_END_DECLS #endif /* __G_DTLS_CONNECTION_H__ */ diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c index 26d1881c3..a1e98c0c9 100644 --- a/gio/gtlsconnection.c +++ b/gio/gtlsconnection.c @@ -54,7 +54,12 @@ * Since: 2.28 */ -G_DEFINE_ABSTRACT_TYPE (GTlsConnection, g_tls_connection, G_TYPE_IO_STREAM) +struct _GTlsConnectionPrivate +{ + gchar *negotiated_protocol; +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GTlsConnection, g_tls_connection, G_TYPE_IO_STREAM) static void g_tls_connection_get_property (GObject *object, guint prop_id, @@ -64,6 +69,7 @@ static void g_tls_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void g_tls_connection_finalize (GObject *object); enum { ACCEPT_CERTIFICATE, @@ -83,7 +89,9 @@ enum { PROP_INTERACTION, PROP_CERTIFICATE, PROP_PEER_CERTIFICATE, - PROP_PEER_CERTIFICATE_ERRORS + PROP_PEER_CERTIFICATE_ERRORS, + PROP_ADVERTISED_PROTOCOLS, + PROP_NEGOTIATED_PROTOCOL, }; static void @@ -93,6 +101,7 @@ g_tls_connection_class_init (GTlsConnectionClass *klass) gobject_class->get_property = g_tls_connection_get_property; gobject_class->set_property = g_tls_connection_set_property; + gobject_class->finalize = g_tls_connection_finalize; /** * GTlsConnection:base-io-stream: @@ -251,6 +260,37 @@ g_tls_connection_class_init (GTlsConnectionClass *klass) 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:advertised-protocols: + * + * The list of application-layer protocols that the connection + * advertises that it is willing to speak. See + * g_tls_connection_set_advertised_protocols(). + * + * Since: 2.60 + */ + g_object_class_install_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, + g_param_spec_boxed ("advertised-protocols", + P_("Advertised Protocols"), + P_("Application-layer protocols available on this connection"), + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:negotiated-protocol: + * + * The application-layer protocol negotiated during the TLS + * handshake. See g_tls_connection_get_negotiated_protocol(). + * + * Since: 2.60 + */ + g_object_class_install_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, + g_param_spec_string ("negotiated-protocol", + P_("Negotiated Protocol"), + P_("Application-layer protocol negotiated for this connection"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /** * GTlsConnection::accept-certificate: @@ -334,6 +374,17 @@ g_tls_connection_set_property (GObject *object, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } +static void +g_tls_connection_finalize (GObject *object) +{ + GTlsConnection *conn = G_TLS_CONNECTION(object); + GTlsConnectionPrivate *priv = g_tls_connection_get_instance_private (conn); + + g_clear_pointer (&priv->negotiated_protocol, g_free); + + G_OBJECT_CLASS (g_tls_connection_parent_class)->finalize (object); +} + /** * g_tls_connection_set_use_system_certdb: * @conn: a #GTlsConnection @@ -742,6 +793,82 @@ g_tls_connection_get_rehandshake_mode (GTlsConnection *conn) return mode; } +/** + * g_tls_connection_set_advertised_protocols: + * @conn: a #GTlsConnection + * @protocols: (array zero-terminated=1) (nullable): a %NULL-terminated + * array of ALPN protocol names (eg, "http/1.1", "h2"), or %NULL + * + * Sets the list of application-layer protocols to advertise that the + * caller is willing to speak on this connection. The + * Application-Layer Protocol Negotiation (ALPN) extension will be + * used to negotiate a compatible protocol with the peer; use + * g_tls_connection_get_negotiated_protocol() to find the negotiated + * protocol after the handshake. Specifying %NULL for the the value + * of @protocols will disable ALPN negotiation. + * + * See [IANA TLS ALPN Protocol IDs](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids) + * for a list of registered protocol IDs. + * + * Since: 2.60 + */ +void +g_tls_connection_set_advertised_protocols (GTlsConnection *conn, + const gchar * const *protocols) +{ + g_return_if_fail (G_IS_TLS_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), + "advertised-protocols", protocols, + NULL); +} + +/** + * g_tls_connection_get_negotiated_protocol: + * @conn: a #GTlsConnection + * + * Gets the name of the application-layer protocol negotiated during + * the handshake. + * + * If the peer did not use the ALPN extension, or did not advertise a + * protocol that matched one of @conn's protocols, or the TLS backend + * does not support ALPN, then this will be %NULL. See + * g_tls_connection_set_advertised_protocols(). + * + * Returns: (nullable): the negotiated protocol, or %NULL + * + * Since: 2.60 + */ +const gchar * +g_tls_connection_get_negotiated_protocol (GTlsConnection *conn) +{ + GTlsConnectionPrivate *priv; + gchar *protocol; + + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), NULL); + + g_object_get (G_OBJECT (conn), + "negotiated-protocol", &protocol, + NULL); + + /* + * Cache the property internally so we can return a `const` pointer + * to the caller. + */ + priv = g_tls_connection_get_instance_private (conn); + if (g_strcmp0 (priv->negotiated_protocol, protocol) != 0) + { + g_free (priv->negotiated_protocol); + priv->negotiated_protocol = protocol; + } + else + { + g_free (protocol); + } + + return priv->negotiated_protocol; +} + /** * g_tls_connection_handshake: * @conn: a #GTlsConnection diff --git a/gio/gtlsconnection.h b/gio/gtlsconnection.h index 0cfcb5910..39ec3fa02 100644 --- a/gio/gtlsconnection.h +++ b/gio/gtlsconnection.h @@ -115,6 +115,13 @@ void g_tls_connection_set_rehandshake_mode (GTlsConnecti GLIB_DEPRECATED_IN_2_60 GTlsRehandshakeMode g_tls_connection_get_rehandshake_mode (GTlsConnection *conn); +GLIB_AVAILABLE_IN_2_60 +void g_tls_connection_set_advertised_protocols (GTlsConnection *conn, + const gchar * const *protocols); + +GLIB_AVAILABLE_IN_2_60 +const gchar * g_tls_connection_get_negotiated_protocol (GTlsConnection *conn); + GLIB_AVAILABLE_IN_ALL gboolean g_tls_connection_handshake (GTlsConnection *conn, GCancellable *cancellable,