diff --git a/pgrx-pg-sys/src/lib.rs b/pgrx-pg-sys/src/lib.rs index 5b9d9db2b..2526554ee 100644 --- a/pgrx-pg-sys/src/lib.rs +++ b/pgrx-pg-sys/src/lib.rs @@ -20,6 +20,8 @@ mod cstr; mod include; mod node; mod port; + +pub mod libpq; pub mod submodules; #[cfg(feature = "cshim")] diff --git a/pgrx-pg-sys/src/libpq.rs b/pgrx-pg-sys/src/libpq.rs new file mode 100644 index 000000000..9a1a5b292 --- /dev/null +++ b/pgrx-pg-sys/src/libpq.rs @@ -0,0 +1,202 @@ +/** Manually-constructed bindings to libpq + +Because pgrx is for extensions which run in the Postgres server, it rarely needs access to libpq. +However, some server-side extensions need to interact with the reality that clients exist. +Unfortunately, doing that means acknowledging that clients need authentication and authorization, +areas of concern that are far beyond what pgrx wants to involve itself with or be responsible for. + +We define some types and signatures here to allow a minimal amount of usage of items from libpq, +while largely rejecting the notion that we should involve ourselves in security-laden concerns. + +*/ + +pub mod be { + + unsafe extern "C" { + pub static mut MyProcPort: *mut Port; + } + + /// #define SCRAM_MAX_KEY_LEN PG_SHA256_DIGEST_LENGTH + /// #define PG_SHA256_DIGEST_LENGTH 32 + #[cfg(feature = "pg18")] + const SCRAM_MAX_KEY_LEN: usize = 32; + + /// Port for Postgres 13..=16 + #[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15", feature = "pg16"))] + pub struct Port { + pub sock: crate::pgsocket, + pub noblock: bool, + pub proto: crate::ProtocolVersion, + pub laddr: crate::SockAddr, + pub raddr: crate::SockAddr, + pub remote_host: *mut core::ffi::c_char, + pub remote_hostname: *mut core::ffi::c_char, + pub remote_hostname_resolv: core::ffi::c_int, + pub remote_hostname_errcode: core::ffi::c_int, + pub remote_port: *mut core::ffi::c_char, + pub canAcceptConnections: core::ffi::c_uint, + pub database_name: *mut core::ffi::c_char, + pub user_name: *mut core::ffi::c_char, + pub cmdline_options: *mut core::ffi::c_char, + pub guc_options: *mut crate::List, + pub application_name: *mut core::ffi::c_char, + + // The remainder is for completeness, so Rust sees Port's layout as correctly as possible. + // Ideally we would use `extern type` so the remainder of this was seen as of unknown size. + // An alternative is to simply treat them as private fields, so we do. + + // This should be `*mut crate::HbaLine` if we ever bind that + hba: *mut core::ffi::c_void, + + #[cfg(any(feature = "pg14", feature = "pg15"))] + authn_id: *const core::ffi::c_char, + + default_keepalives_idle: core::ffi::c_int, + default_keepalives_interval: core::ffi::c_int, + default_keepalives_count: core::ffi::c_int, + default_tcp_user_timeout: core::ffi::c_int, + keepalives_idle: core::ffi::c_int, + keepalives_interval: core::ffi::c_int, + keepalives_count: core::ffi::c_int, + tcp_user_timeout: core::ffi::c_int, + + // as if ENABLE_GSS == false && ENABLE_SSPI == false + gss: *mut core::ffi::c_void, + + ssl_in_use: bool, + peer_cn: *mut core::ffi::c_char, + #[cfg(any(feature = "pg14", feature = "pg15", feature = "pg16"))] + peer_dn: *mut core::ffi::c_char, + peer_cert_valid: bool, + } + + /// Port for Postgres 17 + #[cfg(feature = "pg17")] + #[repr(C)] + pub struct Port { + pub sock: crate::pgsocket, + pub noblock: bool, + pub proto: crate::ProtocolVersion, + pub laddr: crate::SockAddr, + pub raddr: crate::SockAddr, + pub remote_host: *mut core::ffi::c_char, + pub remote_hostname: *mut core::ffi::c_char, + pub remote_hostname_resolv: core::ffi::c_int, + pub remote_hostname_errcode: core::ffi::c_int, + pub remote_port: *mut core::ffi::c_char, + pub canAcceptConnections: core::ffi::c_uint, + pub database_name: *mut core::ffi::c_char, + pub user_name: *mut core::ffi::c_char, + pub cmdline_options: *mut core::ffi::c_char, + pub guc_options: *mut crate::List, + pub application_name: *mut core::ffi::c_char, + + // The remainder is for completeness, so Rust sees Port's layout as correctly as possible. + // Ideally we would use `extern type` so the remainder of this was seen as of unknown size. + // An alternative is to simply treat them as private fields, so we do. + + // This should be `*mut crate::HbaLine` if we ever bind that + hba: *mut core::ffi::c_void, + + default_keepalives_idle: core::ffi::c_int, + default_keepalives_interval: core::ffi::c_int, + default_keepalives_count: core::ffi::c_int, + default_tcp_user_timeout: core::ffi::c_int, + keepalives_idle: core::ffi::c_int, + keepalives_interval: core::ffi::c_int, + keepalives_count: core::ffi::c_int, + tcp_user_timeout: core::ffi::c_int, + + // as if ENABLE_GSS == false && ENABLE_SSPI == false + gss: *mut core::ffi::c_void, + + ssl_in_use: bool, + peer_cn: *mut core::ffi::c_char, + peer_cert_valid: bool, + + alpn_used: bool, + + // NOTE: 5 fields remain on PG17, but two are `#ifdef USE_OPENSSL` in Postgres 17, + // which is complicated to correctly compile due to needing to implement `cfg(accessible)` + #[cfg(false)] + ssl: *mut core::ffi::c_void, + #[cfg(false)] + peer: *mut core::ffi::c_void, + + #[deprecated( + since = "0.17.0", + note = "may be incorrect on Postgres 17 depending on build `#define`s" + )] + raw_buf: *mut core::ffi::c_char, + #[deprecated( + since = "0.17.0", + note = "may be incorrect to access on Postgres 17 depending on build `#define`s" + )] + raw_buf_consumed: isize, + #[deprecated( + since = "0.17.0", + note = "may be incorrect to access on Postgres 17 depending on build `#define`s" + )] + raw_buf_remaining: isize, + } + + /// Port for Postgres 18.. + #[cfg(feature = "pg18")] + #[repr(C)] + pub struct Port { + pub sock: crate::pgsocket, + pub noblock: bool, + pub proto: crate::ProtocolVersion, + pub laddr: crate::SockAddr, + pub raddr: crate::SockAddr, + pub remote_host: *mut core::ffi::c_char, + pub remote_hostname: *mut core::ffi::c_char, + pub remote_hostname_resolv: core::ffi::c_int, + pub remote_hostname_errcode: core::ffi::c_int, + pub remote_port: *mut core::ffi::c_char, + pub local_host: [core::ffi::c_char; 64], + pub database_name: *mut core::ffi::c_char, + pub user_name: *mut core::ffi::c_char, + pub cmdline_options: *mut core::ffi::c_char, + pub guc_options: *mut crate::List, + pub application_name: *mut core::ffi::c_char, + + // The remainder is for completeness, so Rust sees Port's layout as correctly as possible. + // Ideally we would use `extern type` so the remainder of this was seen as of unknown size. + // An alternative is to simply treat them as private fields, so we do. + + // This should be `*mut crate::HbaLine` if we ever bind that + hba: *mut core::ffi::c_void, + + default_keepalives_idle: core::ffi::c_int, + default_keepalives_interval: core::ffi::c_int, + default_keepalives_count: core::ffi::c_int, + default_tcp_user_timeout: core::ffi::c_int, + keepalives_idle: core::ffi::c_int, + keepalives_interval: core::ffi::c_int, + keepalives_count: core::ffi::c_int, + tcp_user_timeout: core::ffi::c_int, + + scram_ClientKey: [u8; SCRAM_MAX_KEY_LEN], + scram_ServerKey: [u8; SCRAM_MAX_KEY_LEN], + has_scram_keys: bool, + + // as if ENABLE_GSS == false && ENABLE_SSPI == false + gss: *mut core::ffi::c_void, + + ssl_in_use: bool, + peer_cn: *mut core::ffi::c_char, + peer_cert_valid: bool, + + alpn_used: bool, + last_read_was_eof: bool, + + // as if USE_OPENSSL == false + ssl: *mut core::ffi::c_void, + peer: *mut core::ffi::c_void, + + raw_buf: *mut core::ffi::c_char, + raw_buf_consumed: isize, + raw_buf_remaining: isize, + } +}