diff options
author | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 17:20:47 +0300 |
---|---|---|
committer | molotkov-and <molotkov-and@ydb.tech> | 2023-08-18 19:42:07 +0300 |
commit | 73215359bc33e76f5b94d1832a377072bf245cfc (patch) | |
tree | 9cb8ad61d8c3cd107353d42951560ff3cf1b966d /contrib/libs/openldap | |
parent | 1cbfd34a55732f7b1d407986b45e40853f01f2c2 (diff) | |
download | ydb-73215359bc33e76f5b94d1832a377072bf245cfc.tar.gz |
KIKIMR-18220: Enrich token with groups from LDAP
Add ldap functions wrapper and separate in different files for compatibility with different OS.
Add user groups fetching from ldap server.
Limitations:
- Fixed 'memberOf' attribute
- No tests to check how filter for search created
- Fetched groups are returned in event as is.
Diffstat (limited to 'contrib/libs/openldap')
165 files changed, 76209 insertions, 0 deletions
diff --git a/contrib/libs/openldap/CHANGES b/contrib/libs/openldap/CHANGES new file mode 100644 index 0000000000..a7e969973a --- /dev/null +++ b/contrib/libs/openldap/CHANGES @@ -0,0 +1,223 @@ +OpenLDAP 2.6 Change Log + +OpenLDAP 2.6.6 Release (2023/07/31) + Fixed slapd cn=config incorrect handling of paused (ITS#10045) + Fixed slapd-meta to account for MOD ops being optional (ITS#10067) + Fixed slapd-asyncmeta to account for MOD ops being optional (ITS#10067) + +OpenLDAP 2.6.5 Release (2023/07/10) + Fixed libldap handling of TCP KEEPALIVE options (ITS#10015) + Fixed libldap with async connections (ITS#10023) + Fixed libldap openssl TLSv1.3 cipher suite handling (ITS#10035) + Fixed slapd callback handling with overlays that do extended operations (ITS#9990) + Fixed slapd conversion of pcache configurations (ITS#10031) + Fixed slapd cn=config modification handling with abandon (ITS#10045) + Fixed slapd-mdb online indexer termination and cleanup (ITS#9993) + Fixed slapd-mdb online indexer when interrupted (ITS#10047) + Fixed slapd-monitor connection cleanup (ITS#10042) + Fixed slapo-constraint handling of push replication (ITS#9953) + Fixed slapo-dynlist filter evaluation efficiency (ITS#10041) + Fixed slapo-pcache handling of invalid schema (ITS#10032) + Fixed slapo-ppolicy handling of push replication (ITS#9953) + Fixed slapo-ppolicy handling of pwdMinDelay (ITS#10028) + Fixed slapo-syncprov abandon handling (ITS#10016) + Fixed slapo-translucent handling of invalid schema (ITS#10032) + Fixed slapo-unique handling of push replication (ITS#9953) + Fixed slapo-variant to improve regex handling (ITS#10048) + Build Environment + Fixed compatibility with stricter C99 compilers (ITS#10011) + Keep .pc files during make clean (ITS#9989) + Contrib + Fixed slapo-variant handling of push replication (ITS#9953) + Minor Cleanup + ITS#9855 + ITS#9995 + ITS#9996 + ITS#9997 + ITS#9998 + ITS#9999 + ITS#10000 + ITS#10003 + ITS#10004 + ITS#10033 + ITS#10037 + ITS#10039 + ITS#10046 + ITS#10063 + +OpenLDAP 2.6.4 Release (2023/02/08) + Fixed client tools to remove 'h' and 'p' options (ITS#9917,ITS#8618) + Fixed ldapsearch memory leak with paged results (ITS#9860) + Fixed libldap ldif_open_urlto check for failure (ITS#9904) + Fixed libldap ldap_url_parsehosts check for failure (ITS#9904) + Fixed liblunicode UTF8bvnormalize buffer size (ITS#9955) + Fixed lloadd memory leaks (ITS#9907) + Fixed lloadd shutdown code to protect memory correctly (ITS#9913) + Fixed lloadd race in epoch.c (ITS#9947) + Fixed lloadd potential deadlock with cn=monitor (ITS#9951) + Fixed lloadd to keep listener base around when not active (ITS#9984) + Fixed lloadd object reclamation sequencing (ITS#9983) + Fixed slapd memory leak with olcAuthIDRewrite (ITS#6035) + Fixed slapd free of redundant cmdline option (ITS#9912) + Fixed slapd transactions extended operations cleanup after write (ITS#9892) + Fixed slapd deadlock with replicated cn=config (ITS#9930,ITS#8102) + Fixed slapd connection close logic (ITS#9991) + Fixed slapd bconfig locking of cn=config entries (ITS#9045) + Fixed slapd-mdb max number of index databases to 256 (ITS#9895) + Fixed slapd-mdb to always release entries from ADD operations (ITS#9942) + Fixed slapd-mdb to fully init empty DN in tool_entry_get (ITS#9940) + Fixed slapd-monitor memory leaks with lloadd (ITS#9906) + Fixed slapd-monitor to free remembered cookies (ITS#9339) + Fixed slapo-accesslog reqStart ordering matching rule (ITS#9880) + Fixed slapo-deref memory leak (ITS#9924) + Fixed slapo-dynlist to ignore irrelevant objectClasses (ITS#9897) + Fixed slapo-dynlist to avoid unnecessary searches (ITS#9929) + Fixed slapo-dynlist to mark internal searches as such (ITS#9960) + Fixed slapo-pcache crash in consistency_check (ITS#9966) + Fixed slapo-remoteauth memory leaks (ITS#9438) + Fixed slapo-rwm memory leaks (ITS#9817) + Build Environment + Fixed ancient DOS related ifdef checks (ITS#9925) + Fixed build process to not use gmake specific features (ITS#9894) + Fixed source tree to remove symlinks (ITS#9926) + Fixed slapo-otp testdir creation (ITS#9437) + Fixed slapd-tester memory leak (ITS#9908) + Fixed usage of non-standard C syntax (ITS#9898, ITS#9899, ITS#9901) + Fixed usage of bashism (ITS#9900) + Fixed test suite portability (ITS#9931) + Documentation + Fixed ldap_bind(3) to document ber_bvfree in ldap_sasl_bind (ITS#9976) + Fixed slapo-asyncmeta(5) to clarify scheduling for target connections (ITS#9941) + Fixed slapo-dynlist(5) to clarify configuration settings (ITS#9957) + Fixed slapo-unique(5) to clarify when quoting should be used (ITS#9915) + Minor cleanup + ITS#9935 + ITS#9336 + ITS#9337 + ITS#9985 + +OpenLDAP 2.6.3 Release (2022/07/14) + Fixed libldap to check for NULL ld (ITS#9157) + Fixed libldap memory leaks (ITS#9876) + Fixed lloadd to correctly tag Notice of Disconnection (ITS#9856) + Fixed slapd kqueue support (ITS#9847) + Fixed slapd delta-sync DN leak on ADD ops (ITS#9866) + Fixed slapd replication with back-glue (ITS#9868) + Fixed slapd lastbind replication with chaining (ITS#9863) + Fixed slapd-ldap to correctly set authzid (ITS#9863) + Fixed slapd-mdb to check for stale readers on MDB_READERS_FULL (ITS#7165) + Fixed slapd-mdb indexer task with replicated config (ITS#9858) + Fixed slapo-accesslog onetime memory leak (ITS#9864) + Fixed slapo-ppolicy interaction with slapo-rwm (ITS#9871) + Fixed slapo-rwm to handle escaping special characters (ITS#9817) + Fixed slapo-syncprov memory leaks (ITS#9867) + Fixed slapo-syncprov fallback in delta-sync mode (ITS#9823) + Fixed slapo-unique to not release NULL entry (ITS#8245) + Build Environment + Added slapd-watcher -c contextDN option (ITS#9865) + Fixed librewrite declaration of calloc (ITS#9841) + Fixed parallel builds (ITS#9840) + Fixed test020 to skip back-wt (ITS#9859) + Fixed slapd-watcher SID handling with single URI (ITS#9850) + Fixed test043 with workaround for ITS#9878 + Contrib + Added slapo-emptyds contrib module (ITS#8882) + Added slapo-ciboolean contrib module (ITS#9855) + Fixed slapo-autogroup backwards compat (ITS#9020) + Update ppm module to the 2.2 release (ITS#9846) + Documentation + Fixed ldap_get_option(3) to clarify ldap_get/set_option restrictions (ITS#9824) + Fixed slapd-ldap(5),slapd-meta(5) missing bold tag on authz parameter (ITS#9872) + +OpenLDAP 2.6.2 Release (2022/05/04) + Added libldap support for OpenSSL 3.0 (ITS#9436) + Added slapd support for OpenSSL 3.0 (ITS#9436) + Fixed ldapdelete to prune LDAP subentries (ITS#9737) + Fixed libldap to drop connection when non-LDAP data is received (ITS#9803) + Fixed libldap to allow newlines at end of included file (ITS#9811) + Fixed slapd slaptest conversion of olcLastBind (ITS#9808) + Fixed slapd to correctly init global_host earlier (ITS#9787) + Fixed slapd bconfig locking for cn=config replication (ITS#9584) + Fixed slapd usage of thread local counters (ITS#9789) + Fixed slapd to clear runqueue task correctly (ITS#9785) + Fixed slapd idletimeout handling (ITS#9820) + Fixed slapd syncrepl handling of new sessions (ITS#9584) + Fixed slapd to clear connections on bind (ITS#9799) + Fixed slapd to correctly advance connections index (ITS#9831) + Fixed slapd syncrepl ODSEE replication of unknown attr (ITS#9801) + Fixed slapd-asyncmeta memory leak in keepalive setting (ITS#9802) + Fixed slapd-ldap memory leak in keepalive setting (ITS#9802) + Fixed slapd-meta SEGV on config rewrite (ITS#9802) + Fixed slapd-meta ordering on config rewrite (ITS#9802) + Fixed slapd-meta memory leak in keepalive setting (ITS#9802) + Fixed slapd-monitor SEGV on shutdown (ITS#9809) + Fixed slapd-monitor crash when hitting sizelimit (ITS#9832) + Fixed slapd-sql to properly escape filter value (ITS#9815) + Added slapo-autoca support for OpenSSL 3.0 (ITS#9436) + Added slapo-otp support for OpenSSL 3.0 (ITS#9436) + Fixed slapo-dynlist dynamic group regression (ITS#9825) + Fixed slapo-pcache SEGV on shutdown (ITS#9809) + Fixed slapo-ppolicy operation handling to be consistent (ITS#9794) + Fixed slapo-translucent to correctly duplicate substring filters (ITS#9818) + Build Environment + Add ability to override default compile time paths (ITS#9675) + Fix compilation with certain versions of gcc (ITS#9790) + Fix compilation with openssl exclusions (ITS#9791) + Fix warnings from make jobserver (ITS#9788) + Contrib + Update ppm module to the 2.1 release (ITS#9814) + Documentation + admin26 Document new lloadd features (ITS#9780) + Fixed slapd.conf(5)/slapd-config(5) syncrepl sizelimit/timelimit documentation (ITS#9804) + Fixed slapd-sock(5) to clarify "sockresps result" behavior (ITS#8255) + +OpenLDAP 2.6.1 Release (2022/01/20) + Fixed libldap to init client socket port (ITS#9743) + Fixed libldap with referrals (ITS#9781) + Added slapd config keyword for logfile format (ITS#9745) + Fixed slapd to allow objectClass edits with no net change (ITS#9772) + Fixed slapd configtable population (ITS#9576) + Fixed slapd to only set loglevel in server mode (ITS#9715) + Fixed slapd logfile-rotate use of uninitialized variable (ITS#9730) + Fixed slapd passwd scheme handling with slapd.conf (ITS#9750) + Fixed slapd postread support for modrdn (ITS#7080) + Fixed slapd syncrepl recreation of deleted entries (ITS#9282) + Fixed slapd syncrepl replication with ODSEE (ITS#9707) + Fixed slapd syncrepl to properly replicate glue entries (ITS#9647) + Fixed slapd syncrepl to reject REFRESH for precise resync (ITS#9742) + Fixed slapd syncrepl to avoid busy loop during refresh (ITS#9584) + Fixed slapd syncrepl when X-ORDERED is specified (ITS#9761) + Fixed slapd syncrepl to better handle out of order delete ops (ITS#9751) + Fixed slapd syncrepl to correctly close connections when config is deleted (ITS#9776) + Fixed slapd-mdb to update indices correctly on replace ops (ITS#9753) + Fixed slapd-wt to set correct flags (ITS#9760) + Fixed slapo-accesslog to fix assertion due to deprecated code (ITS#9738) + Fixed slapo-accesslog to fix inconsistently normalized minCSN (ITS#9752) + Fixed slapo-accesslog delete handling of multi-valued config attrs (ITS#9493) + Fixed slapo-autogroup to maintain values in insertion order (ITS#9766) + Fixed slapo-constraint to maintain values in insertion order (ITS#9770) + Fixed slapo-dyngroup to maintain values in insertion order (ITS#9762) + Fixed slapo-dynlist compare operation for static groups (ITS#9747) + Fixed slapo-dynlist static group filter with multiple members (ITS#9779) + Fixed slapo-ppolicy when not built modularly (ITS#9733) + Fixed slapo-refint to maintain values in insertion order (ITS#9763) + Fixed slapo-retcode to honor requested insert position (ITS#9759) + Fixed slapo-sock cn=config support (ITS#9758) + Fixed slapo-syncprov memory leak (ITS#8039) + Fixed slapo-syncprov to generate a more accurate accesslog query (ITS#9756) + Fixed slapo-syncprov to allow empty DB to host persistent syncrepl connections (ITS#9691) + Fixed slapo-syncprov to consider all deletes for sycnInfo messages (ITS#5972) + Fixed slapo-translucent to warn on invalid config (ITS#9768) + Fixed slapo-unique to warn on invalid config (ITS#9767) + Fixed slapo-valsort to maintain values in insertion order (ITS#9764) + Build Environment + Fix test022 to preserve DELAY search output (ITS#9718) + Fix slapd-watcher to allow startup when servers are down (ITS#9727) + Contrib + Fixed slapo-lastbind to work with 2.6 lastbind-precision configuration (ITS#9725) + Documentation + Fixed slapd.conf(5)/slapd-config(5) documentation on lastbind-precision (ITS#9728) + Fixed slapo-accesslog(5) to clarify logoldattr usage (ITS#9749) + +OpenLDAP 2.6.0 Release (2021/10/25) + Initial release for "general use". diff --git a/contrib/libs/openldap/CMakeLists.darwin-x86_64.txt b/contrib/libs/openldap/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..c24d53771e --- /dev/null +++ b/contrib/libs/openldap/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,108 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) +add_subdirectory(libraries) + +add_library(contrib-libs-openldap) +target_compile_options(contrib-libs-openldap PRIVATE + -DLDAPI_SOCK="/run/openldap/ldapi" + -DLDAP_LIBRARY + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-openldap PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_include_directories(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include +) +target_link_libraries(contrib-libs-openldap PUBLIC + openldap-libraries-liblber + OpenSSL::OpenSSL + contrib-libs-sasl +) +target_sources(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/abandon.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/account_usability.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/add.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/addentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/assertion.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/avl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/bind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cancel.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/charray.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/compare.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/controls.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cyrus.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dds.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/delete.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/deref.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dnssrv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/error.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/extended.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/fetch.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/filter.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/free.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getattr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getvalues.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/init.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/lbase64.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldap_sync.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldif.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldifutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/messages.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modify.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modrdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/msctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/open.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-ip.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-local.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/pagectrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/passwd.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ppolicy.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/print.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/psearchctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rdwr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/references.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/request.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/result.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rq.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sasl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/schema.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/search.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sort.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sortctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/stctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/string.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tavl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_nt.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_posix.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_pth.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_thr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/threads.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls2.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_g.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_o.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tpool.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/turn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/txn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/unbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/url.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8-conv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/util-int.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vc.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/version.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vlvctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/whoami.c +) diff --git a/contrib/libs/openldap/CMakeLists.linux-aarch64.txt b/contrib/libs/openldap/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..9eda5da8d7 --- /dev/null +++ b/contrib/libs/openldap/CMakeLists.linux-aarch64.txt @@ -0,0 +1,109 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) +add_subdirectory(libraries) + +add_library(contrib-libs-openldap) +target_compile_options(contrib-libs-openldap PRIVATE + -DLDAPI_SOCK="/run/openldap/ldapi" + -DLDAP_LIBRARY + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-openldap PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_include_directories(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include +) +target_link_libraries(contrib-libs-openldap PUBLIC + contrib-libs-linux-headers + openldap-libraries-liblber + OpenSSL::OpenSSL + contrib-libs-sasl +) +target_sources(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/abandon.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/account_usability.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/add.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/addentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/assertion.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/avl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/bind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cancel.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/charray.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/compare.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/controls.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cyrus.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dds.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/delete.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/deref.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dnssrv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/error.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/extended.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/fetch.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/filter.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/free.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getattr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getvalues.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/init.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/lbase64.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldap_sync.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldif.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldifutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/messages.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modify.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modrdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/msctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/open.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-ip.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-local.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/pagectrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/passwd.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ppolicy.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/print.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/psearchctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rdwr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/references.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/request.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/result.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rq.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sasl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/schema.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/search.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sort.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sortctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/stctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/string.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tavl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_nt.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_posix.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_pth.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_thr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/threads.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls2.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_g.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_o.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tpool.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/turn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/txn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/unbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/url.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8-conv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/util-int.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vc.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/version.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vlvctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/whoami.c +) diff --git a/contrib/libs/openldap/CMakeLists.linux-x86_64.txt b/contrib/libs/openldap/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..9eda5da8d7 --- /dev/null +++ b/contrib/libs/openldap/CMakeLists.linux-x86_64.txt @@ -0,0 +1,109 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +find_package(OpenSSL REQUIRED) +add_subdirectory(libraries) + +add_library(contrib-libs-openldap) +target_compile_options(contrib-libs-openldap PRIVATE + -DLDAPI_SOCK="/run/openldap/ldapi" + -DLDAP_LIBRARY + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(contrib-libs-openldap PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_include_directories(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/sasl/include +) +target_link_libraries(contrib-libs-openldap PUBLIC + contrib-libs-linux-headers + openldap-libraries-liblber + OpenSSL::OpenSSL + contrib-libs-sasl +) +target_sources(contrib-libs-openldap PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/abandon.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/account_usability.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/add.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/addentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/assertion.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/avl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/bind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cancel.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/charray.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/compare.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/controls.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/cyrus.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dds.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/delete.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/deref.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/dnssrv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/error.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/extended.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/fetch.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/filter.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/free.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getattr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getentry.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/getvalues.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/init.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/lbase64.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldap_sync.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldif.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ldifutil.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/messages.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modify.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/modrdn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/msctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/open.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-ip.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/os-local.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/pagectrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/passwd.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/ppolicy.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/print.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/psearchctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rdwr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/references.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/request.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/result.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/rq.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sasl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/schema.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/search.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sort.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/sortctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/stctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/string.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tavl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_nt.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_posix.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_pth.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/thr_thr.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/threads.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls2.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_g.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tls_o.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/tpool.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/turn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/txn.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/unbind.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/url.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8-conv.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/utf-8.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/util-int.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vc.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/version.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/vlvctrl.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/libldap/whoami.c +) diff --git a/contrib/libs/openldap/CMakeLists.txt b/contrib/libs/openldap/CMakeLists.txt new file mode 100644 index 0000000000..606ff46b4b --- /dev/null +++ b/contrib/libs/openldap/CMakeLists.txt @@ -0,0 +1,15 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/contrib/libs/openldap/COPYRIGHT b/contrib/libs/openldap/COPYRIGHT new file mode 100644 index 0000000000..eecd9c6880 --- /dev/null +++ b/contrib/libs/openldap/COPYRIGHT @@ -0,0 +1,64 @@ +Copyright 1998-2022 The OpenLDAP Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Individual files and/or contributed packages may be copyright by +other parties and/or subject to additional restrictions. + +This work is derived from the University of Michigan LDAP v3.3 +distribution. Information concerning this software is available +at <http://www.umich.edu/~dirsvcs/ldap/ldap.html>. + +This work also contains materials derived from public sources. + +Additional information about OpenLDAP can be obtained at +<http://www.openldap.org/>. + +--- + +Portions Copyright 1998-2013 Kurt D. Zeilenga. +Portions Copyright 1998-2006 Net Boolean Incorporated. +Portions Copyright 2001-2006 IBM Corporation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +--- + +Portions Copyright 1999-2008 Howard Y.H. Chu. +Portions Copyright 1999-2008 Symas Corporation. +Portions Copyright 1998-2003 Hallvard B. Furuseth. +Portions Copyright 2007-2011 Gavin Henry. +Portions Copyright 2007-2011 Suretec Systems Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that this notice is preserved. +The names of the copyright holders may not be used to endorse or +promote products derived from this software without their specific +prior written permission. This software is provided ``as is'' +without express or implied warranty. + +--- + +Portions Copyright (c) 1992-1996 Regents of the University of Michigan. +All rights reserved. + +Redistribution and use in source and binary forms are permitted +provided that this notice is preserved and that due credit is given +to the University of Michigan at Ann Arbor. The name of the +University may not be used to endorse or promote products derived +from this software without specific prior written permission. This +software is provided ``as is'' without express or implied warranty. + diff --git a/contrib/libs/openldap/INSTALL b/contrib/libs/openldap/INSTALL new file mode 100644 index 0000000000..9db9200371 --- /dev/null +++ b/contrib/libs/openldap/INSTALL @@ -0,0 +1,109 @@ +Making and Installing the OpenLDAP Distribution +=============================================== + +This file provides brief instructions on how to build and install +OpenLDAP on UNIX (and UNIX-like) systems. More detailed information +and instructions can be found in The OpenLDAP Administrator's Guide +(available from http://www.openldap.org/doc/). + +It is recommended that you read, or at least skim through, ALL of the +instructions in this file before attempting to build the software. + +Making and Installing the OpenLDAP Distribution +----------------------------------------------- + +1. Unpack the distribution and change directory: + + % tar xfz openldap-VERSION.tgz + % cd openldap-VERSION + + (replacing VERSION with the appropriate version string). If you + are reading this file, you probably have already done this! + +2. Type: + + % ./configure --help + + to list available configuration options. + + The configure script also looks for compiler/linker options on + the command line and in the environment. These include: + + Variable Description Example + CC C compiler gcc + CFLAGS C flags -O -g + CPPFLAGS cpp flags -I/path/include -D__FOO__=42 + LDFLAGS ld flags -L/usr/local/lib + LIBS libraries -llib + PATH command path /usr/local/bin:/usr/bin:/bin + + See doc/install/configure for generic configure documentation. + +3. Configure the build system: + + % ./configure [options] [var=value ...] + + If all goes well, the configure script will automatically detect + the appropriate settings. If the configure script fails, you + should read the config.log file that it generated to see what it + was trying to do and exactly what failed. You may need to + specify additional options and/or variables besides those listed + above to obtain desired results, depending on your operating + system. + +4. Build dependencies: + + % make depend + +5. Build the system: + + % make + + If all goes well, the system will build as configured. If not, + return to step 3 after reviewing the configuration settings. + +6. Test the standalone system: + + This step requires the standalone LDAP server, slapd(8), with MDB + support. + + % make test + + If all goes well, the system has been built as configured. If + not, return to step 2 after reviewing your configuration + settings. + +7. Install the software. You may need to become the super-user + (e.g. root) to do this (depending on where you are installing + things): + + % su root -c 'make install' + +8. That's it. Enjoy! + +See the OpenLDAP Administrator's Guide and the manual pages for the +individual applications for configuration and use information. You may +also want to edit the configuration files used by the various +components. These configuration files are located in the OpenLDAP +configuration directory (normally /usr/local/etc/openldap). + + ldap.conf client defaults + slapd.conf Standalone LDAP daemon + lload.conf LDAP Load Balancer daemon + schema/*.schema Schema Definitions + +--- +This work is part of OpenLDAP Software <http://www.openldap.org/>. + +Copyright 1998-2022 The OpenLDAP Foundation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. diff --git a/contrib/libs/openldap/LICENSE b/contrib/libs/openldap/LICENSE new file mode 100644 index 0000000000..05ad7571e4 --- /dev/null +++ b/contrib/libs/openldap/LICENSE @@ -0,0 +1,47 @@ +The OpenLDAP Public License + Version 2.8, 17 August 2003 + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions in source form must retain copyright statements + and notices, + +2. Redistributions in binary form must reproduce applicable copyright + statements and notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution, and + +3. Redistributions must contain a verbatim copy of this document. + +The OpenLDAP Foundation may revise this license from time to time. +Each revision is distinguished by a version number. You may use +this Software under terms of this license revision or under the +terms of any subsequent revision of the license. + +THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS +CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) +OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +The names of the authors and copyright holders must not be used in +advertising or otherwise to promote the sale, use or other dealing +in this Software without specific, written prior permission. Title +to copyright in this Software shall at all times remain with copyright +holders. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, +California, USA. All Rights Reserved. Permission to copy and +distribute verbatim copies of this document is granted. diff --git a/contrib/libs/openldap/README b/contrib/libs/openldap/README new file mode 100644 index 0000000000..821815e4e8 --- /dev/null +++ b/contrib/libs/openldap/README @@ -0,0 +1,92 @@ +OpenLDAP 2.6 README + For a description of what this distribution contains, see the + ANNOUNCEMENT file in this directory. For a description of + changes from previous releases, see the CHANGES file in this + directory. + + This is 2.6 release, it includes significant changes from prior + releases. + +REQUIRED SOFTWARE + Building OpenLDAP Software requires a number of software packages + to be preinstalled. Additional information regarding prerequisite + software can be found in the OpenLDAP Administrator's Guide. + + Base system (libraries and tools): + Standard C compiler (required) + Cyrus SASL 2.1.27+ (recommended) + OpenSSL 1.1.1+ (recommended) + libevent 2.1.8+ (recommended) + libargon2 or libsodium (recommended) + Reentrant POSIX REGEX software (required) + + SLAPD: + The ARGON2 password hashing module requires either libargon2 + or libsodium + LLOADD: + The LLOADD daemon or integrated slapd module requires + libevent 2.1.8 or later. + + CLIENTS/CONTRIB ware: + Depends on package. See per package README. + + +MAKING AND INSTALLING THE DISTRIBUTION + Please see the INSTALL file for basic instructions. More + detailed instructions can be found in the OpenLDAP Administrator's + Guide (see DOCUMENTATION section). + + +DOCUMENTATION + The OpenLDAP Administrator's Guide is available in the + guide.html file in the doc/guide/admin directory. The + guide and a number of other documents are available at + <http://www.openldap.org/doc/admin/guide.html>. + + The distribution also includes manual pages for most programs + and library APIs. See ldap(3) for details. + + The OpenLDAP website is available and contains the latest LDAP + news, releases announcements, pointers to other LDAP resources, + etc.. It is located at <http://www.OpenLDAP.org/>. + + +SUPPORT / FEEDBACK / PROBLEM REPORTS / DISCUSSIONS + OpenLDAP Software is user supported. If you have problems, please + review the OpenLDAP FAQ <http://www.openldap.org/faq/> and + archives of the OpenLDAP-software and OpenLDAP-bugs mailing lists + <http://www.openldap.org/lists/>. If you cannot find the answer, + please enquire on the OpenLDAP-software list. + + Issues, such as bug reports, should be reported using our + Issue Tracking System <http://www.OpenLDAP.org/its/>. Do not + use this system for software enquiries. Please direct these + to an appropriate mailing list. + + +CONTRIBUTING + See <http://www.openldap.org/devel/contributing.html> for + information regarding how to contribute code or documentation + to the OpenLDAP Project for inclusion in OpenLDAP Software. + While you are encouraged to coordinate and discuss the development + activities on the <openldap-devel@openldap.org> mailing list + prior to submission, it is noted that contributions must be + submitted using the Issue Tracking System + <http://www.openldap.org/its/> to be considered. + +--- + +This work is part of OpenLDAP Software <http://www.openldap.org/>. + +Copyright 1998-2022 The OpenLDAP Foundation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. diff --git a/contrib/libs/openldap/include/ac/assert.h b/contrib/libs/openldap/include/ac/assert.h new file mode 100644 index 0000000000..dbb22951c3 --- /dev/null +++ b/contrib/libs/openldap/include/ac/assert.h @@ -0,0 +1,57 @@ +/* Generic assert.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_ASSERT_H +#define _AC_ASSERT_H + +#undef assert + +#ifdef LDAP_DEBUG + +#if defined( HAVE_ASSERT_H ) || defined( STDC_HEADERS ) + +#undef NDEBUG +#include <assert.h> + +#else /* !(HAVE_ASSERT_H || STDC_HEADERS) */ + +#define LDAP_NEED_ASSERT 1 + +/* + * no assert()... must be a very old compiler. + * create a replacement and hope it works + */ + +LBER_F (void) ber_pvt_assert LDAP_P(( const char *file, int line, + const char *test )); + +/* Can't use LDAP_STRING(test), that'd expand to "test" */ +#if defined(__STDC__) || defined(__cplusplus) +#define assert(test) \ + ((test) ? (void)0 : ber_pvt_assert( __FILE__, __LINE__, #test ) ) +#else +#define assert(test) \ + ((test) ? (void)0 : ber_pvt_assert( __FILE__, __LINE__, "test" ) ) +#endif + +#endif /* (HAVE_ASSERT_H || STDC_HEADERS) */ + +#else /* !LDAP_DEBUG */ +/* no asserts */ +#define assert(test) ((void)0) +#endif /* LDAP_DEBUG */ + +#endif /* _AC_ASSERT_H */ diff --git a/contrib/libs/openldap/include/ac/bytes.h b/contrib/libs/openldap/include/ac/bytes.h new file mode 100644 index 0000000000..f8a6314eb2 --- /dev/null +++ b/contrib/libs/openldap/include/ac/bytes.h @@ -0,0 +1,78 @@ +/* Generic bytes.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_BYTES_H +#define _AC_BYTES_H + +/* cross compilers should define both AC_INT{2,4}_TYPE in CPPFLAGS */ + +#if !defined( AC_INT4_TYPE ) + /* use autoconf defines to provide sized typedefs */ +# if SIZEOF_LONG == 4 +# define AC_INT4_TYPE long +# elif SIZEOF_INT == 4 +# define AC_INT4_TYPE int +# elif SIZEOF_SHORT == 4 +# define AC_INT4_TYPE short +# else +# error "AC_INT4_TYPE?" +# endif +#endif + +typedef AC_INT4_TYPE ac_int4; +typedef signed AC_INT4_TYPE ac_sint4; +typedef unsigned AC_INT4_TYPE ac_uint4; + +#if !defined( AC_INT2_TYPE ) +# if SIZEOF_SHORT == 2 +# define AC_INT2_TYPE short +# elif SIZEOF_INT == 2 +# define AC_INT2_TYPE int +# elif SIZEOF_LONG == 2 +# define AC_INT2_TYPE long +# else +# error "AC_INT2_TYPE?" +# endif +#endif + +#if defined( AC_INT2_TYPE ) +typedef AC_INT2_TYPE ac_int2; +typedef signed AC_INT2_TYPE ac_sint2; +typedef unsigned AC_INT2_TYPE ac_uint2; +#endif + +#ifndef BYTE_ORDER +/* cross compilers should define BYTE_ORDER in CPPFLAGS */ + +/* + * Definitions for byte order, according to byte significance from low + * address to high. + */ +#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long */ + +/* assume autoconf's AC_C_BIGENDIAN has been ran */ +/* if it hasn't, we assume (maybe falsely) the order is LITTLE ENDIAN */ +# ifdef WORDS_BIGENDIAN +# define BYTE_ORDER BIG_ENDIAN +# else +# define BYTE_ORDER LITTLE_ENDIAN +# endif + +#endif /* BYTE_ORDER */ + +#endif /* _AC_BYTES_H */ diff --git a/contrib/libs/openldap/include/ac/ctype.h b/contrib/libs/openldap/include/ac/ctype.h new file mode 100644 index 0000000000..e385f3acbc --- /dev/null +++ b/contrib/libs/openldap/include/ac/ctype.h @@ -0,0 +1,33 @@ +/* Generic ctype.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_CTYPE_H +#define _AC_CTYPE_H + +#include <ctype.h> + +#undef TOUPPER +#undef TOLOWER + +#ifdef C_UPPER_LOWER +# define TOUPPER(c) (islower(c) ? toupper(c) : (c)) +# define TOLOWER(c) (isupper(c) ? tolower(c) : (c)) +#else +# define TOUPPER(c) toupper(c) +# define TOLOWER(c) tolower(c) +#endif + +#endif /* _AC_CTYPE_H */ diff --git a/contrib/libs/openldap/include/ac/dirent.h b/contrib/libs/openldap/include/ac/dirent.h new file mode 100644 index 0000000000..93df7b6f2a --- /dev/null +++ b/contrib/libs/openldap/include/ac/dirent.h @@ -0,0 +1,54 @@ +/* Generic dirent.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_DIRENT_H +#define _AC_DIRENT_H + +#ifdef HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#elif defined(_MSC_VER) +#include <windows.h> +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif +struct dirent { + char *d_name; +}; +typedef struct DIR { + HANDLE dir; + struct dirent data; + int first; + char buf[MAX_PATH+1]; +} DIR; +DIR *opendir(const char *name); +struct dirent *readdir(DIR *dir); +int closedir(DIR *dir); +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#endif /* _AC_DIRENT_H */ diff --git a/contrib/libs/openldap/include/ac/errno.h b/contrib/libs/openldap/include/ac/errno.h new file mode 100644 index 0000000000..8a7f32c909 --- /dev/null +++ b/contrib/libs/openldap/include/ac/errno.h @@ -0,0 +1,32 @@ +/* Generic errno.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_ERRNO_H +#define _AC_ERRNO_H + +#if defined( HAVE_ERRNO_H ) +# include <errno.h> +#elif defined( HAVE_SYS_ERRNO_H ) +# include <sys/errno.h> +#endif + +#if defined( HAVE_SYS_ERRLIST ) && defined( DECL_SYS_ERRLIST ) + /* have sys_errlist but need declaration */ + LDAP_LIBC_V(int) sys_nerr; + LDAP_LIBC_V(char) *sys_errlist[]; +#endif + +#endif /* _AC_ERRNO_H */ diff --git a/contrib/libs/openldap/include/ac/fdset.h b/contrib/libs/openldap/include/ac/fdset.h new file mode 100644 index 0000000000..620850b1fc --- /dev/null +++ b/contrib/libs/openldap/include/ac/fdset.h @@ -0,0 +1,42 @@ +/* redefine FD_SET */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * This header is to be included by portable.h to ensure + * tweaking of FD_SETSIZE is done early enough to be effective. + */ + +#ifndef _AC_FDSET_H +#define _AC_FDSET_H + +#if !defined( OPENLDAP_FD_SETSIZE ) && !defined( FD_SETSIZE ) +# define OPENLDAP_FD_SETSIZE 4096 +#endif + +#ifdef OPENLDAP_FD_SETSIZE + /* assume installer desires to enlarge fd_set */ +# ifdef HAVE_BITS_TYPES_H +# include <bits/types.h> +# endif +# ifdef __FD_SETSIZE +# undef __FD_SETSIZE +# define __FD_SETSIZE OPENLDAP_FD_SETSIZE +# else +# define FD_SETSIZE OPENLDAP_FD_SETSIZE +# endif +#endif + +#endif /* _AC_FDSET_H */ diff --git a/contrib/libs/openldap/include/ac/localize.h b/contrib/libs/openldap/include/ac/localize.h new file mode 100644 index 0000000000..9e19fe6e96 --- /dev/null +++ b/contrib/libs/openldap/include/ac/localize.h @@ -0,0 +1,44 @@ +/* localize.h (i18n/l10n) */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_LOCALIZE_H +#define _AC_LOCALIZE_H + +#ifdef LDAP_LOCALIZE + +# include <locale.h> +# include <libintl.h> + + /* enable i18n/l10n */ +# define gettext_noop(s) s +# define _(s) gettext(s) +# define N_(s) gettext_noop(s) +# define ldap_pvt_setlocale(c,l) ((void) setlocale(c, l)) +# define ldap_pvt_textdomain(d) ((void) textdomain(d)) +# define ldap_pvt_bindtextdomain(p,d) ((void) bindtextdomain(p, d)) + +#else + + /* disable i18n/l10n */ +# define _(s) s +# define N_(s) s +# define ldap_pvt_setlocale(c,l) ((void) 0) +# define ldap_pvt_textdomain(d) ((void) 0) +# define ldap_pvt_bindtextdomain(p,d) ((void) 0) + +#endif + +#endif /* _AC_LOCALIZE_H */ diff --git a/contrib/libs/openldap/include/ac/param.h b/contrib/libs/openldap/include/ac/param.h new file mode 100644 index 0000000000..a3f5d675e0 --- /dev/null +++ b/contrib/libs/openldap/include/ac/param.h @@ -0,0 +1,39 @@ +/* Generic param.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_PARAM_H +#define _AC_PARAM_H + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +/* MAXPATHLEN should come from <unistd.h> */ +#include <ac/unistd.h> + +#ifndef MAXPATHLEN +# if defined(PATH_MAX) +# define MAXPATHLEN PATH_MAX + +# elif defined(_MAX_PATH) +# define MAXPATHLEN _MAX_PATH + +# else +# define MAXPATHLEN 4096 +# endif +#endif + +#endif /* _AC_PARAM_H */ diff --git a/contrib/libs/openldap/include/ac/signal.h b/contrib/libs/openldap/include/ac/signal.h new file mode 100644 index 0000000000..1c7293bba2 --- /dev/null +++ b/contrib/libs/openldap/include/ac/signal.h @@ -0,0 +1,80 @@ +/* Generic signal.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_SIGNAL_H +#define _AC_SIGNAL_H + +#include <signal.h> + +#undef SIGNAL + +#if defined( HAVE_SIGACTION ) +#define SIGNAL lutil_sigaction +typedef void (*lutil_sig_t)(int); +LDAP_LUTIL_F(lutil_sig_t) lutil_sigaction( int sig, lutil_sig_t func ); +#define SIGNAL_REINSTALL(sig,act) (void)0 +#elif defined( HAVE_SIGSET ) +#define SIGNAL sigset +#define SIGNAL_REINSTALL sigset +#else +#define SIGNAL signal +#define SIGNAL_REINSTALL signal +#endif + +#if !defined( LDAP_SIGUSR1 ) || !defined( LDAP_SIGUSR2 ) +#undef LDAP_SIGUSR1 +#undef LDAP_SIGUSR2 + +# if defined(WINNT) || defined(_WINNT) || defined(_WIN32) +# define LDAP_SIGUSR1 SIGILL +# define LDAP_SIGUSR2 SIGTERM + +# elif !defined(HAVE_LINUX_THREADS) +# define LDAP_SIGUSR1 SIGUSR1 +# define LDAP_SIGUSR2 SIGUSR2 + +# else + /* + * Some versions of LinuxThreads unfortunately uses the only + * two signals reserved for user applications. This forces + * OpenLDAP to use other signals reserved for other uses. + */ + +# if defined( SIGSTKFLT ) +# define LDAP_SIGUSR1 SIGSTKFLT +# elif defined ( SIGSYS ) +# define LDAP_SIGUSR1 SIGSYS +# endif + +# if defined( SIGUNUSED ) +# define LDAP_SIGUSR2 SIGUNUSED +# elif defined ( SIGINFO ) +# define LDAP_SIGUSR2 SIGINFO +# elif defined ( SIGEMT ) +# define LDAP_SIGUSR2 SIGEMT +# endif +# endif +#endif + +#ifndef LDAP_SIGCHLD +#ifdef SIGCHLD +#define LDAP_SIGCHLD SIGCHLD +#elif SIGCLD +#define LDAP_SIGCHLD SIGCLD +#endif +#endif + +#endif /* _AC_SIGNAL_H */ diff --git a/contrib/libs/openldap/include/ac/socket.h b/contrib/libs/openldap/include/ac/socket.h new file mode 100644 index 0000000000..ce46c6f8b7 --- /dev/null +++ b/contrib/libs/openldap/include/ac/socket.h @@ -0,0 +1,264 @@ +/* Generic socket.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_SOCKET_H_ +#define _AC_SOCKET_H_ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_POLL_H +#include <poll.h> +#elif defined(HAVE_SYS_POLL_H) +#include <sys/poll.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> + +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#include <netinet/in.h> + +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif + +#include <netdb.h> + +#ifdef HAVE_RESOLV_H +#include <resolv.h> +#endif + +#endif /* HAVE_SYS_SOCKET_H */ + +#ifdef HAVE_WINSOCK2 +#include <winsock2.h> +#include <ws2tcpip.h> +#elif defined(HAVE_WINSOCK) +#include <winsock.h> +#endif + +#ifdef HAVE_PCNFS +#error #include <tklib.h> +#endif /* HAVE_PCNFS */ + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (0x7f000001UL) +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#undef sock_errno +#undef sock_errstr +#define sock_errno() errno +#define sock_errstr(e, b, l) AC_STRERROR_R(e, b, l) +#define sock_errset(e) ((void) (errno = (e))) + +#ifdef HAVE_WINSOCK +# define tcp_read( s, buf, len ) recv( s, buf, len, 0 ) +# define tcp_write( s, buf, len ) send( s, buf, len, 0 ) +# define ioctl( s, c, a ) ioctlsocket( (s), (c), (a) ) +# define ioctl_t u_long +# define AC_SOCKET_INVALID ((unsigned int) -1) + +# ifdef SD_BOTH +# define tcp_close( s ) (shutdown( s, SD_BOTH ), closesocket( s )) +# else +# define tcp_close( s ) closesocket( s ) +# endif + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define ETIMEDOUT WSAETIMEDOUT + +#undef sock_errno +#undef sock_errstr +#undef sock_errset +#define sock_errno() WSAGetLastError() +#define sock_errstr(e, b, l) ber_pvt_wsa_err2string(e) +#define sock_errset(e) WSASetLastError(e) + +LBER_F( char * ) ber_pvt_wsa_err2string LDAP_P((int)); + +#elif defined(MACOS) +# define tcp_close( s ) tcpclose( s ) +# define tcp_read( s, buf, len ) tcpread( s, buf, len ) +# define tcp_write( s, buf, len ) tcpwrite( s, buf, len ) + +#elif defined(HAVE_PCNFS) +# define tcp_close( s ) close( s ) +# define tcp_read( s, buf, len ) recv( s, buf, len, 0 ) +# define tcp_write( s, buf, len ) send( s, buf, len, 0 ) + +#elif defined(HAVE_NCSA) +# define tcp_close( s ) do { netclose( s ); netshut() } while(0) +# define tcp_read( s, buf, len ) nread( s, buf, len ) +# define tcp_write( s, buf, len ) netwrite( s, buf, len ) + +#elif defined(HAVE_CLOSESOCKET) +# define tcp_close( s ) closesocket( s ) + +# ifdef __BEOS__ +# define tcp_read( s, buf, len ) recv( s, buf, len, 0 ) +# define tcp_write( s, buf, len ) send( s, buf, len, 0 ) +# endif + +#else +# define tcp_read( s, buf, len) read( s, buf, len ) +# define tcp_write( s, buf, len) write( s, buf, len ) + +# ifdef SHUT_RDWR +# define tcp_close( s ) (shutdown( s, SHUT_RDWR ), close( s )) +# else +# define tcp_close( s ) close( s ) +# endif + +#ifdef HAVE_PIPE +/* + * Only use pipe() on systems where file and socket descriptors + * are interchangeable + */ +# define USE_PIPE HAVE_PIPE +#endif + +#endif /* MACOS */ + +#ifndef ioctl_t +# define ioctl_t int +#endif + +#ifndef AC_SOCKET_INVALID +# define AC_SOCKET_INVALID (-1) +#endif +#ifndef AC_SOCKET_ERROR +# define AC_SOCKET_ERROR (-1) +#endif + +#if !defined( HAVE_INET_ATON ) && !defined( inet_aton ) +# define inet_aton ldap_pvt_inet_aton +struct in_addr; +LDAP_F (int) ldap_pvt_inet_aton LDAP_P(( const char *, struct in_addr * )); +#endif + +#if defined(__WIN32) && defined(_ALPHA) +/* NT on Alpha is hosed. */ +# define AC_HTONL( l ) \ + ((((l)&0xffU)<<24) + (((l)&0xff00U)<<8) + \ + (((l)&0xff0000U)>>8) + (((l)&0xff000000U)>>24)) +# define AC_NTOHL(l) AC_HTONL(l) + +#else +# define AC_HTONL( l ) htonl( l ) +# define AC_NTOHL( l ) ntohl( l ) +#endif + +/* htons()/ntohs() may be broken much like htonl()/ntohl() */ +#define AC_HTONS( s ) htons( s ) +#define AC_NTOHS( s ) ntohs( s ) + +#ifdef LDAP_PF_LOCAL +# if !defined( AF_LOCAL ) && defined( AF_UNIX ) +# define AF_LOCAL AF_UNIX +# endif +# if !defined( PF_LOCAL ) && defined( PF_UNIX ) +# define PF_LOCAL PF_UNIX +# endif +#endif + +#ifndef INET_ADDRSTRLEN +# define INET_ADDRSTRLEN 16 +#endif +#ifndef INET6_ADDRSTRLEN +# define INET6_ADDRSTRLEN 46 +#endif + +#if defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) +# ifdef HAVE_GAI_STRERROR +# define AC_GAI_STRERROR(x) (gai_strerror((x))) +# else +# define AC_GAI_STRERROR(x) (ldap_pvt_gai_strerror((x))) + LDAP_F (char *) ldap_pvt_gai_strerror( int ); +# endif +#endif + +#if defined(LDAP_PF_LOCAL) && \ + !defined(HAVE_GETPEEREID) && \ + !defined(HAVE_GETPEERUCRED) && \ + !defined(SO_PEERCRED) && !defined(LOCAL_PEERCRED) && \ + defined(HAVE_SENDMSG) && (defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTSLEN) || \ + defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)) +# define LDAP_PF_LOCAL_SENDMSG 1 +#endif + +#ifdef HAVE_GETPEEREID +#define LUTIL_GETPEEREID( s, uid, gid, bv ) getpeereid( s, uid, gid ) +#elif defined(LDAP_PF_LOCAL_SENDMSG) +struct berval; +LDAP_LUTIL_F( int ) lutil_getpeereid( int s, uid_t *, gid_t *, struct berval *bv ); +#define LUTIL_GETPEEREID( s, uid, gid, bv ) lutil_getpeereid( s, uid, gid, bv ) +#else +LDAP_LUTIL_F( int ) lutil_getpeereid( int s, uid_t *, gid_t * ); +#define LUTIL_GETPEEREID( s, uid, gid, bv ) lutil_getpeereid( s, uid, gid ) +#endif + +typedef union Sockaddr { + struct sockaddr sa_addr; + struct sockaddr_in sa_in_addr; +#ifdef LDAP_PF_INET6 + struct sockaddr_storage sa_storage; + struct sockaddr_in6 sa_in6_addr; +#endif +#ifdef LDAP_PF_LOCAL + struct sockaddr_un sa_un_addr; +#endif +} Sockaddr; + +/* DNS RFC defines max host name as 255. New systems seem to use 1024 */ +#ifndef NI_MAXHOST +#define NI_MAXHOST 256 +#endif + +#ifdef HAVE_POLL +# ifndef INFTIM +# define INFTIM (-1) +# endif +#undef POLL_OTHER +#define POLL_OTHER (POLLERR|POLLHUP) +#undef POLL_READ +#define POLL_READ (POLLIN|POLLPRI|POLL_OTHER) +#undef POLL_WRITE +#define POLL_WRITE (POLLOUT|POLL_OTHER) +#endif + +#endif /* _AC_SOCKET_H_ */ diff --git a/contrib/libs/openldap/include/ac/stdarg.h b/contrib/libs/openldap/include/ac/stdarg.h new file mode 100644 index 0000000000..7ba29737e3 --- /dev/null +++ b/contrib/libs/openldap/include/ac/stdarg.h @@ -0,0 +1,28 @@ +/* Generic stdarg.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_STDARG_H +#define _AC_STDARG_H 1 + +/* require STDC variable argument support */ + +#include <stdarg.h> + +#ifndef HAVE_STDARG +# define HAVE_STDARG 1 +#endif + +#endif /* _AC_STDARG_H */ diff --git a/contrib/libs/openldap/include/ac/stdlib.h b/contrib/libs/openldap/include/ac/stdlib.h new file mode 100644 index 0000000000..d9a95136ac --- /dev/null +++ b/contrib/libs/openldap/include/ac/stdlib.h @@ -0,0 +1,48 @@ +/* Generic stdlib.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_STDLIB_H +#define _AC_STDLIB_H + +#if defined( HAVE_CSRIMALLOC ) +#include <stdio.h> +#define MALLOC_TRACE +#error #include <libmalloc.h> +#endif + +#include <stdlib.h> + +/* Ignore malloc.h if we have STDC_HEADERS */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +# include <malloc.h> +#endif + +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# define EXIT_FAILURE 1 +#endif + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#if defined(LINE_MAX) +# define AC_LINE_MAX LINE_MAX +#else +# define AC_LINE_MAX 2048 /* POSIX MIN */ +#endif + +#endif /* _AC_STDLIB_H */ diff --git a/contrib/libs/openldap/include/ac/string.h b/contrib/libs/openldap/include/ac/string.h new file mode 100644 index 0000000000..c4c135410e --- /dev/null +++ b/contrib/libs/openldap/include/ac/string.h @@ -0,0 +1,118 @@ +/* Generic string.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_STRING_H +#define _AC_STRING_H + +#ifdef STDC_HEADERS +# include <string.h> + +#else +# ifdef HAVE_STRING_H +# include <string.h> +# endif +# if defined(HAVE_STRINGS_H) && (!defined(HAVE_STRING_H) || defined(BOTH_STRINGS_H)) +# include <strings.h> +# endif + +# ifdef HAVE_MEMORY_H +# include <memory.h> +# endif + +# ifndef HAVE_STRRCHR +# undef strchr +# define strchr index +# undef strrchr +# define strrchr rindex +# endif + +# ifndef HAVE_MEMCPY +# undef memcpy +# define memcpy(d, s, n) ((void) bcopy ((s), (d), (n))) +# undef memmove +# define memmove(d, s, n) ((void) bcopy ((s), (d), (n))) +# endif +#endif + +/* use ldap_pvt_strtok instead of strtok or strtok_r! */ +LDAP_F(char *) ldap_pvt_strtok LDAP_P(( char *str, + const char *delim, char **pos )); + +#ifndef HAVE_STRDUP + /* strdup() is missing, declare our own version */ +# undef strdup +# define strdup(s) ber_strdup(s) +#elif !defined(_WIN32) + /* some systems fail to declare strdup */ + /* Windows does not require this declaration */ + LDAP_LIBC_F(char *) (strdup)(); +#endif + +/* + * some systems fail to declare strcasecmp() and strncasecmp() + * we need them declared so we can obtain pointers to them + */ + +/* we don't want these declared for Windows or Mingw */ +#ifndef _WIN32 +int (strcasecmp)(); +int (strncasecmp)(); +#endif + +#ifndef SAFEMEMCPY +# if defined( HAVE_MEMMOVE ) +# define SAFEMEMCPY( d, s, n ) memmove((d), (s), (n)) +# elif defined( HAVE_BCOPY ) +# define SAFEMEMCPY( d, s, n ) bcopy((s), (d), (n)) +# else + /* nothing left but memcpy() */ +# define SAFEMEMCPY( d, s, n ) memcpy((d), (s), (n)) +# endif +#endif + +#define AC_MEMCPY( d, s, n ) (SAFEMEMCPY((d),(s),(n))) +#define AC_FMEMCPY( d, s, n ) do { \ + if((n) == 1) *((char*)(d)) = *((char*)(s)); \ + else AC_MEMCPY( (d), (s), (n) ); \ + } while(0) + +#ifdef NEED_MEMCMP_REPLACEMENT + int (lutil_memcmp)(const void *b1, const void *b2, size_t len); +#define memcmp lutil_memcmp +#endif + +void *(lutil_memrchr)(const void *b, int c, size_t n); +/* GNU extension (glibc >= 2.1.91), only declared when defined(_GNU_SOURCE) */ +#if defined(HAVE_MEMRCHR) && defined(_GNU_SOURCE) +#define lutil_memrchr(b, c, n) memrchr(b, c, n) +#endif /* ! HAVE_MEMRCHR */ + +#define STRLENOF(s) (sizeof(s)-1) + +#if defined( HAVE_NONPOSIX_STRERROR_R ) +# define AC_STRERROR_R(e,b,l) (strerror_r((e), (b), (l))) +#elif defined( HAVE_STRERROR_R ) +# define AC_STRERROR_R(e,b,l) (strerror_r((e), (b), (l)) == 0 ? (b) : "Unknown error") +#elif defined( HAVE_SYS_ERRLIST ) +# define AC_STRERROR_R(e,b,l) ((e) > -1 && (e) < sys_nerr \ + ? sys_errlist[(e)] : "Unknown error" ) +#elif defined( HAVE_STRERROR ) +# define AC_STRERROR_R(e,b,l) (strerror(e)) /* NOTE: may be NULL */ +#else +# define AC_STRERROR_R(e,b,l) ("Unknown error") +#endif + +#endif /* _AC_STRING_H */ diff --git a/contrib/libs/openldap/include/ac/syslog.h b/contrib/libs/openldap/include/ac/syslog.h new file mode 100644 index 0000000000..90028ae704 --- /dev/null +++ b/contrib/libs/openldap/include/ac/syslog.h @@ -0,0 +1,38 @@ +/* Generic syslog.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_SYSLOG_H_ +#define _AC_SYSLOG_H_ + +#if defined( HAVE_SYSLOG_H ) +#include <syslog.h> +#elif defined ( HAVE_SYS_SYSLOG_H ) +#include <sys/syslog.h> +#endif + +#if defined( LOG_NDELAY ) && defined( LOG_NOWAIT ) +# define OPENLOG_OPTIONS ( LOG_PID | LOG_NDELAY | LOG_NOWAIT ) +#elif defined( LOG_NDELAY ) +# define OPENLOG_OPTIONS ( LOG_PID | LOG_NDELAY ) +#elif defined( LOG_NOWAIT ) +# define OPENLOG_OPTIONS ( LOG_PID | LOG_NOWAIT ) +#elif defined( LOG_PID ) +# define OPENLOG_OPTIONS ( LOG_PID ) +#else +# define OPENLOG_OPTIONS ( 0 ) +#endif + +#endif /* _AC_SYSLOG_H_ */ diff --git a/contrib/libs/openldap/include/ac/time.h b/contrib/libs/openldap/include/ac/time.h new file mode 100644 index 0000000000..b715db04e8 --- /dev/null +++ b/contrib/libs/openldap/include/ac/time.h @@ -0,0 +1,35 @@ +/* Generic time.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_TIME_H +#define _AC_TIME_H + +#if defined(HAVE_SYS_TIME_H) +# include <sys/time.h> +# ifdef HAVE_SYS_TIMEB_H +# include <sys/timeb.h> +# endif +#endif +# include <time.h> + +#if defined(_WIN32) && !defined(HAVE_CLOCK_GETTIME) + struct timespec { + time_t tv_sec; + int tv_nsec; + }; +#endif + +#endif /* _AC_TIME_H */ diff --git a/contrib/libs/openldap/include/ac/unistd.h b/contrib/libs/openldap/include/ac/unistd.h new file mode 100644 index 0000000000..64e8a8301b --- /dev/null +++ b/contrib/libs/openldap/include/ac/unistd.h @@ -0,0 +1,72 @@ +/* Generic unistd.h */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _AC_UNISTD_H +#define _AC_UNISTD_H + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifdef HAVE_PROCESS_H +# include <process.h> +#endif + +/* note: callers of crypt(3) should include <ac/crypt.h> */ + +#if defined(HAVE_GETPASSPHRASE) +LDAP_LIBC_F(char*)(getpassphrase)(); + +#else +#define getpassphrase(p) lutil_getpass(p) +LDAP_LUTIL_F(char*)(lutil_getpass) LDAP_P((const char *getpass)); +#endif + +/* getopt() defines may be in separate include file */ +#ifdef HAVE_GETOPT_H +# include <getopt.h> + +#elif !defined(HAVE_GETOPT) + /* no getopt, assume we need getopt-compat.h */ +# error #include <getopt-compat.h> + +#else + /* assume we need to declare these externs */ + LDAP_LIBC_V (char *) optarg; + LDAP_LIBC_V (int) optind, opterr, optopt; +#endif + +/* use lutil file locking */ +#define ldap_lockf(x) lutil_lockf(x) +#define ldap_unlockf(x) lutil_unlockf(x) +#include <lutil_lockf.h> + +/* + * Windows: although sleep() will be resolved by both MSVC and Mingw GCC + * linkers, the function is not declared in header files. This is + * because Windows' version of the function is called Sleep(), and it + * is declared in windows.h + */ + +#ifdef _WIN32 +#define sleep(x) Sleep((x) * 1000) +#endif + +#endif /* _AC_UNISTD_H */ diff --git a/contrib/libs/openldap/include/lber.h b/contrib/libs/openldap/include/lber.h new file mode 100644 index 0000000000..d474eb0635 --- /dev/null +++ b/contrib/libs/openldap/include/lber.h @@ -0,0 +1,691 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LBER_H +#define _LBER_H + +#include <lber_types.h> +#include <string.h> + +LDAP_BEGIN_DECL + +/* + * ber_tag_t represents the identifier octets at the beginning of BER + * elements. OpenLDAP treats them as mere big-endian unsigned integers. + * + * Actually the BER identifier octets look like this: + * + * Bits of 1st octet: + * ______ + * 8 7 | CLASS + * 0 0 = UNIVERSAL + * 0 1 = APPLICATION + * 1 0 = CONTEXT-SPECIFIC + * 1 1 = PRIVATE + * _____ + * | 6 | DATA-TYPE + * 0 = PRIMITIVE + * 1 = CONSTRUCTED + * ___________ + * | 5 ... 1 | TAG-NUMBER + * + * For ASN.1 tag numbers >= 0x1F, TAG-NUMBER above is 0x1F and the next + * BER octets contain the actual ASN.1 tag number: Big-endian, base + * 128, 8.bit = 1 in all but the last octet, minimum number of octets. + */ + +/* BER classes and mask (in 1st identifier octet) */ +#define LBER_CLASS_UNIVERSAL ((ber_tag_t) 0x00U) +#define LBER_CLASS_APPLICATION ((ber_tag_t) 0x40U) +#define LBER_CLASS_CONTEXT ((ber_tag_t) 0x80U) +#define LBER_CLASS_PRIVATE ((ber_tag_t) 0xc0U) +#define LBER_CLASS_MASK ((ber_tag_t) 0xc0U) + +/* BER encoding type and mask (in 1st identifier octet) */ +#define LBER_PRIMITIVE ((ber_tag_t) 0x00U) +#define LBER_CONSTRUCTED ((ber_tag_t) 0x20U) +#define LBER_ENCODING_MASK ((ber_tag_t) 0x20U) + +#define LBER_BIG_TAG_MASK ((ber_tag_t) 0x1fU) +#define LBER_MORE_TAG_MASK ((ber_tag_t) 0x80U) + +/* + * LBER_ERROR and LBER_DEFAULT are values that can never appear + * as valid BER tags, so it is safe to use them to report errors. + * Valid tags have (tag & (ber_tag_t) 0xFF) != 0xFF. + */ +#define LBER_ERROR ((ber_tag_t) -1) +#define LBER_DEFAULT ((ber_tag_t) -1) + +/* general BER types we know about */ +#define LBER_BOOLEAN ((ber_tag_t) 0x01UL) +#define LBER_INTEGER ((ber_tag_t) 0x02UL) +#define LBER_BITSTRING ((ber_tag_t) 0x03UL) +#define LBER_OCTETSTRING ((ber_tag_t) 0x04UL) +#define LBER_NULL ((ber_tag_t) 0x05UL) +#define LBER_ENUMERATED ((ber_tag_t) 0x0aUL) +#define LBER_SEQUENCE ((ber_tag_t) 0x30UL) /* constructed */ +#define LBER_SET ((ber_tag_t) 0x31UL) /* constructed */ + +/* LBER BerElement options */ +#define LBER_USE_DER 0x01 + +/* get/set options for BerElement */ +#define LBER_OPT_BER_OPTIONS 0x01 +#define LBER_OPT_BER_DEBUG 0x02 +#define LBER_OPT_BER_REMAINING_BYTES 0x03 +#define LBER_OPT_BER_TOTAL_BYTES 0x04 +#define LBER_OPT_BER_BYTES_TO_WRITE 0x05 +#define LBER_OPT_BER_MEMCTX 0x06 + +#define LBER_OPT_DEBUG_LEVEL LBER_OPT_BER_DEBUG +#define LBER_OPT_REMAINING_BYTES LBER_OPT_BER_REMAINING_BYTES +#define LBER_OPT_TOTAL_BYTES LBER_OPT_BER_TOTAL_BYTES +#define LBER_OPT_BYTES_TO_WRITE LBER_OPT_BER_BYTES_TO_WRITE + +#define LBER_OPT_LOG_PRINT_FN 0x8001 +#define LBER_OPT_MEMORY_FNS 0x8002 +#define LBER_OPT_ERROR_FN 0x8003 +#define LBER_OPT_LOG_PRINT_FILE 0x8004 + +/* get/set Memory Debug options */ +#define LBER_OPT_MEMORY_INUSE 0x8005 /* for memory debugging */ +#define LBER_OPT_LOG_PROC 0x8006 /* for external logging function */ + +typedef int* (*BER_ERRNO_FN) LDAP_P(( void )); + +typedef void (*BER_LOG_PRINT_FN) LDAP_P(( LDAP_CONST char *buf )); + +typedef void* (BER_MEMALLOC_FN) LDAP_P(( ber_len_t size, void *ctx )); +typedef void* (BER_MEMCALLOC_FN) LDAP_P(( ber_len_t n, ber_len_t size, void *ctx )); +typedef void* (BER_MEMREALLOC_FN) LDAP_P(( void *p, ber_len_t size, void *ctx )); +typedef void (BER_MEMFREE_FN) LDAP_P(( void *p, void *ctx )); + +typedef struct lber_memory_fns { + BER_MEMALLOC_FN *bmf_malloc; + BER_MEMCALLOC_FN *bmf_calloc; + BER_MEMREALLOC_FN *bmf_realloc; + BER_MEMFREE_FN *bmf_free; +} BerMemoryFunctions; + +/* LBER Sockbuf_IO options */ +#define LBER_SB_OPT_GET_FD 1 +#define LBER_SB_OPT_SET_FD 2 +#define LBER_SB_OPT_HAS_IO 3 +#define LBER_SB_OPT_SET_NONBLOCK 4 +#define LBER_SB_OPT_GET_SSL 7 +#define LBER_SB_OPT_DATA_READY 8 +#define LBER_SB_OPT_SET_READAHEAD 9 +#define LBER_SB_OPT_DRAIN 10 +#define LBER_SB_OPT_NEEDS_READ 11 +#define LBER_SB_OPT_NEEDS_WRITE 12 +#define LBER_SB_OPT_GET_MAX_INCOMING 13 +#define LBER_SB_OPT_SET_MAX_INCOMING 14 + +/* Only meaningful ifdef LDAP_PF_LOCAL_SENDMSG */ +#define LBER_SB_OPT_UNGET_BUF 15 + +/* Largest option used by the library */ +#define LBER_SB_OPT_OPT_MAX 15 + +/* LBER IO operations stacking levels */ +#define LBER_SBIOD_LEVEL_PROVIDER 10 +#define LBER_SBIOD_LEVEL_TRANSPORT 20 +#define LBER_SBIOD_LEVEL_APPLICATION 30 + +/* get/set options for Sockbuf */ +#define LBER_OPT_SOCKBUF_DESC 0x1000 +#define LBER_OPT_SOCKBUF_OPTIONS 0x1001 +#define LBER_OPT_SOCKBUF_DEBUG 0x1002 + +/* on/off values */ +LBER_V( char ) ber_pvt_opt_on; +#define LBER_OPT_ON ((void *) &ber_pvt_opt_on) +#define LBER_OPT_OFF ((void *) 0) + +#define LBER_OPT_SUCCESS (0) +#define LBER_OPT_ERROR (-1) + +typedef struct berelement BerElement; +typedef struct sockbuf Sockbuf; + +typedef struct sockbuf_io Sockbuf_IO; + +/* Structure for LBER IO operation descriptor */ +typedef struct sockbuf_io_desc { + int sbiod_level; + Sockbuf *sbiod_sb; + Sockbuf_IO *sbiod_io; + void *sbiod_pvt; + struct sockbuf_io_desc *sbiod_next; +} Sockbuf_IO_Desc; + +/* Structure for LBER IO operation functions */ +struct sockbuf_io { + int (*sbi_setup)( Sockbuf_IO_Desc *sbiod, void *arg ); + int (*sbi_remove)( Sockbuf_IO_Desc *sbiod ); + int (*sbi_ctrl)( Sockbuf_IO_Desc *sbiod, int opt, void *arg); + + ber_slen_t (*sbi_read)( Sockbuf_IO_Desc *sbiod, void *buf, + ber_len_t len ); + ber_slen_t (*sbi_write)( Sockbuf_IO_Desc *sbiod, void *buf, + ber_len_t len ); + + int (*sbi_close)( Sockbuf_IO_Desc *sbiod ); +}; + +/* Helper macros for LBER IO functions */ +#define LBER_SBIOD_READ_NEXT( sbiod, buf, len ) \ + ( (sbiod)->sbiod_next->sbiod_io->sbi_read( (sbiod)->sbiod_next, \ + buf, len ) ) +#define LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ) \ + ( (sbiod)->sbiod_next->sbiod_io->sbi_write( (sbiod)->sbiod_next, \ + buf, len ) ) +#define LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ) \ + ( (sbiod)->sbiod_next ? \ + ( (sbiod)->sbiod_next->sbiod_io->sbi_ctrl( \ + (sbiod)->sbiod_next, opt, arg ) ) : 0 ) + +/* structure for returning a sequence of octet strings + length */ +typedef struct berval { + ber_len_t bv_len; + char *bv_val; +} BerValue; + +typedef BerValue *BerVarray; /* To distinguish from a single bv */ + +/* this should be moved to lber-int.h */ + +/* + * in bprint.c: + */ +LBER_F( void ) +ber_error_print LDAP_P(( + LDAP_CONST char *data )); + +LBER_F( void ) +ber_bprint LDAP_P(( + LDAP_CONST char *data, ber_len_t len )); + +LBER_F( void ) +ber_dump LDAP_P(( + BerElement *ber, int inout )); + +/* + * in decode.c: + */ +typedef int (*BERDecodeCallback) LDAP_P(( + BerElement *ber, + void *data, + int mode )); + +LBER_F( ber_tag_t ) +ber_get_tag LDAP_P(( + BerElement *ber )); + +LBER_F( ber_tag_t ) +ber_skip_tag LDAP_P(( + BerElement *ber, + ber_len_t *len )); + +LBER_F( ber_tag_t ) +ber_peek_tag LDAP_P(( + BerElement *ber, + ber_len_t *len )); + +LBER_F( ber_tag_t ) +ber_skip_raw LDAP_P(( + BerElement *ber, + struct berval *bv )); + +LBER_F( ber_tag_t ) +ber_skip_element LDAP_P(( + BerElement *ber, + struct berval *bv )); + +LBER_F( ber_tag_t ) +ber_peek_element LDAP_P(( + LDAP_CONST BerElement *ber, + struct berval *bv )); + +LBER_F( ber_tag_t ) +ber_get_int LDAP_P(( + BerElement *ber, + ber_int_t *num )); + +LBER_F( ber_tag_t ) +ber_get_enum LDAP_P(( + BerElement *ber, + ber_int_t *num )); + +LBER_F( int ) +ber_decode_int LDAP_P(( + const struct berval *bv, + ber_int_t *num )); + +LBER_F( ber_tag_t ) +ber_get_stringb LDAP_P(( + BerElement *ber, + char *buf, + ber_len_t *len )); + +#define LBER_BV_ALLOC 0x01 /* allocate/copy result, otherwise in-place */ +#define LBER_BV_NOTERM 0x02 /* omit NUL-terminator if parsing in-place */ +#define LBER_BV_STRING 0x04 /* fail if berval contains embedded \0 */ +/* LBER_BV_STRING currently accepts a terminating \0 in the berval, because + * Active Directory sends that in at least the diagonsticMessage field. + */ + +LBER_F( ber_tag_t ) +ber_get_stringbv LDAP_P(( + BerElement *ber, + struct berval *bv, + int options )); + +LBER_F( ber_tag_t ) +ber_get_stringa LDAP_P(( + BerElement *ber, + char **buf )); + +LBER_F( ber_tag_t ) +ber_get_stringal LDAP_P(( + BerElement *ber, + struct berval **bv )); + +LBER_F( ber_tag_t ) +ber_get_bitstringa LDAP_P(( + BerElement *ber, + char **buf, + ber_len_t *len )); + +LBER_F( ber_tag_t ) +ber_get_null LDAP_P(( + BerElement *ber )); + +LBER_F( ber_tag_t ) +ber_get_boolean LDAP_P(( + BerElement *ber, + ber_int_t *boolval )); + +LBER_F( ber_tag_t ) +ber_first_element LDAP_P(( + BerElement *ber, + ber_len_t *len, + char **last )); + +LBER_F( ber_tag_t ) +ber_next_element LDAP_P(( + BerElement *ber, + ber_len_t *len, + LDAP_CONST char *last )); + +LBER_F( ber_tag_t ) +ber_scanf LDAP_P(( + BerElement *ber, + LDAP_CONST char *fmt, + ... )); + +LBER_F( int ) +ber_decode_oid LDAP_P(( + struct berval *in, + struct berval *out )); + +/* + * in encode.c + */ +LBER_F( int ) +ber_encode_oid LDAP_P(( + struct berval *in, + struct berval *out )); + +typedef int (*BEREncodeCallback) LDAP_P(( + BerElement *ber, + void *data )); + +LBER_F( int ) +ber_put_enum LDAP_P(( + BerElement *ber, + ber_int_t num, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_int LDAP_P(( + BerElement *ber, + ber_int_t num, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_ostring LDAP_P(( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t len, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_berval LDAP_P(( + BerElement *ber, + struct berval *bv, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_string LDAP_P(( + BerElement *ber, + LDAP_CONST char *str, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_bitstring LDAP_P(( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t bitlen, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_null LDAP_P(( + BerElement *ber, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_boolean LDAP_P(( + BerElement *ber, + ber_int_t boolval, + ber_tag_t tag )); + +LBER_F( int ) +ber_start_seq LDAP_P(( + BerElement *ber, + ber_tag_t tag )); + +LBER_F( int ) +ber_start_set LDAP_P(( + BerElement *ber, + ber_tag_t tag )); + +LBER_F( int ) +ber_put_seq LDAP_P(( + BerElement *ber )); + +LBER_F( int ) +ber_put_set LDAP_P(( + BerElement *ber )); + +LBER_F( int ) +ber_printf LDAP_P(( + BerElement *ber, + LDAP_CONST char *fmt, + ... )); + + +/* + * in io.c: + */ + +LBER_F( ber_slen_t ) +ber_skip_data LDAP_P(( + BerElement *ber, + ber_len_t len )); + +LBER_F( ber_slen_t ) +ber_read LDAP_P(( + BerElement *ber, + char *buf, + ber_len_t len )); + +LBER_F( ber_slen_t ) +ber_write LDAP_P(( + BerElement *ber, + LDAP_CONST char *buf, + ber_len_t len, + int zero )); /* nonzero is unsupported from OpenLDAP 2.4.18 */ + +LBER_F( void ) +ber_free LDAP_P(( + BerElement *ber, + int freebuf )); + +LBER_F( void ) +ber_free_buf LDAP_P(( BerElement *ber )); + +LBER_F( int ) +ber_flush2 LDAP_P(( + Sockbuf *sb, + BerElement *ber, + int freeit )); +#define LBER_FLUSH_FREE_NEVER (0x0) /* traditional behavior */ +#define LBER_FLUSH_FREE_ON_SUCCESS (0x1) /* traditional behavior */ +#define LBER_FLUSH_FREE_ON_ERROR (0x2) +#define LBER_FLUSH_FREE_ALWAYS (LBER_FLUSH_FREE_ON_SUCCESS|LBER_FLUSH_FREE_ON_ERROR) + +LBER_F( int ) +ber_flush LDAP_P(( + Sockbuf *sb, + BerElement *ber, + int freeit )); /* DEPRECATED */ + +LBER_F( BerElement * ) +ber_alloc LDAP_P(( void )); /* DEPRECATED */ + +LBER_F( BerElement * ) +der_alloc LDAP_P(( void )); /* DEPRECATED */ + +LBER_F( BerElement * ) +ber_alloc_t LDAP_P(( + int beroptions )); + +LBER_F( BerElement * ) +ber_dup LDAP_P(( + BerElement *ber )); + +LBER_F( ber_tag_t ) +ber_get_next LDAP_P(( + Sockbuf *sb, + ber_len_t *len, + BerElement *ber )); + +LBER_F( void ) +ber_init2 LDAP_P(( + BerElement *ber, + struct berval *bv, + int options )); + +LBER_F( void ) +ber_init_w_nullc LDAP_P(( /* DEPRECATED */ + BerElement *ber, + int options )); + +LBER_F( void ) +ber_reset LDAP_P(( + BerElement *ber, + int was_writing )); + +LBER_F( BerElement * ) +ber_init LDAP_P(( + struct berval *bv )); + +LBER_F( int ) +ber_flatten LDAP_P(( + BerElement *ber, + struct berval **bvPtr )); + +LBER_F( int ) +ber_flatten2 LDAP_P(( + BerElement *ber, + struct berval *bv, + int alloc )); + +LBER_F( int ) +ber_remaining LDAP_P(( + BerElement *ber )); + +/* + * LBER ber accessor functions + */ + +LBER_F( int ) +ber_get_option LDAP_P(( + void *item, + int option, + void *outvalue)); + +LBER_F( int ) +ber_set_option LDAP_P(( + void *item, + int option, + LDAP_CONST void *invalue)); + +/* + * LBER sockbuf.c + */ + +LBER_F( Sockbuf * ) +ber_sockbuf_alloc LDAP_P(( + void )); + +LBER_F( void ) +ber_sockbuf_free LDAP_P(( + Sockbuf *sb )); + +LBER_F( int ) +ber_sockbuf_add_io LDAP_P(( + Sockbuf *sb, + Sockbuf_IO *sbio, + int layer, + void *arg )); + +LBER_F( int ) +ber_sockbuf_remove_io LDAP_P(( + Sockbuf *sb, + Sockbuf_IO *sbio, + int layer )); + +LBER_F( int ) +ber_sockbuf_ctrl LDAP_P(( + Sockbuf *sb, + int opt, + void *arg )); + +LBER_V( Sockbuf_IO ) ber_sockbuf_io_tcp; +LBER_V( Sockbuf_IO ) ber_sockbuf_io_readahead; +LBER_V( Sockbuf_IO ) ber_sockbuf_io_fd; +LBER_V( Sockbuf_IO ) ber_sockbuf_io_debug; +LBER_V( Sockbuf_IO ) ber_sockbuf_io_udp; + +/* + * LBER memory.c + */ +LBER_F( void * ) +ber_memalloc LDAP_P(( + ber_len_t s )); + +LBER_F( void * ) +ber_memrealloc LDAP_P(( + void* p, + ber_len_t s )); + +LBER_F( void * ) +ber_memcalloc LDAP_P(( + ber_len_t n, + ber_len_t s )); + +LBER_F( void ) +ber_memfree LDAP_P(( + void* p )); + +LBER_F( void ) +ber_memvfree LDAP_P(( + void** vector )); + +LBER_F( void ) +ber_bvfree LDAP_P(( + struct berval *bv )); + +LBER_F( void ) +ber_bvecfree LDAP_P(( + struct berval **bv )); + +LBER_F( int ) +ber_bvecadd LDAP_P(( + struct berval ***bvec, + struct berval *bv )); + +LBER_F( struct berval * ) +ber_dupbv LDAP_P(( + struct berval *dst, struct berval *src )); + +LBER_F( struct berval * ) +ber_bvdup LDAP_P(( + struct berval *src )); + +LBER_F( struct berval * ) +ber_mem2bv LDAP_P(( + LDAP_CONST char *, ber_len_t len, int duplicate, struct berval *bv)); + +LBER_F( struct berval * ) +ber_str2bv LDAP_P(( + LDAP_CONST char *, ber_len_t len, int duplicate, struct berval *bv)); + +#define ber_bvstr(a) ((ber_str2bv)((a), 0, 0, NULL)) +#define ber_bvstrdup(a) ((ber_str2bv)((a), 0, 1, NULL)) + +LBER_F( char * ) +ber_strdup LDAP_P(( + LDAP_CONST char * )); + +LBER_F( ber_len_t ) +ber_strnlen LDAP_P(( + LDAP_CONST char *s, ber_len_t len )); + +LBER_F( char * ) +ber_strndup LDAP_P(( + LDAP_CONST char *s, ber_len_t l )); + +LBER_F( struct berval * ) +ber_bvreplace LDAP_P(( + struct berval *dst, LDAP_CONST struct berval *src )); + +LBER_F( void ) +ber_bvarray_free LDAP_P(( BerVarray p )); + +LBER_F( int ) +ber_bvarray_add LDAP_P(( BerVarray *p, BerValue *bv )); + +#define ber_bvcmp(v1,v2) \ + ((v1)->bv_len < (v2)->bv_len \ + ? -1 : ((v1)->bv_len > (v2)->bv_len \ + ? 1 : memcmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) )) + +/* + * error.c + */ +LBER_F( int * ) ber_errno_addr LDAP_P((void)); +#define ber_errno (*(ber_errno_addr)()) + +#define LBER_ERROR_NONE 0 +#define LBER_ERROR_PARAM 0x1 +#define LBER_ERROR_MEMORY 0x2 + +LDAP_END_DECL + +#endif /* _LBER_H */ diff --git a/contrib/libs/openldap/include/lber_pvt.h b/contrib/libs/openldap/include/lber_pvt.h new file mode 100644 index 0000000000..474c291e26 --- /dev/null +++ b/contrib/libs/openldap/include/lber_pvt.h @@ -0,0 +1,223 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * lber_pvt.h - Header for ber_pvt_ functions. + * These are meant to be internal to OpenLDAP Software. + */ + +#ifndef _LBER_PVT_H +#define _LBER_PVT_H 1 + +#include <lber.h> + +LDAP_BEGIN_DECL + +/* for allocating aligned buffers (on the stack) */ +#define LBER_ALIGNED_BUFFER(uname,size) \ + union uname { \ + char buffer[size]; \ + /* force alignment */ \ + int ialign; \ + long lalign; \ + float falign; \ + double dalign; \ + char* palign; \ + } + +#define LBER_ELEMENT_SIZEOF (256) /* must be >= sizeof(BerElement) */ +typedef LBER_ALIGNED_BUFFER(lber_berelement_u,LBER_ELEMENT_SIZEOF) + BerElementBuffer; + +typedef struct sockbuf_buf { + ber_len_t buf_size; + ber_len_t buf_ptr; + ber_len_t buf_end; + char *buf_base; +} Sockbuf_Buf; + +/* + * bprint.c + */ +LBER_V( BER_LOG_PRINT_FN ) ber_pvt_log_print; + +LBER_F( int ) +ber_pvt_log_printf LDAP_P(( + int errlvl, + int loglvl, + const char *fmt, + ... )) LDAP_GCCATTR((format(printf, 3, 4))); + +/* + * sockbuf.c + */ +LBER_F( ber_slen_t ) +ber_pvt_sb_do_write LDAP_P(( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out )); + +LBER_F( void ) +ber_pvt_sb_buf_init LDAP_P(( Sockbuf_Buf *buf )); + +LBER_F( void ) +ber_pvt_sb_buf_destroy LDAP_P(( Sockbuf_Buf *buf )); + +LBER_F( int ) +ber_pvt_sb_grow_buffer LDAP_P(( Sockbuf_Buf *buf, ber_len_t minsize )); + +LBER_F( ber_len_t ) +ber_pvt_sb_copy_out LDAP_P(( Sockbuf_Buf *sbb, char *buf, ber_len_t len )); + +LBER_F( int ) +ber_pvt_socket_set_nonblock LDAP_P(( ber_socket_t sd, int nb )); + +/* + * memory.c + */ +LBER_F( void * ) +ber_memalloc_x LDAP_P(( + ber_len_t s, void *ctx)); + +LBER_F( void * ) +ber_memrealloc_x LDAP_P(( + void* p, + ber_len_t s, void *ctx )); + +LBER_F( void * ) +ber_memcalloc_x LDAP_P(( + ber_len_t n, + ber_len_t s, void *ctx )); + +LBER_F( void ) +ber_memfree_x LDAP_P(( + void* p, void *ctx )); + +LBER_F( void ) +ber_memvfree_x LDAP_P(( + void** vector, void *ctx )); + +LBER_F( void ) +ber_bvfree_x LDAP_P(( + struct berval *bv, void *ctx )); + +LBER_F( void ) +ber_bvecfree_x LDAP_P(( + struct berval **bv, void *ctx )); + +LBER_F( int ) +ber_bvecadd_x LDAP_P(( + struct berval ***bvec, + struct berval *bv, void *ctx )); + +LBER_F( struct berval * ) +ber_dupbv_x LDAP_P(( + struct berval *dst, struct berval *src, void *ctx )); + +LBER_F( struct berval * ) +ber_str2bv_x LDAP_P(( + LDAP_CONST char *, ber_len_t len, int dup, struct berval *bv, void *ctx)); + +LBER_F( struct berval * ) +ber_mem2bv_x LDAP_P(( + LDAP_CONST char *, ber_len_t len, int dup, struct berval *bv, void *ctx)); + +LBER_F( char * ) +ber_strdup_x LDAP_P(( + LDAP_CONST char *, void *ctx )); + +LBER_F( struct berval * ) +ber_bvreplace_x LDAP_P(( + struct berval *dst, LDAP_CONST struct berval *src, void *ctx )); + +LBER_F( void ) +ber_bvarray_free_x LDAP_P(( BerVarray p, void *ctx )); + +LBER_F( int ) +ber_bvarray_add_x LDAP_P(( BerVarray *p, BerValue *bv, void *ctx )); + +LBER_F( int ) +ber_bvarray_dup_x LDAP_P(( BerVarray *dst, BerVarray src, void *ctx )); + +#if 0 +#define ber_bvstrcmp(v1,v2) \ + ((v1)->bv_len < (v2)->bv_len \ + ? -1 : ((v1)->bv_len > (v2)->bv_len \ + ? 1 : strncmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) )) +#else + /* avoid strncmp() */ +#define ber_bvstrcmp(v1,v2) ber_bvcmp((v1),(v2)) +#endif + +#define ber_bvstrcasecmp(v1,v2) \ + ((v1)->bv_len < (v2)->bv_len \ + ? -1 : ((v1)->bv_len > (v2)->bv_len \ + ? 1 : strncasecmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) )) + +#define ber_bvccmp(v1,c) \ + ( (v1)->bv_len == 1 && (v1)->bv_val[0] == (c) ) + +#define ber_strccmp(s,c) \ + ( (s)[0] == (c) && (s)[1] == '\0' ) + +#define ber_bvchr(bv,c) \ + ((char *) memchr( (bv)->bv_val, (c), (bv)->bv_len )) + +#define ber_bvrchr(bv,c) \ + ((char *) lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len )) + +#define ber_bvchr_post(dst,bv,c) \ + do { \ + (dst)->bv_val = memchr( (bv)->bv_val, (c), (bv)->bv_len ); \ + (dst)->bv_len = (dst)->bv_val ? (bv)->bv_len - ((dst)->bv_val - (bv)->bv_val) : 0; \ + } while (0) + +#define ber_bvchr_pre(dst,bv,c) \ + do { \ + (dst)->bv_val = memchr( (bv)->bv_val, (c), (bv)->bv_len ); \ + (dst)->bv_len = (dst)->bv_val ? ((dst)->bv_val - (bv)->bv_val) : (bv)->bv_len; \ + (dst)->bv_val = (bv)->bv_val; \ + } while (0) + +#define ber_bvrchr_post(dst,bv,c) \ + do { \ + (dst)->bv_val = lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len ); \ + (dst)->bv_len = (dst)->bv_val ? (bv)->bv_len - ((dst)->bv_val - (bv)->bv_val) : 0; \ + } while (0) + +#define ber_bvrchr_pre(dst,bv,c) \ + do { \ + (dst)->bv_val = lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len ); \ + (dst)->bv_len = (dst)->bv_val ? ((dst)->bv_val - (bv)->bv_val) : (bv)->bv_len; \ + (dst)->bv_val = (bv)->bv_val; \ + } while (0) + +#define BER_STRLENOF(s) (sizeof(s)-1) +#define BER_BVC(s) { BER_STRLENOF(s), (char *)(s) } +#define BER_BVNULL { 0L, NULL } +#define BER_BVZERO(bv) \ + do { \ + (bv)->bv_len = 0; \ + (bv)->bv_val = NULL; \ + } while (0) +#define BER_BVSTR(bv,s) \ + do { \ + (bv)->bv_len = BER_STRLENOF(s); \ + (bv)->bv_val = (s); \ + } while (0) +#define BER_BVISNULL(bv) ((bv)->bv_val == NULL) +#define BER_BVISEMPTY(bv) ((bv)->bv_len == 0) + +LDAP_END_DECL + +#endif + diff --git a/contrib/libs/openldap/include/lber_types.h b/contrib/libs/openldap/include/lber_types.h new file mode 100644 index 0000000000..c32b803a89 --- /dev/null +++ b/contrib/libs/openldap/include/lber_types.h @@ -0,0 +1,63 @@ +/* include/lber_types.h. Generated from lber_types.hin by configure. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * LBER types + */ + +#ifndef _LBER_TYPES_H +#define _LBER_TYPES_H + +#include <ldap_cdefs.h> + +LDAP_BEGIN_DECL + +/* LBER boolean, enum, integers (32 bits or larger) */ +#define LBER_INT_T int + +/* LBER tags (32 bits or larger) */ +#define LBER_TAG_T long + +/* LBER socket descriptor */ +#define LBER_SOCKET_T int + +/* LBER lengths (32 bits or larger) */ +#define LBER_LEN_T long + +/* ------------------------------------------------------------ */ + +/* booleans, enumerations, and integers */ +typedef LBER_INT_T ber_int_t; + +/* signed and unsigned versions */ +typedef signed LBER_INT_T ber_sint_t; +typedef unsigned LBER_INT_T ber_uint_t; + +/* tags */ +typedef unsigned LBER_TAG_T ber_tag_t; + +/* "socket" descriptors */ +typedef LBER_SOCKET_T ber_socket_t; + +/* lengths */ +typedef unsigned LBER_LEN_T ber_len_t; + +/* signed lengths */ +typedef signed LBER_LEN_T ber_slen_t; + +LDAP_END_DECL + +#endif /* _LBER_TYPES_H */ diff --git a/contrib/libs/openldap/include/ldap.h b/contrib/libs/openldap/include/ldap.h new file mode 100644 index 0000000000..f0cce01f43 --- /dev/null +++ b/contrib/libs/openldap/include/ldap.h @@ -0,0 +1,2817 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LDAP_H +#define _LDAP_H + +/* pull in lber */ +#include <lber.h> + +/* include version and API feature defines */ +#include <ldap_features.h> + +LDAP_BEGIN_DECL + +#define LDAP_VERSION1 1 +#define LDAP_VERSION2 2 +#define LDAP_VERSION3 3 + +#define LDAP_VERSION_MIN LDAP_VERSION2 +#define LDAP_VERSION LDAP_VERSION2 +#define LDAP_VERSION_MAX LDAP_VERSION3 + +/* + * We use 3000+n here because it is above 1823 (for RFC 1823), + * above 2000+rev of IETF LDAPEXT draft (now quite dated), + * yet below allocations for new RFCs (just in case there is + * someday an RFC produced). + */ +#define LDAP_API_VERSION 3001 +#define LDAP_VENDOR_NAME "OpenLDAP" + +/* OpenLDAP API Features */ +#define LDAP_API_FEATURE_X_OPENLDAP LDAP_VENDOR_VERSION + +#if defined( LDAP_API_FEATURE_X_OPENLDAP_REENTRANT ) +# define LDAP_API_FEATURE_THREAD_SAFE 1 +#endif +#if defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) +# define LDAP_API_FEATURE_SESSION_THREAD_SAFE 1 +# define LDAP_API_FEATURE_OPERATION_THREAD_SAFE 1 +#endif + + +#define LDAP_PORT 389 /* ldap:/// default LDAP port */ +#define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */ + +#define LDAP_ROOT_DSE "" +#define LDAP_NO_ATTRS "1.1" +#define LDAP_ALL_USER_ATTRIBUTES "*" +#define LDAP_ALL_OPERATIONAL_ATTRIBUTES "+" /* RFC 3673 */ + +/* RFC 4511: maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- */ +#define LDAP_MAXINT (2147483647) + +/* + * LDAP_OPTions + * 0x0000 - 0x0fff reserved for api options + * 0x1000 - 0x3fff reserved for api extended options + * 0x4000 - 0x7fff reserved for private and experimental options + */ + +#define LDAP_OPT_API_INFO 0x0000 +#define LDAP_OPT_DESC 0x0001 /* historic */ +#define LDAP_OPT_DEREF 0x0002 +#define LDAP_OPT_SIZELIMIT 0x0003 +#define LDAP_OPT_TIMELIMIT 0x0004 +/* 0x05 - 0x07 not defined */ +#define LDAP_OPT_REFERRALS 0x0008 +#define LDAP_OPT_RESTART 0x0009 +/* 0x0a - 0x10 not defined */ +#define LDAP_OPT_PROTOCOL_VERSION 0x0011 +#define LDAP_OPT_SERVER_CONTROLS 0x0012 +#define LDAP_OPT_CLIENT_CONTROLS 0x0013 +/* 0x14 not defined */ +#define LDAP_OPT_API_FEATURE_INFO 0x0015 +/* 0x16 - 0x2f not defined */ +#define LDAP_OPT_HOST_NAME 0x0030 +#define LDAP_OPT_RESULT_CODE 0x0031 +#define LDAP_OPT_ERROR_NUMBER LDAP_OPT_RESULT_CODE +#define LDAP_OPT_DIAGNOSTIC_MESSAGE 0x0032 +#define LDAP_OPT_ERROR_STRING LDAP_OPT_DIAGNOSTIC_MESSAGE +#define LDAP_OPT_MATCHED_DN 0x0033 +/* 0x0034 - 0x3fff not defined */ +/* 0x0091 used by Microsoft for LDAP_OPT_AUTO_RECONNECT */ +#define LDAP_OPT_SSPI_FLAGS 0x0092 +/* 0x0093 used by Microsoft for LDAP_OPT_SSL_INFO */ +/* 0x0094 used by Microsoft for LDAP_OPT_REF_DEREF_CONN_PER_MSG */ +#define LDAP_OPT_SIGN 0x0095 +#define LDAP_OPT_ENCRYPT 0x0096 +#define LDAP_OPT_SASL_METHOD 0x0097 +/* 0x0098 used by Microsoft for LDAP_OPT_AREC_EXCLUSIVE */ +#define LDAP_OPT_SECURITY_CONTEXT 0x0099 +/* 0x009A used by Microsoft for LDAP_OPT_ROOTDSE_CACHE */ +/* 0x009B - 0x3fff not defined */ + +/* API Extensions */ +#define LDAP_OPT_API_EXTENSION_BASE 0x4000 /* API extensions */ + +/* private and experimental options */ +/* OpenLDAP specific options */ +#define LDAP_OPT_DEBUG_LEVEL 0x5001 /* debug level */ +#define LDAP_OPT_TIMEOUT 0x5002 /* default timeout */ +#define LDAP_OPT_REFHOPLIMIT 0x5003 /* ref hop limit */ +#define LDAP_OPT_NETWORK_TIMEOUT 0x5005 /* socket level timeout */ +#define LDAP_OPT_URI 0x5006 +#define LDAP_OPT_REFERRAL_URLS 0x5007 /* Referral URLs */ +#define LDAP_OPT_SOCKBUF 0x5008 /* sockbuf */ +#define LDAP_OPT_DEFBASE 0x5009 /* searchbase */ +#define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */ +#define LDAP_OPT_CONNECT_CB 0x5011 /* connection callbacks */ +#define LDAP_OPT_SESSION_REFCNT 0x5012 /* session reference count */ +#define LDAP_OPT_KEEPCONN 0x5013 /* keep the connection on read error or NoD */ +#define LDAP_OPT_SOCKET_BIND_ADDRESSES 0x5014 /* user configured bind IPs */ +#define LDAP_OPT_TCP_USER_TIMEOUT 0x5015 /* set TCP_USER_TIMEOUT if the OS supports it, ignored otherwise */ + +/* OpenLDAP TLS options */ +#define LDAP_OPT_X_TLS 0x6000 +#define LDAP_OPT_X_TLS_CTX 0x6001 /* OpenSSL CTX* */ +#define LDAP_OPT_X_TLS_CACERTFILE 0x6002 +#define LDAP_OPT_X_TLS_CACERTDIR 0x6003 +#define LDAP_OPT_X_TLS_CERTFILE 0x6004 +#define LDAP_OPT_X_TLS_KEYFILE 0x6005 +#define LDAP_OPT_X_TLS_REQUIRE_CERT 0x6006 +#define LDAP_OPT_X_TLS_PROTOCOL_MIN 0x6007 +#define LDAP_OPT_X_TLS_CIPHER_SUITE 0x6008 +#define LDAP_OPT_X_TLS_RANDOM_FILE 0x6009 +#define LDAP_OPT_X_TLS_SSL_CTX 0x600a /* OpenSSL SSL* */ +#define LDAP_OPT_X_TLS_CRLCHECK 0x600b +#define LDAP_OPT_X_TLS_CONNECT_CB 0x600c +#define LDAP_OPT_X_TLS_CONNECT_ARG 0x600d +#define LDAP_OPT_X_TLS_DHFILE 0x600e +#define LDAP_OPT_X_TLS_NEWCTX 0x600f +#define LDAP_OPT_X_TLS_CRLFILE 0x6010 /* GNUtls only */ +#define LDAP_OPT_X_TLS_PACKAGE 0x6011 +#define LDAP_OPT_X_TLS_ECNAME 0x6012 +#define LDAP_OPT_X_TLS_VERSION 0x6013 /* read-only */ +#define LDAP_OPT_X_TLS_CIPHER 0x6014 /* read-only */ +#define LDAP_OPT_X_TLS_PEERCERT 0x6015 /* read-only */ +#define LDAP_OPT_X_TLS_CACERT 0x6016 +#define LDAP_OPT_X_TLS_CERT 0x6017 +#define LDAP_OPT_X_TLS_KEY 0x6018 +#define LDAP_OPT_X_TLS_PEERKEY_HASH 0x6019 +#define LDAP_OPT_X_TLS_REQUIRE_SAN 0x601a +#define LDAP_OPT_X_TLS_PROTOCOL_MAX 0x601b + +#define LDAP_OPT_X_TLS_NEVER 0 +#define LDAP_OPT_X_TLS_HARD 1 +#define LDAP_OPT_X_TLS_DEMAND 2 +#define LDAP_OPT_X_TLS_ALLOW 3 +#define LDAP_OPT_X_TLS_TRY 4 + +#define LDAP_OPT_X_TLS_CRL_NONE 0 +#define LDAP_OPT_X_TLS_CRL_PEER 1 +#define LDAP_OPT_X_TLS_CRL_ALL 2 + +/* for LDAP_OPT_X_TLS_PROTOCOL_MIN/MAX */ +#define LDAP_OPT_X_TLS_PROTOCOL(maj,min) (((maj) << 8) + (min)) +#define LDAP_OPT_X_TLS_PROTOCOL_SSL2 (2 << 8) +#define LDAP_OPT_X_TLS_PROTOCOL_SSL3 (3 << 8) +#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 ((3 << 8) + 1) +#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ((3 << 8) + 2) +#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ((3 << 8) + 3) +#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 ((3 << 8) + 4) + +#define LDAP_OPT_X_SASL_CBINDING_NONE 0 +#define LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE 1 +#define LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT 2 + +/* OpenLDAP SASL options */ +#define LDAP_OPT_X_SASL_MECH 0x6100 +#define LDAP_OPT_X_SASL_REALM 0x6101 +#define LDAP_OPT_X_SASL_AUTHCID 0x6102 +#define LDAP_OPT_X_SASL_AUTHZID 0x6103 +#define LDAP_OPT_X_SASL_SSF 0x6104 /* read-only */ +#define LDAP_OPT_X_SASL_SSF_EXTERNAL 0x6105 /* write-only */ +#define LDAP_OPT_X_SASL_SECPROPS 0x6106 /* write-only */ +#define LDAP_OPT_X_SASL_SSF_MIN 0x6107 +#define LDAP_OPT_X_SASL_SSF_MAX 0x6108 +#define LDAP_OPT_X_SASL_MAXBUFSIZE 0x6109 +#define LDAP_OPT_X_SASL_MECHLIST 0x610a /* read-only */ +#define LDAP_OPT_X_SASL_NOCANON 0x610b +#define LDAP_OPT_X_SASL_USERNAME 0x610c /* read-only */ +#define LDAP_OPT_X_SASL_GSS_CREDS 0x610d +#define LDAP_OPT_X_SASL_CBINDING 0x610e + +/* + * OpenLDAP per connection tcp-keepalive settings + * (Linux only, ignored where unsupported) + */ +#define LDAP_OPT_X_KEEPALIVE_IDLE 0x6300 +#define LDAP_OPT_X_KEEPALIVE_PROBES 0x6301 +#define LDAP_OPT_X_KEEPALIVE_INTERVAL 0x6302 + +/* Private API Extensions -- reserved for application use */ +#define LDAP_OPT_PRIVATE_EXTENSION_BASE 0x7000 /* Private API inclusive */ + +/* + * ldap_get_option() and ldap_set_option() return values. + * As later versions may return other values indicating + * failure, current applications should only compare returned + * value against LDAP_OPT_SUCCESS. + */ +#define LDAP_OPT_SUCCESS 0 +#define LDAP_OPT_ERROR (-1) + +/* option on/off values */ +#define LDAP_OPT_ON ((void *) &ber_pvt_opt_on) +#define LDAP_OPT_OFF ((void *) 0) + +typedef struct ldapapiinfo { + int ldapai_info_version; /* version of LDAPAPIInfo */ +#define LDAP_API_INFO_VERSION (1) + int ldapai_api_version; /* revision of API supported */ + int ldapai_protocol_version; /* highest LDAP version supported */ + char **ldapai_extensions; /* names of API extensions */ + char *ldapai_vendor_name; /* name of supplier */ + int ldapai_vendor_version; /* supplier-specific version * 100 */ +} LDAPAPIInfo; + +typedef struct ldap_apifeature_info { + int ldapaif_info_version; /* version of LDAPAPIFeatureInfo */ +#define LDAP_FEATURE_INFO_VERSION (1) /* apifeature_info struct version */ + char* ldapaif_name; /* LDAP_API_FEATURE_* (less prefix) */ + int ldapaif_version; /* value of LDAP_API_FEATURE_... */ +} LDAPAPIFeatureInfo; + +/* + * LDAP Control structure + */ +typedef struct ldapcontrol { + char * ldctl_oid; /* numericoid of control */ + struct berval ldctl_value; /* encoded value of control */ + char ldctl_iscritical; /* criticality */ +} LDAPControl; + +/* LDAP Controls */ +/* standard track controls */ +#define LDAP_CONTROL_MANAGEDSAIT "2.16.840.1.113730.3.4.2" /* RFC 3296 */ +#define LDAP_CONTROL_PROXY_AUTHZ "2.16.840.1.113730.3.4.18" /* RFC 4370 */ +#define LDAP_CONTROL_SUBENTRIES "1.3.6.1.4.1.4203.1.10.1" /* RFC 3672 */ + +#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3"/* RFC 3876 */ + +#define LDAP_CONTROL_ASSERT "1.3.6.1.1.12" /* RFC 4528 */ +#define LDAP_CONTROL_PRE_READ "1.3.6.1.1.13.1" /* RFC 4527 */ +#define LDAP_CONTROL_POST_READ "1.3.6.1.1.13.2" /* RFC 4527 */ + +#define LDAP_CONTROL_SORTREQUEST "1.2.840.113556.1.4.473" /* RFC 2891 */ +#define LDAP_CONTROL_SORTRESPONSE "1.2.840.113556.1.4.474" /* RFC 2891 */ + +/* non-standard track controls */ +#define LDAP_CONTROL_PAGEDRESULTS "1.2.840.113556.1.4.319" /* RFC 2696 */ + +#define LDAP_CONTROL_AUTHZID_REQUEST "2.16.840.1.113730.3.4.16" /* RFC 3829 */ +#define LDAP_CONTROL_AUTHZID_RESPONSE "2.16.840.1.113730.3.4.15" /* RFC 3829 */ + +/* LDAP Content Synchronization Operation -- RFC 4533 */ +#define LDAP_SYNC_OID "1.3.6.1.4.1.4203.1.9.1" +#define LDAP_CONTROL_SYNC LDAP_SYNC_OID ".1" +#define LDAP_CONTROL_SYNC_STATE LDAP_SYNC_OID ".2" +#define LDAP_CONTROL_SYNC_DONE LDAP_SYNC_OID ".3" +#define LDAP_SYNC_INFO LDAP_SYNC_OID ".4" + +#define LDAP_SYNC_NONE 0x00 +#define LDAP_SYNC_REFRESH_ONLY 0x01 +#define LDAP_SYNC_RESERVED 0x02 +#define LDAP_SYNC_REFRESH_AND_PERSIST 0x03 + +#define LDAP_SYNC_REFRESH_PRESENTS 0 +#define LDAP_SYNC_REFRESH_DELETES 1 + +#define LDAP_TAG_SYNC_NEW_COOKIE ((ber_tag_t) 0x80U) +#define LDAP_TAG_SYNC_REFRESH_DELETE ((ber_tag_t) 0xa1U) +#define LDAP_TAG_SYNC_REFRESH_PRESENT ((ber_tag_t) 0xa2U) +#define LDAP_TAG_SYNC_ID_SET ((ber_tag_t) 0xa3U) + +#define LDAP_TAG_SYNC_COOKIE ((ber_tag_t) 0x04U) +#define LDAP_TAG_REFRESHDELETES ((ber_tag_t) 0x01U) +#define LDAP_TAG_REFRESHDONE ((ber_tag_t) 0x01U) +#define LDAP_TAG_RELOAD_HINT ((ber_tag_t) 0x01U) + +#define LDAP_SYNC_PRESENT 0 +#define LDAP_SYNC_ADD 1 +#define LDAP_SYNC_MODIFY 2 +#define LDAP_SYNC_DELETE 3 +#define LDAP_SYNC_NEW_COOKIE 4 + +/* LDAP Don't Use Copy Control (RFC 6171) */ +#define LDAP_CONTROL_DONTUSECOPY "1.3.6.1.1.22" + +/* Password policy Controls *//* work in progress */ +/* ITS#3458: released; disabled by default */ +#define LDAP_CONTROL_PASSWORDPOLICYREQUEST "1.3.6.1.4.1.42.2.27.8.5.1" +#define LDAP_CONTROL_PASSWORDPOLICYRESPONSE "1.3.6.1.4.1.42.2.27.8.5.1" + +/* various works in progress */ +#define LDAP_CONTROL_NOOP "1.3.6.1.4.1.4203.666.5.2" +#define LDAP_CONTROL_NO_SUBORDINATES "1.3.6.1.4.1.4203.666.5.11" +#define LDAP_CONTROL_RELAX "1.3.6.1.4.1.4203.666.5.12" +#define LDAP_CONTROL_MANAGEDIT LDAP_CONTROL_RELAX +#define LDAP_CONTROL_SLURP "1.3.6.1.4.1.4203.666.5.13" +#define LDAP_CONTROL_VALSORT "1.3.6.1.4.1.4203.666.5.14" +#define LDAP_CONTROL_X_DEREF "1.3.6.1.4.1.4203.666.5.16" +#define LDAP_CONTROL_X_WHATFAILED "1.3.6.1.4.1.4203.666.5.17" + +/* LDAP Chaining Behavior Control *//* work in progress */ +/* <draft-sermersheim-ldap-chaining>; + * see also LDAP_NO_REFERRALS_FOUND, LDAP_CANNOT_CHAIN */ +#define LDAP_CONTROL_X_CHAINING_BEHAVIOR "1.3.6.1.4.1.4203.666.11.3" + +#define LDAP_CHAINING_PREFERRED 0 +#define LDAP_CHAINING_REQUIRED 1 +#define LDAP_REFERRALS_PREFERRED 2 +#define LDAP_REFERRALS_REQUIRED 3 + +/* MS Active Directory controls (for compatibility) */ +#define LDAP_CONTROL_X_LAZY_COMMIT "1.2.840.113556.1.4.619" +#define LDAP_CONTROL_X_INCREMENTAL_VALUES "1.2.840.113556.1.4.802" +#define LDAP_CONTROL_X_DOMAIN_SCOPE "1.2.840.113556.1.4.1339" +#define LDAP_CONTROL_X_PERMISSIVE_MODIFY "1.2.840.113556.1.4.1413" +#define LDAP_CONTROL_X_SEARCH_OPTIONS "1.2.840.113556.1.4.1340" +#define LDAP_SEARCH_FLAG_DOMAIN_SCOPE 1 /* do not generate referrals */ +#define LDAP_SEARCH_FLAG_PHANTOM_ROOT 2 /* search all subordinate NCs */ +#define LDAP_CONTROL_X_TREE_DELETE "1.2.840.113556.1.4.805" + +/* MS Active Directory controls - not implemented in slapd(8) */ +#define LDAP_CONTROL_X_SERVER_NOTIFICATION "1.2.840.113556.1.4.528" +#define LDAP_CONTROL_X_EXTENDED_DN "1.2.840.113556.1.4.529" +#define LDAP_CONTROL_X_SHOW_DELETED "1.2.840.113556.1.4.417" +#define LDAP_CONTROL_X_DIRSYNC "1.2.840.113556.1.4.841" + +#define LDAP_CONTROL_X_DIRSYNC_OBJECT_SECURITY 0x00000001 +#define LDAP_CONTROL_X_DIRSYNC_ANCESTORS_FIRST 0x00000800 +#define LDAP_CONTROL_X_DIRSYNC_PUBLIC_DATA_ONLY 0x00002000 +#define LDAP_CONTROL_X_DIRSYNC_INCREMENTAL_VALUES 0x80000000 + + +/* <draft-wahl-ldap-session> */ +#define LDAP_CONTROL_X_SESSION_TRACKING "1.3.6.1.4.1.21008.108.63.1" +#define LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_SESSION_ID \ + LDAP_CONTROL_X_SESSION_TRACKING ".1" +#define LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_MULTI_SESSION_ID \ + LDAP_CONTROL_X_SESSION_TRACKING ".2" +#define LDAP_CONTROL_X_SESSION_TRACKING_USERNAME \ + LDAP_CONTROL_X_SESSION_TRACKING ".3" +/* various expired works */ + +/* LDAP Duplicated Entry Control Extension *//* not implemented in slapd(8) */ +#define LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1" +#define LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2" +#define LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3" +#define LDAP_CONTROL_DUPENT LDAP_CONTROL_DUPENT_REQUEST + +/* LDAP Persistent Search Control *//* not implemented in slapd(8) */ +#define LDAP_CONTROL_PERSIST_REQUEST "2.16.840.1.113730.3.4.3" +#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE "2.16.840.1.113730.3.4.7" +#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD 0x1 +#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE 0x2 +#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY 0x4 +#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME 0x8 + +/* LDAP VLV */ +#define LDAP_CONTROL_VLVREQUEST "2.16.840.1.113730.3.4.9" +#define LDAP_CONTROL_VLVRESPONSE "2.16.840.1.113730.3.4.10" + +/* Sun's analogue to ppolicy */ +#define LDAP_CONTROL_X_ACCOUNT_USABILITY "1.3.6.1.4.1.42.2.27.9.5.8" + +#define LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ((ber_tag_t) 0x80U) /* primitive + 0 */ +#define LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ((ber_tag_t) 0xA1U) /* constructed + 1 */ + +#define LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE ((ber_tag_t) 0x80U) /* primitive + 0 */ +#define LDAP_TAG_X_ACCOUNT_USABILITY_RESET ((ber_tag_t) 0x81U) /* primitive + 1 */ +#define LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED ((ber_tag_t) 0x82U) /* primitive + 2 */ +#define LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE ((ber_tag_t) 0x83U) /* primitive + 3 */ +#define LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK ((ber_tag_t) 0x84U) /* primitive + 4 */ + +/* Netscape Password policy response controls */ +/* <draft-vchu-ldap-pwd-policy> */ +#define LDAP_CONTROL_X_PASSWORD_EXPIRED "2.16.840.1.113730.3.4.4" +#define LDAP_CONTROL_X_PASSWORD_EXPIRING "2.16.840.1.113730.3.4.5" + +/* LDAP Unsolicited Notifications */ +#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" /* RFC 4511 */ +#define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION + +/* LDAP Extended Operations */ +#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" /* RFC 4511 */ + +#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1" /* RFC 3062 */ +#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U) +#define LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ((ber_tag_t) 0x81U) +#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U) +#define LDAP_TAG_EXOP_MODIFY_PASSWD_GEN ((ber_tag_t) 0x80U) + +#define LDAP_EXOP_CANCEL "1.3.6.1.1.8" /* RFC 3909 */ +#define LDAP_EXOP_X_CANCEL LDAP_EXOP_CANCEL + +#define LDAP_EXOP_REFRESH "1.3.6.1.4.1.1466.101.119.1" /* RFC 2589 */ +#define LDAP_TAG_EXOP_REFRESH_REQ_DN ((ber_tag_t) 0x80U) +#define LDAP_TAG_EXOP_REFRESH_REQ_TTL ((ber_tag_t) 0x81U) +#define LDAP_TAG_EXOP_REFRESH_RES_TTL ((ber_tag_t) 0x81U) + +#define LDAP_EXOP_VERIFY_CREDENTIALS "1.3.6.1.4.1.4203.666.6.5" +#define LDAP_EXOP_X_VERIFY_CREDENTIALS LDAP_EXOP_VERIFY_CREDENTIALS + +#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ((ber_tag_t) 0x80U) +#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ((ber_tag_t) 0x81U) +#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ((ber_tag_t) 0xa2U) /* context specific + constructed + 2 */ + +#define LDAP_EXOP_WHO_AM_I "1.3.6.1.4.1.4203.1.11.3" /* RFC 4532 */ +#define LDAP_EXOP_X_WHO_AM_I LDAP_EXOP_WHO_AM_I + +/* various works in progress */ +#define LDAP_EXOP_TURN "1.3.6.1.1.19" /* RFC 4531 */ +#define LDAP_EXOP_X_TURN LDAP_EXOP_TURN + +/* LDAP Distributed Procedures <draft-sermersheim-ldap-distproc> */ +/* a work in progress */ +#define LDAP_X_DISTPROC_BASE "1.3.6.1.4.1.4203.666.11.6" +#define LDAP_EXOP_X_CHAINEDREQUEST LDAP_X_DISTPROC_BASE ".1" +#define LDAP_FEATURE_X_CANCHAINOPS LDAP_X_DISTPROC_BASE ".2" +#define LDAP_CONTROL_X_RETURNCONTREF LDAP_X_DISTPROC_BASE ".3" +#define LDAP_URLEXT_X_LOCALREFOID LDAP_X_DISTPROC_BASE ".4" +#define LDAP_URLEXT_X_REFTYPEOID LDAP_X_DISTPROC_BASE ".5" +#define LDAP_URLEXT_X_SEARCHEDSUBTREEOID \ + LDAP_X_DISTPROC_BASE ".6" +#define LDAP_URLEXT_X_FAILEDNAMEOID LDAP_X_DISTPROC_BASE ".7" +#define LDAP_URLEXT_X_LOCALREF "x-localReference" +#define LDAP_URLEXT_X_REFTYPE "x-referenceType" +#define LDAP_URLEXT_X_SEARCHEDSUBTREE "x-searchedSubtree" +#define LDAP_URLEXT_X_FAILEDNAME "x-failedName" + +#define LDAP_TXN "1.3.6.1.1.21" /* RFC 5805 */ +#define LDAP_EXOP_TXN_START LDAP_TXN ".1" +#define LDAP_CONTROL_TXN_SPEC LDAP_TXN ".2" +#define LDAP_EXOP_TXN_END LDAP_TXN ".3" +#define LDAP_EXOP_TXN_ABORTED_NOTICE LDAP_TXN ".4" + +/* LDAP Features */ +#define LDAP_FEATURE_ALL_OP_ATTRS "1.3.6.1.4.1.4203.1.5.1" /* RFC 3673 */ +#define LDAP_FEATURE_OBJECTCLASS_ATTRS \ + "1.3.6.1.4.1.4203.1.5.2" /* @objectClass - new number to be assigned */ +#define LDAP_FEATURE_ABSOLUTE_FILTERS "1.3.6.1.4.1.4203.1.5.3" /* (&) (|) */ +#define LDAP_FEATURE_LANGUAGE_TAG_OPTIONS "1.3.6.1.4.1.4203.1.5.4" +#define LDAP_FEATURE_LANGUAGE_RANGE_OPTIONS "1.3.6.1.4.1.4203.1.5.5" +#define LDAP_FEATURE_MODIFY_INCREMENT "1.3.6.1.1.14" + +/* LDAP Experimental (works in progress) Features */ +#define LDAP_FEATURE_SUBORDINATE_SCOPE \ + "1.3.6.1.4.1.4203.666.8.1" /* "children" */ +#define LDAP_FEATURE_CHILDREN_SCOPE LDAP_FEATURE_SUBORDINATE_SCOPE + +/* + * specific LDAP instantiations of BER types we know about + */ + +/* Overview of LBER tag construction + * + * Bits + * ______ + * 8 7 | CLASS + * 0 0 = UNIVERSAL + * 0 1 = APPLICATION + * 1 0 = CONTEXT-SPECIFIC + * 1 1 = PRIVATE + * _____ + * | 6 | DATA-TYPE + * 0 = PRIMITIVE + * 1 = CONSTRUCTED + * ___________ + * | 5 ... 1 | TAG-NUMBER + */ + +/* general stuff */ +#define LDAP_TAG_MESSAGE ((ber_tag_t) 0x30U) /* constructed + 16 */ +#define LDAP_TAG_MSGID ((ber_tag_t) 0x02U) /* integer */ + +#define LDAP_TAG_LDAPDN ((ber_tag_t) 0x04U) /* octet string */ +#define LDAP_TAG_LDAPCRED ((ber_tag_t) 0x04U) /* octet string */ + +#define LDAP_TAG_CONTROLS ((ber_tag_t) 0xa0U) /* context specific + constructed + 0 */ +#define LDAP_TAG_REFERRAL ((ber_tag_t) 0xa3U) /* context specific + constructed + 3 */ + +#define LDAP_TAG_NEWSUPERIOR ((ber_tag_t) 0x80U) /* context-specific + primitive + 0 */ + +#define LDAP_TAG_EXOP_REQ_OID ((ber_tag_t) 0x80U) /* context specific + primitive */ +#define LDAP_TAG_EXOP_REQ_VALUE ((ber_tag_t) 0x81U) /* context specific + primitive */ +#define LDAP_TAG_EXOP_RES_OID ((ber_tag_t) 0x8aU) /* context specific + primitive */ +#define LDAP_TAG_EXOP_RES_VALUE ((ber_tag_t) 0x8bU) /* context specific + primitive */ + +#define LDAP_TAG_IM_RES_OID ((ber_tag_t) 0x80U) /* context specific + primitive */ +#define LDAP_TAG_IM_RES_VALUE ((ber_tag_t) 0x81U) /* context specific + primitive */ + +#define LDAP_TAG_SASL_RES_CREDS ((ber_tag_t) 0x87U) /* context specific + primitive */ + +/* LDAP Request Messages */ +#define LDAP_REQ_BIND ((ber_tag_t) 0x60U) /* application + constructed */ +#define LDAP_REQ_UNBIND ((ber_tag_t) 0x42U) /* application + primitive */ +#define LDAP_REQ_SEARCH ((ber_tag_t) 0x63U) /* application + constructed */ +#define LDAP_REQ_MODIFY ((ber_tag_t) 0x66U) /* application + constructed */ +#define LDAP_REQ_ADD ((ber_tag_t) 0x68U) /* application + constructed */ +#define LDAP_REQ_DELETE ((ber_tag_t) 0x4aU) /* application + primitive */ +#define LDAP_REQ_MODDN ((ber_tag_t) 0x6cU) /* application + constructed */ +#define LDAP_REQ_MODRDN LDAP_REQ_MODDN +#define LDAP_REQ_RENAME LDAP_REQ_MODDN +#define LDAP_REQ_COMPARE ((ber_tag_t) 0x6eU) /* application + constructed */ +#define LDAP_REQ_ABANDON ((ber_tag_t) 0x50U) /* application + primitive */ +#define LDAP_REQ_EXTENDED ((ber_tag_t) 0x77U) /* application + constructed */ + +/* LDAP Response Messages */ +#define LDAP_RES_BIND ((ber_tag_t) 0x61U) /* application + constructed */ +#define LDAP_RES_SEARCH_ENTRY ((ber_tag_t) 0x64U) /* application + constructed */ +#define LDAP_RES_SEARCH_REFERENCE ((ber_tag_t) 0x73U) /* V3: application + constructed */ +#define LDAP_RES_SEARCH_RESULT ((ber_tag_t) 0x65U) /* application + constructed */ +#define LDAP_RES_MODIFY ((ber_tag_t) 0x67U) /* application + constructed */ +#define LDAP_RES_ADD ((ber_tag_t) 0x69U) /* application + constructed */ +#define LDAP_RES_DELETE ((ber_tag_t) 0x6bU) /* application + constructed */ +#define LDAP_RES_MODDN ((ber_tag_t) 0x6dU) /* application + constructed */ +#define LDAP_RES_MODRDN LDAP_RES_MODDN /* application + constructed */ +#define LDAP_RES_RENAME LDAP_RES_MODDN /* application + constructed */ +#define LDAP_RES_COMPARE ((ber_tag_t) 0x6fU) /* application + constructed */ +#define LDAP_RES_EXTENDED ((ber_tag_t) 0x78U) /* V3: application + constructed */ +#define LDAP_RES_INTERMEDIATE ((ber_tag_t) 0x79U) /* V3+: application + constructed */ + +#define LDAP_RES_ANY (-1) +#define LDAP_RES_UNSOLICITED (0) + + +/* sasl methods */ +#define LDAP_SASL_SIMPLE ((char*)0) +#define LDAP_SASL_NULL ("") + + +/* authentication methods available */ +#define LDAP_AUTH_NONE ((ber_tag_t) 0x00U) /* no authentication */ +#define LDAP_AUTH_SIMPLE ((ber_tag_t) 0x80U) /* context specific + primitive */ +#define LDAP_AUTH_SASL ((ber_tag_t) 0xa3U) /* context specific + constructed */ +#define LDAP_AUTH_KRBV4 ((ber_tag_t) 0xffU) /* means do both of the following */ +#define LDAP_AUTH_KRBV41 ((ber_tag_t) 0x81U) /* context specific + primitive */ +#define LDAP_AUTH_KRBV42 ((ber_tag_t) 0x82U) /* context specific + primitive */ + +/* used by the Windows API but not used on the wire */ +#define LDAP_AUTH_NEGOTIATE ((ber_tag_t) 0x04FFU) + +/* filter types */ +#define LDAP_FILTER_AND ((ber_tag_t) 0xa0U) /* context specific + constructed */ +#define LDAP_FILTER_OR ((ber_tag_t) 0xa1U) /* context specific + constructed */ +#define LDAP_FILTER_NOT ((ber_tag_t) 0xa2U) /* context specific + constructed */ +#define LDAP_FILTER_EQUALITY ((ber_tag_t) 0xa3U) /* context specific + constructed */ +#define LDAP_FILTER_SUBSTRINGS ((ber_tag_t) 0xa4U) /* context specific + constructed */ +#define LDAP_FILTER_GE ((ber_tag_t) 0xa5U) /* context specific + constructed */ +#define LDAP_FILTER_LE ((ber_tag_t) 0xa6U) /* context specific + constructed */ +#define LDAP_FILTER_PRESENT ((ber_tag_t) 0x87U) /* context specific + primitive */ +#define LDAP_FILTER_APPROX ((ber_tag_t) 0xa8U) /* context specific + constructed */ +#define LDAP_FILTER_EXT ((ber_tag_t) 0xa9U) /* context specific + constructed */ + +/* extended filter component types */ +#define LDAP_FILTER_EXT_OID ((ber_tag_t) 0x81U) /* context specific */ +#define LDAP_FILTER_EXT_TYPE ((ber_tag_t) 0x82U) /* context specific */ +#define LDAP_FILTER_EXT_VALUE ((ber_tag_t) 0x83U) /* context specific */ +#define LDAP_FILTER_EXT_DNATTRS ((ber_tag_t) 0x84U) /* context specific */ + +/* substring filter component types */ +#define LDAP_SUBSTRING_INITIAL ((ber_tag_t) 0x80U) /* context specific */ +#define LDAP_SUBSTRING_ANY ((ber_tag_t) 0x81U) /* context specific */ +#define LDAP_SUBSTRING_FINAL ((ber_tag_t) 0x82U) /* context specific */ + +/* search scopes */ +#define LDAP_SCOPE_BASE ((ber_int_t) 0x0000) +#define LDAP_SCOPE_BASEOBJECT LDAP_SCOPE_BASE +#define LDAP_SCOPE_ONELEVEL ((ber_int_t) 0x0001) +#define LDAP_SCOPE_ONE LDAP_SCOPE_ONELEVEL +#define LDAP_SCOPE_SUBTREE ((ber_int_t) 0x0002) +#define LDAP_SCOPE_SUB LDAP_SCOPE_SUBTREE +#define LDAP_SCOPE_SUBORDINATE ((ber_int_t) 0x0003) /* OpenLDAP extension */ +#define LDAP_SCOPE_CHILDREN LDAP_SCOPE_SUBORDINATE +#define LDAP_SCOPE_DEFAULT ((ber_int_t) -1) /* OpenLDAP extension */ + +/* substring filter component types */ +#define LDAP_SUBSTRING_INITIAL ((ber_tag_t) 0x80U) /* context specific */ +#define LDAP_SUBSTRING_ANY ((ber_tag_t) 0x81U) /* context specific */ +#define LDAP_SUBSTRING_FINAL ((ber_tag_t) 0x82U) /* context specific */ + +/* + * LDAP Result Codes + */ +#define LDAP_SUCCESS 0x00 + +#define LDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y))) + +#define LDAP_OPERATIONS_ERROR 0x01 +#define LDAP_PROTOCOL_ERROR 0x02 +#define LDAP_TIMELIMIT_EXCEEDED 0x03 +#define LDAP_SIZELIMIT_EXCEEDED 0x04 +#define LDAP_COMPARE_FALSE 0x05 +#define LDAP_COMPARE_TRUE 0x06 +#define LDAP_AUTH_METHOD_NOT_SUPPORTED 0x07 +#define LDAP_STRONG_AUTH_NOT_SUPPORTED LDAP_AUTH_METHOD_NOT_SUPPORTED +#define LDAP_STRONG_AUTH_REQUIRED 0x08 +#define LDAP_STRONGER_AUTH_REQUIRED LDAP_STRONG_AUTH_REQUIRED +#define LDAP_PARTIAL_RESULTS 0x09 /* LDAPv2+ (not LDAPv3) */ + +#define LDAP_REFERRAL 0x0a /* LDAPv3 */ +#define LDAP_ADMINLIMIT_EXCEEDED 0x0b /* LDAPv3 */ +#define LDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* LDAPv3 */ +#define LDAP_CONFIDENTIALITY_REQUIRED 0x0d /* LDAPv3 */ +#define LDAP_SASL_BIND_IN_PROGRESS 0x0e /* LDAPv3 */ + +#define LDAP_ATTR_ERROR(n) LDAP_RANGE((n),0x10,0x15) /* 16-21 */ + +#define LDAP_NO_SUCH_ATTRIBUTE 0x10 +#define LDAP_UNDEFINED_TYPE 0x11 +#define LDAP_INAPPROPRIATE_MATCHING 0x12 +#define LDAP_CONSTRAINT_VIOLATION 0x13 +#define LDAP_TYPE_OR_VALUE_EXISTS 0x14 +#define LDAP_INVALID_SYNTAX 0x15 + +#define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */ + +#define LDAP_NO_SUCH_OBJECT 0x20 +#define LDAP_ALIAS_PROBLEM 0x21 +#define LDAP_INVALID_DN_SYNTAX 0x22 +#define LDAP_IS_LEAF 0x23 /* not LDAPv3 */ +#define LDAP_ALIAS_DEREF_PROBLEM 0x24 + +#define LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x2F,0x32) /* 47-50 */ + +#define LDAP_X_PROXY_AUTHZ_FAILURE 0x2F /* LDAPv3 proxy authorization */ +#define LDAP_INAPPROPRIATE_AUTH 0x30 +#define LDAP_INVALID_CREDENTIALS 0x31 +#define LDAP_INSUFFICIENT_ACCESS 0x32 + +#define LDAP_SERVICE_ERROR(n) LDAP_RANGE((n),0x33,0x36) /* 51-54 */ + +#define LDAP_BUSY 0x33 +#define LDAP_UNAVAILABLE 0x34 +#define LDAP_UNWILLING_TO_PERFORM 0x35 +#define LDAP_LOOP_DETECT 0x36 + +#define LDAP_UPDATE_ERROR(n) LDAP_RANGE((n),0x40,0x47) /* 64-69,71 */ + +#define LDAP_NAMING_VIOLATION 0x40 +#define LDAP_OBJECT_CLASS_VIOLATION 0x41 +#define LDAP_NOT_ALLOWED_ON_NONLEAF 0x42 +#define LDAP_NOT_ALLOWED_ON_RDN 0x43 +#define LDAP_ALREADY_EXISTS 0x44 +#define LDAP_NO_OBJECT_CLASS_MODS 0x45 +#define LDAP_RESULTS_TOO_LARGE 0x46 /* CLDAP */ +#define LDAP_AFFECTS_MULTIPLE_DSAS 0x47 + +#define LDAP_VLV_ERROR 0x4C + +#define LDAP_OTHER 0x50 + +/* LCUP operation codes (113-117) - not implemented */ +#define LDAP_CUP_RESOURCES_EXHAUSTED 0x71 +#define LDAP_CUP_SECURITY_VIOLATION 0x72 +#define LDAP_CUP_INVALID_DATA 0x73 +#define LDAP_CUP_UNSUPPORTED_SCHEME 0x74 +#define LDAP_CUP_RELOAD_REQUIRED 0x75 + +/* Cancel operation codes (118-121) */ +#define LDAP_CANCELLED 0x76 +#define LDAP_NO_SUCH_OPERATION 0x77 +#define LDAP_TOO_LATE 0x78 +#define LDAP_CANNOT_CANCEL 0x79 + +/* Assertion control (122) */ +#define LDAP_ASSERTION_FAILED 0x7A + +/* Proxied Authorization Denied (123) */ +#define LDAP_PROXIED_AUTHORIZATION_DENIED 0x7B + +/* Experimental result codes */ +#define LDAP_E_ERROR(n) LDAP_RANGE((n),0x1000,0x3FFF) + +/* LDAP Sync (4096) */ +#define LDAP_SYNC_REFRESH_REQUIRED 0x1000 + + +/* Private Use result codes */ +#define LDAP_X_ERROR(n) LDAP_RANGE((n),0x4000,0xFFFF) + +#define LDAP_X_SYNC_REFRESH_REQUIRED 0x4100 /* defunct */ +#define LDAP_X_ASSERTION_FAILED 0x410f /* defunct */ + +/* for the LDAP No-Op control */ +#define LDAP_X_NO_OPERATION 0x410e + +/* for the Chaining Behavior control (consecutive result codes requested; + * see <draft-sermersheim-ldap-chaining> ) */ +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR +#define LDAP_X_NO_REFERRALS_FOUND 0x4110 +#define LDAP_X_CANNOT_CHAIN 0x4111 +#endif + +/* for Distributed Procedures (see <draft-sermersheim-ldap-distproc>) */ +#ifdef LDAP_X_DISTPROC_BASE +#define LDAP_X_INVALIDREFERENCE 0x4112 +#endif + +#define LDAP_TXN_SPECIFY_OKAY 0x4120 +#define LDAP_TXN_ID_INVALID 0x4121 + +/* API Error Codes + * + * Based on draft-ietf-ldap-c-api-xx + * but with new negative code values + */ +#define LDAP_API_ERROR(n) ((n)<0) +#define LDAP_API_RESULT(n) ((n)<=0) + +#define LDAP_SERVER_DOWN (-1) +#define LDAP_LOCAL_ERROR (-2) +#define LDAP_ENCODING_ERROR (-3) +#define LDAP_DECODING_ERROR (-4) +#define LDAP_TIMEOUT (-5) +#define LDAP_AUTH_UNKNOWN (-6) +#define LDAP_FILTER_ERROR (-7) +#define LDAP_USER_CANCELLED (-8) +#define LDAP_PARAM_ERROR (-9) +#define LDAP_NO_MEMORY (-10) +#define LDAP_CONNECT_ERROR (-11) +#define LDAP_NOT_SUPPORTED (-12) +#define LDAP_CONTROL_NOT_FOUND (-13) +#define LDAP_NO_RESULTS_RETURNED (-14) +#define LDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */ +#define LDAP_CLIENT_LOOP (-16) +#define LDAP_REFERRAL_LIMIT_EXCEEDED (-17) +#define LDAP_X_CONNECTING (-18) + + +/* + * This structure represents both ldap messages and ldap responses. + * These are really the same, except in the case of search responses, + * where a response has multiple messages. + */ + +typedef struct ldapmsg LDAPMessage; + +/* for modifications */ +typedef struct ldapmod { + int mod_op; + +#define LDAP_MOD_OP (0x0007) +#define LDAP_MOD_ADD (0x0000) +#define LDAP_MOD_DELETE (0x0001) +#define LDAP_MOD_REPLACE (0x0002) +#define LDAP_MOD_INCREMENT (0x0003) /* OpenLDAP extension */ +#define LDAP_MOD_BVALUES (0x0080) +/* IMPORTANT: do not use code 0x1000 (or above), + * it is used internally by the backends! + * (see ldap/servers/slapd/slap.h) + */ + + char *mod_type; + union mod_vals_u { + char **modv_strvals; + struct berval **modv_bvals; + } mod_vals; +#define mod_values mod_vals.modv_strvals +#define mod_bvalues mod_vals.modv_bvals +} LDAPMod; + +/* + * structure representing an ldap session which can + * encompass connections to multiple servers (in the + * face of referrals). + */ +typedef struct ldap LDAP; + +#define LDAP_DEREF_NEVER 0x00 +#define LDAP_DEREF_SEARCHING 0x01 +#define LDAP_DEREF_FINDING 0x02 +#define LDAP_DEREF_ALWAYS 0x03 + +#define LDAP_NO_LIMIT 0 + +/* how many messages to retrieve results for */ +#define LDAP_MSG_ONE 0x00 +#define LDAP_MSG_ALL 0x01 +#define LDAP_MSG_RECEIVED 0x02 + +/* + * types for ldap URL handling + */ +typedef struct ldap_url_desc { + struct ldap_url_desc *lud_next; + char *lud_scheme; + char *lud_host; + int lud_port; + char *lud_dn; + char **lud_attrs; + int lud_scope; + char *lud_filter; + char **lud_exts; + int lud_crit_exts; +} LDAPURLDesc; + +#define LDAP_URL_SUCCESS 0x00 /* Success */ +#define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */ +#define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */ + +#define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */ +#define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */ +#define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */ +#define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */ +#define LDAP_URL_ERR_BADATTRS 0x07 /* bad (or missing) attributes */ +#define LDAP_URL_ERR_BADSCOPE 0x08 /* scope string is invalid (or missing) */ +#define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */ +#define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */ + +/* + * LDAP sync (RFC4533) API + */ + +typedef struct ldap_sync_t ldap_sync_t; + +typedef enum { + /* these are private - the client should never see them */ + LDAP_SYNC_CAPI_NONE = -1, + + LDAP_SYNC_CAPI_PHASE_FLAG = 0x10U, + LDAP_SYNC_CAPI_IDSET_FLAG = 0x20U, + LDAP_SYNC_CAPI_DONE_FLAG = 0x40U, + + /* these are passed to ls_search_entry() */ + LDAP_SYNC_CAPI_PRESENT = LDAP_SYNC_PRESENT, + LDAP_SYNC_CAPI_ADD = LDAP_SYNC_ADD, + LDAP_SYNC_CAPI_MODIFY = LDAP_SYNC_MODIFY, + LDAP_SYNC_CAPI_DELETE = LDAP_SYNC_DELETE, + + /* these are passed to ls_intermediate() */ + LDAP_SYNC_CAPI_PRESENTS = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_PRESENT ), + LDAP_SYNC_CAPI_DELETES = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_DELETE ), + + LDAP_SYNC_CAPI_PRESENTS_IDSET = ( LDAP_SYNC_CAPI_PRESENTS | LDAP_SYNC_CAPI_IDSET_FLAG ), + LDAP_SYNC_CAPI_DELETES_IDSET = ( LDAP_SYNC_CAPI_DELETES | LDAP_SYNC_CAPI_IDSET_FLAG ), + + LDAP_SYNC_CAPI_DONE = ( LDAP_SYNC_CAPI_DONE_FLAG | LDAP_SYNC_CAPI_PRESENTS ) +} ldap_sync_refresh_t; + +/* + * Called when an entry is returned by ldap_result(). + * If phase is LDAP_SYNC_CAPI_ADD or LDAP_SYNC_CAPI_MODIFY, + * the entry has been either added or modified, and thus + * the complete view of the entry should be in the LDAPMessage. + * If phase is LDAP_SYNC_CAPI_PRESENT or LDAP_SYNC_CAPI_DELETE, + * only the DN should be in the LDAPMessage. + */ +typedef int (*ldap_sync_search_entry_f) LDAP_P(( + ldap_sync_t *ls, + LDAPMessage *msg, + struct berval *entryUUID, + ldap_sync_refresh_t phase )); + +/* + * Called when a reference is returned; the client should know + * what to do with it. + */ +typedef int (*ldap_sync_search_reference_f) LDAP_P(( + ldap_sync_t *ls, + LDAPMessage *msg )); + +/* + * Called when specific intermediate/final messages are returned. + * If phase is LDAP_SYNC_CAPI_PRESENTS or LDAP_SYNC_CAPI_DELETES, + * a "presents" or "deletes" phase begins. + * If phase is LDAP_SYNC_CAPI_DONE, a special "presents" phase + * with refreshDone set to "TRUE" has been returned, to indicate + * that the refresh phase of a refreshAndPersist is complete. + * In the above cases, syncUUIDs is NULL. + * + * If phase is LDAP_SYNC_CAPI_PRESENTS_IDSET or + * LDAP_SYNC_CAPI_DELETES_IDSET, syncUUIDs is an array of UUIDs + * that are either present or have been deleted. + */ +typedef int (*ldap_sync_intermediate_f) LDAP_P(( + ldap_sync_t *ls, + LDAPMessage *msg, + BerVarray syncUUIDs, + ldap_sync_refresh_t phase )); + +/* + * Called when a searchResultDone is returned. In refreshAndPersist, + * this can only occur if the search for any reason is being terminated + * by the server. + */ +typedef int (*ldap_sync_search_result_f) LDAP_P(( + ldap_sync_t *ls, + LDAPMessage *msg, + int refreshDeletes )); + +/* + * This structure contains all information about the persistent search; + * the caller is responsible for connecting, setting version, binding, tls... + */ +struct ldap_sync_t { + /* conf search params */ + char *ls_base; + int ls_scope; + char *ls_filter; + char **ls_attrs; + int ls_timelimit; + int ls_sizelimit; + + /* poll timeout */ + int ls_timeout; + + /* helpers - add as appropriate */ + ldap_sync_search_entry_f ls_search_entry; + ldap_sync_search_reference_f ls_search_reference; + ldap_sync_intermediate_f ls_intermediate; + ldap_sync_search_result_f ls_search_result; + + /* set by the caller as appropriate */ + void *ls_private; + + /* conn stuff */ + LDAP *ls_ld; + + /* --- the parameters below are private - do not modify --- */ + + /* FIXME: make the structure opaque, and provide an interface + * to modify the public values? */ + + /* result stuff */ + int ls_msgid; + + /* sync stuff */ + /* needed by refreshOnly */ + int ls_reloadHint; + + /* opaque - need to pass between sessions, updated by the API */ + struct berval ls_cookie; + + /* state variable - do not modify */ + ldap_sync_refresh_t ls_refreshPhase; +}; + +/* + * End of LDAP sync (RFC4533) API + */ + +/* + * Connection callbacks... + */ +struct ldap_conncb; +struct sockaddr; + +/* Called after a connection is established */ +typedef int (ldap_conn_add_f) LDAP_P(( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, struct sockaddr *addr, + struct ldap_conncb *ctx )); +/* Called before a connection is closed */ +typedef void (ldap_conn_del_f) LDAP_P(( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx )); + +/* Callbacks are pushed on a stack. Last one pushed is first one executed. The + * delete callback is called with a NULL Sockbuf just before freeing the LDAP handle. + */ +typedef struct ldap_conncb { + ldap_conn_add_f *lc_add; + ldap_conn_del_f *lc_del; + void *lc_arg; +} ldap_conncb; + +/* + * The API draft spec says we should declare (or cause to be declared) + * 'struct timeval'. We don't. See IETF LDAPext discussions. + */ +struct timeval; + +/* + * in options.c: + */ +LDAP_F( int ) +ldap_get_option LDAP_P(( + LDAP *ld, + int option, + void *outvalue)); + +LDAP_F( int ) +ldap_set_option LDAP_P(( + LDAP *ld, + int option, + LDAP_CONST void *invalue)); + +/* V3 REBIND Function Callback Prototype */ +typedef int (LDAP_REBIND_PROC) LDAP_P(( + LDAP *ld, LDAP_CONST char *url, + ber_tag_t request, ber_int_t msgid, + void *params )); + +LDAP_F( int ) +ldap_set_rebind_proc LDAP_P(( + LDAP *ld, + LDAP_REBIND_PROC *rebind_proc, + void *params )); + +/* V3 referral selection Function Callback Prototype */ +typedef int (LDAP_NEXTREF_PROC) LDAP_P(( + LDAP *ld, char ***refsp, int *cntp, + void *params )); + +LDAP_F( int ) +ldap_set_nextref_proc LDAP_P(( + LDAP *ld, + LDAP_NEXTREF_PROC *nextref_proc, + void *params )); + +/* V3 URLLIST Function Callback Prototype */ +typedef int (LDAP_URLLIST_PROC) LDAP_P(( + LDAP *ld, + LDAPURLDesc **urllist, + LDAPURLDesc **url, + void *params )); + +LDAP_F( int ) +ldap_set_urllist_proc LDAP_P(( + LDAP *ld, + LDAP_URLLIST_PROC *urllist_proc, + void *params )); + +/* + * in controls.c: + */ +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_create_control LDAP_P(( /* deprecated, use ldap_control_create */ + LDAP_CONST char *requestOID, + BerElement *ber, + int iscritical, + LDAPControl **ctrlp )); + +LDAP_F( LDAPControl * ) +ldap_find_control LDAP_P(( /* deprecated, use ldap_control_find */ + LDAP_CONST char *oid, + LDAPControl **ctrls )); +#endif + +LDAP_F( int ) +ldap_control_create LDAP_P(( + LDAP_CONST char *requestOID, + int iscritical, + struct berval *value, + int dupval, + LDAPControl **ctrlp )); + +LDAP_F( LDAPControl * ) +ldap_control_find LDAP_P(( + LDAP_CONST char *oid, + LDAPControl **ctrls, + LDAPControl ***nextctrlp )); + +LDAP_F( void ) +ldap_control_free LDAP_P(( + LDAPControl *ctrl )); + +LDAP_F( void ) +ldap_controls_free LDAP_P(( + LDAPControl **ctrls )); + +LDAP_F( LDAPControl ** ) +ldap_controls_dup LDAP_P(( + LDAPControl *LDAP_CONST *controls )); + +LDAP_F( LDAPControl * ) +ldap_control_dup LDAP_P(( + LDAP_CONST LDAPControl *c )); + +/* + * in dnssrv.c: + */ +LDAP_F( int ) +ldap_domain2dn LDAP_P(( + LDAP_CONST char* domain, + char** dn )); + +LDAP_F( int ) +ldap_dn2domain LDAP_P(( + LDAP_CONST char* dn, + char** domain )); + +LDAP_F( int ) +ldap_domain2hostlist LDAP_P(( + LDAP_CONST char *domain, + char** hostlist )); + +/* + * in extended.c: + */ +LDAP_F( int ) +ldap_extended_operation LDAP_P(( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_extended_operation_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + char **retoidp, + struct berval **retdatap )); + +LDAP_F( int ) +ldap_parse_extended_result LDAP_P(( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + int freeit )); + +LDAP_F( int ) +ldap_parse_intermediate LDAP_P(( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + LDAPControl ***serverctrls, + int freeit )); + + +/* + * in abandon.c: + */ +LDAP_F( int ) +ldap_abandon_ext LDAP_P(( + LDAP *ld, + int msgid, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_abandon LDAP_P(( /* deprecated, use ldap_abandon_ext */ + LDAP *ld, + int msgid )); +#endif + +/* + * in add.c: + */ +LDAP_F( int ) +ldap_add_ext LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_add_ext_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_add LDAP_P(( /* deprecated, use ldap_add_ext */ + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs )); + +LDAP_F( int ) +ldap_add_s LDAP_P(( /* deprecated, use ldap_add_ext_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs )); +#endif + + +/* + * in sasl.c: + */ +LDAP_F( int ) +ldap_sasl_bind LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +/* Interaction flags (should be passed about in a control) + * Automatic (default): use defaults, prompt otherwise + * Interactive: prompt always + * Quiet: never prompt + */ +#define LDAP_SASL_AUTOMATIC 0U +#define LDAP_SASL_INTERACTIVE 1U +#define LDAP_SASL_QUIET 2U + +/* + * V3 SASL Interaction Function Callback Prototype + * when using Cyrus SASL, interact is pointer to sasl_interact_t + * should likely passed in a control (and provided controls) + */ +typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P(( + LDAP *ld, unsigned flags, void* defaults, void *interact )); + +LDAP_F( int ) +ldap_sasl_interactive_bind LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *saslMechanism, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults, + + /* as obtained from ldap_result() */ + LDAPMessage *result, + + /* returned during bind processing */ + const char **rmech, + int *msgid )); + +LDAP_F( int ) +ldap_sasl_interactive_bind_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *saslMechanism, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults )); + +LDAP_F( int ) +ldap_sasl_bind_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct berval **servercredp )); + +LDAP_F( int ) +ldap_parse_sasl_bind_result LDAP_P(( + LDAP *ld, + LDAPMessage *res, + struct berval **servercredp, + int freeit )); + +#if LDAP_DEPRECATED +/* + * in bind.c: + * (deprecated) + */ +LDAP_F( int ) +ldap_bind LDAP_P(( /* deprecated, use ldap_sasl_bind */ + LDAP *ld, + LDAP_CONST char *who, + LDAP_CONST char *passwd, + int authmethod )); + +LDAP_F( int ) +ldap_bind_s LDAP_P(( /* deprecated, use ldap_sasl_bind_s */ + LDAP *ld, + LDAP_CONST char *who, + LDAP_CONST char *cred, + int authmethod )); + +/* + * in sbind.c: + */ +LDAP_F( int ) +ldap_simple_bind LDAP_P(( /* deprecated, use ldap_sasl_bind */ + LDAP *ld, + LDAP_CONST char *who, + LDAP_CONST char *passwd )); + +LDAP_F( int ) +ldap_simple_bind_s LDAP_P(( /* deprecated, use ldap_sasl_bind_s */ + LDAP *ld, + LDAP_CONST char *who, + LDAP_CONST char *passwd )); + +#endif + + +/* + * in compare.c: + */ +LDAP_F( int ) +ldap_compare_ext LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_compare_ext_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_compare LDAP_P(( /* deprecated, use ldap_compare_ext */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value )); + +LDAP_F( int ) +ldap_compare_s LDAP_P(( /* deprecated, use ldap_compare_ext_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value )); +#endif + + +/* + * in delete.c: + */ +LDAP_F( int ) +ldap_delete_ext LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_delete_ext_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_delete LDAP_P(( /* deprecated, use ldap_delete_ext */ + LDAP *ld, + LDAP_CONST char *dn )); + +LDAP_F( int ) +ldap_delete_s LDAP_P(( /* deprecated, use ldap_delete_ext_s */ + LDAP *ld, + LDAP_CONST char *dn )); +#endif + + +/* + * in error.c: + */ +LDAP_F( int ) +ldap_parse_result LDAP_P(( + LDAP *ld, + LDAPMessage *res, + int *errcodep, + char **matcheddnp, + char **diagmsgp, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit )); + +LDAP_F( char * ) +ldap_err2string LDAP_P(( + int err )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_result2error LDAP_P(( /* deprecated, use ldap_parse_result */ + LDAP *ld, + LDAPMessage *r, + int freeit )); + +LDAP_F( void ) +ldap_perror LDAP_P(( /* deprecated, use ldap_err2string */ + LDAP *ld, + LDAP_CONST char *s )); +#endif + + +/* + * in modify.c: + */ +LDAP_F( int ) +ldap_modify_ext LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_modify_ext_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_modify LDAP_P(( /* deprecated, use ldap_modify_ext */ + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods )); + +LDAP_F( int ) +ldap_modify_s LDAP_P(( /* deprecated, use ldap_modify_ext_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods )); +#endif + + +/* + * in modrdn.c: + */ +LDAP_F( int ) +ldap_rename LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_rename_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_rename2 LDAP_P(( /* deprecated, use ldap_rename */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn )); + +LDAP_F( int ) +ldap_rename2_s LDAP_P(( /* deprecated, use ldap_rename_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn )); + +LDAP_F( int ) +ldap_modrdn LDAP_P(( /* deprecated, use ldap_rename */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn )); + +LDAP_F( int ) +ldap_modrdn_s LDAP_P(( /* deprecated, use ldap_rename_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn )); + +LDAP_F( int ) +ldap_modrdn2 LDAP_P(( /* deprecated, use ldap_rename */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + int deleteoldrdn )); + +LDAP_F( int ) +ldap_modrdn2_s LDAP_P(( /* deprecated, use ldap_rename_s */ + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + int deleteoldrdn)); +#endif + + +/* + * in open.c: + */ +#if LDAP_DEPRECATED +LDAP_F( LDAP * ) +ldap_init LDAP_P(( /* deprecated, use ldap_create or ldap_initialize */ + LDAP_CONST char *host, + int port )); + +LDAP_F( LDAP * ) +ldap_open LDAP_P(( /* deprecated, use ldap_create or ldap_initialize */ + LDAP_CONST char *host, + int port )); +#endif + +LDAP_F( int ) +ldap_create LDAP_P(( + LDAP **ldp )); + +LDAP_F( int ) +ldap_initialize LDAP_P(( + LDAP **ldp, + LDAP_CONST char *url )); + +LDAP_F( LDAP * ) +ldap_dup LDAP_P(( + LDAP *old )); + +LDAP_F( int ) +ldap_connect( LDAP *ld ); + +/* + * in tls.c + */ + +LDAP_F( int ) +ldap_tls_inplace LDAP_P(( + LDAP *ld )); + +LDAP_F( int ) +ldap_start_tls LDAP_P(( + LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_install_tls LDAP_P(( + LDAP *ld )); + +LDAP_F( int ) +ldap_start_tls_s LDAP_P(( + LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls )); + +/* + * in messages.c: + */ +LDAP_F( LDAPMessage * ) +ldap_first_message LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +LDAP_F( LDAPMessage * ) +ldap_next_message LDAP_P(( + LDAP *ld, + LDAPMessage *msg )); + +LDAP_F( int ) +ldap_count_messages LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +/* + * in references.c: + */ +LDAP_F( LDAPMessage * ) +ldap_first_reference LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +LDAP_F( LDAPMessage * ) +ldap_next_reference LDAP_P(( + LDAP *ld, + LDAPMessage *ref )); + +LDAP_F( int ) +ldap_count_references LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +LDAP_F( int ) +ldap_parse_reference LDAP_P(( + LDAP *ld, + LDAPMessage *ref, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit)); + + +/* + * in getentry.c: + */ +LDAP_F( LDAPMessage * ) +ldap_first_entry LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +LDAP_F( LDAPMessage * ) +ldap_next_entry LDAP_P(( + LDAP *ld, + LDAPMessage *entry )); + +LDAP_F( int ) +ldap_count_entries LDAP_P(( + LDAP *ld, + LDAPMessage *chain )); + +LDAP_F( int ) +ldap_get_entry_controls LDAP_P(( + LDAP *ld, + LDAPMessage *entry, + LDAPControl ***serverctrls)); + + +/* + * in addentry.c + */ +LDAP_F( LDAPMessage * ) +ldap_delete_result_entry LDAP_P(( + LDAPMessage **list, + LDAPMessage *e )); + +LDAP_F( void ) +ldap_add_result_entry LDAP_P(( + LDAPMessage **list, + LDAPMessage *e )); + + +/* + * in getdn.c + */ +LDAP_F( char * ) +ldap_get_dn LDAP_P(( + LDAP *ld, + LDAPMessage *entry )); + +typedef struct ldap_ava { + struct berval la_attr; + struct berval la_value; + unsigned la_flags; +#define LDAP_AVA_NULL 0x0000U +#define LDAP_AVA_STRING 0x0001U +#define LDAP_AVA_BINARY 0x0002U +#define LDAP_AVA_NONPRINTABLE 0x0004U +#define LDAP_AVA_FREE_ATTR 0x0010U +#define LDAP_AVA_FREE_VALUE 0x0020U + + void *la_private; +} LDAPAVA; + +typedef LDAPAVA** LDAPRDN; +typedef LDAPRDN* LDAPDN; + +/* DN formats */ +#define LDAP_DN_FORMAT_LDAP 0x0000U +#define LDAP_DN_FORMAT_LDAPV3 0x0010U +#define LDAP_DN_FORMAT_LDAPV2 0x0020U +#define LDAP_DN_FORMAT_DCE 0x0030U +#define LDAP_DN_FORMAT_UFN 0x0040U /* dn2str only */ +#define LDAP_DN_FORMAT_AD_CANONICAL 0x0050U /* dn2str only */ +#define LDAP_DN_FORMAT_LBER 0x00F0U /* for testing only */ +#define LDAP_DN_FORMAT_MASK 0x00F0U + +/* DN flags */ +#define LDAP_DN_PRETTY 0x0100U +#define LDAP_DN_SKIP 0x0200U +#define LDAP_DN_P_NOLEADTRAILSPACES 0x1000U +#define LDAP_DN_P_NOSPACEAFTERRDN 0x2000U +#define LDAP_DN_PEDANTIC 0xF000U + +LDAP_F( void ) ldap_rdnfree LDAP_P(( LDAPRDN rdn )); +LDAP_F( void ) ldap_dnfree LDAP_P(( LDAPDN dn )); + +LDAP_F( int ) +ldap_bv2dn LDAP_P(( + struct berval *bv, + LDAPDN *dn, + unsigned flags )); + +LDAP_F( int ) +ldap_str2dn LDAP_P(( + LDAP_CONST char *str, + LDAPDN *dn, + unsigned flags )); + +LDAP_F( int ) +ldap_dn2bv LDAP_P(( + LDAPDN dn, + struct berval *bv, + unsigned flags )); + +LDAP_F( int ) +ldap_dn2str LDAP_P(( + LDAPDN dn, + char **str, + unsigned flags )); + +LDAP_F( int ) +ldap_bv2rdn LDAP_P(( + struct berval *bv, + LDAPRDN *rdn, + char **next, + unsigned flags )); + +LDAP_F( int ) +ldap_str2rdn LDAP_P(( + LDAP_CONST char *str, + LDAPRDN *rdn, + char **next, + unsigned flags )); + +LDAP_F( int ) +ldap_rdn2bv LDAP_P(( + LDAPRDN rdn, + struct berval *bv, + unsigned flags )); + +LDAP_F( int ) +ldap_rdn2str LDAP_P(( + LDAPRDN rdn, + char **str, + unsigned flags )); + +LDAP_F( int ) +ldap_dn_normalize LDAP_P(( + LDAP_CONST char *in, unsigned iflags, + char **out, unsigned oflags )); + +LDAP_F( char * ) +ldap_dn2ufn LDAP_P(( /* deprecated, use ldap_str2dn/dn2str */ + LDAP_CONST char *dn )); + +LDAP_F( char ** ) +ldap_explode_dn LDAP_P(( /* deprecated, ldap_str2dn */ + LDAP_CONST char *dn, + int notypes )); + +LDAP_F( char ** ) +ldap_explode_rdn LDAP_P(( /* deprecated, ldap_str2rdn */ + LDAP_CONST char *rdn, + int notypes )); + +typedef int LDAPDN_rewrite_func + LDAP_P(( LDAPDN dn, unsigned flags, void *ctx )); + +LDAP_F( int ) +ldap_X509dn2bv LDAP_P(( void *x509_name, struct berval *dn, + LDAPDN_rewrite_func *func, unsigned flags )); + +LDAP_F( char * ) +ldap_dn2dcedn LDAP_P(( /* deprecated, ldap_str2dn/dn2str */ + LDAP_CONST char *dn )); + +LDAP_F( char * ) +ldap_dcedn2dn LDAP_P(( /* deprecated, ldap_str2dn/dn2str */ + LDAP_CONST char *dce )); + +LDAP_F( char * ) +ldap_dn2ad_canonical LDAP_P(( /* deprecated, ldap_str2dn/dn2str */ + LDAP_CONST char *dn )); + +LDAP_F( int ) +ldap_get_dn_ber LDAP_P(( + LDAP *ld, LDAPMessage *e, BerElement **berout, struct berval *dn )); + +LDAP_F( int ) +ldap_get_attribute_ber LDAP_P(( + LDAP *ld, LDAPMessage *e, BerElement *ber, struct berval *attr, + struct berval **vals )); + +/* + * in getattr.c + */ +LDAP_F( char * ) +ldap_first_attribute LDAP_P(( + LDAP *ld, + LDAPMessage *entry, + BerElement **ber )); + +LDAP_F( char * ) +ldap_next_attribute LDAP_P(( + LDAP *ld, + LDAPMessage *entry, + BerElement *ber )); + + +/* + * in getvalues.c + */ +LDAP_F( struct berval ** ) +ldap_get_values_len LDAP_P(( + LDAP *ld, + LDAPMessage *entry, + LDAP_CONST char *target )); + +LDAP_F( int ) +ldap_count_values_len LDAP_P(( + struct berval **vals )); + +LDAP_F( void ) +ldap_value_free_len LDAP_P(( + struct berval **vals )); + +#if LDAP_DEPRECATED +LDAP_F( char ** ) +ldap_get_values LDAP_P(( /* deprecated, use ldap_get_values_len */ + LDAP *ld, + LDAPMessage *entry, + LDAP_CONST char *target )); + +LDAP_F( int ) +ldap_count_values LDAP_P(( /* deprecated, use ldap_count_values_len */ + char **vals )); + +LDAP_F( void ) +ldap_value_free LDAP_P(( /* deprecated, use ldap_value_free_len */ + char **vals )); +#endif + +/* + * in result.c: + */ +LDAP_F( int ) +ldap_result LDAP_P(( + LDAP *ld, + int msgid, + int all, + struct timeval *timeout, + LDAPMessage **result )); + +LDAP_F( int ) +ldap_msgtype LDAP_P(( + LDAPMessage *lm )); + +LDAP_F( int ) +ldap_msgid LDAP_P(( + LDAPMessage *lm )); + +LDAP_F( int ) +ldap_msgfree LDAP_P(( + LDAPMessage *lm )); + +LDAP_F( int ) +ldap_msgdelete LDAP_P(( + LDAP *ld, + int msgid )); + + +/* + * in search.c: + */ +LDAP_F( int ) +ldap_bv2escaped_filter_value LDAP_P(( + struct berval *in, + struct berval *out )); + +LDAP_F( int ) +ldap_search_ext LDAP_P(( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct timeval *timeout, + int sizelimit, + int *msgidp )); + +LDAP_F( int ) +ldap_search_ext_s LDAP_P(( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + struct timeval *timeout, + int sizelimit, + LDAPMessage **res )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_search LDAP_P(( /* deprecated, use ldap_search_ext */ + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly )); + +LDAP_F( int ) +ldap_search_s LDAP_P(( /* deprecated, use ldap_search_ext_s */ + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPMessage **res )); + +LDAP_F( int ) +ldap_search_st LDAP_P(( /* deprecated, use ldap_search_ext_s */ + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + struct timeval *timeout, + LDAPMessage **res )); +#endif + +/* + * in unbind.c + */ +LDAP_F( int ) +ldap_unbind_ext LDAP_P(( + LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls)); + +LDAP_F( int ) +ldap_unbind_ext_s LDAP_P(( + LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls)); + +LDAP_F( int ) +ldap_destroy LDAP_P(( + LDAP *ld)); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_unbind LDAP_P(( /* deprecated, use ldap_unbind_ext */ + LDAP *ld )); + +LDAP_F( int ) +ldap_unbind_s LDAP_P(( /* deprecated, use ldap_unbind_ext_s */ + LDAP *ld )); +#endif + +/* + * in filter.c + */ +LDAP_F( int ) +ldap_put_vrFilter LDAP_P(( + BerElement *ber, + const char *vrf )); + +/* + * in free.c + */ + +LDAP_F( void * ) +ldap_memalloc LDAP_P(( + ber_len_t s )); + +LDAP_F( void * ) +ldap_memrealloc LDAP_P(( + void* p, + ber_len_t s )); + +LDAP_F( void * ) +ldap_memcalloc LDAP_P(( + ber_len_t n, + ber_len_t s )); + +LDAP_F( void ) +ldap_memfree LDAP_P(( + void* p )); + +LDAP_F( void ) +ldap_memvfree LDAP_P(( + void** v )); + +LDAP_F( char * ) +ldap_strdup LDAP_P(( + LDAP_CONST char * )); + +LDAP_F( void ) +ldap_mods_free LDAP_P(( + LDAPMod **mods, + int freemods )); + + +#if LDAP_DEPRECATED +/* + * in sort.c (deprecated, use custom code instead) + */ +typedef int (LDAP_SORT_AD_CMP_PROC) LDAP_P(( /* deprecated */ + LDAP_CONST char *left, + LDAP_CONST char *right )); + +typedef int (LDAP_SORT_AV_CMP_PROC) LDAP_P(( /* deprecated */ + LDAP_CONST void *left, + LDAP_CONST void *right )); + +LDAP_F( int ) /* deprecated */ +ldap_sort_entries LDAP_P(( LDAP *ld, + LDAPMessage **chain, + LDAP_CONST char *attr, + LDAP_SORT_AD_CMP_PROC *cmp )); + +LDAP_F( int ) /* deprecated */ +ldap_sort_values LDAP_P(( + LDAP *ld, + char **vals, + LDAP_SORT_AV_CMP_PROC *cmp )); + +LDAP_F( int ) /* deprecated */ +ldap_sort_strcasecmp LDAP_P(( + LDAP_CONST void *a, + LDAP_CONST void *b )); +#endif + +/* + * in url.c + */ +LDAP_F( int ) +ldap_is_ldap_url LDAP_P(( + LDAP_CONST char *url )); + +LDAP_F( int ) +ldap_is_ldaps_url LDAP_P(( + LDAP_CONST char *url )); + +LDAP_F( int ) +ldap_is_ldapi_url LDAP_P(( + LDAP_CONST char *url )); + +#ifdef LDAP_CONNECTIONLESS +LDAP_F( int ) +ldap_is_ldapc_url LDAP_P(( + LDAP_CONST char *url )); +#endif + +LDAP_F( int ) +ldap_url_parse LDAP_P(( + LDAP_CONST char *url, + LDAPURLDesc **ludpp )); + +LDAP_F( char * ) +ldap_url_desc2str LDAP_P(( + LDAPURLDesc *ludp )); + +LDAP_F( void ) +ldap_free_urldesc LDAP_P(( + LDAPURLDesc *ludp )); + + +/* + * LDAP Cancel Extended Operation <draft-zeilenga-ldap-cancel-xx.txt> + * in cancel.c + */ +#define LDAP_API_FEATURE_CANCEL 1000 + +LDAP_F( int ) +ldap_cancel LDAP_P(( LDAP *ld, + int cancelid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_cancel_s LDAP_P(( LDAP *ld, + int cancelid, + LDAPControl **sctrl, + LDAPControl **cctrl )); + +/* + * LDAP Turn Extended Operation <draft-zeilenga-ldap-turn-xx.txt> + * in turn.c + */ +#define LDAP_API_FEATURE_TURN 1000 + +LDAP_F( int ) +ldap_turn LDAP_P(( LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_turn_s LDAP_P(( LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrl, + LDAPControl **cctrl )); + +/* + * LDAP Paged Results + * in pagectrl.c + */ +#define LDAP_API_FEATURE_PAGED_RESULTS 2000 + +LDAP_F( int ) +ldap_create_page_control_value LDAP_P(( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + struct berval *value )); + +LDAP_F( int ) +ldap_create_page_control LDAP_P(( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + int iscritical, + LDAPControl **ctrlp )); + +#if LDAP_DEPRECATED +LDAP_F( int ) +ldap_parse_page_control LDAP_P(( + /* deprecated, use ldap_parse_pageresponse_control */ + LDAP *ld, + LDAPControl **ctrls, + ber_int_t *count, + struct berval **cookie )); +#endif + +LDAP_F( int ) +ldap_parse_pageresponse_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *count, + struct berval *cookie )); + +/* + * LDAP Server Side Sort + * in sortctrl.c + */ +#define LDAP_API_FEATURE_SERVER_SIDE_SORT 2000 + +/* structure for a sort-key */ +typedef struct ldapsortkey { + char *attributeType; + char *orderingRule; + int reverseOrder; +} LDAPSortKey; + +LDAP_F( int ) +ldap_create_sort_keylist LDAP_P(( + LDAPSortKey ***sortKeyList, + char *keyString )); + +LDAP_F( void ) +ldap_free_sort_keylist LDAP_P(( + LDAPSortKey **sortkeylist )); + +LDAP_F( int ) +ldap_create_sort_control_value LDAP_P(( + LDAP *ld, + LDAPSortKey **keyList, + struct berval *value )); + +LDAP_F( int ) +ldap_create_sort_control LDAP_P(( + LDAP *ld, + LDAPSortKey **keyList, + int iscritical, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_sortresponse_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *result, + char **attribute )); + +/* + * LDAP Virtual List View + * in vlvctrl.c + */ +#define LDAP_API_FEATURE_VIRTUAL_LIST_VIEW 2000 + +/* structure for virtual list */ +typedef struct ldapvlvinfo { + ber_int_t ldvlv_version; + ber_int_t ldvlv_before_count; + ber_int_t ldvlv_after_count; + ber_int_t ldvlv_offset; + ber_int_t ldvlv_count; + struct berval * ldvlv_attrvalue; + struct berval * ldvlv_context; + void * ldvlv_extradata; +} LDAPVLVInfo; + +LDAP_F( int ) +ldap_create_vlv_control_value LDAP_P(( + LDAP *ld, + LDAPVLVInfo *ldvlistp, + struct berval *value)); + +LDAP_F( int ) +ldap_create_vlv_control LDAP_P(( + LDAP *ld, + LDAPVLVInfo *ldvlistp, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_vlvresponse_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrls, + ber_int_t *target_posp, + ber_int_t *list_countp, + struct berval **contextp, + int *errcodep )); + +/* + * LDAP Verify Credentials + */ +#define LDAP_API_FEATURE_VERIFY_CREDENTIALS 1000 + +LDAP_F( int ) +ldap_verify_credentials LDAP_P(( + LDAP *ld, + struct berval *cookie, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **ctrls, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_verify_credentials_s LDAP_P(( + LDAP *ld, + struct berval *cookie, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **vcictrls, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *code, + char **diagmsgp, + struct berval **scookie, + struct berval **servercredp, + LDAPControl ***vcoctrls)); + + +LDAP_F( int ) +ldap_parse_verify_credentials LDAP_P(( + LDAP *ld, + LDAPMessage *res, + int *code, + char **diagmsgp, + struct berval **cookie, + struct berval **servercredp, + LDAPControl ***vcctrls)); + +/* not yet implemented */ +/* #define LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 1000 */ +#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE +LDAP_F( int ) +ldap_verify_credentials_interactive LDAP_P(( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *saslMechanism, + LDAPControl **vcControls, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults, + void *context, + + /* as obtained from ldap_result() */ + LDAPMessage *result, + + /* returned during bind processing */ + const char **rmech, + int *msgid )); +#endif + +/* + * LDAP Who Am I? + * in whoami.c + */ +#define LDAP_API_FEATURE_WHOAMI 1000 + +LDAP_F( int ) +ldap_parse_whoami LDAP_P(( + LDAP *ld, + LDAPMessage *res, + struct berval **authzid )); + +LDAP_F( int ) +ldap_whoami LDAP_P(( LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_whoami_s LDAP_P(( + LDAP *ld, + struct berval **authzid, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +/* + * LDAP Password Modify + * in passwd.c + */ +#define LDAP_API_FEATURE_PASSWD_MODIFY 1000 + +LDAP_F( int ) +ldap_parse_passwd LDAP_P(( + LDAP *ld, + LDAPMessage *res, + struct berval *newpasswd )); + +LDAP_F( int ) +ldap_passwd LDAP_P(( LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_passwd_s LDAP_P(( + LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + struct berval *newpasswd, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +/* + * LDAP Password Policy controls + * in ppolicy.c + */ +#define LDAP_API_FEATURE_PASSWORD_POLICY 1000 + +typedef enum passpolicyerror_enum { + PP_passwordExpired = 0, + PP_accountLocked = 1, + PP_changeAfterReset = 2, + PP_passwordModNotAllowed = 3, + PP_mustSupplyOldPassword = 4, + PP_insufficientPasswordQuality = 5, + PP_passwordTooShort = 6, + PP_passwordTooYoung = 7, + PP_passwordInHistory = 8, + PP_passwordTooLong = 9, + PP_noError = 65535 +} LDAPPasswordPolicyError; + +LDAP_F( int ) +ldap_create_passwordpolicy_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_passwordpolicy_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *expirep, + ber_int_t *gracep, + LDAPPasswordPolicyError *errorp )); + +LDAP_F( const char * ) +ldap_passwordpolicy_err2txt LDAP_P(( LDAPPasswordPolicyError )); +#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ + +LDAP_F( int ) +ldap_parse_password_expiring_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + long *secondsp )); + +/* + * LDAP Dynamic Directory Services Refresh -- RFC 2589 + * in dds.c + */ +#define LDAP_API_FEATURE_REFRESH 1000 + +LDAP_F( int ) +ldap_parse_refresh LDAP_P(( + LDAP *ld, + LDAPMessage *res, + ber_int_t *newttl )); + +LDAP_F( int ) +ldap_refresh LDAP_P(( LDAP *ld, + struct berval *dn, + ber_int_t ttl, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_refresh_s LDAP_P(( + LDAP *ld, + struct berval *dn, + ber_int_t ttl, + ber_int_t *newttl, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +/* + * LDAP Transactions + */ +LDAP_F( int ) +ldap_txn_start LDAP_P(( LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_txn_start_s LDAP_P(( LDAP *ld, + LDAPControl **sctrl, + LDAPControl **cctrl, + struct berval **rettxnid )); + +LDAP_F( int ) +ldap_txn_end LDAP_P(( LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp )); + +LDAP_F( int ) +ldap_txn_end_s LDAP_P(( LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrl, + LDAPControl **cctrl, + int *retidp )); + +/* + * in ldap_sync.c + */ + +/* + * initialize the persistent search structure + */ +LDAP_F( ldap_sync_t * ) +ldap_sync_initialize LDAP_P(( + ldap_sync_t *ls )); + +/* + * destroy the persistent search structure + */ +LDAP_F( void ) +ldap_sync_destroy LDAP_P(( + ldap_sync_t *ls, + int freeit )); + +/* + * initialize a refreshOnly sync + */ +LDAP_F( int ) +ldap_sync_init LDAP_P(( + ldap_sync_t *ls, + int mode )); + +/* + * initialize a refreshOnly sync + */ +LDAP_F( int ) +ldap_sync_init_refresh_only LDAP_P(( + ldap_sync_t *ls )); + +/* + * initialize a refreshAndPersist sync + */ +LDAP_F( int ) +ldap_sync_init_refresh_and_persist LDAP_P(( + ldap_sync_t *ls )); + +/* + * poll for new responses + */ +LDAP_F( int ) +ldap_sync_poll LDAP_P(( + ldap_sync_t *ls )); + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + +/* + * in stctrl.c + */ +LDAP_F( int ) +ldap_create_session_tracking_value LDAP_P(( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + struct berval *value )); + +LDAP_F( int ) +ldap_create_session_tracking_control LDAP_P(( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_session_tracking_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + struct berval *ip, + struct berval *name, + struct berval *oid, + struct berval *id )); + +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + +/* + * in msctrl.c + */ +#ifdef LDAP_CONTROL_X_DIRSYNC +LDAP_F( int ) +ldap_create_dirsync_value LDAP_P(( + LDAP *ld, + int flags, + int maxAttrCount, + struct berval *cookie, + struct berval *value )); + +LDAP_F( int ) +ldap_create_dirsync_control LDAP_P(( + LDAP *ld, + int flags, + int maxAttrCount, + struct berval *cookie, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_dirsync_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + int *continueFlag, + struct berval *cookie )); +#endif /* LDAP_CONTROL_X_DIRSYNC */ + +#ifdef LDAP_CONTROL_X_EXTENDED_DN +LDAP_F( int ) +ldap_create_extended_dn_value LDAP_P(( + LDAP *ld, + int flag, + struct berval *value )); + +LDAP_F( int ) +ldap_create_extended_dn_control LDAP_P(( + LDAP *ld, + int flag, + LDAPControl **ctrlp )); +#endif /* LDAP_CONTROL_X_EXTENDED_DN */ + +#ifdef LDAP_CONTROL_X_SHOW_DELETED +LDAP_F( int ) +ldap_create_show_deleted_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); +#endif /* LDAP_CONTROL_X_SHOW_DELETED */ + +#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION +LDAP_F( int ) +ldap_create_server_notification_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); +#endif /* LDAP_CONTROL_X_SERVER_NOTIFICATION */ + +/* + * in assertion.c + */ +LDAP_F (int) +ldap_create_assertion_control_value LDAP_P(( + LDAP *ld, + char *assertion, + struct berval *value )); + +LDAP_F( int ) +ldap_create_assertion_control LDAP_P(( + LDAP *ld, + char *filter, + int iscritical, + LDAPControl **ctrlp )); + +/* + * in deref.c + */ + +typedef struct LDAPDerefSpec { + char *derefAttr; + char **attributes; +} LDAPDerefSpec; + +typedef struct LDAPDerefVal { + char *type; + BerVarray vals; + struct LDAPDerefVal *next; +} LDAPDerefVal; + +typedef struct LDAPDerefRes { + char *derefAttr; + struct berval derefVal; + LDAPDerefVal *attrVals; + struct LDAPDerefRes *next; +} LDAPDerefRes; + +LDAP_F( int ) +ldap_create_deref_control_value LDAP_P(( + LDAP *ld, + LDAPDerefSpec *ds, + struct berval *value )); + +LDAP_F( int ) +ldap_create_deref_control LDAP_P(( + LDAP *ld, + LDAPDerefSpec *ds, + int iscritical, + LDAPControl **ctrlp )); + +LDAP_F( void ) +ldap_derefresponse_free LDAP_P(( + LDAPDerefRes *dr )); + +LDAP_F( int ) +ldap_parse_derefresponse_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + LDAPDerefRes **drp )); + +LDAP_F( int ) +ldap_parse_deref_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrls, + LDAPDerefRes **drp )); + +/* + * in psearch.c + */ + +LDAP_F( int ) +ldap_create_persistentsearch_control_value LDAP_P(( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + struct berval *value )); + +LDAP_F( int ) +ldap_create_persistentsearch_control LDAP_P(( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + int isCritical, + LDAPControl **ctrlp )); + +LDAP_F( int ) +ldap_parse_entrychange_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + int *chgtypep, + struct berval *prevdnp, + int *chgnumpresentp, + long *chgnump )); + +/* in account_usability.c */ + +LDAP_F( int ) +ldap_create_accountusability_control LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); + +typedef struct LDAPAccountUsabilityMoreInfo { + ber_int_t inactive; + ber_int_t reset; + ber_int_t expired; + ber_int_t remaining_grace; + ber_int_t seconds_before_unlock; +} LDAPAccountUsabilityMoreInfo; + +typedef union LDAPAccountUsability { + ber_int_t seconds_remaining; + LDAPAccountUsabilityMoreInfo more_info; +} LDAPAccountUsability; + +LDAP_F( int ) +ldap_parse_accountusability_control LDAP_P(( + LDAP *ld, + LDAPControl *ctrl, + int *availablep, + LDAPAccountUsability *usabilityp )); + + +/* + * high level LDIF to LDAP structure support + */ +#define LDIF_DEFAULT_ADD 0x01 /* if changetype missing, assume LDAP_ADD */ +#define LDIF_ENTRIES_ONLY 0x02 /* ignore changetypes other than add */ +#define LDIF_NO_CONTROLS 0x04 /* ignore control specifications */ +#define LDIF_MODS_ONLY 0x08 /* no changetypes, assume LDAP_MODIFY */ +#define LDIF_NO_DN 0x10 /* dn is not present */ + +typedef struct ldifrecord { + ber_tag_t lr_op; /* type of operation - LDAP_REQ_MODIFY, LDAP_REQ_ADD, etc. */ + struct berval lr_dn; /* DN of operation */ + LDAPControl **lr_ctrls; /* controls specified for operation */ + /* some ops such as LDAP_REQ_DELETE require only a DN */ + /* other ops require different data - the ldif_ops union + is used to specify the data for each type of operation */ + union ldif_ops_u { + LDAPMod **lr_mods; /* list of mods for LDAP_REQ_MODIFY, LDAP_REQ_ADD */ +#define lrop_mods ldif_ops.lr_mods + struct ldif_op_rename_s { + struct berval lr_newrdn; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */ +#define lrop_newrdn ldif_ops.ldif_op_rename.lr_newrdn + struct berval lr_newsuperior; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */ +#define lrop_newsup ldif_ops.ldif_op_rename.lr_newsuperior + int lr_deleteoldrdn; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */ +#define lrop_delold ldif_ops.ldif_op_rename.lr_deleteoldrdn + } ldif_op_rename; /* rename/moddn/modrdn */ + /* the following are for future support */ + struct ldif_op_ext_s { + struct berval lr_extop_oid; /* LDAP_REQ_EXTENDED */ +#define lrop_extop_oid ldif_ops.ldif_op_ext.lr_extop_oid + struct berval lr_extop_data; /* LDAP_REQ_EXTENDED */ +#define lrop_extop_data ldif_ops.ldif_op_ext.lr_extop_data + } ldif_op_ext; /* extended operation */ + struct ldif_op_cmp_s { + struct berval lr_cmp_attr; /* LDAP_REQ_COMPARE */ +#define lrop_cmp_attr ldif_ops.ldif_op_cmp.lr_cmp_attr + struct berval lr_cmp_bvalue; /* LDAP_REQ_COMPARE */ +#define lrop_cmp_bval ldif_ops.ldif_op_cmp.lr_cmp_bvalue + } ldif_op_cmp; /* compare operation */ + } ldif_ops; + /* PRIVATE STUFF - DO NOT TOUCH */ + /* for efficiency, the implementation allocates memory */ + /* in large blobs, and makes the above fields point to */ + /* locations inside those blobs - one consequence is that */ + /* you cannot simply free the above allocated fields, nor */ + /* assign them to be owned by another memory context which */ + /* might free them (unless providing your own mem ctx) */ + /* we use the fields below to keep track of those blobs */ + /* so we that we can free them later */ + void *lr_ctx; /* the memory context or NULL */ + int lr_lines; + LDAPMod *lr_lm; + unsigned char *lr_mops; + char *lr_freeval; + struct berval *lr_vals; + struct berval *lr_btype; +} LDIFRecord; + +/* free internal fields - does not free the LDIFRecord */ +LDAP_F( void ) +ldap_ldif_record_done LDAP_P(( + LDIFRecord *lr )); + +LDAP_F( int ) +ldap_parse_ldif_record LDAP_P(( + struct berval *rbuf, + unsigned long linenum, + LDIFRecord *lr, + const char *errstr, + unsigned int flags )); + +LDAP_END_DECL +#endif /* _LDAP_H */ diff --git a/contrib/libs/openldap/include/ldap_avl.h b/contrib/libs/openldap/include/ldap_avl.h new file mode 100644 index 0000000000..4bb1c39780 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_avl.h @@ -0,0 +1,165 @@ +/* ldap_avl.h - avl tree definitions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + + +#ifndef _AVL +#define _AVL + +#include <ldap_cdefs.h> + +/* + * this structure represents a generic avl tree node. + */ + +LDAP_BEGIN_DECL + +typedef struct avlnode Avlnode; + +struct avlnode { + void* avl_data; + struct avlnode *avl_link[2]; + char avl_bits[2]; + signed char avl_bf; +}; + +#define avl_left avl_link[0] +#define avl_right avl_link[1] +#define avl_lbit avl_bits[0] +#define avl_rbit avl_bits[1] + +typedef struct tavlnode TAvlnode; + +struct tavlnode { + void* avl_data; + struct tavlnode *avl_link[2]; + char avl_bits[2]; + signed char avl_bf; +}; + +#ifdef AVL_INTERNAL + +/* balance factor values */ +#define LH (-1) +#define EH 0 +#define RH 1 + +#define avl_bf2str(bf) ((bf) == -1 ? "LH" : (bf) == 0 ? "EH" : (bf) == 1 ? "RH" : "(unknown)" ) + +/* thread bits */ +#define AVL_CHILD 0 +#define AVL_THREAD 1 + +/* avl routines */ +#define ldap_avl_getone(x) ((x) == 0 ? 0 : (x)->avl_data) +#define ldap_avl_onenode(x) ((x) == 0 || ((x)->avl_left == 0 && (x)->avl_right == 0)) + +#endif /* AVL_INTERNALS */ + +#define ldap_avl_child(x,dir) ((x)->avl_bits[dir]) == AVL_CHILD ? \ + (x)->avl_link[dir] : NULL +#define ldap_avl_lchild(x) ldap_avl_child(x,0) +#define ldap_avl_rchild(x) ldap_avl_child(x,1) + +typedef int (*AVL_APPLY) LDAP_P((void *, void*)); +typedef int (*AVL_CMP) LDAP_P((const void*, const void*)); +typedef int (*AVL_DUP) LDAP_P((void*, void*)); +typedef void (*AVL_FREE) LDAP_P((void*)); + +LDAP_AVL_F( int ) +ldap_avl_free LDAP_P(( Avlnode *root, AVL_FREE dfree )); + +LDAP_AVL_F( int ) +ldap_avl_insert LDAP_P((Avlnode **, void*, AVL_CMP, AVL_DUP)); + +LDAP_AVL_F( void* ) +ldap_avl_delete LDAP_P((Avlnode **, void*, AVL_CMP)); + +LDAP_AVL_F( void* ) +ldap_avl_find LDAP_P((Avlnode *, const void*, AVL_CMP)); + +LDAP_AVL_F( Avlnode* ) +ldap_avl_find2 LDAP_P((Avlnode *, const void*, AVL_CMP)); + +LDAP_AVL_F( void* ) +ldap_avl_find_lin LDAP_P((Avlnode *, const void*, AVL_CMP)); + +#ifdef AVL_NONREENTRANT +LDAP_AVL_F( void* ) +ldap_avl_getfirst LDAP_P((Avlnode *)); + +LDAP_AVL_F( void* ) +ldap_avl_getnext LDAP_P((void)); +#endif + +LDAP_AVL_F( int ) +ldap_avl_dup_error LDAP_P((void*, void*)); + +LDAP_AVL_F( int ) +ldap_avl_dup_ok LDAP_P((void*, void*)); + +LDAP_AVL_F( int ) +ldap_avl_apply LDAP_P((Avlnode *, AVL_APPLY, void*, int, int)); + +LDAP_AVL_F( int ) +ldap_avl_prefixapply LDAP_P((Avlnode *, void*, AVL_CMP, void*, AVL_CMP, void*, int)); + +LDAP_AVL_F( int ) +ldap_tavl_free LDAP_P(( TAvlnode *root, AVL_FREE dfree )); + +LDAP_AVL_F( int ) +ldap_tavl_insert LDAP_P((TAvlnode **, void*, AVL_CMP, AVL_DUP)); + +LDAP_AVL_F( void* ) +ldap_tavl_delete LDAP_P((TAvlnode **, void*, AVL_CMP)); + +LDAP_AVL_F( void* ) +ldap_tavl_find LDAP_P((TAvlnode *, const void*, AVL_CMP)); + +LDAP_AVL_F( TAvlnode* ) +ldap_tavl_find2 LDAP_P((TAvlnode *, const void*, AVL_CMP)); + +LDAP_AVL_F( TAvlnode* ) +ldap_tavl_find3 LDAP_P((TAvlnode *, const void*, AVL_CMP, int *ret)); + +#define TAVL_DIR_LEFT 0 +#define TAVL_DIR_RIGHT 1 + +LDAP_AVL_F( TAvlnode* ) +ldap_tavl_end LDAP_P((TAvlnode *, int direction)); + +LDAP_AVL_F( TAvlnode* ) +ldap_tavl_next LDAP_P((TAvlnode *, int direction)); + +/* apply traversal types */ +#define AVL_PREORDER 1 +#define AVL_INORDER 2 +#define AVL_POSTORDER 3 +/* what apply returns if it ran out of nodes */ +#define AVL_NOMORE (-6) + +LDAP_END_DECL + +#endif /* _AVL */ diff --git a/contrib/libs/openldap/include/ldap_cdefs.h b/contrib/libs/openldap/include/ldap_cdefs.h new file mode 100644 index 0000000000..2f5fa7f7e9 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_cdefs.h @@ -0,0 +1,248 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* LDAP C Defines */ + +#ifndef _LDAP_CDEFS_H +#define _LDAP_CDEFS_H + +#if defined(__cplusplus) || defined(c_plusplus) +# define LDAP_BEGIN_DECL extern "C" { +# define LDAP_END_DECL } +#else +# define LDAP_BEGIN_DECL /* begin declarations */ +# define LDAP_END_DECL /* end declarations */ +#endif + +#if !defined(LDAP_NO_PROTOTYPES) && ( defined(LDAP_NEEDS_PROTOTYPES) || \ + defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus) ) + + /* ANSI C or C++ */ +# define LDAP_P(protos) protos +# define LDAP_CONCAT1(x,y) x ## y +# define LDAP_CONCAT(x,y) LDAP_CONCAT1(x,y) +# define LDAP_STRING(x) #x /* stringify without expanding x */ +# define LDAP_XSTRING(x) LDAP_STRING(x) /* expand x, then stringify */ + +#ifndef LDAP_CONST +# define LDAP_CONST const +#endif + +#else /* no prototypes */ + + /* traditional C */ +# define LDAP_P(protos) () +# define LDAP_CONCAT(x,y) x/**/y +# define LDAP_STRING(x) "x" + +#ifndef LDAP_CONST +# define LDAP_CONST /* no const */ +#endif + +#endif /* no prototypes */ + +#if (__GNUC__) * 1000 + (__GNUC_MINOR__) >= 2006 +# define LDAP_GCCATTR(attrs) __attribute__(attrs) +#else +# define LDAP_GCCATTR(attrs) +#endif + +/* + * Support for Windows DLLs. + * + * When external source code includes header files for dynamic libraries, + * the external source code is "importing" DLL symbols into its resulting + * object code. On Windows, symbols imported from DLLs must be explicitly + * indicated in header files with the __declspec(dllimport) directive. + * This is not totally necessary for functions because the compiler + * (gcc or MSVC) will generate stubs when this directive is absent. + * However, this is required for imported variables. + * + * The LDAP libraries, i.e. liblber and libldap, can be built as + * static or shared, based on configuration. Just about all other source + * code in OpenLDAP use these libraries. If the LDAP libraries + * are configured as shared, 'configure' defines the LDAP_LIBS_DYNAMIC + * macro. When other source files include LDAP library headers, the + * LDAP library symbols will automatically be marked as imported. When + * the actual LDAP libraries are being built, the symbols will not + * be marked as imported because the LBER_LIBRARY or LDAP_LIBRARY macros + * will be respectively defined. + * + * Any project outside of OpenLDAP with source code wanting to use + * LDAP dynamic libraries should explicitly define LDAP_LIBS_DYNAMIC. + * This will ensure that external source code appropriately marks symbols + * that will be imported. + * + * The slapd executable, itself, can be used as a dynamic library. + * For example, if a backend module is compiled as shared, it will + * import symbols from slapd. When this happens, the slapd symbols + * must be marked as imported in header files that the backend module + * includes. Remember that slapd links with various static libraries. + * If the LDAP libraries were configured as static, their object + * code is also part of the monolithic slapd executable. Thus, when + * a backend module imports symbols from slapd, it imports symbols from + * all of the static libraries in slapd as well. Thus, the SLAP_IMPORT + * macro, when defined, will appropriately mark symbols as imported. + * This macro should be used by shared backend modules as well as any + * other external source code that imports symbols from the slapd + * executable as if it were a DLL. + * + * Note that we don't actually have to worry about using the + * __declspec(dllexport) directive anywhere. This is because both + * MSVC and Mingw provide alternate (more effective) methods for exporting + * symbols out of binaries, i.e. the use of a DEF file. + * + * NOTE ABOUT BACKENDS: Backends can be configured as static or dynamic. + * When a backend is configured as dynamic, slapd will load the backend + * explicitly and populate function pointer structures by calling + * the backend's well-known initialization function. Because of this + * procedure, slapd never implicitly imports symbols from dynamic backends. + * This makes it unnecessary to tag various backend functions with the + * __declspec(dllimport) directive. This is because neither slapd nor + * any other external binary should ever be implicitly loading a backend + * dynamic module. + * + * Backends are supposed to be self-contained. However, it appears that + * back-meta DOES implicitly import symbols from back-ldap. This means + * that the __declspec(dllimport) directive should be marked on back-ldap + * functions (in its header files) if and only if we're compiling for + * windows AND back-ldap has been configured as dynamic AND back-meta + * is the client of back-ldap. When client is slapd, there is no effect + * since slapd does not implicitly import symbols. + * + * TODO(?): Currently, back-meta nor back-ldap is supported for Mingw32. + * Thus, there's no need to worry about this right now. This is something that + * may or may not have to be addressed in the future. + */ + +/* LBER library */ +#if defined(_WIN32) && \ + ((defined(LDAP_LIBS_DYNAMIC) && !defined(LBER_LIBRARY)) || \ + (!defined(LDAP_LIBS_DYNAMIC) && defined(SLAPD_IMPORT))) +# define LBER_F(type) extern __declspec(dllimport) type +# define LBER_V(type) extern __declspec(dllimport) type +#else +# define LBER_F(type) extern type +# define LBER_V(type) extern type +#endif + +/* LDAP library */ +#if defined(_WIN32) && \ + ((defined(LDAP_LIBS_DYNAMIC) && !defined(LDAP_LIBRARY)) || \ + (!defined(LDAP_LIBS_DYNAMIC) && defined(SLAPD_IMPORT))) +# define LDAP_F(type) extern __declspec(dllimport) type +# define LDAP_V(type) extern __declspec(dllimport) type +#else +# define LDAP_F(type) extern type +# define LDAP_V(type) extern type +#endif + +/* AVL library */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_AVL_F(type) extern __declspec(dllimport) type +# define LDAP_AVL_V(type) extern __declspec(dllimport) type +#else +# define LDAP_AVL_F(type) extern type +# define LDAP_AVL_V(type) extern type +#endif + +/* LDIF library */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_LDIF_F(type) extern __declspec(dllimport) type +# define LDAP_LDIF_V(type) extern __declspec(dllimport) type +#else +# define LDAP_LDIF_F(type) extern type +# define LDAP_LDIF_V(type) extern type +#endif + +/* LUNICODE library */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_LUNICODE_F(type) extern __declspec(dllimport) type +# define LDAP_LUNICODE_V(type) extern __declspec(dllimport) type +#else +# define LDAP_LUNICODE_F(type) extern type +# define LDAP_LUNICODE_V(type) extern type +#endif + +/* LUTIL library */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_LUTIL_F(type) extern __declspec(dllimport) type +# define LDAP_LUTIL_V(type) extern __declspec(dllimport) type +#else +# define LDAP_LUTIL_F(type) extern type +# define LDAP_LUTIL_V(type) extern type +#endif + +/* REWRITE library */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_REWRITE_F(type) extern __declspec(dllimport) type +# define LDAP_REWRITE_V(type) extern __declspec(dllimport) type +#else +# define LDAP_REWRITE_F(type) extern type +# define LDAP_REWRITE_V(type) extern type +#endif + +/* SLAPD (as a dynamic library exporting symbols) */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_SLAPD_F(type) extern __declspec(dllimport) type +# define LDAP_SLAPD_V(type) extern __declspec(dllimport) type +#else +# define LDAP_SLAPD_F(type) extern type +# define LDAP_SLAPD_V(type) extern type +#endif + +/* SLAPD (as a dynamic library exporting symbols) */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define LDAP_SLAPI_F(type) extern __declspec(dllimport) type +# define LDAP_SLAPI_V(type) extern __declspec(dllimport) type +#else +# define LDAP_SLAPI_F(type) extern type +# define LDAP_SLAPI_V(type) extern type +#endif + +/* SLAPD (as a dynamic library exporting symbols) */ +#if defined(_WIN32) && defined(SLAPD_IMPORT) +# define SLAPI_F(type) extern __declspec(dllimport) type +# define SLAPI_V(type) extern __declspec(dllimport) type +#else +# define SLAPI_F(type) extern type +# define SLAPI_V(type) extern type +#endif + +/* + * C library. Mingw32 links with the dynamic C run-time library by default, + * so the explicit definition of CSTATIC will keep dllimport from + * being defined, if desired. + * + * MSVC defines the _DLL macro when the compiler is invoked with /MD or /MDd, + * which means the resulting object code will be linked with the dynamic + * C run-time library. + * + * Technically, it shouldn't be necessary to redefine any functions that + * the headers for the C library should already contain. Nevertheless, this + * is here as a safe-guard. + * + * TODO: Determine if these macros ever get expanded for Windows. If not, + * the declspec expansion can probably be removed. + */ +#if (defined(__MINGW32__) && !defined(CSTATIC)) || \ + (defined(_MSC_VER) && defined(_DLL)) +# define LDAP_LIBC_F(type) extern __declspec(dllimport) type +# define LDAP_LIBC_V(type) extern __declspec(dllimport) type +#else +# define LDAP_LIBC_F(type) extern type +# define LDAP_LIBC_V(type) extern type +#endif + +#endif /* _LDAP_CDEFS_H */ diff --git a/contrib/libs/openldap/include/ldap_config.h b/contrib/libs/openldap/include/ldap_config.h new file mode 100644 index 0000000000..2f58b1620c --- /dev/null +++ b/contrib/libs/openldap/include/ldap_config.h @@ -0,0 +1,74 @@ +/* Generated from ./ldap_config.hin on */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * This file works in conjunction with OpenLDAP configure system. + * If you do no like the values below, adjust your configure options. + */ + +#ifndef _LDAP_CONFIG_H +#define _LDAP_CONFIG_H + +/* directory separator */ +#ifndef LDAP_DIRSEP +#ifndef _WIN32 +#define LDAP_DIRSEP "/" +#else +#define LDAP_DIRSEP "\\" +#endif +#endif + +/* directory for temporary files */ +#if defined(_WIN32) +# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */ +#elif defined( _P_tmpdir ) +# define LDAP_TMPDIR _P_tmpdir +#elif defined( P_tmpdir ) +# define LDAP_TMPDIR P_tmpdir +#elif defined( _PATH_TMPDIR ) +# define LDAP_TMPDIR _PATH_TMPDIR +#else +# define LDAP_TMPDIR LDAP_DIRSEP "tmp" +#endif + +/* directories */ +#ifndef LDAP_BINDIR +#define LDAP_BINDIR "/var/empty/tmp/out/bin" +#endif +#ifndef LDAP_SBINDIR +#define LDAP_SBINDIR "/var/empty/tmp/out/sbin" +#endif +#ifndef LDAP_DATADIR +#define LDAP_DATADIR "/var/empty/openldap-2.6.6/share/openldap" +#endif +#ifndef LDAP_SYSCONFDIR +#define LDAP_SYSCONFDIR "/var/empty/openldap-2.6.6/etc/openldap" +#endif +#ifndef LDAP_LIBEXECDIR +#define LDAP_LIBEXECDIR "/var/empty/tmp/out/libexec" +#endif +#ifndef LDAP_MODULEDIR +#define LDAP_MODULEDIR "/var/empty/tmp/out/libexec/openldap" +#endif +#ifndef LDAP_RUNDIR +#define LDAP_RUNDIR "/var/empty/openldap-2.6.6/var" +#endif +#ifndef LDAP_LOCALEDIR +#define LDAP_LOCALEDIR "" +#endif + + +#endif /* _LDAP_CONFIG_H */ diff --git a/contrib/libs/openldap/include/ldap_defaults.h b/contrib/libs/openldap/include/ldap_defaults.h new file mode 100644 index 0000000000..c8632d7dce --- /dev/null +++ b/contrib/libs/openldap/include/ldap_defaults.h @@ -0,0 +1,78 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* + * This file controls defaults for OpenLDAP package. + * You probably do not need to edit the defaults provided by this file. + */ + +#ifndef _LDAP_DEFAULTS_H +#define _LDAP_DEFAULTS_H + + +#include <ldap_config.h> + +#define LDAP_CONF_FILE LDAP_SYSCONFDIR LDAP_DIRSEP "ldap.conf" +#define LDAP_USERRC_FILE "ldaprc" +#define LDAP_ENV_PREFIX "LDAP" + +/* default ldapi:// socket */ +#ifndef LDAPI_SOCK +#define LDAPI_SOCK LDAP_RUNDIR LDAP_DIRSEP "run" LDAP_DIRSEP "ldapi" +#endif + +/* + * SLAPD DEFINITIONS + */ + /* location of the default slapd config file */ +#ifndef SLAPD_DEFAULT_CONFIGFILE +#define SLAPD_DEFAULT_CONFIGFILE LDAP_SYSCONFDIR LDAP_DIRSEP "slapd.conf" +#endif +#ifndef SLAPD_DEFAULT_CONFIGDIR +#define SLAPD_DEFAULT_CONFIGDIR LDAP_SYSCONFDIR LDAP_DIRSEP "slapd.d" +#endif +#ifndef SLAPD_DEFAULT_DB_DIR +#define SLAPD_DEFAULT_DB_DIR LDAP_RUNDIR LDAP_DIRSEP "openldap-data" +#endif +#define SLAPD_DEFAULT_DB_MODE 0600 + /* default max deref depth for aliases */ +#define SLAPD_DEFAULT_MAXDEREFDEPTH 15 + /* default sizelimit on number of entries from a search */ +#define SLAPD_DEFAULT_SIZELIMIT 500 + /* default timelimit to spend on a search */ +#define SLAPD_DEFAULT_TIMELIMIT 3600 + +/* the following DNs must be normalized! */ + /* dn of the default subschema subentry */ +#define SLAPD_SCHEMA_DN "cn=Subschema" + /* dn of the default "monitor" subentry */ +#define SLAPD_MONITOR_DN "cn=Monitor" + +/* + * LLOADD DEFINITIONS + */ +#define LLOADD_DEFAULT_CONFIGFILE LDAP_SYSCONFDIR LDAP_DIRSEP "lloadd.conf" + +#endif /* _LDAP_CONFIG_H */ diff --git a/contrib/libs/openldap/include/ldap_features.h b/contrib/libs/openldap/include/ldap_features.h new file mode 100644 index 0000000000..e8d9676c22 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_features.h @@ -0,0 +1,56 @@ +/* include/ldap_features.h. Generated from ldap_features.hin by configure. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * LDAP Features + */ + +#ifndef _LDAP_FEATURES_H +#define _LDAP_FEATURES_H 1 + +/* OpenLDAP API version macros */ +#define LDAP_VENDOR_VERSION 20606 +#define LDAP_VENDOR_VERSION_MAJOR 2 +#define LDAP_VENDOR_VERSION_MINOR 6 +#define LDAP_VENDOR_VERSION_PATCH 6 + +/* +** WORK IN PROGRESS! +** +** OpenLDAP reentrancy/thread-safeness should be dynamically +** checked using ldap_get_option(). +** +** If built with thread support, the -lldap implementation is: +** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety) +** LDAP_API_FEATURE_SESSION_THREAD_SAFE +** LDAP_API_FEATURE_OPERATION_THREAD_SAFE +** +** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE +** can be used to determine if -lldap is thread safe at compile +** time. +** +*/ + +/* is -lldap reentrant or not */ +#define LDAP_API_FEATURE_X_OPENLDAP_REENTRANT 1 + +/* is -lldap thread safe or not */ +#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1 + +/* LDAP v2 Referrals */ +/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */ + +#endif /* LDAP_FEATURES */ diff --git a/contrib/libs/openldap/include/ldap_int_thread.h b/contrib/libs/openldap/include/ldap_int_thread.h new file mode 100644 index 0000000000..2c82d6825b --- /dev/null +++ b/contrib/libs/openldap/include/ldap_int_thread.h @@ -0,0 +1,290 @@ +/* ldap_int_thread.h - ldap internal thread wrappers header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + + +LDAP_BEGIN_DECL + +/* Can be done twice. See libldap/ldap_thr_debug.h. */ +LDAP_F(int) ldap_int_thread_initialize LDAP_P(( void )); +LDAP_F(int) ldap_int_thread_destroy LDAP_P(( void )); + +LDAP_END_DECL + +#ifndef _LDAP_INT_THREAD_H +#define _LDAP_INT_THREAD_H + +#if defined( HAVE_PTHREADS ) +/********************************** + * * + * definitions for POSIX Threads * + * * + **********************************/ + +#include <pthread.h> +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +LDAP_BEGIN_DECL + +typedef pthread_t ldap_int_thread_t; +typedef pthread_mutex_t ldap_int_thread_mutex_t; +typedef pthread_cond_t ldap_int_thread_cond_t; +typedef pthread_key_t ldap_int_thread_key_t; + +#define ldap_int_thread_equal(a, b) pthread_equal((a), (b)) + +#if defined( _POSIX_REENTRANT_FUNCTIONS ) || \ + defined( _POSIX_THREAD_SAFE_FUNCTIONS ) || \ + defined( _POSIX_THREADSAFE_FUNCTIONS ) +#define HAVE_REENTRANT_FUNCTIONS 1 +#endif + +#if defined( HAVE_PTHREAD_GETCONCURRENCY ) || \ + defined( HAVE_THR_GETCONCURRENCY ) +#define LDAP_THREAD_HAVE_GETCONCURRENCY 1 +#endif + +#if defined( HAVE_PTHREAD_SETCONCURRENCY ) || \ + defined( HAVE_THR_SETCONCURRENCY ) +#define LDAP_THREAD_HAVE_SETCONCURRENCY 1 +#endif + +#if defined( HAVE_PTHREAD_RWLOCK_DESTROY ) +#define LDAP_THREAD_HAVE_RDWR 1 +typedef pthread_rwlock_t ldap_int_thread_rdwr_t; +#endif + +#ifndef LDAP_INT_MUTEX_NULL +#define LDAP_INT_MUTEX_NULL PTHREAD_MUTEX_INITIALIZER +#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0) +#endif + +LDAP_END_DECL + +#elif defined( HAVE_GNU_PTH ) +/*********************************** + * * + * thread definitions for GNU Pth * + * * + ***********************************/ + +#define PTH_SYSCALL_SOFT 1 +#include <pth.h> + +LDAP_BEGIN_DECL + +typedef pth_t ldap_int_thread_t; +typedef pth_mutex_t ldap_int_thread_mutex_t; +typedef pth_cond_t ldap_int_thread_cond_t; +typedef pth_key_t ldap_int_thread_key_t; + +#if 0 +#define LDAP_THREAD_HAVE_RDWR 1 +typedef pth_rwlock_t ldap_int_thread_rdwr_t; +#endif + +#ifndef LDAP_INT_MUTEX_NULL +#define LDAP_INT_MUTEX_NULL PTH_MUTEX_INIT +#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0) +#endif + +LDAP_END_DECL + +#elif defined( HAVE_THR ) +/******************************************** + * * + * thread definitions for Solaris LWP (THR) * + * * + ********************************************/ + +#include <thread.h> +#include <synch.h> + +LDAP_BEGIN_DECL + +typedef thread_t ldap_int_thread_t; +typedef mutex_t ldap_int_thread_mutex_t; +typedef cond_t ldap_int_thread_cond_t; +typedef thread_key_t ldap_int_thread_key_t; + +#define HAVE_REENTRANT_FUNCTIONS 1 + +#ifdef HAVE_THR_GETCONCURRENCY +#define LDAP_THREAD_HAVE_GETCONCURRENCY 1 +#endif +#ifdef HAVE_THR_SETCONCURRENCY +#define LDAP_THREAD_HAVE_SETCONCURRENCY 1 +#endif + +#ifndef LDAP_INT_MUTEX_NULL +#define LDAP_INT_MUTEX_NULL DEFAULTMUTEX +#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0) +#endif + +#elif defined(HAVE_NT_THREADS) +/************************************* + * * + * thread definitions for NT threads * + * * + *************************************/ + +#include <process.h> +#include <windows.h> + +LDAP_BEGIN_DECL + +typedef unsigned long ldap_int_thread_t; +typedef HANDLE ldap_int_thread_mutex_t; +typedef HANDLE ldap_int_thread_cond_t; +typedef DWORD ldap_int_thread_key_t; + +LDAP_F( int ) +ldap_int_mutex_firstcreate LDAP_P(( ldap_int_thread_mutex_t *mutex )); + +#ifndef LDAP_INT_MUTEX_NULL +#define LDAP_INT_MUTEX_NULL ((HANDLE)0) +#define LDAP_INT_MUTEX_FIRSTCREATE(m) \ + ldap_int_mutex_firstcreate(&(m)) +#endif + +LDAP_END_DECL + +#else +/*********************************** + * * + * thread definitions for no * + * underlying library support * + * * + ***********************************/ + +#ifndef NO_THREADS +#define NO_THREADS 1 +#endif + +LDAP_BEGIN_DECL + +typedef int ldap_int_thread_t; +typedef int ldap_int_thread_mutex_t; +typedef int ldap_int_thread_cond_t; +typedef int ldap_int_thread_key_t; + +#define LDAP_THREAD_HAVE_TPOOL 1 +typedef int ldap_int_thread_pool_t; + +#ifndef LDAP_INT_MUTEX_NULL +#define LDAP_INT_MUTEX_NULL 0 +#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0) +#endif + +LDAP_END_DECL + +#endif /* no threads support */ + + +LDAP_BEGIN_DECL + +#ifndef ldap_int_thread_equal +#define ldap_int_thread_equal(a, b) ((a) == (b)) +#endif + +#ifndef LDAP_THREAD_HAVE_RDWR +typedef struct ldap_int_thread_rdwr_s * ldap_int_thread_rdwr_t; +#endif + +LDAP_F(int) ldap_int_thread_pool_startup ( void ); +LDAP_F(int) ldap_int_thread_pool_shutdown ( void ); + +#ifndef LDAP_THREAD_HAVE_TPOOL +typedef struct ldap_int_thread_pool_s * ldap_int_thread_pool_t; +#endif +LDAP_END_DECL + + +#if defined(LDAP_THREAD_DEBUG) && !((LDAP_THREAD_DEBUG +0) & 2U) +#define LDAP_THREAD_DEBUG_WRAP 1 +#endif + +#ifdef LDAP_THREAD_DEBUG_WRAP +/************************************** + * * + * definitions for type-wrapped debug * + * * + **************************************/ + +LDAP_BEGIN_DECL + +#ifndef LDAP_UINTPTR_T /* May be configured in CPPFLAGS */ +#define LDAP_UINTPTR_T unsigned long +#endif + +typedef enum { + ldap_debug_magic = -(int) (((unsigned)-1)/19) +} ldap_debug_magic_t; + +typedef enum { + /* Could fill in "locked" etc here later */ + ldap_debug_state_inited = (int) (((unsigned)-1)/11), + ldap_debug_state_destroyed +} ldap_debug_state_t; + +typedef struct { + /* Enclosed in magic numbers in the hope of catching overwrites */ + ldap_debug_magic_t magic; /* bit pattern to recognize usages */ + LDAP_UINTPTR_T self; /* ~(LDAP_UINTPTR_T)&(this struct) */ + union ldap_debug_mem_u { /* Dummy memory reference */ + unsigned char *ptr; + LDAP_UINTPTR_T num; + } mem; + ldap_debug_state_t state; /* doubles as another magic number */ +} ldap_debug_usage_info_t; + +typedef struct { + ldap_int_thread_mutex_t wrapped; + ldap_debug_usage_info_t usage; + ldap_int_thread_t owner; +} ldap_debug_thread_mutex_t; + +#define LDAP_DEBUG_MUTEX_NULL {LDAP_INT_MUTEX_NULL, {0,0,{0},0} /*,owner*/} +#define LDAP_DEBUG_MUTEX_FIRSTCREATE(m) \ + ((void) ((m).usage.state || ldap_pvt_thread_mutex_init(&(m)))) + +typedef struct { + ldap_int_thread_cond_t wrapped; + ldap_debug_usage_info_t usage; +} ldap_debug_thread_cond_t; + +typedef struct { + ldap_int_thread_rdwr_t wrapped; + ldap_debug_usage_info_t usage; +} ldap_debug_thread_rdwr_t; + +#ifndef NDEBUG +#define LDAP_INT_THREAD_ASSERT_MUTEX_OWNER(mutex) \ + ldap_debug_thread_assert_mutex_owner( \ + __FILE__, __LINE__, "owns(" #mutex ")", mutex ) +LDAP_F(void) ldap_debug_thread_assert_mutex_owner LDAP_P(( + LDAP_CONST char *file, + int line, + LDAP_CONST char *msg, + ldap_debug_thread_mutex_t *mutex )); +#endif /* NDEBUG */ + +LDAP_END_DECL + +#endif /* LDAP_THREAD_DEBUG_WRAP */ + +#endif /* _LDAP_INT_THREAD_H */ diff --git a/contrib/libs/openldap/include/ldap_log.h b/contrib/libs/openldap/include/ldap_log.h new file mode 100644 index 0000000000..8b8b0b654e --- /dev/null +++ b/contrib/libs/openldap/include/ldap_log.h @@ -0,0 +1,211 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef LDAP_LOG_H +#define LDAP_LOG_H + +#include <stdio.h> +#include <ldap_cdefs.h> + +LDAP_BEGIN_DECL + +/* + * debug reporting levels. + * + * They start with the syslog levels, and + * go down in importance. The normal + * debugging levels begin with LDAP_LEVEL_ENTRY + * + */ + +/* + * The "OLD_DEBUG" means that all logging occurs at LOG_DEBUG + */ + +#ifdef OLD_DEBUG +/* original behavior: all logging occurs at the same severity level */ +#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG) +#define LDAP_LEVEL_EMERG ldap_syslog_level +#define LDAP_LEVEL_ALERT ldap_syslog_level +#define LDAP_LEVEL_CRIT ldap_syslog_level +#define LDAP_LEVEL_ERR ldap_syslog_level +#define LDAP_LEVEL_WARNING ldap_syslog_level +#define LDAP_LEVEL_NOTICE ldap_syslog_level +#define LDAP_LEVEL_INFO ldap_syslog_level +#define LDAP_LEVEL_DEBUG ldap_syslog_level +#else /* !LDAP_DEBUG || !LDAP_SYSLOG */ +#define LDAP_LEVEL_EMERG (7) +#define LDAP_LEVEL_ALERT (7) +#define LDAP_LEVEL_CRIT (7) +#define LDAP_LEVEL_ERR (7) +#define LDAP_LEVEL_WARNING (7) +#define LDAP_LEVEL_NOTICE (7) +#define LDAP_LEVEL_INFO (7) +#define LDAP_LEVEL_DEBUG (7) +#endif /* !LDAP_DEBUG || !LDAP_SYSLOG */ + +#else /* ! OLD_DEBUG */ +/* map syslog onto LDAP severity levels */ +#ifdef LOG_DEBUG +#define LDAP_LEVEL_EMERG LOG_EMERG +#define LDAP_LEVEL_ALERT LOG_ALERT +#define LDAP_LEVEL_CRIT LOG_CRIT +#define LDAP_LEVEL_ERR LOG_ERR +#define LDAP_LEVEL_WARNING LOG_WARNING +#define LDAP_LEVEL_NOTICE LOG_NOTICE +#define LDAP_LEVEL_INFO LOG_INFO +#define LDAP_LEVEL_DEBUG LOG_DEBUG +#else /* ! LOG_DEBUG */ +#define LDAP_LEVEL_EMERG (0) +#define LDAP_LEVEL_ALERT (1) +#define LDAP_LEVEL_CRIT (2) +#define LDAP_LEVEL_ERR (3) +#define LDAP_LEVEL_WARNING (4) +#define LDAP_LEVEL_NOTICE (5) +#define LDAP_LEVEL_INFO (6) +#define LDAP_LEVEL_DEBUG (7) +#endif /* ! LOG_DEBUG */ +#endif /* ! OLD_DEBUG */ +#if 0 +/* in case we need to reuse the unused bits of severity */ +#define LDAP_LEVEL_MASK(s) ((s) & 0x7) +#else +#define LDAP_LEVEL_MASK(s) (s) +#endif + +/* (yet) unused */ +#define LDAP_LEVEL_ENTRY (0x08) /* log function entry points */ +#define LDAP_LEVEL_ARGS (0x10) /* log function call parameters */ +#define LDAP_LEVEL_RESULTS (0x20) /* Log function results */ +#define LDAP_LEVEL_DETAIL1 (0x40) /* log level 1 function operational details */ +#define LDAP_LEVEL_DETAIL2 (0x80) /* Log level 2 function operational details */ +/* end of (yet) unused */ + +/* original subsystem selection mechanism */ +#define LDAP_DEBUG_TRACE 0x0001 +#define LDAP_DEBUG_PACKETS 0x0002 +#define LDAP_DEBUG_ARGS 0x0004 +#define LDAP_DEBUG_CONNS 0x0008 +#define LDAP_DEBUG_BER 0x0010 +#define LDAP_DEBUG_FILTER 0x0020 +#define LDAP_DEBUG_CONFIG 0x0040 +#define LDAP_DEBUG_ACL 0x0080 +#define LDAP_DEBUG_STATS 0x0100 +#define LDAP_DEBUG_STATS2 0x0200 +#define LDAP_DEBUG_SHELL 0x0400 +#define LDAP_DEBUG_PARSE 0x0800 +#if 0 /* no longer used (nor supported) */ +#define LDAP_DEBUG_CACHE 0x1000 +#define LDAP_DEBUG_INDEX 0x2000 +#endif +#define LDAP_DEBUG_SYNC 0x4000 + +#define LDAP_DEBUG_NONE 0x8000 +#define LDAP_DEBUG_ANY (-1) + +/* debugging stuff */ +#ifdef LDAP_DEBUG + /* + * This is a bogus extern declaration for the compiler. No need to ensure + * a 'proper' dllimport. + */ +#ifndef ldap_debug +extern int ldap_debug; +#endif /* !ldap_debug */ + +#ifdef LDAP_SYSLOG +extern int ldap_syslog; +extern int ldap_syslog_level; + +#ifdef HAVE_EBCDIC +#define syslog eb_syslog +extern void eb_syslog(int pri, const char *fmt, ...); +#endif /* HAVE_EBCDIC */ + +#endif /* LDAP_SYSLOG */ +#endif /* LDAP_DEBUG */ + +/* we keep libldap working with preprocessors that can't do variadic macros */ +#ifndef LDAP_INT_DEBUG +/* this doesn't below as part of ldap.h */ +#ifdef LDAP_DEBUG +#ifdef LDAP_SYSLOG + +#define LogTest(level) ( ( ldap_debug | ldap_syslog ) & (level) ) +#define Log(level, severity, ...) \ + do { \ + if ( ldap_debug & (level) ) \ + lutil_debug( ldap_debug, (level), __VA_ARGS__ ); \ + if ( ldap_syslog & (level) ) \ + syslog( LDAP_LEVEL_MASK((severity)), __VA_ARGS__ ); \ + } while ( 0 ) + +#else /* ! LDAP_SYSLOG */ + +#define LogTest(level) ( ldap_debug & (level) ) +#define Log(level, severity, ...) \ + do { \ + if ( ldap_debug & (level) ) \ + lutil_debug( ldap_debug, (level), __VA_ARGS__ ); \ + } while ( 0 ) + +#endif /* ! LDAP_SYSLOG */ +#else /* ! LDAP_DEBUG */ + +/* TODO: in case LDAP_DEBUG is undefined, make sure logs with appropriate + * severity gets thru anyway */ +#define LogTest(level) ( 0 ) +#define Log(level, severity, ...) ((void) 0) + +#endif /* ! LDAP_DEBUG */ + +#define Debug(level, ...) \ + Log((level), ldap_syslog_level, __VA_ARGS__ ) +#endif /* ! LDAP_INT_DEBUG */ + +/* Actually now in liblber/debug.c */ +LDAP_LUTIL_F(int) lutil_debug_file LDAP_P(( FILE *file )); + +LDAP_LUTIL_F(void) lutil_debug LDAP_P(( + int debug, int level, + const char* fmt, ... )) LDAP_GCCATTR((format(printf, 3, 4))); + +#ifdef LDAP_DEFINE_LDAP_DEBUG +/* This struct matches the head of ldapoptions in <ldap-int.h> */ +struct ldapoptions_prefix { + short ldo_valid; + int ldo_debug; +}; +#define ldap_debug \ + (*(int *) ((char *)&ldap_int_global_options \ + + offsetof(struct ldapoptions_prefix, ldo_debug))) + +struct ldapoptions; +LDAP_V ( struct ldapoptions ) ldap_int_global_options; +#endif /* LDAP_DEFINE_LDAP_DEBUG */ + +LDAP_END_DECL + +#endif /* LDAP_LOG_H */ diff --git a/contrib/libs/openldap/include/ldap_pvt.h b/contrib/libs/openldap/include/ldap_pvt.h new file mode 100644 index 0000000000..7d840dafd4 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_pvt.h @@ -0,0 +1,588 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* ldap-pvt.h - Header for ldap_pvt_ functions. + * These are meant to be internal to OpenLDAP Software. + */ + +#ifndef _LDAP_PVT_H +#define _LDAP_PVT_H 1 + +#include <openldap.h> /* get public interfaces */ +#include <lber.h> /* get ber_slen_t */ +#include <lber_pvt.h> /* get Sockbuf_Buf */ + +LDAP_BEGIN_DECL + +LDAP_F ( int ) +ldap_pvt_url_scheme2proto LDAP_P(( + const char * )); +LDAP_F ( int ) +ldap_pvt_url_scheme2tls LDAP_P(( + const char * )); +LDAP_F ( int ) +ldap_pvt_url_scheme2proxied LDAP_P(( + const char * )); + +LDAP_F ( int ) +ldap_pvt_url_scheme_port LDAP_P(( + const char *, int )); + +struct ldap_url_desc; /* avoid pulling in <ldap.h> */ + +#define LDAP_PVT_URL_PARSE_NONE (0x00U) +#define LDAP_PVT_URL_PARSE_NOEMPTY_HOST (0x01U) +#define LDAP_PVT_URL_PARSE_DEF_PORT (0x02U) +#define LDAP_PVT_URL_PARSE_NOEMPTY_DN (0x04U) +#define LDAP_PVT_URL_PARSE_NODEF_SCOPE (0x08U) +#define LDAP_PVT_URL_PARSE_HISTORIC (LDAP_PVT_URL_PARSE_NODEF_SCOPE | \ + LDAP_PVT_URL_PARSE_NOEMPTY_HOST | \ + LDAP_PVT_URL_PARSE_DEF_PORT) + +LDAP_F( int ) +ldap_url_parse_ext LDAP_P(( + LDAP_CONST char *url, + struct ldap_url_desc **ludpp, + unsigned flags )); + +LDAP_F (int) ldap_url_parselist LDAP_P(( /* deprecated, use ldap_url_parselist_ext() */ + struct ldap_url_desc **ludlist, + const char *url )); + +LDAP_F (int) ldap_url_parselist_ext LDAP_P(( + struct ldap_url_desc **ludlist, + const char *url, + const char *sep, + unsigned flags )); + +LDAP_F (char *) ldap_url_list2urls LDAP_P(( + struct ldap_url_desc *ludlist )); + +LDAP_F (void) ldap_free_urllist LDAP_P(( + struct ldap_url_desc *ludlist )); + +LDAP_F (int) ldap_pvt_scope2bv LDAP_P (( + int scope, struct berval *bv )); + +LDAP_F (LDAP_CONST char *) ldap_pvt_scope2str LDAP_P (( + int scope )); + +LDAP_F (int) ldap_pvt_bv2scope LDAP_P (( + struct berval *bv )); + +LDAP_F (int) ldap_pvt_str2scope LDAP_P (( + LDAP_CONST char * )); + +LDAP_F( char * ) +ldap_pvt_ctime LDAP_P(( + const time_t *tp, + char *buf )); + +# if defined( HAVE_GMTIME_R ) +# define USE_GMTIME_R +# define ldap_pvt_gmtime(timep, result) gmtime_r((timep), (result)) +# else +LDAP_F( struct tm * ) +ldap_pvt_gmtime LDAP_P(( + LDAP_CONST time_t *timep, + struct tm *result )); +#endif + +# if defined( HAVE_LOCALTIME_R ) +# define USE_LOCALTIME_R +# define ldap_pvt_localtime(timep, result) localtime_r((timep), (result)) +# else +LDAP_F( struct tm * ) +ldap_pvt_localtime LDAP_P(( + LDAP_CONST time_t *timep, + struct tm *result )); +# endif + +#if defined( USE_GMTIME_R ) && defined( USE_LOCALTIME_R ) +# define ldap_pvt_gmtime_lock() (0) +# define ldap_pvt_gmtime_unlock() (0) +#else +LDAP_F( int ) +ldap_pvt_gmtime_lock LDAP_P(( void )); + +LDAP_F( int ) +ldap_pvt_gmtime_unlock LDAP_P(( void )); +#endif /* USE_GMTIME_R && USE_LOCALTIME_R */ + +/* Get current time as a structured time */ +struct lutil_tm; +LDAP_F( void ) +ldap_pvt_gettime LDAP_P(( struct lutil_tm * )); + +#ifdef _WIN32 +#define gettimeofday(tv,tz) ldap_pvt_gettimeofday(tv,tz) +struct timeval; +LDAP_F( int ) +ldap_pvt_gettimeofday LDAP_P(( struct timeval *tv, void *unused )); +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif +#define clock_gettime(clkid,tv) ldap_pvt_clock_gettime(clkid,tv) +struct timespec; +LDAP_F( int ) +ldap_pvt_clock_gettime LDAP_P(( int clkid, struct timespec *tv )); +#endif + +/* use this macro to allocate buffer for ldap_pvt_csnstr */ +#define LDAP_PVT_CSNSTR_BUFSIZE 64 +LDAP_F( size_t ) +ldap_pvt_csnstr( char *buf, size_t len, unsigned int replica, unsigned int mod ); + +LDAP_F( char *) ldap_pvt_get_fqdn LDAP_P(( char * )); + +struct hostent; /* avoid pulling in <netdb.h> */ + +LDAP_F( int ) +ldap_pvt_gethostbyname_a LDAP_P(( + const char *name, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr )); + +LDAP_F( int ) +ldap_pvt_gethostbyaddr_a LDAP_P(( + const char *addr, + int len, + int type, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr )); + +struct sockaddr; + +LDAP_F( int ) +ldap_pvt_get_hname LDAP_P(( + const struct sockaddr * sa, + int salen, + char *name, + int namelen, + char **herr )); + +#ifdef LDAP_PF_LOCAL +#define LDAP_IPADDRLEN (MAXPATHLEN + sizeof("PATH=")) +#elif defined(LDAP_PF_INET6) +#define LDAP_IPADDRLEN sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535") +#else +#define LDAP_IPADDRLEN sizeof("IP=255.255.255.255:65336") +#endif + +union Sockaddr; + +LDAP_F (void) +ldap_pvt_sockaddrstr LDAP_P(( + union Sockaddr *sa, + struct berval * )); + + +/* charray.c */ + +LDAP_F( int ) +ldap_charray_add LDAP_P(( + char ***a, + const char *s )); + +LDAP_F( int ) +ldap_charray_merge LDAP_P(( + char ***a, + char **s )); + +LDAP_F( void ) +ldap_charray_free LDAP_P(( char **a )); + +LDAP_F( int ) +ldap_charray_inlist LDAP_P(( + char **a, + const char *s )); + +LDAP_F( char ** ) +ldap_charray_dup LDAP_P(( char **a )); + +LDAP_F( char ** ) +ldap_str2charray LDAP_P(( + const char *str, + const char *brkstr )); + +LDAP_F( char * ) +ldap_charray2str LDAP_P(( + char **array, const char* sep )); + +/* getdn.c */ + +#ifdef LDAP_AVA_NULL /* in ldap.h */ +LDAP_F( void ) ldap_rdnfree_x LDAP_P(( LDAPRDN rdn, void *ctx )); +LDAP_F( void ) ldap_dnfree_x LDAP_P(( LDAPDN dn, void *ctx )); + +LDAP_F( int ) ldap_bv2dn_x LDAP_P(( + struct berval *bv, LDAPDN *dn, unsigned flags, void *ctx )); +LDAP_F( int ) ldap_dn2bv_x LDAP_P(( + LDAPDN dn, struct berval *bv, unsigned flags, void *ctx )); +LDAP_F( int ) ldap_bv2rdn_x LDAP_P(( + struct berval *, LDAPRDN *, char **, unsigned flags, void *ctx )); +LDAP_F( int ) ldap_rdn2bv_x LDAP_P(( + LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx )); +#endif /* LDAP_AVA_NULL */ + +/* url.c */ +LDAP_F (void) ldap_pvt_hex_unescape LDAP_P(( char *s )); + +/* + * these macros assume 'x' is an ASCII x + * and assume the "C" locale + */ +#define LDAP_ASCII(c) (!((c) & 0x80)) +#define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') +#define LDAP_DIGIT(c) ((c) >= '0' && (c) <= '9') +#define LDAP_LOWER(c) ((c) >= 'a' && (c) <= 'z') +#define LDAP_UPPER(c) ((c) >= 'A' && (c) <= 'Z') +#define LDAP_ALPHA(c) (LDAP_LOWER(c) || LDAP_UPPER(c)) +#define LDAP_ALNUM(c) (LDAP_ALPHA(c) || LDAP_DIGIT(c)) + +#define LDAP_LDH(c) (LDAP_ALNUM(c) || (c) == '-') + +#define LDAP_HEXLOWER(c) ((c) >= 'a' && (c) <= 'f') +#define LDAP_HEXUPPER(c) ((c) >= 'A' && (c) <= 'F') +#define LDAP_HEX(c) (LDAP_DIGIT(c) || \ + LDAP_HEXLOWER(c) || LDAP_HEXUPPER(c)) + +/* controls.c */ +struct ldapcontrol; +LDAP_F (int) +ldap_pvt_put_control LDAP_P(( + const struct ldapcontrol *c, + BerElement *ber )); +LDAP_F (int) ldap_pvt_get_controls LDAP_P(( + BerElement *be, + struct ldapcontrol ***ctrlsp)); + +#ifdef HAVE_CYRUS_SASL +/* cyrus.c */ +struct sasl_security_properties; /* avoid pulling in <sasl.h> */ +LDAP_F (int) ldap_pvt_sasl_secprops LDAP_P(( + const char *in, + struct sasl_security_properties *secprops )); +LDAP_F (void) ldap_pvt_sasl_secprops_unparse LDAP_P(( + struct sasl_security_properties *secprops, + struct berval *out )); + +LDAP_F (void *) ldap_pvt_sasl_mutex_new LDAP_P((void)); +LDAP_F (int) ldap_pvt_sasl_mutex_lock LDAP_P((void *mutex)); +LDAP_F (int) ldap_pvt_sasl_mutex_unlock LDAP_P((void *mutex)); +LDAP_F (void) ldap_pvt_sasl_mutex_dispose LDAP_P((void *mutex)); + +LDAP_F (int) ldap_pvt_sasl_cbinding_parse LDAP_P(( const char *arg )); +LDAP_F (void *) ldap_pvt_sasl_cbinding LDAP_P(( void *ssl, int type, + int is_server )); +#endif /* HAVE_CYRUS_SASL */ + +struct sockbuf; /* avoid pulling in <lber.h> */ +LDAP_F (int) ldap_pvt_sasl_install LDAP_P(( struct sockbuf *, void * )); +LDAP_F (void) ldap_pvt_sasl_remove LDAP_P(( struct sockbuf * )); + +/* + * SASL encryption support for LBER Sockbufs + */ + +struct sb_sasl_generic_data; + +struct sb_sasl_generic_ops { + void (*init)(struct sb_sasl_generic_data *p, + ber_len_t *min_send, + ber_len_t *max_send, + ber_len_t *max_recv); + ber_int_t (*encode)(struct sb_sasl_generic_data *p, + unsigned char *buf, + ber_len_t len, + Sockbuf_Buf *dst); + ber_int_t (*decode)(struct sb_sasl_generic_data *p, + const Sockbuf_Buf *src, + Sockbuf_Buf *dst); + void (*reset_buf)(struct sb_sasl_generic_data *p, + Sockbuf_Buf *buf); + void (*fini)(struct sb_sasl_generic_data *p); +}; + +struct sb_sasl_generic_install { + const struct sb_sasl_generic_ops *ops; + void *ops_private; +}; + +struct sb_sasl_generic_data { + const struct sb_sasl_generic_ops *ops; + void *ops_private; + Sockbuf_IO_Desc *sbiod; + ber_len_t min_send; + ber_len_t max_send; + ber_len_t max_recv; + Sockbuf_Buf sec_buf_in; + Sockbuf_Buf buf_in; + Sockbuf_Buf buf_out; + unsigned int flags; +#define LDAP_PVT_SASL_PARTIAL_WRITE 1 +}; + +#ifndef LDAP_PVT_SASL_LOCAL_SSF +#define LDAP_PVT_SASL_LOCAL_SSF 71 /* SSF for Unix Domain Sockets */ +#endif /* ! LDAP_PVT_SASL_LOCAL_SSF */ + +struct ldap; +struct ldapmsg; +struct ldifrecord; + +/* abandon */ +LDAP_F ( int ) ldap_pvt_discard LDAP_P(( + struct ldap *ld, ber_int_t msgid )); + +/* init.c */ +LDAP_F( int ) +ldap_pvt_conf_option LDAP_P(( + char *cmd, char *opt, int userconf )); + +/* ldifutil.c */ +LDAP_F( int ) +ldap_parse_ldif_record_x LDAP_P(( + struct berval *rbuf, + unsigned long linenum, + struct ldifrecord *lr, + const char *errstr, + unsigned int flags, + void *ctx )); + +/* messages.c */ +LDAP_F( BerElement * ) +ldap_get_message_ber LDAP_P(( + struct ldapmsg * )); + +/* open */ +LDAP_F (int) ldap_open_internal_connection LDAP_P(( + struct ldap **ldp, ber_socket_t *fdp )); + +/* sasl.c */ +LDAP_F (int) ldap_pvt_sasl_generic_install LDAP_P(( Sockbuf *sb, + struct sb_sasl_generic_install *install_arg )); +LDAP_F (void) ldap_pvt_sasl_generic_remove LDAP_P(( Sockbuf *sb )); + +/* search.c */ +LDAP_F( int ) ldap_pvt_put_filter LDAP_P(( + BerElement *ber, + const char *str )); + +LDAP_F( char * ) +ldap_pvt_find_wildcard LDAP_P(( const char *s )); + +LDAP_F( ber_slen_t ) +ldap_pvt_filter_value_unescape LDAP_P(( char *filter )); + +LDAP_F( ber_len_t ) +ldap_bv2escaped_filter_value_len LDAP_P(( struct berval *in )); + +LDAP_F( int ) +ldap_bv2escaped_filter_value_x LDAP_P(( struct berval *in, struct berval *out, + int inplace, void *ctx )); + +LDAP_F (int) ldap_pvt_search LDAP_P(( + struct ldap *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + struct ldapcontrol **sctrls, + struct ldapcontrol **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + int *msgidp )); + +LDAP_F(int) ldap_pvt_search_s LDAP_P(( + struct ldap *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + struct ldapcontrol **sctrls, + struct ldapcontrol **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + struct ldapmsg **res )); + +/* string.c */ +LDAP_F( char * ) +ldap_pvt_str2upper LDAP_P(( char *str )); + +LDAP_F( char * ) +ldap_pvt_str2lower LDAP_P(( char *str )); + +LDAP_F( struct berval * ) +ldap_pvt_str2upperbv LDAP_P(( char *str, struct berval *bv )); + +LDAP_F( struct berval * ) +ldap_pvt_str2lowerbv LDAP_P(( char *str, struct berval *bv )); + +/* tls.c */ +LDAP_F (int) ldap_pvt_tls_config LDAP_P(( struct ldap *ld, + int option, const char *arg )); +LDAP_F (int) ldap_pvt_tls_get_option LDAP_P(( struct ldap *ld, + int option, void *arg )); +LDAP_F (int) ldap_pvt_tls_set_option LDAP_P(( struct ldap *ld, + int option, void *arg )); + +LDAP_F (void) ldap_pvt_tls_destroy LDAP_P(( void )); +LDAP_F (int) ldap_pvt_tls_init LDAP_P(( int do_threads )); +LDAP_F (int) ldap_pvt_tls_init_def_ctx LDAP_P(( int is_server )); +LDAP_F (int) ldap_pvt_tls_accept LDAP_P(( Sockbuf *sb, void *ctx_arg )); +LDAP_F (int) ldap_pvt_tls_connect LDAP_P(( struct ldap *ld, Sockbuf *sb, const char *host )); +LDAP_F (int) ldap_pvt_tls_inplace LDAP_P(( Sockbuf *sb )); +LDAP_F (void *) ldap_pvt_tls_sb_ctx LDAP_P(( Sockbuf *sb )); +LDAP_F (void) ldap_pvt_tls_ctx_free LDAP_P(( void * )); + +typedef int LDAPDN_rewrite_dummy LDAP_P (( void *dn, unsigned flags )); + +typedef int (LDAP_TLS_CONNECT_CB) LDAP_P (( struct ldap *ld, void *ssl, + void *ctx, void *arg )); + +LDAP_F (int) ldap_pvt_tls_get_my_dn LDAP_P(( void *ctx, struct berval *dn, + LDAPDN_rewrite_dummy *func, unsigned flags )); +LDAP_F (int) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx, struct berval *dn, + LDAPDN_rewrite_dummy *func, unsigned flags )); +LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx )); +LDAP_F (int) ldap_pvt_tls_get_unique LDAP_P(( void *ctx, struct berval *buf, int is_server )); +LDAP_F (int) ldap_pvt_tls_get_endpoint LDAP_P(( void *ctx, struct berval *buf, int is_server )); +LDAP_F (const char *) ldap_pvt_tls_get_version LDAP_P(( void *ctx )); +LDAP_F (const char *) ldap_pvt_tls_get_cipher LDAP_P(( void *ctx )); + +LDAP_END_DECL + +/* + * Multiple precision stuff + * + * May use OpenSSL's BIGNUM if built with TLS, + * or GNU's multiple precision library. But if + * long long is available, that's big enough + * and much more efficient. + * + * If none is available, unsigned long data is used. + */ + +LDAP_BEGIN_DECL + +#ifdef USE_MP_BIGNUM +/* + * Use OpenSSL's BIGNUM + */ +#error #include <openssl/crypto.h> +#error #include <openssl/bn.h> + +typedef BIGNUM* ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT (NULL) + +#define ldap_pvt_mp_init(mp) \ + do { (mp) = BN_new(); } while (0) + +/* FIXME: we rely on mpr being initialized */ +#define ldap_pvt_mp_init_set(mpr,mpv) \ + do { ldap_pvt_mp_init((mpr)); BN_add((mpr), (mpr), (mpv)); } while (0) + +#define ldap_pvt_mp_add(mpr,mpv) \ + BN_add((mpr), (mpr), (mpv)) + +#define ldap_pvt_mp_add_ulong(mp,v) \ + BN_add_word((mp), (v)) + +#define ldap_pvt_mp_clear(mp) \ + do { BN_free((mp)); (mp) = 0; } while (0) + +#elif defined(USE_MP_GMP) +/* + * Use GNU's multiple precision library + */ +#include <gmp.h> + +typedef mpz_t ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT { 0 } + +#define ldap_pvt_mp_init(mp) \ + mpz_init((mp)) + +#define ldap_pvt_mp_init_set(mpr,mpv) \ + mpz_init_set((mpr), (mpv)) + +#define ldap_pvt_mp_add(mpr,mpv) \ + mpz_add((mpr), (mpr), (mpv)) + +#define ldap_pvt_mp_add_ulong(mp,v) \ + mpz_add_ui((mp), (mp), (v)) + +#define ldap_pvt_mp_clear(mp) \ + mpz_clear((mp)) + +#else +/* + * Use unsigned long long + */ + +#ifdef USE_MP_LONG_LONG +typedef unsigned long long ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT (0LL) +#elif defined(USE_MP_LONG) +typedef unsigned long ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT (0L) +#elif defined(HAVE_LONG_LONG) +typedef unsigned long long ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT (0LL) +#else +typedef unsigned long ldap_pvt_mp_t; +#define LDAP_PVT_MP_INIT (0L) +#endif + +#define ldap_pvt_mp_init(mp) \ + do { (mp) = 0; } while (0) + +#define ldap_pvt_mp_init_set(mpr,mpv) \ + do { (mpr) = (mpv); } while (0) + +#define ldap_pvt_mp_add(mpr,mpv) \ + do { (mpr) += (mpv); } while (0) + +#define ldap_pvt_mp_add_ulong(mp,v) \ + do { (mp) += (v); } while (0) + +#define ldap_pvt_mp_clear(mp) \ + do { (mp) = 0; } while (0) + +#endif /* MP */ + +#include "ldap_pvt_uc.h" + +LDAP_END_DECL + +LDAP_BEGIN_DECL + +#include <limits.h> /* get CHAR_BIT */ + +/* Buffer space for sign, decimal digits and \0. Note: log10(2) < 146/485. */ +#define LDAP_PVT_INTTYPE_CHARS(type) (((sizeof(type)*CHAR_BIT-1)*146)/485 + 3) + +LDAP_END_DECL + +#endif /* _LDAP_PVT_H */ diff --git a/contrib/libs/openldap/include/ldap_pvt_thread.h b/contrib/libs/openldap/include/ldap_pvt_thread.h new file mode 100644 index 0000000000..829ce8a1a8 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_pvt_thread.h @@ -0,0 +1,347 @@ +/* ldap_pvt_thread.h - ldap threads header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _LDAP_PVT_THREAD_H +#define _LDAP_PVT_THREAD_H /* libldap/ldap_thr_debug.h #undefines this */ + +#include "ldap_cdefs.h" +#include "ldap_int_thread.h" + +LDAP_BEGIN_DECL + +#ifndef LDAP_PVT_THREAD_H_DONE +typedef ldap_int_thread_t ldap_pvt_thread_t; +#ifdef LDAP_THREAD_DEBUG_WRAP +typedef ldap_debug_thread_mutex_t ldap_pvt_thread_mutex_t; +typedef ldap_debug_thread_cond_t ldap_pvt_thread_cond_t; +typedef ldap_debug_thread_rdwr_t ldap_pvt_thread_rdwr_t; +#define LDAP_PVT_MUTEX_FIRSTCREATE LDAP_DEBUG_MUTEX_FIRSTCREATE +#define LDAP_PVT_MUTEX_NULL LDAP_DEBUG_MUTEX_NULL +#else +typedef ldap_int_thread_mutex_t ldap_pvt_thread_mutex_t; +typedef ldap_int_thread_cond_t ldap_pvt_thread_cond_t; +typedef ldap_int_thread_rdwr_t ldap_pvt_thread_rdwr_t; +#define LDAP_PVT_MUTEX_FIRSTCREATE LDAP_INT_MUTEX_FIRSTCREATE +#define LDAP_PVT_MUTEX_NULL LDAP_INT_MUTEX_NULL +#endif +typedef ldap_int_thread_key_t ldap_pvt_thread_key_t; +#endif /* !LDAP_PVT_THREAD_H_DONE */ + +#define ldap_pvt_thread_equal ldap_int_thread_equal + +LDAP_F( int ) +ldap_pvt_thread_initialize LDAP_P(( void )); + +LDAP_F( int ) +ldap_pvt_thread_destroy LDAP_P(( void )); + +LDAP_F( unsigned int ) +ldap_pvt_thread_sleep LDAP_P(( unsigned int s )); + +LDAP_F( int ) +ldap_pvt_thread_get_concurrency LDAP_P(( void )); + +LDAP_F( int ) +ldap_pvt_thread_set_concurrency LDAP_P(( int )); + +#define LDAP_PVT_THREAD_CREATE_JOINABLE 0 +#define LDAP_PVT_THREAD_CREATE_DETACHED 1 + +#ifndef LDAP_PVT_THREAD_H_DONE +#define LDAP_PVT_THREAD_SET_STACK_SIZE +/* The size may be explicitly #defined to zero to disable it. */ +#if defined( LDAP_PVT_THREAD_STACK_SIZE ) && LDAP_PVT_THREAD_STACK_SIZE == 0 +# undef LDAP_PVT_THREAD_SET_STACK_SIZE +#elif !defined( LDAP_PVT_THREAD_STACK_SIZE ) + /* LARGE stack. Will be twice as large on 64 bit machine. */ +# define LDAP_PVT_THREAD_STACK_SIZE ( 1 * 1024 * 1024 * sizeof(void *) ) +#endif +#endif /* !LDAP_PVT_THREAD_H_DONE */ + +LDAP_F( int ) +ldap_pvt_thread_create LDAP_P(( + ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void * ), + void *arg)); + +LDAP_F( void ) +ldap_pvt_thread_exit LDAP_P(( void *retval )); + +LDAP_F( int ) +ldap_pvt_thread_join LDAP_P(( ldap_pvt_thread_t thread, void **status )); + +LDAP_F( int ) +ldap_pvt_thread_kill LDAP_P(( ldap_pvt_thread_t thread, int signo )); + +LDAP_F( int ) +ldap_pvt_thread_yield LDAP_P(( void )); + +LDAP_F( int ) +ldap_pvt_thread_cond_init LDAP_P(( ldap_pvt_thread_cond_t *cond )); + +LDAP_F( int ) +ldap_pvt_thread_cond_destroy LDAP_P(( ldap_pvt_thread_cond_t *cond )); + +LDAP_F( int ) +ldap_pvt_thread_cond_signal LDAP_P(( ldap_pvt_thread_cond_t *cond )); + +LDAP_F( int ) +ldap_pvt_thread_cond_broadcast LDAP_P(( ldap_pvt_thread_cond_t *cond )); + +LDAP_F( int ) +ldap_pvt_thread_cond_wait LDAP_P(( + ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_init LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_recursive_init LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_destroy LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_lock LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_trylock LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( int ) +ldap_pvt_thread_mutex_unlock LDAP_P(( ldap_pvt_thread_mutex_t *mutex )); + +LDAP_F( ldap_pvt_thread_t ) +ldap_pvt_thread_self LDAP_P(( void )); + +#ifdef LDAP_INT_THREAD_ASSERT_MUTEX_OWNER +#define LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER LDAP_INT_THREAD_ASSERT_MUTEX_OWNER +#else +#define LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex) ((void) 0) +#endif + +LDAP_F( int ) +ldap_pvt_thread_rdwr_init LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_destroy LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_rlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_rtrylock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_runlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_wlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_wtrylock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_wunlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_key_create LDAP_P((ldap_pvt_thread_key_t *keyp)); + +LDAP_F( int ) +ldap_pvt_thread_key_destroy LDAP_P((ldap_pvt_thread_key_t key)); + +LDAP_F( int ) +ldap_pvt_thread_key_setdata LDAP_P((ldap_pvt_thread_key_t key, void *data)); + +LDAP_F( int ) +ldap_pvt_thread_key_getdata LDAP_P((ldap_pvt_thread_key_t key, void **data)); + +#ifdef LDAP_DEBUG +LDAP_F( int ) +ldap_pvt_thread_rdwr_readers LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_writers LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); + +LDAP_F( int ) +ldap_pvt_thread_rdwr_active LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp)); +#endif /* LDAP_DEBUG */ + +#define LDAP_PVT_THREAD_EINVAL EINVAL +#define LDAP_PVT_THREAD_EBUSY EINVAL + +#ifndef LDAP_PVT_THREAD_H_DONE +typedef ldap_int_thread_pool_t ldap_pvt_thread_pool_t; + +typedef void * (ldap_pvt_thread_start_t) LDAP_P((void *ctx, void *arg)); +typedef int (ldap_pvt_thread_walk_t) LDAP_P((ldap_pvt_thread_start_t *start, void *start_arg, void *arg)); +typedef void (ldap_pvt_thread_pool_keyfree_t) LDAP_P((void *key, void *data)); +#endif /* !LDAP_PVT_THREAD_H_DONE */ + +LDAP_F( int ) +ldap_pvt_thread_pool_init LDAP_P(( + ldap_pvt_thread_pool_t *pool_out, + int max_threads, + int max_pending )); + +LDAP_F( int ) +ldap_pvt_thread_pool_init_q LDAP_P(( + ldap_pvt_thread_pool_t *pool_out, + int max_threads, + int max_pending, + int num_qs )); + +LDAP_F( int ) +ldap_pvt_thread_pool_submit LDAP_P(( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_start_t *start, + void *arg )); + +LDAP_F( int ) +ldap_pvt_thread_pool_submit2 LDAP_P(( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_start_t *start, + void *arg, + void **cookie )); + +LDAP_F( int ) +ldap_pvt_thread_pool_retract LDAP_P(( + void *cookie )); + +LDAP_F( int ) +ldap_pvt_thread_pool_walk LDAP_P(( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_start_t *start, + ldap_pvt_thread_walk_t *cb, + void *arg )); + +LDAP_F( int ) +ldap_pvt_thread_pool_maxthreads LDAP_P(( + ldap_pvt_thread_pool_t *pool, + int max_threads )); + +LDAP_F( int ) +ldap_pvt_thread_pool_queues LDAP_P(( + ldap_pvt_thread_pool_t *pool, + int numqs )); + +#ifndef LDAP_PVT_THREAD_H_DONE +typedef enum { + LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN = -1, + LDAP_PVT_THREAD_POOL_PARAM_MAX, + LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING, + LDAP_PVT_THREAD_POOL_PARAM_OPEN, + LDAP_PVT_THREAD_POOL_PARAM_STARTING, + LDAP_PVT_THREAD_POOL_PARAM_ACTIVE, + LDAP_PVT_THREAD_POOL_PARAM_PAUSING, + LDAP_PVT_THREAD_POOL_PARAM_PENDING, + LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, + LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX, + LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX, + LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX, + LDAP_PVT_THREAD_POOL_PARAM_STATE, + LDAP_PVT_THREAD_POOL_PARAM_PAUSED +} ldap_pvt_thread_pool_param_t; +#endif /* !LDAP_PVT_THREAD_H_DONE */ + +LDAP_F( int ) +ldap_pvt_thread_pool_query LDAP_P(( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_pool_param_t param, void *value )); + +LDAP_F( int ) +ldap_pvt_thread_pool_pausing LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_backload LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( void ) +ldap_pvt_thread_pool_idle LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( void ) +ldap_pvt_thread_pool_unidle LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_pausequery LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_pausewait LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_pausecheck_native LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_pause LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_resume LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_destroy LDAP_P(( + ldap_pvt_thread_pool_t *pool, + int run_pending )); + +LDAP_F( int ) +ldap_pvt_thread_pool_close LDAP_P(( + ldap_pvt_thread_pool_t *pool, + int run_pending )); + +LDAP_F( int ) +ldap_pvt_thread_pool_free LDAP_P(( + ldap_pvt_thread_pool_t *pool )); + +LDAP_F( int ) +ldap_pvt_thread_pool_getkey LDAP_P(( + void *ctx, + void *key, + void **data, + ldap_pvt_thread_pool_keyfree_t **kfree )); + +LDAP_F( int ) +ldap_pvt_thread_pool_setkey LDAP_P(( + void *ctx, + void *key, + void *data, + ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, + ldap_pvt_thread_pool_keyfree_t **oldkfreep )); + +LDAP_F( void ) +ldap_pvt_thread_pool_purgekey LDAP_P(( void *key )); + +LDAP_F( void *) +ldap_pvt_thread_pool_context LDAP_P(( void )); + +LDAP_F( void ) +ldap_pvt_thread_pool_context_reset LDAP_P(( void *key )); + +LDAP_F( ldap_pvt_thread_t ) +ldap_pvt_thread_pool_tid LDAP_P(( void *ctx )); + +LDAP_END_DECL + +#define LDAP_PVT_THREAD_H_DONE +#endif /* _LDAP_PVT_THREAD_H */ diff --git a/contrib/libs/openldap/include/ldap_pvt_uc.h b/contrib/libs/openldap/include/ldap_pvt_uc.h new file mode 100644 index 0000000000..1f2a50de1a --- /dev/null +++ b/contrib/libs/openldap/include/ldap_pvt_uc.h @@ -0,0 +1,163 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * ldap_pvt_uc.h - Header for Unicode functions. + * These are meant to be used by the OpenLDAP distribution only. + * These should be named ldap_pvt_....() + */ + +#ifndef _LDAP_PVT_UC_H +#define _LDAP_PVT_UC_H 1 + +#include <lber.h> /* get ber_slen_t */ + +#include <ac/bytes.h> +#include "../libraries/liblunicode/ucdata/ucdata.h" + +LDAP_BEGIN_DECL + +/* + * UTF-8 (in utf-8.c) + */ + +/* UCDATA uses UCS-2 passed in a 4 byte unsigned int */ +typedef ac_uint4 ldap_unicode_t; + +/* Convert a string with csize octets per character to UTF-8 */ +LDAP_F( int ) ldap_ucs_to_utf8s LDAP_P(( + struct berval *ucs, int csize, struct berval *utf8s )); + + +/* returns the number of bytes in the UTF-8 string */ +LDAP_F (ber_len_t) ldap_utf8_bytes( const char * ); +/* returns the number of UTF-8 characters in the string */ +LDAP_F (ber_len_t) ldap_utf8_chars( const char * ); +/* returns the length (in bytes) of the UTF-8 character */ +LDAP_F (int) ldap_utf8_offset( const char * ); +/* returns the length (in bytes) indicated by the UTF-8 character */ +LDAP_F (int) ldap_utf8_charlen( const char * ); + +/* returns the length (in bytes) indicated by the UTF-8 character + * also checks that shortest possible encoding was used + */ +LDAP_F (int) ldap_utf8_charlen2( const char * ); + +/* copies a UTF-8 character and returning number of bytes copied */ +LDAP_F (int) ldap_utf8_copy( char *, const char *); + +/* returns pointer of next UTF-8 character in string */ +LDAP_F (char*) ldap_utf8_next( const char * ); +/* returns pointer of previous UTF-8 character in string */ +LDAP_F (char*) ldap_utf8_prev( const char * ); + +/* primitive ctype routines -- not aware of non-ascii characters */ +LDAP_F (int) ldap_utf8_isascii( const char * ); +LDAP_F (int) ldap_utf8_isalpha( const char * ); +LDAP_F (int) ldap_utf8_isalnum( const char * ); +LDAP_F (int) ldap_utf8_isdigit( const char * ); +LDAP_F (int) ldap_utf8_isxdigit( const char * ); +LDAP_F (int) ldap_utf8_isspace( const char * ); + +/* span characters not in set, return bytes spanned */ +LDAP_F (ber_len_t) ldap_utf8_strcspn( const char* str, const char *set); +/* span characters in set, return bytes spanned */ +LDAP_F (ber_len_t) ldap_utf8_strspn( const char* str, const char *set); +/* return first occurrence of character in string */ +LDAP_F (char *) ldap_utf8_strchr( const char* str, const char *chr); +/* return first character of set in string */ +LDAP_F (char *) ldap_utf8_strpbrk( const char* str, const char *set); +/* reentrant tokenizer */ +LDAP_F (char*) ldap_utf8_strtok( char* sp, const char* sep, char **last); + +/* Optimizations */ +LDAP_V (const char) ldap_utf8_lentab[128]; +LDAP_V (const char) ldap_utf8_mintab[32]; + +#define LDAP_UTF8_ISASCII(p) ( !(*(const unsigned char *)(p) & 0x80 ) ) +#define LDAP_UTF8_CHARLEN(p) ( LDAP_UTF8_ISASCII(p) \ + ? 1 : ldap_utf8_lentab[*(const unsigned char *)(p) ^ 0x80] ) + +/* This is like CHARLEN but additionally validates to make sure + * the char used the shortest possible encoding. + * 'l' is used to temporarily hold the result of CHARLEN. + */ +#define LDAP_UTF8_CHARLEN2(p, l) ( ( ( l = LDAP_UTF8_CHARLEN( p )) < 3 || \ + ( ldap_utf8_mintab[*(const unsigned char *)(p) & 0x1f] & (p)[1] ) ) ? \ + l : 0 ) + +#define LDAP_UTF8_OFFSET(p) ( LDAP_UTF8_ISASCII(p) \ + ? 1 : ldap_utf8_offset((p)) ) + +#define LDAP_UTF8_COPY(d,s) ( LDAP_UTF8_ISASCII(s) \ + ? (*(d) = *(s), 1) : ldap_utf8_copy((d),(s)) ) + +#define LDAP_UTF8_NEXT(p) ( LDAP_UTF8_ISASCII(p) \ + ? (char *)(p)+1 : ldap_utf8_next((p)) ) + +#define LDAP_UTF8_INCR(p) ((p) = LDAP_UTF8_NEXT(p)) + +/* For symmetry */ +#define LDAP_UTF8_PREV(p) (ldap_utf8_prev((p))) +#define LDAP_UTF8_DECR(p) ((p)=LDAP_UTF8_PREV((p))) + + +/* these probably should be renamed */ +LDAP_LUNICODE_F(int) ucstrncmp( + const ldap_unicode_t *, + const ldap_unicode_t *, + ber_len_t ); + +LDAP_LUNICODE_F(int) ucstrncasecmp( + const ldap_unicode_t *, + const ldap_unicode_t *, + ber_len_t ); + +LDAP_LUNICODE_F(ldap_unicode_t *) ucstrnchr( + const ldap_unicode_t *, + ber_len_t, + ldap_unicode_t ); + +LDAP_LUNICODE_F(ldap_unicode_t *) ucstrncasechr( + const ldap_unicode_t *, + ber_len_t, + ldap_unicode_t ); + +LDAP_LUNICODE_F(void) ucstr2upper( + ldap_unicode_t *, + ber_len_t ); + +#define LDAP_UTF8_NOCASEFOLD 0x0U +#define LDAP_UTF8_CASEFOLD 0x1U +#define LDAP_UTF8_ARG1NFC 0x2U +#define LDAP_UTF8_ARG2NFC 0x4U +#define LDAP_UTF8_APPROX 0x8U + +LDAP_LUNICODE_F(struct berval *) UTF8bvnormalize( + struct berval *, + struct berval *, + unsigned, + void *memctx ); + +LDAP_LUNICODE_F(int) UTF8bvnormcmp( + struct berval *, + struct berval *, + unsigned, + void *memctx ); + +LDAP_END_DECL + +#endif + diff --git a/contrib/libs/openldap/include/ldap_queue.h b/contrib/libs/openldap/include/ldap_queue.h new file mode 100644 index 0000000000..6d32370107 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_queue.h @@ -0,0 +1,593 @@ +/* ldap_queue.h -- queue macros */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2001-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.32.2.5 2001/09/30 21:12:54 luigi Exp $ + * + * See also: ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change + */ +/* ACKNOWLEDGEMENTS: + * This work is derived from FreeBSD queue.h work. Adapted for use in + * OpenLDAP Software by Kurt D. Zeilenga. + */ + +#ifndef _LDAP_QUEUE_H_ +#define _LDAP_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * singly-linked tail queues, lists, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. Also, it is possible to rotate the queue, + * rejoining the ends and splitting it so that a given element becomes the + * new head or tail. + * + * For details on the use of these macros, see the queue(3) manual page. + * All macros are prefixed with LDAP_. + * + * SLIST_ LIST_ STAILQ_ TAILQ_ CIRCLEQ_ + * _HEAD + + + + + + * _ENTRY + + + + + + * _INIT + + + + + + * _ENTRY_INIT + + + + + + * _EMPTY + + + + + + * _FIRST + + + + + + * _NEXT + + + + + + * _PREV - - - + + + * _LAST - - + + + + * _FOREACH + + + + + + * _FOREACH_REVERSE - - - + + + * _INSERT_HEAD + + + + + + * _INSERT_BEFORE - + - + + + * _INSERT_AFTER + + + + + + * _INSERT_TAIL - - + + + + * _REMOVE_HEAD + - + - - + * _REMOVE + + + + + + * + */ + +/* + * Singly-linked List definitions. + */ +#define LDAP_SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define LDAP_SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LDAP_SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +#define LDAP_SLIST_ENTRY_INITIALIZER(entry) \ + { NULL } + +/* + * Singly-linked List functions. + */ +#define LDAP_SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define LDAP_SLIST_FIRST(head) ((head)->slh_first) + +#define LDAP_SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define LDAP_SLIST_INIT(head) { \ + (head)->slh_first = NULL; \ +} + +#define LDAP_SLIST_ENTRY_INIT(var, field) { \ + (var)->field.sle_next = NULL; \ +} + +#define LDAP_SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define LDAP_SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define LDAP_SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define LDAP_SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define LDAP_SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + LDAP_SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + +/* + * Singly-linked Tail queue definitions. + */ +#define LDAP_STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define LDAP_STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define LDAP_STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#define LDAP_STAILQ_ENTRY_INITIALIZER(entry) \ + { NULL } + +/* + * Singly-linked Tail queue functions. + */ +#define LDAP_STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define LDAP_STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) + +#define LDAP_STAILQ_ENTRY_INIT(var, field) { \ + (var)->field.stqe_next = NULL; \ +} + +#define LDAP_STAILQ_FIRST(head) ((head)->stqh_first) + +#define LDAP_STAILQ_LAST(head, type, field) \ + (LDAP_STAILQ_EMPTY(head) ? \ + NULL : \ + ((struct type *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#define LDAP_STAILQ_FOREACH(var, head, field) \ + for((var) = (head)->stqh_first; (var); (var) = (var)->field.stqe_next) + +#define LDAP_STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (0) + +#define LDAP_STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (0) + +#define LDAP_STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (tqelm)->field.stqe_next = (elm); \ +} while (0) + +#define LDAP_STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define LDAP_STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = \ + (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) + +#define LDAP_STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (0) + +#define LDAP_STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + LDAP_STAILQ_REMOVE_HEAD(head, field); \ + } \ + else { \ + struct type *curelm = (head)->stqh_first; \ + while( curelm->field.stqe_next != (elm) ) \ + curelm = curelm->field.stqe_next; \ + if((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LDAP_LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LDAP_LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LDAP_LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +#define LDAP_LIST_ENTRY_INITIALIZER(entry) \ + { NULL, NULL } + +/* + * List functions. + */ + +#define LDAP_LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LDAP_LIST_FIRST(head) ((head)->lh_first) + +#define LDAP_LIST_FOREACH(var, head, field) \ + for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next) + +#define LDAP_LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (0) + +#define LDAP_LIST_ENTRY_INIT(var, field) do { \ + (var)->field.le_next = NULL; \ + (var)->field.le_prev = NULL; \ +} while (0) + +#define LDAP_LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LDAP_LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LDAP_LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LDAP_LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LDAP_LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define LDAP_TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define LDAP_TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define LDAP_TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +#define LDAP_TAILQ_ENTRY_INITIALIZER(entry) \ + { NULL, NULL } + +/* + * Tail queue functions. + */ +#define LDAP_TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define LDAP_TAILQ_FOREACH(var, head, field) \ + for (var = LDAP_TAILQ_FIRST(head); var; var = LDAP_TAILQ_NEXT(var, field)) + +#define LDAP_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = LDAP_TAILQ_LAST((head), headname); \ + (var); \ + (var) = LDAP_TAILQ_PREV((var), headname, field)) + +#define LDAP_TAILQ_FIRST(head) ((head)->tqh_first) + +#define LDAP_TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define LDAP_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define LDAP_TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define LDAP_TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define LDAP_TAILQ_ENTRY_INIT(var, field) do { \ + (var)->field.tqe_next = NULL; \ + (var)->field.tqe_prev = NULL; \ +} while (0) + +#define LDAP_TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define LDAP_TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define LDAP_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define LDAP_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define LDAP_TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +/* + * Circular queue definitions. + */ +#define LDAP_CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define LDAP_CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&(head), (void *)&(head) } + +#define LDAP_CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define LDAP_CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) + +#define LDAP_CIRCLEQ_FIRST(head) ((head)->cqh_first) + +#define LDAP_CIRCLEQ_FOREACH(var, head, field) \ + for((var) = (head)->cqh_first; \ + (var) != (void *)(head); \ + (var) = (var)->field.cqe_next) + +#define LDAP_CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = (head)->cqh_last; \ + (var) != (void *)(head); \ + (var) = (var)->field.cqe_prev) + +#define LDAP_CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (0) + +#define LDAP_CIRCLEQ_ENTRY_INIT(var, field) do { \ + (var)->field.cqe_next = NULL; \ + (var)->field.cqe_prev = NULL; \ +} while (0) + +#define LDAP_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define LDAP_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define LDAP_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define LDAP_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define LDAP_CIRCLEQ_LAST(head) ((head)->cqh_last) + +#define LDAP_CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next) + +#define LDAP_CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev) + +#define LDAP_CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#define LDAP_CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : ((elm)->field.cqe_next)) + +#define LDAP_CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : ((elm)->field.cqe_prev)) + +#define LDAP_CIRCLEQ_MAKE_HEAD(head, elm, field) do { \ + if ((elm)->field.cqe_prev != (void *)(head)) { \ + (head)->cqh_first->field.cqe_prev = (head)->cqh_last; \ + (head)->cqh_last->field.cqe_next = (head)->cqh_first; \ + (head)->cqh_first = elm; \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + (elm)->field.cqe_prev->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (void *)(head); \ + } \ +} while (0) + +#define LDAP_CIRCLEQ_MAKE_TAIL(head, elm, field) do { \ + if ((elm)->field.cqe_next != (void *)(head)) { \ + (head)->cqh_first->field.cqe_prev = (head)->cqh_last; \ + (head)->cqh_last->field.cqe_next = (head)->cqh_first; \ + (head)->cqh_first = (elm)->field.cqe_next; \ + (head)->cqh_last = elm; \ + (elm)->field.cqe_next->field.cqe_prev = (void *)(head); \ + (elm)->field.cqe_next = (void *)(head); \ + } \ +} while (0) + +#endif /* !_LDAP_QUEUE_H_ */ diff --git a/contrib/libs/openldap/include/ldap_rq.h b/contrib/libs/openldap/include/ldap_rq.h new file mode 100644 index 0000000000..1050550ba8 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_rq.h @@ -0,0 +1,102 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef LDAP_RQ_H +#define LDAP_RQ_H 1 + +#include <ldap_cdefs.h> + +LDAP_BEGIN_DECL + +typedef struct re_s { + struct timeval next_sched; + struct timeval interval; + LDAP_STAILQ_ENTRY(re_s) tnext; /* it includes running */ + LDAP_STAILQ_ENTRY(re_s) rnext; + ldap_pvt_thread_start_t *routine; + void *arg; + char *tname; + char *tspec; + void *pool_cookie; +} re_t; + +typedef struct runqueue_s { + LDAP_STAILQ_HEAD(l, re_s) task_list; + LDAP_STAILQ_HEAD(rl, re_s) run_list; + ldap_pvt_thread_mutex_t rq_mutex; +} runqueue_t; + +LDAP_F( struct re_s* ) +ldap_pvt_runqueue_insert( + struct runqueue_s* rq, + time_t interval, + ldap_pvt_thread_start_t* routine, + void *arg, + char *tname, + char *tspec +); + +LDAP_F( struct re_s* ) +ldap_pvt_runqueue_find( + struct runqueue_s* rq, + ldap_pvt_thread_start_t* routine, + void *arg +); + +LDAP_F( void ) +ldap_pvt_runqueue_remove( + struct runqueue_s* rq, + struct re_s* entry +); + +LDAP_F( struct re_s* ) +ldap_pvt_runqueue_next_sched( + struct runqueue_s* rq, + struct timeval* next_run +); + +LDAP_F( void ) +ldap_pvt_runqueue_runtask( + struct runqueue_s* rq, + struct re_s* entry +); + +LDAP_F( void ) +ldap_pvt_runqueue_stoptask( + struct runqueue_s* rq, + struct re_s* entry +); + +LDAP_F( int ) +ldap_pvt_runqueue_isrunning( + struct runqueue_s* rq, + struct re_s* entry +); + +LDAP_F( void ) +ldap_pvt_runqueue_resched( + struct runqueue_s* rq, + struct re_s* entry, + int defer +); + +LDAP_F( int ) +ldap_pvt_runqueue_persistent_backload( + struct runqueue_s* rq +); + +LDAP_END_DECL + +#endif diff --git a/contrib/libs/openldap/include/ldap_schema.h b/contrib/libs/openldap/include/ldap_schema.h new file mode 100644 index 0000000000..9632180025 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_schema.h @@ -0,0 +1,360 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* ldap-schema.h - Header for basic schema handling functions that can be + * used by both clients and servers. + * these routines should be renamed ldap_x_... + */ + +#ifndef _LDAP_SCHEMA_H +#define _LDAP_SCHEMA_H 1 + +#include <ldap_cdefs.h> + +LDAP_BEGIN_DECL + +/* Codes for parsing errors */ + +#define LDAP_SCHERR_OUTOFMEM 1 +#define LDAP_SCHERR_UNEXPTOKEN 2 +#define LDAP_SCHERR_NOLEFTPAREN 3 +#define LDAP_SCHERR_NORIGHTPAREN 4 +#define LDAP_SCHERR_NODIGIT 5 +#define LDAP_SCHERR_BADNAME 6 +#define LDAP_SCHERR_BADDESC 7 +#define LDAP_SCHERR_BADSUP 8 +#define LDAP_SCHERR_DUPOPT 9 +#define LDAP_SCHERR_EMPTY 10 +#define LDAP_SCHERR_MISSING 11 +#define LDAP_SCHERR_OUT_OF_ORDER 12 + +typedef struct ldap_schema_extension_item { + char *lsei_name; + char **lsei_values; +} LDAPSchemaExtensionItem; + +typedef struct ldap_syntax { + char *syn_oid; /* REQUIRED */ + char **syn_names; /* OPTIONAL */ + char *syn_desc; /* OPTIONAL */ + LDAPSchemaExtensionItem **syn_extensions; /* OPTIONAL */ +} LDAPSyntax; + +typedef struct ldap_matchingrule { + char *mr_oid; /* REQUIRED */ + char **mr_names; /* OPTIONAL */ + char *mr_desc; /* OPTIONAL */ + int mr_obsolete; /* OPTIONAL */ + char *mr_syntax_oid; /* REQUIRED */ + LDAPSchemaExtensionItem **mr_extensions; /* OPTIONAL */ +} LDAPMatchingRule; + +typedef struct ldap_matchingruleuse { + char *mru_oid; /* REQUIRED */ + char **mru_names; /* OPTIONAL */ + char *mru_desc; /* OPTIONAL */ + int mru_obsolete; /* OPTIONAL */ + char **mru_applies_oids; /* REQUIRED */ + LDAPSchemaExtensionItem **mru_extensions; /* OPTIONAL */ +} LDAPMatchingRuleUse; + +typedef struct ldap_attributetype { + char *at_oid; /* REQUIRED */ + char **at_names; /* OPTIONAL */ + char *at_desc; /* OPTIONAL */ + int at_obsolete; /* 0=no, 1=yes */ + char *at_sup_oid; /* OPTIONAL */ + char *at_equality_oid; /* OPTIONAL */ + char *at_ordering_oid; /* OPTIONAL */ + char *at_substr_oid; /* OPTIONAL */ + char *at_syntax_oid; /* OPTIONAL */ + int at_syntax_len; /* OPTIONAL */ + int at_single_value; /* 0=no, 1=yes */ + int at_collective; /* 0=no, 1=yes */ + int at_no_user_mod; /* 0=no, 1=yes */ + int at_usage; /* 0=userApplications, 1=directoryOperation, + 2=distributedOperation, 3=dSAOperation */ + LDAPSchemaExtensionItem **at_extensions; /* OPTIONAL */ +} LDAPAttributeType; + +typedef struct ldap_objectclass { + char *oc_oid; /* REQUIRED */ + char **oc_names; /* OPTIONAL */ + char *oc_desc; /* OPTIONAL */ + int oc_obsolete; /* 0=no, 1=yes */ + char **oc_sup_oids; /* OPTIONAL */ + int oc_kind; /* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */ + char **oc_at_oids_must; /* OPTIONAL */ + char **oc_at_oids_may; /* OPTIONAL */ + LDAPSchemaExtensionItem **oc_extensions; /* OPTIONAL */ +} LDAPObjectClass; + +typedef struct ldap_contentrule { + char *cr_oid; /* REQUIRED */ + char **cr_names; /* OPTIONAL */ + char *cr_desc; /* OPTIONAL */ + char **cr_sup_oids; /* OPTIONAL */ + int cr_obsolete; /* 0=no, 1=yes */ + char **cr_oc_oids_aux; /* OPTIONAL */ + char **cr_at_oids_must; /* OPTIONAL */ + char **cr_at_oids_may; /* OPTIONAL */ + char **cr_at_oids_not; /* OPTIONAL */ + LDAPSchemaExtensionItem **cr_extensions; /* OPTIONAL */ +} LDAPContentRule; + +typedef struct ldap_nameform { + char *nf_oid; /* REQUIRED */ + char **nf_names; /* OPTIONAL */ + char *nf_desc; /* OPTIONAL */ + int nf_obsolete; /* 0=no, 1=yes */ + char *nf_objectclass; /* REQUIRED */ + char **nf_at_oids_must; /* REQUIRED */ + char **nf_at_oids_may; /* OPTIONAL */ + LDAPSchemaExtensionItem **nf_extensions; /* OPTIONAL */ +} LDAPNameForm; + +typedef struct ldap_structurerule { + int sr_ruleid; /* REQUIRED */ + char **sr_names; /* OPTIONAL */ + char *sr_desc; /* OPTIONAL */ + int sr_obsolete; /* 0=no, 1=yes */ + char *sr_nameform; /* REQUIRED */ + int sr_nsup_ruleids;/* number of sr_sup_ruleids */ + int *sr_sup_ruleids;/* OPTIONAL */ + LDAPSchemaExtensionItem **sr_extensions; /* OPTIONAL */ +} LDAPStructureRule; + +/* + * Misc macros + */ +#define LDAP_SCHEMA_NO 0 +#define LDAP_SCHEMA_YES 1 + +#define LDAP_SCHEMA_USER_APPLICATIONS 0 +#define LDAP_SCHEMA_DIRECTORY_OPERATION 1 +#define LDAP_SCHEMA_DISTRIBUTED_OPERATION 2 +#define LDAP_SCHEMA_DSA_OPERATION 3 + +#define LDAP_SCHEMA_ABSTRACT 0 +#define LDAP_SCHEMA_STRUCTURAL 1 +#define LDAP_SCHEMA_AUXILIARY 2 + + +/* + * Flags that control how liberal the parsing routines are. + */ +#define LDAP_SCHEMA_ALLOW_NONE 0x00U /* Strict parsing */ +#define LDAP_SCHEMA_ALLOW_NO_OID 0x01U /* Allow missing oid */ +#define LDAP_SCHEMA_ALLOW_QUOTED 0x02U /* Allow bogus extra quotes */ +#define LDAP_SCHEMA_ALLOW_DESCR 0x04U /* Allow descr instead of OID */ +#define LDAP_SCHEMA_ALLOW_DESCR_PREFIX 0x08U /* Allow descr as OID prefix */ +#define LDAP_SCHEMA_ALLOW_OID_MACRO 0x10U /* Allow OID macros in slapd */ +#define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */ +#define LDAP_SCHEMA_ALLOW_ALL 0x3fU /* Be very liberal in parsing */ +#define LDAP_SCHEMA_SKIP 0x80U /* Don't malloc any result */ + + +LDAP_F( LDAP_CONST char * ) +ldap_syntax2name LDAP_P(( + LDAPSyntax * syn )); + +LDAP_F( LDAP_CONST char * ) +ldap_matchingrule2name LDAP_P(( + LDAPMatchingRule * mr )); + +LDAP_F( LDAP_CONST char * ) +ldap_matchingruleuse2name LDAP_P(( + LDAPMatchingRuleUse * mru )); + +LDAP_F( LDAP_CONST char * ) +ldap_attributetype2name LDAP_P(( + LDAPAttributeType * at )); + +LDAP_F( LDAP_CONST char * ) +ldap_objectclass2name LDAP_P(( + LDAPObjectClass * oc )); + +LDAP_F( LDAP_CONST char * ) +ldap_contentrule2name LDAP_P(( + LDAPContentRule * cr )); + +LDAP_F( LDAP_CONST char * ) +ldap_nameform2name LDAP_P(( + LDAPNameForm * nf )); + +LDAP_F( LDAP_CONST char * ) +ldap_structurerule2name LDAP_P(( + LDAPStructureRule * sr )); + +LDAP_F( void ) +ldap_syntax_free LDAP_P(( + LDAPSyntax * syn )); + +LDAP_F( void ) +ldap_matchingrule_free LDAP_P(( + LDAPMatchingRule * mr )); + +LDAP_F( void ) +ldap_matchingruleuse_free LDAP_P(( + LDAPMatchingRuleUse * mr )); + +LDAP_F( void ) +ldap_attributetype_free LDAP_P(( + LDAPAttributeType * at )); + +LDAP_F( void ) +ldap_objectclass_free LDAP_P(( + LDAPObjectClass * oc )); + +LDAP_F( void ) +ldap_contentrule_free LDAP_P(( + LDAPContentRule * cr )); + +LDAP_F( void ) +ldap_nameform_free LDAP_P(( + LDAPNameForm * nf )); + +LDAP_F( void ) +ldap_structurerule_free LDAP_P(( + LDAPStructureRule * sr )); + +LDAP_F( LDAPStructureRule * ) +ldap_str2structurerule LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPNameForm * ) +ldap_str2nameform LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPContentRule * ) +ldap_str2contentrule LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPObjectClass * ) +ldap_str2objectclass LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPAttributeType * ) +ldap_str2attributetype LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPSyntax * ) +ldap_str2syntax LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPMatchingRule * ) +ldap_str2matchingrule LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( LDAPMatchingRuleUse * ) +ldap_str2matchingruleuse LDAP_P(( + LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags )); + +LDAP_F( char * ) +ldap_structurerule2str LDAP_P(( + LDAPStructureRule * sr )); + +LDAP_F( struct berval * ) +ldap_structurerule2bv LDAP_P(( + LDAPStructureRule * sr, struct berval *bv )); + +LDAP_F( char * ) +ldap_nameform2str LDAP_P(( + LDAPNameForm * nf )); + +LDAP_F( struct berval * ) +ldap_nameform2bv LDAP_P(( + LDAPNameForm * nf, struct berval *bv )); + +LDAP_F( char * ) +ldap_contentrule2str LDAP_P(( + LDAPContentRule * cr )); + +LDAP_F( struct berval * ) +ldap_contentrule2bv LDAP_P(( + LDAPContentRule * cr, struct berval *bv )); + +LDAP_F( char * ) +ldap_objectclass2str LDAP_P(( + LDAPObjectClass * oc )); + +LDAP_F( struct berval * ) +ldap_objectclass2bv LDAP_P(( + LDAPObjectClass * oc, struct berval *bv )); + +LDAP_F( char * ) +ldap_attributetype2str LDAP_P(( + LDAPAttributeType * at )); + +LDAP_F( struct berval * ) +ldap_attributetype2bv LDAP_P(( + LDAPAttributeType * at, struct berval *bv )); + +LDAP_F( char * ) +ldap_syntax2str LDAP_P(( + LDAPSyntax * syn )); + +LDAP_F( struct berval * ) +ldap_syntax2bv LDAP_P(( + LDAPSyntax * syn, struct berval *bv )); + +LDAP_F( char * ) +ldap_matchingrule2str LDAP_P(( + LDAPMatchingRule * mr )); + +LDAP_F( struct berval * ) +ldap_matchingrule2bv LDAP_P(( + LDAPMatchingRule * mr, struct berval *bv )); + +LDAP_F( char * ) +ldap_matchingruleuse2str LDAP_P(( + LDAPMatchingRuleUse * mru )); + +LDAP_F( struct berval * ) +ldap_matchingruleuse2bv LDAP_P(( + LDAPMatchingRuleUse * mru, struct berval *bv )); + +LDAP_F( char * ) +ldap_scherr2str LDAP_P(( + int code )) LDAP_GCCATTR((const)); + +LDAP_END_DECL + +#endif + diff --git a/contrib/libs/openldap/include/ldap_utf8.h b/contrib/libs/openldap/include/ldap_utf8.h new file mode 100644 index 0000000000..663043a594 --- /dev/null +++ b/contrib/libs/openldap/include/ldap_utf8.h @@ -0,0 +1,106 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + */ +/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#ifndef _LDAP_UTF8_H +#define _LDAP_UTF8_H + +#include <lber_types.h> /* get ber_*_t */ + +/* + * UTF-8 Utility Routines + */ + +LDAP_BEGIN_DECL + +#define LDAP_UCS4_INVALID (0x80000000U) +typedef ber_int_t ldap_ucs4_t; + + +/* LDAP_MAX_UTF8_LEN is 3 or 6 depending on size of wchar_t */ +#define LDAP_MAX_UTF8_LEN ( sizeof(wchar_t) * 3/2 ) + +/* Unicode conversion routines */ +LDAP_F( ldap_ucs4_t ) ldap_x_utf8_to_ucs4( LDAP_CONST char * p ); +LDAP_F( int ) ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf ); + + +/* + * Wide Char / UTF-8 Conversion Routines + */ + +/* UTF-8 character to Wide Char */ +LDAP_F(int) ldap_x_utf8_to_wc LDAP_P(( + wchar_t *wchar, LDAP_CONST char *utf8char )); + +/* UTF-8 string to Wide Char string */ +LDAP_F(int) ldap_x_utf8s_to_wcs LDAP_P(( + wchar_t *wcstr, LDAP_CONST char *utf8str, size_t count )); + +/* Wide Char to UTF-8 character */ +LDAP_F(int) ldap_x_wc_to_utf8 LDAP_P(( + char *utf8char, wchar_t wchar, size_t count )); + +/* Wide Char string to UTF-8 string */ +LDAP_F(int) ldap_x_wcs_to_utf8s LDAP_P(( + char *utf8str, LDAP_CONST wchar_t *wcstr, size_t count )); + +/* + * MultiByte Char / UTF-8 Conversion Routines + */ + +/* UTF-8 character to MultiByte character */ +LDAP_F(int) ldap_x_utf8_to_mb LDAP_P(( + char *mbchar, LDAP_CONST char *utf8char, + int (*ldap_f_wctomb)( char *mbchar, wchar_t wchar ))); + +/* UTF-8 string to MultiByte string */ +LDAP_F(int) ldap_x_utf8s_to_mbs LDAP_P(( + char *mbstr, LDAP_CONST char *utf8str, size_t count, + size_t (*ldap_f_wcstombs)( char *mbstr, + LDAP_CONST wchar_t *wcstr, size_t count) )); + +/* MultiByte character to UTF-8 character */ +LDAP_F(int) ldap_x_mb_to_utf8 LDAP_P(( + char *utf8char, LDAP_CONST char *mbchar, size_t mbsize, + int (*ldap_f_mbtowc)( wchar_t *wchar, + LDAP_CONST char *mbchar, size_t count) )); + +/* MultiByte string to UTF-8 string */ +LDAP_F(int) ldap_x_mbs_to_utf8s LDAP_P(( + char *utf8str, LDAP_CONST char *mbstr, size_t count, + size_t (*ldap_f_mbstowcs)( wchar_t *wcstr, + LDAP_CONST char *mbstr, size_t count) )); + +LDAP_END_DECL + +#endif /* _LDAP_UTF8_H */ diff --git a/contrib/libs/openldap/include/ldif.h b/contrib/libs/openldap/include/ldif.h new file mode 100644 index 0000000000..62cacdc8ee --- /dev/null +++ b/contrib/libs/openldap/include/ldif.h @@ -0,0 +1,171 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LDIF_H +#define _LDIF_H + +#include <ldap_cdefs.h> + +LDAP_BEGIN_DECL + +/* This is NOT a bogus extern declaration (unlike ldap_debug) */ +LDAP_LDIF_V (int) ldif_debug; + +#define LDIF_LINE_WIDTH 78 /* default maximum length of LDIF lines */ +#define LDIF_LINE_WIDTH_MAX ((ber_len_t)-1) /* maximum length of LDIF lines */ +#define LDIF_LINE_WIDTH_WRAP(wrap) ((wrap) == 0 ? LDIF_LINE_WIDTH : (wrap)) + +/* + * Macro to calculate maximum number of bytes that the base64 equivalent + * of an item that is "len" bytes long will take up. Base64 encoding + * uses one byte for every six bits in the value plus up to two pad bytes. + */ +#define LDIF_BASE64_LEN(len) (((len) * 4 / 3 ) + 3) + +/* + * Macro to calculate maximum size that an LDIF-encoded type (length + * tlen) and value (length vlen) will take up: room for type + ":: " + + * first newline + base64 value + continued lines. Each continued line + * needs room for a newline and a leading space character. + */ +#define LDIF_SIZE_NEEDED(nlen,vlen) LDIF_SIZE_NEEDED_WRAP(nlen, vlen, 0) + +#define LDIF_SIZE_NEEDED_WRAP(nlen,vlen,wrap) \ + ((nlen) + 4 + LDIF_BASE64_LEN(vlen) \ + + ((wrap) == 0 ? ((LDIF_BASE64_LEN(vlen) + (nlen) + 3) / ( LDIF_LINE_WIDTH-1 ) * 2 ) : \ + ((wrap) == LDIF_LINE_WIDTH_MAX ? 0 : ((LDIF_BASE64_LEN(vlen) + (nlen) + 3) / (wrap-1) * 2 )))) + +LDAP_LDIF_F( int ) +ldif_parse_line LDAP_P(( + LDAP_CONST char *line, + char **name, + char **value, + ber_len_t *vlen )); + +LDAP_LDIF_F( int ) +ldif_parse_line2 LDAP_P(( + char *line, + struct berval *type, + struct berval *value, + int *freeval )); + +LDAP_LDIF_F( FILE * ) +ldif_open_url LDAP_P(( LDAP_CONST char *urlstr )); + +LDAP_LDIF_F( int ) +ldif_fetch_url LDAP_P(( + LDAP_CONST char *line, + char **value, + ber_len_t *vlen )); + +LDAP_LDIF_F( char * ) +ldif_getline LDAP_P(( char **next )); + +LDAP_LDIF_F( int ) +ldif_countlines LDAP_P(( LDAP_CONST char *line )); + +/* ldif_ropen, rclose, read_record - just for reading LDIF files, + * no special open/close needed to write LDIF files. + */ +typedef struct LDIFFP { + FILE *fp; + struct LDIFFP *prev; +} LDIFFP; + +LDAP_LDIF_F( LDIFFP * ) +ldif_open LDAP_P(( LDAP_CONST char *file, LDAP_CONST char *mode )); + +/* ldif_open equivalent that opens ldif stream in memory rather than from file */ +LDAP_LDIF_F( LDIFFP * ) +ldif_open_mem LDAP_P(( char *ldif, size_t size, LDAP_CONST char *mode )); + +LDAP_LDIF_F( void ) +ldif_close LDAP_P(( LDIFFP * )); + +LDAP_LDIF_F( int ) +ldif_read_record LDAP_P(( + LDIFFP *fp, + unsigned long *lineno, + char **bufp, + int *buflen )); + +LDAP_LDIF_F( int ) +ldif_must_b64_encode_register LDAP_P(( + LDAP_CONST char *name, + LDAP_CONST char *oid )); + +LDAP_LDIF_F( void ) +ldif_must_b64_encode_release LDAP_P(( void )); + +#define LDIF_PUT_NOVALUE 0x0000 /* no value */ +#define LDIF_PUT_VALUE 0x0001 /* value w/ auto detection */ +#define LDIF_PUT_TEXT 0x0002 /* assume text */ +#define LDIF_PUT_BINARY 0x0004 /* assume binary (convert to base64) */ +#define LDIF_PUT_B64 0x0008 /* pre-converted base64 value */ + +#define LDIF_PUT_COMMENT 0x0010 /* comment */ +#define LDIF_PUT_URL 0x0020 /* url */ +#define LDIF_PUT_SEP 0x0040 /* separator */ + +LDAP_LDIF_F( void ) +ldif_sput LDAP_P(( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen )); + +LDAP_LDIF_F( void ) +ldif_sput_wrap LDAP_P(( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap )); + +LDAP_LDIF_F( char * ) +ldif_put LDAP_P(( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen )); + +LDAP_LDIF_F( char * ) +ldif_put_wrap LDAP_P(( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap )); + +LDAP_LDIF_F( int ) +ldif_is_not_printable LDAP_P(( + LDAP_CONST char *val, + ber_len_t vlen )); + +LDAP_END_DECL + +#endif /* _LDIF_H */ diff --git a/contrib/libs/openldap/include/lutil.h b/contrib/libs/openldap/include/lutil.h new file mode 100644 index 0000000000..fe0e6deab6 --- /dev/null +++ b/contrib/libs/openldap/include/lutil.h @@ -0,0 +1,375 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _LUTIL_H +#define _LUTIL_H 1 + +#include <ldap_cdefs.h> +#include <lber_types.h> +#include <ac/socket.h> + +#ifdef HAVE_TCPD +# include <tcpd.h> +# define LUTIL_STRING_UNKNOWN STRING_UNKNOWN +#else /* ! TCP Wrappers */ +# define LUTIL_STRING_UNKNOWN "unknown" +#endif /* ! TCP Wrappers */ + +/* + * Include file for LDAP utility routine + */ + +LDAP_BEGIN_DECL + +/* n octets encode into ceiling(n/3) * 4 bytes */ +/* Avoid floating point math through extra padding */ + +#define LUTIL_BASE64_ENCODE_LEN(n) (((n)+2)/3 * 4) +#define LUTIL_BASE64_DECODE_LEN(n) ((n)/4*3) + +/* ISC Base64 Routines */ +/* base64.c */ + +LDAP_LUTIL_F( int ) +lutil_b64_ntop LDAP_P(( + unsigned char const *, + size_t, + char *, + size_t)); + +LDAP_LUTIL_F( int ) +lutil_b64_pton LDAP_P(( + char const *, + unsigned char *, + size_t)); + +/* detach.c */ +LDAP_LUTIL_F( int ) +lutil_detach LDAP_P(( + int debug, + int do_close)); + +/* entropy.c */ +LDAP_LUTIL_F( int ) +lutil_entropy LDAP_P(( + unsigned char *buf, + ber_len_t nbytes )); + +/* passfile.c */ +struct berval; /* avoid pulling in lber.h */ + +LDAP_LUTIL_F( int ) +lutil_get_filed_password LDAP_P(( + const char *filename, + struct berval * )); + +/* passwd.c */ +struct lutil_pw_scheme; + +#define LUTIL_PASSWD_OK (0) +#define LUTIL_PASSWD_ERR (-1) + +typedef int (LUTIL_PASSWD_CHK_FUNC)( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text ); + +typedef int (LUTIL_PASSWD_HASH_FUNC) ( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ); + +LDAP_LUTIL_F( int ) +lutil_passwd_add LDAP_P(( + struct berval *scheme, + LUTIL_PASSWD_CHK_FUNC *chk_fn, + LUTIL_PASSWD_HASH_FUNC *hash_fn )); + +LDAP_LUTIL_F( void ) +lutil_passwd_init LDAP_P(( void )); + +LDAP_LUTIL_F( void ) +lutil_passwd_destroy LDAP_P(( void )); + +LDAP_LUTIL_F( int ) +lutil_authpasswd LDAP_P(( + const struct berval *passwd, /* stored password */ + const struct berval *cred, /* user supplied value */ + const char **methods )); + +LDAP_LUTIL_F( int ) +lutil_authpasswd_hash LDAP_P(( + const struct berval *cred, + struct berval **passwd, /* password to store */ + struct berval **salt, /* salt to store */ + const char *method )); + +#ifdef SLAPD_CRYPT +typedef int (lutil_cryptfunc) LDAP_P(( + const char *key, + const char *salt, + char **hash )); +LDAP_LUTIL_V (lutil_cryptfunc *) lutil_cryptptr; +#endif + +LDAP_LUTIL_F( int ) +lutil_passwd LDAP_P(( + const struct berval *passwd, /* stored password */ + const struct berval *cred, /* user supplied value */ + const char **methods, + const char **text )); /* error message */ + +LDAP_LUTIL_F( int ) +lutil_passwd_generate LDAP_P(( struct berval *pw, ber_len_t )); + +LDAP_LUTIL_F( int ) +lutil_passwd_hash LDAP_P(( + const struct berval *passwd, + const char *method, + struct berval *hash, + const char **text )); + +LDAP_LUTIL_F( int ) +lutil_passwd_scheme LDAP_P(( + const char *scheme )); + +LDAP_LUTIL_F( int ) +lutil_salt_format LDAP_P(( + const char *format )); + +LDAP_LUTIL_F( int ) +lutil_passwd_string64 LDAP_P(( + const struct berval *sc, + const struct berval *hash, + struct berval *b64, + const struct berval *salt )); + +/* utils.c */ +LDAP_LUTIL_F( char* ) +lutil_progname LDAP_P(( + const char* name, + int argc, + char *argv[] )); + +typedef struct lutil_tm { + int tm_sec; /* seconds 0-60 (1 leap second) */ + int tm_min; /* minutes 0-59 */ + int tm_hour; /* hours 0-23 */ + int tm_mday; /* day 1-31 */ + int tm_mon; /* month 0-11 */ + int tm_year; /* year - 1900 */ + int tm_nsec; /* nanoseconds */ + int tm_usub; /* submicro */ +} lutil_tm; + +typedef struct lutil_timet { + unsigned int tt_sec; /* seconds since epoch, 0000 or 1970 */ + int tt_gsec; /* seconds since epoch, high 7 bits, maybe sign-flipped */ + /* sign flipped to sort properly as unsigned ints */ + unsigned int tt_nsec; /* nanoseconds */ +} lutil_timet; + +/* Parse a timestamp string into a structure */ +LDAP_LUTIL_F( int ) +lutil_parsetime LDAP_P(( + char *atm, struct lutil_tm * )); + +/* Convert structured time to time in seconds since 1970 (Unix epoch) */ +LDAP_LUTIL_F( int ) +lutil_tm2time LDAP_P(( + struct lutil_tm *, struct lutil_timet * )); + +/* Convert structured time to time in seconds since 0000 (Proleptic Gregorian) */ +LDAP_LUTIL_F( int ) +lutil_tm2gtime LDAP_P(( + struct lutil_tm *, struct lutil_timet * )); + +#ifdef _WIN32 +LDAP_LUTIL_F( void ) +lutil_slashpath LDAP_P(( char* path )); +#define LUTIL_SLASHPATH(p) lutil_slashpath(p) +#else +#define LUTIL_SLASHPATH(p) +#endif + +LDAP_LUTIL_F( char* ) +lutil_strcopy LDAP_P(( char *dst, const char *src )); + +LDAP_LUTIL_F( char* ) +lutil_strncopy LDAP_P(( char *dst, const char *src, size_t n )); + +LDAP_LUTIL_F( char* ) +lutil_memcopy LDAP_P(( char *dst, const char *src, size_t n )); + +#define lutil_strbvcopy(a, bv) lutil_memcopy((a),(bv)->bv_val,(bv)->bv_len) + +struct tm; + +/* use this macro to statically allocate buffer for lutil_gentime */ +#define LDAP_LUTIL_GENTIME_BUFSIZE 22 +#define lutil_gentime(s,m,t) lutil_localtime((s),(m),(t),0) +LDAP_LUTIL_F( size_t ) +lutil_localtime LDAP_P(( char *s, size_t smax, const struct tm *tm, + long delta )); + +#ifndef HAVE_MKSTEMP +LDAP_LUTIL_F( int ) +mkstemp LDAP_P (( char * template )); +#endif + +/* sockpair.c */ +LDAP_LUTIL_F( int ) +lutil_pair( ber_socket_t sd[2] ); + +/* uuid.c */ +/* use this macro to allocate buffer for lutil_uuidstr */ +#define LDAP_LUTIL_UUIDSTR_BUFSIZE 40 +LDAP_LUTIL_F( size_t ) +lutil_uuidstr( char *buf, size_t len ); + +LDAP_LUTIL_F( int ) +lutil_uuidstr_from_normalized( + char *uuid, + size_t uuidlen, + char *buf, + size_t buflen ); + +/* + * Sometimes not all declarations in a header file are needed. + * An indicator to this is whether or not the symbol's type has + * been defined. Thus, we don't need to include a symbol if + * its type has not been defined through another header file. + */ + +#ifdef HAVE_NT_SERVICE_MANAGER +LDAP_LUTIL_V (int) is_NT_Service; + +#ifdef _LDAP_PVT_THREAD_H +LDAP_LUTIL_V (ldap_pvt_thread_cond_t) started_event; +#endif /* _LDAP_PVT_THREAD_H */ + +/* macros are different between Windows and Mingw */ +#if defined(_WINSVC_H) || defined(_WINSVC_) +LDAP_LUTIL_V (SERVICE_STATUS) lutil_ServiceStatus; +LDAP_LUTIL_V (SERVICE_STATUS_HANDLE) hlutil_ServiceStatus; +#endif /* _WINSVC_H */ + +LDAP_LUTIL_F (void) +lutil_CommenceStartupProcessing( char *serverName, void (*stopper)(int)) ; + +LDAP_LUTIL_F (void) +lutil_ReportShutdownComplete( void ); + +LDAP_LUTIL_F (void *) +lutil_getRegParam( char *svc, char *value ); + +LDAP_LUTIL_F (int) +lutil_srv_install( char* service, char * displayName, char* filename, + int auto_start ); +LDAP_LUTIL_F (int) +lutil_srv_remove ( char* service, char* filename ); + +#endif /* HAVE_NT_SERVICE_MANAGER */ + +#ifdef HAVE_NT_EVENT_LOG +LDAP_LUTIL_F (void) +lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls ); + +LDAP_LUTIL_F (void) +lutil_LogStoppedEvent( char *svc ); +#endif + +#ifdef HAVE_EBCDIC +/* Generally this has only been used to put '\n' to stdout. We need to + * make sure it is output in EBCDIC. + */ +#undef putchar +#undef putc +#define putchar(c) putc((c), stdout) +#define putc(c,fp) do { char x=(c); __atoe_l(&x,1); putc(x,fp); } while(0) +#endif + +LDAP_LUTIL_F (int) +lutil_atoix( int *v, const char *s, int x ); + +LDAP_LUTIL_F (int) +lutil_atoux( unsigned *v, const char *s, int x ); + +LDAP_LUTIL_F (int) +lutil_atolx( long *v, const char *s, int x ); + +LDAP_LUTIL_F (int) +lutil_atoulx( unsigned long *v, const char *s, int x ); + +#define lutil_atoi(v, s) lutil_atoix((v), (s), 10) +#define lutil_atou(v, s) lutil_atoux((v), (s), 10) +#define lutil_atol(v, s) lutil_atolx((v), (s), 10) +#define lutil_atoul(v, s) lutil_atoulx((v), (s), 10) + +#ifdef HAVE_LONG_LONG +#if defined(HAVE_STRTOLL) || defined(HAVE_STRTOQ) +LDAP_LUTIL_F (int) +lutil_atollx( long long *v, const char *s, int x ); +#define lutil_atoll(v, s) lutil_atollx((v), (s), 10) +#endif /* HAVE_STRTOLL || HAVE_STRTOQ */ + +#if defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ) +LDAP_LUTIL_F (int) +lutil_atoullx( unsigned long long *v, const char *s, int x ); +#define lutil_atoull(v, s) lutil_atoullx((v), (s), 10) +#endif /* HAVE_STRTOULL || HAVE_STRTOUQ */ +#endif /* HAVE_LONG_LONG */ + +LDAP_LUTIL_F (int) +lutil_str2bin( struct berval *in, struct berval *out, void *ctx ); + +/* Parse and unparse time intervals */ +LDAP_LUTIL_F (int) +lutil_parse_time( const char *in, unsigned long *tp ); + +LDAP_LUTIL_F (int) +lutil_unparse_time( char *buf, size_t buflen, unsigned long t ); + +#ifdef timerdiv +#define lutil_timerdiv timerdiv +#else /* ! timerdiv */ +/* works inplace (x == t) */ +#define lutil_timerdiv(t,d,x) \ + do { \ + time_t s = (t)->tv_sec; \ + assert( d > 0 ); \ + (x)->tv_sec = s / d; \ + (x)->tv_usec = ( (t)->tv_usec + 1000000 * ( s % d ) ) / d; \ + } while ( 0 ) +#endif /* ! timerdiv */ + +#ifdef timermul +#define lutil_timermul timermul +#else /* ! timermul */ +/* works inplace (x == t) */ +#define lutil_timermul(t,m,x) \ + do { \ + time_t u = (t)->tv_usec * m; \ + assert( m > 0 ); \ + (x)->tv_sec = (t)->tv_sec * m + u / 1000000; \ + (x)->tv_usec = u % 1000000; \ + } while ( 0 ); +#endif /* ! timermul */ + +LDAP_END_DECL + +#endif /* _LUTIL_H */ diff --git a/contrib/libs/openldap/include/lutil_lockf.h b/contrib/libs/openldap/include/lutil_lockf.h new file mode 100644 index 0000000000..b24bde881d --- /dev/null +++ b/contrib/libs/openldap/include/lutil_lockf.h @@ -0,0 +1,34 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* File locking methods + * + * lutil_lockf() will block until an exclusive lock is acquired. + */ + +#ifndef _LUTIL_LOCKF_H_ +#define _LUTIL_LOCKF_H_ + +LDAP_BEGIN_DECL + +LDAP_LUTIL_F( int ) +lutil_lockf LDAP_P(( int fd )); + +LDAP_LUTIL_F( int ) +lutil_unlockf LDAP_P(( int fd )); + +LDAP_END_DECL + +#endif /* _LUTIL_LOCKF_H_ */ diff --git a/contrib/libs/openldap/include/openldap.h b/contrib/libs/openldap/include/openldap.h new file mode 100644 index 0000000000..70a39c69da --- /dev/null +++ b/contrib/libs/openldap/include/openldap.h @@ -0,0 +1,39 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2019-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* openldap.h - Header for openldap specific interfaces. */ + +#ifndef _OPENLDAP_H +#define _OPENLDAP_H 1 + +#include <ldap.h> + +LDAP_BEGIN_DECL + +#define LDAP_PROTO_TCP 1 /* ldap:// */ +#define LDAP_PROTO_UDP 2 /* reserved */ +#define LDAP_PROTO_IPC 3 /* ldapi:// */ +#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */ + +LDAP_F( int ) +ldap_init_fd LDAP_P(( + ber_socket_t fd, + int proto, + LDAP_CONST char *url, + LDAP **ldp )); + +LDAP_END_DECL + +#endif /* _OPENLDAP_H */ diff --git a/contrib/libs/openldap/include/portable-linux.h b/contrib/libs/openldap/include/portable-linux.h new file mode 100644 index 0000000000..b1b7269060 --- /dev/null +++ b/contrib/libs/openldap/include/portable-linux.h @@ -0,0 +1,1191 @@ +/* include/portable.h. Generated from portable.hin by configure. */ +/* include/portable.hin. Generated from configure.ac by autoheader. */ + + +/* begin of portable.h.pre */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _LDAP_PORTABLE_H +#define _LDAP_PORTABLE_H + +/* define this if needed to get reentrant functions */ +#ifndef REENTRANT +#define REENTRANT 1 +#endif +#ifndef _REENTRANT +#define _REENTRANT 1 +#endif + +/* define this if needed to get threadsafe functions */ +#ifndef THREADSAFE +#define THREADSAFE 1 +#endif +#ifndef _THREADSAFE +#define _THREADSAFE 1 +#endif +#ifndef THREAD_SAFE +#define THREAD_SAFE 1 +#endif +#ifndef _THREAD_SAFE +#define _THREAD_SAFE 1 +#endif + +#ifndef _SGI_MP_SOURCE +#define _SGI_MP_SOURCE 1 +#endif + +/* end of portable.h.pre */ + + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* define to use both <string.h> and <strings.h> */ +/* #undef BOTH_STRINGS_H */ + +/* define if cross compiling */ +/* #undef CROSS_COMPILING */ + +/* set to the number of arguments ctime_r() expects */ +#define CTIME_R_NARGS 2 + +/* define if toupper() requires islower() */ +/* #undef C_UPPER_LOWER */ + +/* define if sys_errlist is not declared in stdio.h or errno.h */ +/* #undef DECL_SYS_ERRLIST */ + +/* define to enable slapi library */ +/* #undef ENABLE_SLAPI */ + +/* defined to be the EXE extension */ +#define EXEEXT "" + +/* set to the number of arguments gethostbyaddr_r() expects */ +#define GETHOSTBYADDR_R_NARGS 8 + +/* set to the number of arguments gethostbyname_r() expects */ +#define GETHOSTBYNAME_R_NARGS 6 + +/* Define to 1 if `TIOCGWINSZ' requires <sys/ioctl.h>. */ +#define GWINSZ_IN_SYS_IOCTL 1 + +/* define if you have AIX security lib */ +/* #undef HAVE_AIX_SECURITY */ + +/* Define to 1 if you have the <argon2.h> header file. */ +/* #undef HAVE_ARGON2_H */ + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the <arpa/nameser.h> header file. */ +#define HAVE_ARPA_NAMESER_H 1 + +/* Define to 1 if you have the <assert.h> header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the `bcopy' function. */ +#define HAVE_BCOPY 1 + +/* Define to 1 if you have the <bits/types.h> header file. */ +#ifdef __linux__ +#define HAVE_BITS_TYPES_H 1 +#endif + +/* Define to 1 if you have the `chroot' function. */ +#define HAVE_CHROOT 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the `closesocket' function. */ +/* #undef HAVE_CLOSESOCKET */ + +/* Define to 1 if you have the <conio.h> header file. */ +/* #undef HAVE_CONIO_H */ + +/* define if crypt(3) is available */ +#define HAVE_CRYPT 1 + +/* Define to 1 if you have the <crypt.h> header file. */ +#define HAVE_CRYPT_H 1 + +/* define if crypt_r() is also available */ +#define HAVE_CRYPT_R 1 + +/* Define to 1 if you have the `ctime_r' function. */ +#define HAVE_CTIME_R 1 + +/* define if you have Cyrus SASL */ +#define HAVE_CYRUS_SASL 1 + +/* define if your system supports /dev/poll */ +/* #undef HAVE_DEVPOLL */ + +/* Define to 1 if you have the <direct.h> header file. */ +/* #undef HAVE_DIRECT_H */ + +/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* define if system uses EBCDIC instead of ASCII */ +/* #undef HAVE_EBCDIC */ + +/* Define to 1 if you have the `endgrent' function. */ +#define HAVE_ENDGRENT 1 + +/* Define to 1 if you have the `endpwent' function. */ +#define HAVE_ENDPWENT 1 + +/* define if your system supports epoll */ +#define HAVE_EPOLL 1 + +/* Define to 1 if you have the <errno.h> header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `fcntl' function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* define if you actually have FreeBSD fetch(3) */ +/* #undef HAVE_FETCH */ + +/* Define to 1 if you have the <filio.h> header file. */ +/* #undef HAVE_FILIO_H */ + +/* Define to 1 if you have the `flock' function. */ +#define HAVE_FLOCK 1 + +/* Define to 1 if you have the `fmemopen' function. */ +#define HAVE_FMEMOPEN 1 + +/* Define to 1 if you have the `fstat' function. */ +#define HAVE_FSTAT 1 + +/* Define to 1 if you have the `gai_strerror' function. */ +#define HAVE_GAI_STRERROR 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getdtablesize' function. */ +#define HAVE_GETDTABLESIZE 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define HAVE_GETEUID 1 + +/* Define to 1 if you have the `getgrgid' function. */ +#define HAVE_GETGRGID 1 + +/* Define to 1 if you have the `gethostbyaddr_r' function. */ +#define HAVE_GETHOSTBYADDR_R 1 + +/* Define to 1 if you have the `gethostbyname_r' function. */ +#define HAVE_GETHOSTBYNAME_R 1 + +/* Define to 1 if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getopt' function. */ +#define HAVE_GETOPT 1 + +/* Define to 1 if you have the <getopt.h> header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getpassphrase' function. */ +/* #undef HAVE_GETPASSPHRASE */ + +/* Define to 1 if you have the `getpeereid' function. */ +/* #undef HAVE_GETPEEREID */ + +/* Define to 1 if you have the `getpeerucred' function. */ +/* #undef HAVE_GETPEERUCRED */ + +/* Define to 1 if you have the `getpwnam' function. */ +#define HAVE_GETPWNAM 1 + +/* Define to 1 if you have the `getpwuid' function. */ +#define HAVE_GETPWUID 1 + +/* Define to 1 if you have the `getspnam' function. */ +#define HAVE_GETSPNAM 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the <gmp.h> header file. */ +/* #undef HAVE_GMP_H */ + +/* Define to 1 if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + +/* define if you have GNUtls */ +/* #undef HAVE_GNUTLS */ + +/* Define to 1 if you have the <gnutls/gnutls.h> header file. */ +/* #undef HAVE_GNUTLS_GNUTLS_H */ + +/* if you have GNU Pth */ +/* #undef HAVE_GNU_PTH */ + +/* Define to 1 if you have the <grp.h> header file. */ +#define HAVE_GRP_H 1 + +/* Define to 1 if you have the `hstrerror' function. */ +#define HAVE_HSTRERROR 1 + +/* define to you inet_aton(3) is available */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if you have the `inet_ntoa_b' function. */ +/* #undef HAVE_INET_NTOA_B */ + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `initgroups' function. */ +#define HAVE_INITGROUPS 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `ioctl' function. */ +#define HAVE_IOCTL 1 + +/* Define to 1 if you have the <io.h> header file. */ +/* #undef HAVE_IO_H */ + +/* define if your system supports kqueue */ +/* #undef HAVE_KQUEUE */ + +/* define if you have libargon2 */ +/* #undef HAVE_LIBARGON2 */ + +/* define if you have -levent */ +/* #undef HAVE_LIBEVENT */ + +/* Define to 1 if you have the `gen' library (-lgen). */ +/* #undef HAVE_LIBGEN */ + +/* Define to 1 if you have the `gmp' library (-lgmp). */ +/* #undef HAVE_LIBGMP */ + +/* Define to 1 if you have the `inet' library (-linet). */ +/* #undef HAVE_LIBINET */ + +/* define if you have libtool -ltdl */ +#define HAVE_LIBLTDL 1 + +/* Define to 1 if you have the `net' library (-lnet). */ +/* #undef HAVE_LIBNET */ + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define to 1 if you have the `nsl_s' library (-lnsl_s). */ +/* #undef HAVE_LIBNSL_S */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* define if you have libsodium */ +#define HAVE_LIBSODIUM 1 + +/* Define to 1 if you have the <libutil.h> header file. */ +/* #undef HAVE_LIBUTIL_H */ + +/* Define to 1 if you have the `V3' library (-lV3). */ +/* #undef HAVE_LIBV3 */ + +/* Define to 1 if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* if you have LinuxThreads */ +/* #undef HAVE_LINUX_THREADS */ + +/* Define to 1 if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `localtime_r' function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the `lockf' function. */ +#define HAVE_LOCKF 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the <ltdl.h> header file. */ +#define HAVE_LTDL_H 1 + +/* Define to 1 if you have the <malloc.h> header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memrchr' function. */ +#define HAVE_MEMRCHR 1 + +/* Define to 1 if you have the `mkstemp' function. */ +#define HAVE_MKSTEMP 1 + +/* Define to 1 if you have the `mktemp' function. */ +#define HAVE_MKTEMP 1 + +/* define this if you have mkversion */ +#define HAVE_MKVERSION 1 + +/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the <netinet/tcp.h> header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* define if strerror_r returns char* instead of int */ +/* #undef HAVE_NONPOSIX_STRERROR_R */ + +/* if you have NT Event Log */ +/* #undef HAVE_NT_EVENT_LOG */ + +/* if you have NT Service Manager */ +/* #undef HAVE_NT_SERVICE_MANAGER */ + +/* if you have NT Threads */ +/* #undef HAVE_NT_THREADS */ + +/* define if you have OpenSSL */ +#define HAVE_OPENSSL 1 + +/* Define to 1 if you have the <openssl/bn.h> header file. */ +/* #undef HAVE_OPENSSL_BN_H */ + +/* Define to 1 if you have the <openssl/crypto.h> header file. */ +/* #undef HAVE_OPENSSL_CRYPTO_H */ + +/* Define to 1 if you have the <openssl/ssl.h> header file. */ +#define HAVE_OPENSSL_SSL_H 1 + +/* Define to 1 if you have the `pipe' function. */ +#define HAVE_PIPE 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the <poll.h> header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the <process.h> header file. */ +/* #undef HAVE_PROCESS_H */ + +/* Define to 1 if you have the <psap.h> header file. */ +/* #undef HAVE_PSAP_H */ + +/* define to pthreads API spec revision */ +#define HAVE_PTHREADS 10 + +/* define if you have pthread_detach function */ +#define HAVE_PTHREAD_DETACH 1 + +/* Define to 1 if you have the `pthread_getconcurrency' function. */ +#define HAVE_PTHREAD_GETCONCURRENCY 1 + +/* Define to 1 if you have the <pthread.h> header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the `pthread_kill' function. */ +#define HAVE_PTHREAD_KILL 1 + +/* Define to 1 if you have the `pthread_kill_other_threads_np' function. */ +/* #undef HAVE_PTHREAD_KILL_OTHER_THREADS_NP */ + +/* define if you have pthread_rwlock_destroy function */ +#define HAVE_PTHREAD_RWLOCK_DESTROY 1 + +/* Define to 1 if you have the `pthread_setconcurrency' function. */ +#define HAVE_PTHREAD_SETCONCURRENCY 1 + +/* Define to 1 if you have the `pthread_yield' function. */ +/* #undef HAVE_PTHREAD_YIELD */ + +/* Define to 1 if you have the <pth.h> header file. */ +/* #undef HAVE_PTH_H */ + +/* Define to 1 if the system has the type `ptrdiff_t'. */ +#define HAVE_PTRDIFF_T 1 + +/* Define to 1 if you have the <pwd.h> header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `read' function. */ +#define HAVE_READ 1 + +/* Define to 1 if you have the `recv' function. */ +#define HAVE_RECV 1 + +/* Define to 1 if you have the `recvfrom' function. */ +#define HAVE_RECVFROM 1 + +/* Define to 1 if you have the <regex.h> header file. */ +#define HAVE_REGEX_H 1 + +/* Define to 1 if you have the <resolv.h> header file. */ +#define HAVE_RESOLV_H 1 + +/* define if you have res_query() */ +#define HAVE_RES_QUERY 1 + +/* Define to 1 if you have the <sasl.h> header file. */ +/* #undef HAVE_SASL_H */ + +/* Define to 1 if you have the <sasl/sasl.h> header file. */ +#define HAVE_SASL_SASL_H 1 + +/* define if your SASL library has sasl_version() */ +#define HAVE_SASL_VERSION 1 + +/* Define to 1 if you have the <sched.h> header file. */ +#define HAVE_SCHED_H 1 + +/* Define to 1 if you have the `sched_yield' function. */ +#define HAVE_SCHED_YIELD 1 + +/* Define to 1 if you have the `send' function. */ +#define HAVE_SEND 1 + +/* Define to 1 if you have the `sendmsg' function. */ +#define HAVE_SENDMSG 1 + +/* Define to 1 if you have the `sendto' function. */ +#define HAVE_SENDTO 1 + +/* Define to 1 if you have the `setegid' function. */ +#define HAVE_SETEGID 1 + +/* Define to 1 if you have the `seteuid' function. */ +#define HAVE_SETEUID 1 + +/* Define to 1 if you have the `setgid' function. */ +#define HAVE_SETGID 1 + +/* Define to 1 if you have the `setpwfile' function. */ +/* #undef HAVE_SETPWFILE */ + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `setuid' function. */ +#define HAVE_SETUID 1 + +/* Define to 1 if you have the <sgtty.h> header file. */ +#define HAVE_SGTTY_H 1 + +/* Define to 1 if you have the <shadow.h> header file. */ +#define HAVE_SHADOW_H 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if you have the `signal' function. */ +#define HAVE_SIGNAL 1 + +/* Define to 1 if you have the `sigset' function. */ +#define HAVE_SIGSET 1 + +/* define if you have -lslp */ +/* #undef HAVE_SLP */ + +/* Define to 1 if you have the <slp.h> header file. */ +/* #undef HAVE_SLP_H */ + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the <sodium.h> header file. */ +#define HAVE_SODIUM_H 1 + +/* if you have spawnlp() */ +/* #undef HAVE_SPAWNLP */ + +/* Define to 1 if you have the <sqlext.h> header file. */ +/* #undef HAVE_SQLEXT_H */ + +/* Define to 1 if you have the <sql.h> header file. */ +/* #undef HAVE_SQL_H */ + +/* Define to 1 if you have the <stddef.h> header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strerror_r' function. */ +#define HAVE_STRERROR_R 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strpbrk' function. */ +#define HAVE_STRPBRK 1 + +/* Define to 1 if you have the `strrchr' function. */ +#define HAVE_STRRCHR 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoq' function. */ +#define HAVE_STRTOQ 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `strtouq' function. */ +#define HAVE_STRTOUQ 1 + +/* Define to 1 if `msg_accrightslen' is a member of `struct msghdr'. */ +/* #undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTSLEN */ + +/* Define to 1 if `msg_control' is a member of `struct msghdr'. */ +#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1 + +/* Define to 1 if `pw_gecos' is a member of `struct passwd'. */ +#define HAVE_STRUCT_PASSWD_PW_GECOS 1 + +/* Define to 1 if `pw_passwd' is a member of `struct passwd'. */ +#define HAVE_STRUCT_PASSWD_PW_PASSWD 1 + +/* Define to 1 if `st_blksize' is a member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_BLKSIZE 1 + +/* Define to 1 if `st_fstype' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_FSTYPE */ + +/* define to 1 if st_fstype is char * */ +/* #undef HAVE_STRUCT_STAT_ST_FSTYPE_CHAR */ + +/* define to 1 if st_fstype is int */ +/* #undef HAVE_STRUCT_STAT_ST_FSTYPE_INT */ + +/* Define to 1 if `st_vfstype' is a member of `struct stat'. */ +/* #undef HAVE_STRUCT_STAT_ST_VFSTYPE */ + +/* Define to 1 if you have the <synch.h> header file. */ +/* #undef HAVE_SYNCH_H */ + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the <sysexits.h> header file. */ +#define HAVE_SYSEXITS_H 1 + +/* Define to 1 if you have the <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* define if you have systemd */ +#define HAVE_SYSTEMD 1 + +/* Define to 1 if you have the <systemd/sd-daemon.h> header file. */ +#define HAVE_SYSTEMD_SD_DAEMON_H 1 + +/* Define to 1 if you have the <sys/devpoll.h> header file. */ +/* #undef HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the <sys/epoll.h> header file. */ +#define HAVE_SYS_EPOLL_H 1 + +/* define if you actually have sys_errlist in your libs */ +/* #undef HAVE_SYS_ERRLIST */ + +/* Define to 1 if you have the <sys/errno.h> header file. */ +#define HAVE_SYS_ERRNO_H 1 + +/* Define to 1 if you have the <sys/event.h> header file. */ +/* #undef HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the <sys/file.h> header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define to 1 if you have the <sys/filio.h> header file. */ +/* #undef HAVE_SYS_FILIO_H */ + +/* Define to 1 if you have the <sys/fstyp.h> header file. */ +/* #undef HAVE_SYS_FSTYP_H */ + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/poll.h> header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the <sys/privgrp.h> header file. */ +/* #undef HAVE_SYS_PRIVGRP_H */ + +/* Define to 1 if you have the <sys/resource.h> header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the <sys/select.h> header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/syslog.h> header file. */ +#define HAVE_SYS_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/ucred.h> header file. */ +/* #undef HAVE_SYS_UCRED_H */ + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the <sys/un.h> header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the <sys/uuid.h> header file. */ +/* #undef HAVE_SYS_UUID_H */ + +/* Define to 1 if you have the <sys/vmount.h> header file. */ +/* #undef HAVE_SYS_VMOUNT_H */ + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* define if you have -lwrap */ +/* #undef HAVE_TCPD */ + +/* Define to 1 if you have the <tcpd.h> header file. */ +/* #undef HAVE_TCPD_H */ + +/* Define to 1 if you have the <termios.h> header file. */ +#define HAVE_TERMIOS_H 1 + +/* if you have Solaris LWP (thr) package */ +/* #undef HAVE_THR */ + +/* Define to 1 if you have the <thread.h> header file. */ +/* #undef HAVE_THREAD_H */ + +/* Define to 1 if you have the `thr_getconcurrency' function. */ +/* #undef HAVE_THR_GETCONCURRENCY */ + +/* Define to 1 if you have the `thr_setconcurrency' function. */ +/* #undef HAVE_THR_SETCONCURRENCY */ + +/* Define to 1 if you have the `thr_yield' function. */ +/* #undef HAVE_THR_YIELD */ + +/* define if you have TLS */ +#define HAVE_TLS 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <utime.h> header file. */ +#define HAVE_UTIME_H 1 + +/* define if you have uuid_generate() */ +/* #undef HAVE_UUID_GENERATE */ + +/* define if you have uuid_to_str() */ +/* #undef HAVE_UUID_TO_STR */ + +/* Define to 1 if you have the <uuid/uuid.h> header file. */ +/* #undef HAVE_UUID_UUID_H */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `wait4' function. */ +#define HAVE_WAIT4 1 + +/* Define to 1 if you have the `waitpid' function. */ +#define HAVE_WAITPID 1 + +/* define if you have winsock */ +/* #undef HAVE_WINSOCK */ + +/* define if you have winsock2 */ +/* #undef HAVE_WINSOCK2 */ + +/* Define to 1 if you have the <winsock2.h> header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if you have the <winsock.h> header file. */ +/* #undef HAVE_WINSOCK_H */ + +/* Define to 1 if you have the `write' function. */ +#define HAVE_WRITE 1 + +/* define if select implicitly yields */ +#define HAVE_YIELDING_SELECT 1 + +/* Define to 1 if you have the `_vsnprintf' function. */ +/* #undef HAVE__VSNPRINTF */ + +/* define to 32-bit or greater integer type */ +#define LBER_INT_T int + +/* define to large integer type */ +#define LBER_LEN_T long + +/* define to socket descriptor type */ +#define LBER_SOCKET_T int + +/* define to large integer type */ +#define LBER_TAG_T long + +/* define to 1 if library is reentrant */ +#define LDAP_API_FEATURE_X_OPENLDAP_REENTRANT 1 + +/* define to 1 if library is thread safe */ +#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1 + +/* define to LDAP VENDOR VERSION */ +/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */ + +/* define this to add debugging code */ +#define LDAP_DEBUG 1 + +/* define if LDAP libs are dynamic */ +#define LDAP_LIBS_DYNAMIC 1 + +/* define to support PF_INET6 */ +#define LDAP_PF_INET6 1 + +/* define to support PF_LOCAL */ +#define LDAP_PF_LOCAL 1 + +/* define this to add SLAPI code */ +/* #undef LDAP_SLAPI */ + +/* define this to add syslog code */ +#define LDAP_SYSLOG 1 + +/* Version */ +#define LDAP_VENDOR_VERSION 20606 + +/* Major */ +#define LDAP_VENDOR_VERSION_MAJOR 2 + +/* Minor */ +#define LDAP_VENDOR_VERSION_MINOR 6 + +/* Patch */ +#define LDAP_VENDOR_VERSION_PATCH 6 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* define if memcmp is not 8-bit clean or is otherwise broken */ +/* #undef NEED_MEMCMP_REPLACEMENT */ + +/* define if you have (or want) no threads */ +/* #undef NO_THREADS */ + +/* define to use the original debug style */ +/* #undef OLD_DEBUG */ + +/* Package */ +#define OPENLDAP_PACKAGE "OpenLDAP" + +/* Version */ +#define OPENLDAP_VERSION "2.6.6" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* define if sched_yield yields the entire process */ +/* #undef REPLACE_BROKEN_YIELD */ + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (fd_set *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of `wchar_t', as computed by sizeof. */ +#define SIZEOF_WCHAR_T 4 + +/* define to support per-object ACIs */ +/* #undef SLAPD_ACI_ENABLED */ + +/* define to support LDAP Async Metadirectory backend */ +/* #undef SLAPD_ASYNCMETA */ + +/* define to support cleartext passwords */ +#define SLAPD_CLEARTEXT 1 + +/* define to support crypt(3) passwords */ +#define SLAPD_CRYPT 1 + +/* define to support DNS SRV backend */ +/* #undef SLAPD_DNSSRV */ + +/* define to support LDAP backend */ +/* #undef SLAPD_LDAP */ + +/* define to support MDB backend */ +#define SLAPD_MDB SLAPD_MOD_STATIC + +/* define to support LDAP Metadirectory backend */ +/* #undef SLAPD_META */ + +/* define to support modules */ +#define SLAPD_MODULES 1 + +/* dynamically linked module */ +#define SLAPD_MOD_DYNAMIC 2 + +/* statically linked module */ +#define SLAPD_MOD_STATIC 1 + +/* define to support NULL backend */ +/* #undef SLAPD_NULL */ + +/* define for In-Directory Access Logging overlay */ +#define SLAPD_OVER_ACCESSLOG SLAPD_MOD_STATIC + +/* define for Audit Logging overlay */ +#define SLAPD_OVER_AUDITLOG SLAPD_MOD_STATIC + +/* define for Automatic Certificate Authority overlay */ +#define SLAPD_OVER_AUTOCA SLAPD_MOD_STATIC + +/* define for Collect overlay */ +#define SLAPD_OVER_COLLECT SLAPD_MOD_STATIC + +/* define for Attribute Constraint overlay */ +#define SLAPD_OVER_CONSTRAINT SLAPD_MOD_STATIC + +/* define for Dynamic Directory Services overlay */ +#define SLAPD_OVER_DDS SLAPD_MOD_STATIC + +/* define for Dynamic Directory Services overlay */ +#define SLAPD_OVER_DEREF SLAPD_MOD_STATIC + +/* define for Dynamic Group overlay */ +#define SLAPD_OVER_DYNGROUP SLAPD_MOD_STATIC + +/* define for Dynamic List overlay */ +#define SLAPD_OVER_DYNLIST SLAPD_MOD_STATIC + +/* define for Home Directory Management overlay */ +#define SLAPD_OVER_HOMEDIR SLAPD_MOD_STATIC + +/* define for Reverse Group Membership overlay */ +#define SLAPD_OVER_MEMBEROF SLAPD_MOD_STATIC + +/* define for OTP 2-factor Authentication overlay */ +#define SLAPD_OVER_OTP SLAPD_MOD_STATIC + +/* define for Password Policy overlay */ +#define SLAPD_OVER_PPOLICY SLAPD_MOD_STATIC + +/* define for Proxy Cache overlay */ +#define SLAPD_OVER_PROXYCACHE SLAPD_MOD_STATIC + +/* define for Referential Integrity overlay */ +#define SLAPD_OVER_REFINT SLAPD_MOD_STATIC + +/* define for Deferred Authentication overlay */ +#define SLAPD_OVER_REMOTEAUTH SLAPD_MOD_STATIC + +/* define for Return Code overlay */ +#define SLAPD_OVER_RETCODE SLAPD_MOD_STATIC + +/* define for Rewrite/Remap overlay */ +#define SLAPD_OVER_RWM SLAPD_MOD_STATIC + +/* define for Sequential Modify overlay */ +#define SLAPD_OVER_SEQMOD SLAPD_MOD_STATIC + +/* define for ServerSideSort/VLV overlay */ +#define SLAPD_OVER_SSSVLV SLAPD_MOD_STATIC + +/* define for Syncrepl Provider overlay */ +#define SLAPD_OVER_SYNCPROV SLAPD_MOD_STATIC + +/* define for Translucent Proxy overlay */ +#define SLAPD_OVER_TRANSLUCENT SLAPD_MOD_STATIC + +/* define for Attribute Uniqueness overlay */ +#define SLAPD_OVER_UNIQUE SLAPD_MOD_STATIC + +/* define for Value Sorting overlay */ +#define SLAPD_OVER_VALSORT SLAPD_MOD_STATIC + +/* define to support PASSWD backend */ +/* #undef SLAPD_PASSWD */ + +/* define to support PERL backend */ +/* #undef SLAPD_PERL */ + +/* define for Argon2 Password hashing module */ +#define SLAPD_PWMOD_PW_ARGON2 + +/* define to support relay backend */ +#define SLAPD_RELAY SLAPD_MOD_STATIC + +/* define to support reverse lookups */ +/* #undef SLAPD_RLOOKUPS */ + +/* define to support SOCK backend */ +/* #undef SLAPD_SOCK */ + +/* define to support SASL passwords */ +/* #undef SLAPD_SPASSWD */ + +/* define to support SQL backend */ +/* #undef SLAPD_SQL */ + +/* define to support WiredTiger backend */ +/* #undef SLAPD_WT */ + +/* define to support run-time loadable ACL */ +/* #undef SLAP_DYNACL */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if your <sys/time.h> declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* set to urandom device */ +#define URANDOM_DEVICE "/dev/urandom" + +/* define to use OpenSSL BIGNUM for MP */ +/* #undef USE_MP_BIGNUM */ + +/* define to use GMP for MP */ +/* #undef USE_MP_GMP */ + +/* define to use 'long' for MP */ +/* #undef USE_MP_LONG */ + +/* define to use 'long long' for MP */ +#define USE_MP_LONG_LONG 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to the type of arg 3 for `accept'. */ +#define ber_socklen_t socklen_t + +/* Define to `char *' if <sys/types.h> does not define. */ +/* #undef caddr_t */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef gid_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef mode_t */ + +/* Define to `long' if <sys/types.h> does not define. */ +/* #undef off_t */ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef pid_t */ + +/* Define to `int' if <signal.h> does not define. */ +/* #undef sig_atomic_t */ + +/* Define to `unsigned' if <sys/types.h> does not define. */ +/* #undef size_t */ + +/* define to snprintf routine */ +/* #undef snprintf */ + +/* Define like ber_socklen_t if <sys/socket.h> does not define. */ +/* #undef socklen_t */ + +/* Define to `signed int' if <sys/types.h> does not define. */ +/* #undef ssize_t */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef uid_t */ + +/* define as empty if volatile is not supported */ +/* #undef volatile */ + +/* define to snprintf routine */ +/* #undef vsnprintf */ + + +/* begin of portable.h.post */ + +#define RETSIGTYPE void + +#ifdef _WIN32 + /* don't suck in all of the win32 api */ +# define WIN32_LEAN_AND_MEAN 1 +#endif + +#ifndef LDAP_NEEDS_PROTOTYPES +/* force LDAP_P to always include prototypes */ +#define LDAP_NEEDS_PROTOTYPES 1 +#endif + +#ifndef LDAP_REL_ENG +#if (LDAP_VENDOR_VERSION == 000000) && !defined(LDAP_DEVEL) +#define LDAP_DEVEL +#endif +#if defined(LDAP_DEVEL) && !defined(LDAP_TEST) +#define LDAP_TEST +#endif +#endif + +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif + +#ifdef HAVE_EBCDIC +/* ASCII/EBCDIC converting replacements for stdio funcs + * vsnprintf and snprintf are used too, but they are already + * checked by the configure script + */ +#define fputs ber_pvt_fputs +#define fgets ber_pvt_fgets +#define printf ber_pvt_printf +#define fprintf ber_pvt_fprintf +#define vfprintf ber_pvt_vfprintf +#define vsprintf ber_pvt_vsprintf +#endif + +#include "ac/fdset.h" + +#include "ldap_cdefs.h" +#include "ldap_features.h" + +#include "ac/assert.h" +#include "ac/localize.h" + +#endif /* _LDAP_PORTABLE_H */ +/* end of portable.h.post */ + diff --git a/contrib/libs/openldap/include/portable-musl.h b/contrib/libs/openldap/include/portable-musl.h new file mode 100644 index 0000000000..7806675793 --- /dev/null +++ b/contrib/libs/openldap/include/portable-musl.h @@ -0,0 +1,6 @@ +#pragma once + +#include "portable-linux.h" + +#define HAVE_SYS_ERRLIST 1 +#define DECL_SYS_ERRLIST 1 diff --git a/contrib/libs/openldap/include/portable.h b/contrib/libs/openldap/include/portable.h new file mode 100644 index 0000000000..b283c34e1b --- /dev/null +++ b/contrib/libs/openldap/include/portable.h @@ -0,0 +1,7 @@ +#pragma once + +#include "portable-linux.h" + +#if defined(_musl_) +# include "portable-musl.h" +#endif diff --git a/contrib/libs/openldap/libraries/CMakeLists.darwin-x86_64.txt b/contrib/libs/openldap/libraries/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..a1663bfa57 --- /dev/null +++ b/contrib/libs/openldap/libraries/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,9 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(liblber) diff --git a/contrib/libs/openldap/libraries/CMakeLists.linux-aarch64.txt b/contrib/libs/openldap/libraries/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..a1663bfa57 --- /dev/null +++ b/contrib/libs/openldap/libraries/CMakeLists.linux-aarch64.txt @@ -0,0 +1,9 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(liblber) diff --git a/contrib/libs/openldap/libraries/CMakeLists.linux-x86_64.txt b/contrib/libs/openldap/libraries/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..a1663bfa57 --- /dev/null +++ b/contrib/libs/openldap/libraries/CMakeLists.linux-x86_64.txt @@ -0,0 +1,9 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +add_subdirectory(liblber) diff --git a/contrib/libs/openldap/libraries/CMakeLists.txt b/contrib/libs/openldap/libraries/CMakeLists.txt new file mode 100644 index 0000000000..606ff46b4b --- /dev/null +++ b/contrib/libs/openldap/libraries/CMakeLists.txt @@ -0,0 +1,15 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/contrib/libs/openldap/libraries/liblber/CMakeLists.darwin-x86_64.txt b/contrib/libs/openldap/libraries/liblber/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..c7754efb60 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,31 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(openldap-libraries-liblber) +target_compile_options(openldap-libraries-liblber PRIVATE + -DLBER_LIBRARY + -DLDAPI_SOCK="/run/openldap/ldapi" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_sources(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/assert.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/bprint.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/decode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/encode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/io.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/memory.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/sockbuf.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/stdio.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/version.c +) diff --git a/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-aarch64.txt b/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..f2e0e80cb4 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-aarch64.txt @@ -0,0 +1,34 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(openldap-libraries-liblber) +target_compile_options(openldap-libraries-liblber PRIVATE + -DLBER_LIBRARY + -DLDAPI_SOCK="/run/openldap/ldapi" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_link_libraries(openldap-libraries-liblber PUBLIC + contrib-libs-linux-headers +) +target_sources(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/assert.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/bprint.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/decode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/encode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/io.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/memory.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/sockbuf.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/stdio.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/version.c +) diff --git a/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-x86_64.txt b/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..f2e0e80cb4 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/CMakeLists.linux-x86_64.txt @@ -0,0 +1,34 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(openldap-libraries-liblber) +target_compile_options(openldap-libraries-liblber PRIVATE + -DLBER_LIBRARY + -DLDAPI_SOCK="/run/openldap/ldapi" + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/include +) +target_link_libraries(openldap-libraries-liblber PUBLIC + contrib-libs-linux-headers +) +target_sources(openldap-libraries-liblber PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/assert.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/bprint.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/debug.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/decode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/encode.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/io.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/memory.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/options.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/sockbuf.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/stdio.c + ${CMAKE_SOURCE_DIR}/contrib/libs/openldap/libraries/liblber/version.c +) diff --git a/contrib/libs/openldap/libraries/liblber/CMakeLists.txt b/contrib/libs/openldap/libraries/liblber/CMakeLists.txt new file mode 100644 index 0000000000..606ff46b4b --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/CMakeLists.txt @@ -0,0 +1,15 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/contrib/libs/openldap/libraries/liblber/assert.c b/contrib/libs/openldap/libraries/liblber/assert.c new file mode 100644 index 0000000000..bcaa9af8c5 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/assert.c @@ -0,0 +1,40 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef LDAP_NEED_ASSERT + +#include <stdio.h> + +/* + * helper for our private assert() macro + * + * note: if assert() doesn't exist, like abort() or raise() won't either. + * could use kill() but that might be problematic. I'll just ignore this + * issue for now. + */ + +void +ber_pvt_assert( const char *file, int line, const char *test ) +{ + fprintf(stderr, + _("Assertion failed: %s, file %s, line %d\n"), + test, file, line); + + abort(); +} + +#endif diff --git a/contrib/libs/openldap/libraries/liblber/bprint.c b/contrib/libs/openldap/libraries/liblber/bprint.c new file mode 100644 index 0000000000..4b179b107e --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/bprint.c @@ -0,0 +1,296 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* + * Copyright (c) 1991 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/stdarg.h> +#include <ac/string.h> + +#include "lber-int.h" + +#define ber_log_check(errlvl, loglvl) ((errlvl) & (loglvl)) + +BER_LOG_FN ber_int_log_proc = NULL; + +/* + * We don't just set ber_pvt_err_file to stderr here, because in NT, + * stderr is a symbol imported from a DLL. As such, the compiler + * doesn't recognize the symbol as having a constant address. Thus + * we set ber_pvt_err_file to stderr later, when it first gets + * referenced. + */ +FILE *ber_pvt_err_file = NULL; + +/* + * ber errno + */ +BER_ERRNO_FN ber_int_errno_fn = NULL; + +int * ber_errno_addr(void) +{ + static int ber_int_errno = LBER_ERROR_NONE; + + if( ber_int_errno_fn ) { + return (*ber_int_errno_fn)(); + } + + return &ber_int_errno; +} + +/* + * Print stuff + */ +void ber_error_print( LDAP_CONST char *data ) +{ + assert( data != NULL ); + + if (!ber_pvt_err_file) ber_pvt_err_file = stderr; + + fputs( data, ber_pvt_err_file ); + + /* Print to both streams */ + if (ber_pvt_err_file != stderr) { + fputs( data, stderr ); + fflush( stderr ); + } + + fflush( ber_pvt_err_file ); +} + +BER_LOG_PRINT_FN ber_pvt_log_print = ber_error_print; + +/* + * lber log + */ + +int ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, + ... ) +{ + char buf[1024]; + va_list vl; + va_start( vl, fmt ); + + if ( ber_int_log_proc != NULL ) { + ber_int_log_proc( ber_pvt_err_file, subsystem, level, fmt, vl ); + + } else { + int level; + ber_get_option( NULL, LBER_OPT_BER_DEBUG, &level ); + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, vl ); + if ( ber_log_check( LDAP_DEBUG_BER, level ) ) { + (*ber_pvt_log_print)( buf ); + } + } + + va_end(vl); + return 1; +} + +int ber_pvt_log_printf( int errlvl, int loglvl, const char *fmt, ... ) +{ + char buf[1024]; + va_list ap; + + assert( fmt != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + va_start( ap, fmt ); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + (*ber_pvt_log_print)( buf ); + return 1; +} + +#if 0 +static int ber_log_puts(int errlvl, int loglvl, char *buf) +{ + assert( buf != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + (*ber_pvt_log_print)( buf ); + return 1; +} +#endif + +/* + * Print arbitrary stuff, for debugging. + */ + +int +ber_log_bprint(int errlvl, + int loglvl, + const char *data, + ber_len_t len ) +{ + assert( data != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_bprint(data, len); + return 1; +} + +void +ber_bprint( + LDAP_CONST char *data, + ber_len_t len ) +{ + static const char hexdig[] = "0123456789abcdef"; +#define BP_OFFSET 9 +#define BP_GRAPH 60 +#define BP_LEN 80 + char line[BP_LEN]; + ber_len_t i; + + assert( data != NULL ); + + /* in case len is zero */ + line[0] = '\n'; + line[1] = '\0'; + + for ( i = 0 ; i < len ; i++ ) { + int n = i % 16; + unsigned off; + + if( !n ) { + if( i ) (*ber_pvt_log_print)( line ); + memset( line, ' ', sizeof(line)-2 ); + line[sizeof(line)-2] = '\n'; + line[sizeof(line)-1] = '\0'; + + off = i % 0x0ffffU; + + line[2] = hexdig[0x0f & (off >> 12)]; + line[3] = hexdig[0x0f & (off >> 8)]; + line[4] = hexdig[0x0f & (off >> 4)]; + line[5] = hexdig[0x0f & off]; + line[6] = ':'; + } + + off = BP_OFFSET + n*3 + ((n >= 8)?1:0); + line[off] = hexdig[0x0f & ( data[i] >> 4 )]; + line[off+1] = hexdig[0x0f & data[i]]; + + off = BP_GRAPH + n + ((n >= 8)?1:0); + + if ( isprint( (unsigned char) data[i] )) { + line[BP_GRAPH + n] = data[i]; + } else { + line[BP_GRAPH + n] = '.'; + } + } + + (*ber_pvt_log_print)( line ); +} + + +int +ber_log_dump( + int errlvl, + int loglvl, + BerElement *ber, + int inout ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_dump(ber, inout); + return 1; +} + +void +ber_dump( + BerElement *ber, + int inout ) +{ + char buf[132]; + ber_len_t len; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( inout == 1 ) { + len = ber_pvt_ber_remaining(ber); + } else { + len = ber_pvt_ber_write(ber); + } + + sprintf( buf, "ber_dump: buf=%p ptr=%p end=%p len=%ld\n", + (void *) ber->ber_buf, + (void *) ber->ber_ptr, + (void *) ber->ber_end, + (long) len ); + + (void) (*ber_pvt_log_print)( buf ); + + ber_bprint( ber->ber_ptr, len ); +} + +typedef struct seqorset Seqorset; + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +int +ber_log_sos_dump( + int errlvl, + int loglvl, + Seqorset *sos ) +{ + return 0; +} + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +void +ber_sos_dump( + Seqorset *sos ) +{ +} diff --git a/contrib/libs/openldap/libraries/liblber/debug.c b/contrib/libs/openldap/libraries/liblber/debug.c new file mode 100644 index 0000000000..1744e58a33 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/debug.c @@ -0,0 +1,73 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/ctype.h> + +#ifdef LDAP_SYSLOG +#include <ac/syslog.h> +#endif + +#include "ldap_log.h" +#include "ldap_defaults.h" +#include "lber.h" +#include "ldap_pvt.h" + +int lutil_debug_file( FILE *file ) +{ + ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, file ); + + return 0; +} + +void (lutil_debug)( int debug, int level, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + + if ( !(level & debug ) ) return; + + va_start( vl, fmt ); + vsnprintf( buffer, sizeof(buffer), fmt, vl ); + va_end( vl ); + ber_pvt_log_print( buffer ); +} + +#if defined(HAVE_EBCDIC) && defined(LDAP_SYSLOG) +#undef syslog +void eb_syslog( int pri, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + + va_start( vl, fmt ); + vsnprintf( buffer, sizeof(buffer), fmt, vl ); + buffer[sizeof(buffer)-1] = '\0'; + + /* The syslog function appears to only work with pure EBCDIC */ + __atoe(buffer); +#pragma convlit(suspend) + syslog( pri, "%s", buffer ); +#pragma convlit(resume) + va_end( vl ); +} +#endif diff --git a/contrib/libs/openldap/libraries/liblber/decode.c b/contrib/libs/openldap/libraries/liblber/decode.c new file mode 100644 index 0000000000..48696e0510 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/decode.c @@ -0,0 +1,1026 @@ +/* decode.c - ber input decoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "lber-int.h" + + +/* out->bv_len should be the buffer size on input */ +int +ber_decode_oid( BerValue *in, BerValue *out ) +{ + const unsigned char *der; + unsigned long val; + unsigned val1; + ber_len_t i; + char *ptr; + + assert( in != NULL ); + assert( out != NULL ); + + /* need 4 chars/inbyte + \0 for input={7f 7f 7f...} */ + if ( !out->bv_val || (out->bv_len+3)/4 <= in->bv_len ) + return -1; + + ptr = NULL; + der = (unsigned char *) in->bv_val; + val = 0; + for ( i=0; i < in->bv_len; i++ ) { + val |= der[i] & 0x7f; + if ( !( der[i] & 0x80 )) { + if ( ptr == NULL ) { + /* Initial "x.y": val=x*40+y, x<=2, y<40 if x<2 */ + ptr = out->bv_val; + val1 = (val < 80 ? val/40 : 2); + val -= val1*40; + ptr += sprintf( ptr, "%u", val1 ); + } + ptr += sprintf( ptr, ".%lu", val ); + val = 0; + } else if ( val - 1UL < LBER_OID_COMPONENT_MAX >> 7 ) { + val <<= 7; + } else { + /* val would overflow, or is 0 from invalid initial 0x80 octet */ + return -1; + } + } + if ( ptr == NULL || val != 0 ) + return -1; + + out->bv_len = ptr - out->bv_val; + return 0; +} + +/* Return tag, with *bv = rest of element (starting at length octets) */ +static ber_tag_t +ber_tag_and_rest( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ptrdiff_t rest; + unsigned char *ptr; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + ptr = (unsigned char *) ber->ber_ptr; + rest = (unsigned char *) ber->ber_end - ptr; + if ( rest <= 0 ) { + goto fail; + } + + tag = ber->ber_tag; + if ( (char *) ptr == ber->ber_buf ) { + tag = *ptr; + } + ptr++; + rest--; + if ( (tag & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) { + goto done; + } + + do { + if ( rest <= 0 ) { + break; + } + tag <<= 8; + tag |= *ptr++ & 0xffU; + rest--; + + if ( ! (tag & LBER_MORE_TAG_MASK) ) { + goto done; + } + } while ( tag <= (ber_tag_t)-1 / 256 ); + + fail: + /* Error or unsupported tag size */ + tag = LBER_DEFAULT; + + done: + bv->bv_len = rest; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Return the tag - LBER_DEFAULT returned means trouble */ +ber_tag_t +ber_get_tag( BerElement *ber ) +{ + struct berval bv; + ber_tag_t tag = ber_tag_and_rest( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + return tag; +} + +/* Return next element's tag and point *bv at its contents in-place */ +ber_tag_t +ber_peek_element( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ber_len_t len, rest; + unsigned i; + unsigned char *ptr; + + assert( bv != NULL ); + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag, and point + * bv at the contents. + * + * Assumptions: + * 1) definite lengths + * 2) primitive encodings used whenever possible + */ + + len = 0; + + /* + * First, we read the tag. + */ + tag = ber_tag_and_rest( ber, bv ); + + rest = bv->bv_len; + ptr = (unsigned char *) bv->bv_val; + if ( tag == LBER_DEFAULT || rest == 0 ) { + goto fail; + } + + /* + * Next, read the length. The first octet determines the length + * of the length. If bit 8 is 0, the length is the short form, + * otherwise if the octet != 0x80 it's the long form, otherwise + * the ber element has the unsupported indefinite-length format. + * Lengths that do not fit in a ber_len_t are not accepted. + */ + + len = *ptr++; + rest--; + + if ( len & 0x80U ) { + len &= 0x7fU; + if ( len - 1U > sizeof(ber_len_t) - 1U || rest < len ) { + /* Indefinite-length/too long length/not enough data */ + goto fail; + } + + rest -= len; + i = len; + for( len = *ptr++ & 0xffU; --i; len |= *ptr++ & 0xffU ) { + len <<= 8; + } + } + + /* BER element should have enough data left */ + if( len > rest ) { + fail: + tag = LBER_DEFAULT; + } + + bv->bv_len = len; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Move past next element, point *bv at it in-place, and return its tag. + * The caller may \0-terminate *bv, as next octet is saved in ber->ber_tag. + * Similar to ber_get_stringbv(ber, bv, LBER_BV_NOTERM) except on error. + */ +ber_tag_t +ber_skip_element( BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag = ber_peek_element( ber, bv ); + + if ( tag != LBER_DEFAULT ) { + ber->ber_ptr = bv->bv_val + bv->bv_len; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + } + + return tag; +} + +/* Move past next element, point *bv at the complete element in-place, and + * return its tag. The caller may \0-terminate *bv, as next octet is saved in + * ber->ber_tag. Similar to ber_skip_element(ber, bv) except the tag+length + * header is also included in *bv. + */ +ber_tag_t +ber_skip_raw( BerElement *ber, struct berval *bv ) +{ + char *val = ber->ber_ptr; + ber_tag_t tag = ber_skip_element( ber, bv ); + + if ( tag != LBER_DEFAULT ) { + bv->bv_len += bv->bv_val - val; + bv->bv_val = val; + } + + return tag; +} + +ber_tag_t +ber_peek_tag( + BerElement *ber, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + *len = bv.bv_len; + return tag; +} + +ber_tag_t +ber_skip_tag( BerElement *ber, ber_len_t *lenp ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + + *lenp = bv.bv_len; + return tag; +} + +ber_tag_t +ber_get_int( + BerElement *ber, + ber_int_t *num ) +{ + struct berval bv; + ber_tag_t tag = ber_skip_element( ber, &bv ); + + if ( tag == LBER_DEFAULT ) { + return tag; + } + + return ber_decode_int( &bv, num ) ? LBER_DEFAULT : tag; +} + +int +ber_decode_int( const struct berval *bv, ber_int_t *num ) +{ + ber_len_t len = bv->bv_len; + if ( len > sizeof(ber_int_t) ) + return -1; + + assert( num != NULL ); + + /* parse two's complement integer */ + if( len ) { + unsigned char *buf = (unsigned char *) bv->bv_val; + ber_len_t i; + ber_int_t netnum = buf[0] & 0xff; + + /* sign extend */ + netnum = (netnum ^ 0x80) - 0x80; + + /* shift in the bytes */ + for( i = 1; i < len; i++ ) { + netnum = (netnum << 8 ) | buf[i]; + } + + *num = netnum; + + } else { + *num = 0; + } + + return 0; +} + +ber_tag_t +ber_get_enum( + BerElement *ber, + ber_int_t *num ) +{ + return ber_get_int( ber, num ); +} + +ber_tag_t +ber_get_stringb( + BerElement *ber, + char *buf, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag; + + if ( (tag = ber_skip_element( ber, &bv )) == LBER_DEFAULT ) { + return LBER_DEFAULT; + } + + /* must fit within allocated space with termination */ + if ( bv.bv_len >= *len ) { + return LBER_DEFAULT; + } + + memcpy( buf, bv.bv_val, bv.bv_len ); + buf[bv.bv_len] = '\0'; + + *len = bv.bv_len; + return tag; +} + +/* Definitions for get_string vector + * + * ChArray, BvArray, and BvVec are self-explanatory. + * BvOff is a struct berval embedded in an array of larger structures + * of siz bytes at off bytes from the beginning of the struct. + */ +enum bgbvc { ChArray, BvArray, BvVec, BvOff }; + +/* Use this single cookie for state, to keep actual + * stack use to the absolute minimum. + */ +typedef struct bgbvr { + const enum bgbvc choice; + const int option; /* (ALLOC unless BvOff) | (STRING if ChArray) */ + ber_len_t siz; /* input array element size, output count */ + ber_len_t off; /* BvOff offset to the struct berval */ + void *result; +} bgbvr; + +static ber_tag_t +ber_get_stringbvl( BerElement *ber, bgbvr *b ) +{ + int i = 0, n; + ber_tag_t tag; + ber_len_t tot_size = 0, siz = b->siz; + char *last, *orig; + struct berval bv, *bvp = NULL; + union stringbvl_u { + char **ca; /* ChArray */ + BerVarray ba; /* BvArray */ + struct berval **bv; /* BvVec */ + char *bo; /* BvOff */ + } res; + + tag = ber_skip_tag( ber, &bv.bv_len ); + + if ( tag != LBER_DEFAULT ) { + tag = 0; + orig = ber->ber_ptr; + last = orig + bv.bv_len; + + for ( ; ber->ber_ptr < last; i++, tot_size += siz ) { + if ( ber_skip_element( ber, &bv ) == LBER_DEFAULT ) + break; + } + if ( ber->ber_ptr != last ) { + i = 0; + tag = LBER_DEFAULT; + } + + ber->ber_ptr = orig; + ber->ber_tag = *(unsigned char *) orig; + } + + b->siz = i; + if ( i == 0 ) { + return tag; + } + + /* Allocate and NULL-terminate the result vector */ + b->result = ber_memalloc_x( tot_size + siz, ber->ber_memctx ); + if ( b->result == NULL ) { + return LBER_DEFAULT; + } + switch (b->choice) { + case ChArray: + res.ca = b->result; + res.ca[i] = NULL; + break; + case BvArray: + res.ba = b->result; + res.ba[i].bv_val = NULL; + break; + case BvVec: + res.bv = b->result; + res.bv[i] = NULL; + break; + case BvOff: + res.bo = (char *) b->result + b->off; + ((struct berval *) (res.bo + tot_size))->bv_val = NULL; + tot_size = 0; + break; + } + + n = 0; + do { + tag = ber_get_stringbv( ber, &bv, b->option ); + if ( tag == LBER_DEFAULT ) { + goto failed; + } + + /* store my result */ + switch (b->choice) { + case ChArray: + res.ca[n] = bv.bv_val; + break; + case BvArray: + res.ba[n] = bv; + break; + case BvVec: + bvp = ber_memalloc_x( sizeof( struct berval ), + ber->ber_memctx ); + if ( !bvp ) { + ber_memfree_x( bv.bv_val, ber->ber_memctx ); + goto failed; + } + res.bv[n] = bvp; + *bvp = bv; + break; + case BvOff: + *(struct berval *)(res.bo + tot_size) = bv; + tot_size += siz; + break; + } + } while (++n < i); + return tag; + +failed: + if (b->choice != BvOff) { /* BvOff does not have LBER_BV_ALLOC set */ + while (--n >= 0) { + switch(b->choice) { + case ChArray: + ber_memfree_x(res.ca[n], ber->ber_memctx); + break; + case BvArray: + ber_memfree_x(res.ba[n].bv_val, ber->ber_memctx); + break; + case BvVec: + ber_memfree_x(res.bv[n]->bv_val, ber->ber_memctx); + ber_memfree_x(res.bv[n], ber->ber_memctx); + break; + default: + break; + } + } + } + ber_memfree_x(b->result, ber->ber_memctx); + b->result = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_stringbv( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || + (( option & LBER_BV_STRING ) && + bv->bv_len && memchr( bv->bv_val, 0, bv->bv_len - 1 ))) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + if ( bv->bv_len != 0 ) { + memcpy( bv->bv_val, data, bv->bv_len ); + } + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringbv_null( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || bv->bv_len == 0 ) { + bv->bv_val = NULL; + return tag; + } + + if (( option & LBER_BV_STRING ) && + memchr( bv->bv_val, 0, bv->bv_len - 1 )) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + memcpy( bv->bv_val, data, bv->bv_len ); + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringa( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringa_null( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv_null( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringal( BerElement *ber, struct berval **bv ) +{ + ber_tag_t tag; + + assert( ber != NULL ); + assert( bv != NULL ); + + *bv = (struct berval *) ber_memalloc_x( sizeof(struct berval), + ber->ber_memctx ); + if ( *bv == NULL ) { + return LBER_DEFAULT; + } + + tag = ber_get_stringbv( ber, *bv, LBER_BV_ALLOC ); + if ( tag == LBER_DEFAULT ) { + ber_memfree_x( *bv, ber->ber_memctx ); + *bv = NULL; + } + return tag; +} + +ber_tag_t +ber_get_bitstringa( + BerElement *ber, + char **buf, + ber_len_t *blen ) +{ + ber_tag_t tag; + struct berval data; + unsigned char unusedbits; + + assert( buf != NULL ); + assert( blen != NULL ); + + if ( (tag = ber_skip_element( ber, &data )) == LBER_DEFAULT ) { + goto fail; + } + + if ( --data.bv_len > (ber_len_t)-1 / 8 ) { + goto fail; + } + unusedbits = *(unsigned char *) data.bv_val++; + if ( unusedbits > 7 ) { + goto fail; + } + + if ( memchr( data.bv_val, 0, data.bv_len )) { + goto fail; + } + + *buf = (char *) ber_memalloc_x( data.bv_len, ber->ber_memctx ); + if ( *buf == NULL ) { + return LBER_DEFAULT; + } + memcpy( *buf, data.bv_val, data.bv_len ); + + *blen = data.bv_len * 8 - unusedbits; + return tag; + + fail: + *buf = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_null( BerElement *ber ) +{ + ber_len_t len; + ber_tag_t tag = ber_skip_tag( ber, &len ); + + return( len == 0 ? tag : LBER_DEFAULT ); +} + +ber_tag_t +ber_get_boolean( + BerElement *ber, + ber_int_t *boolval ) +{ + return ber_get_int( ber, boolval ); +} + +ber_tag_t +ber_first_element( + BerElement *ber, + ber_len_t *len, + char **last ) +{ + assert( last != NULL ); + + /* skip the sequence header, use the len to mark where to stop */ + if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) { + *last = NULL; + return LBER_DEFAULT; + } + + *last = ber->ber_ptr + *len; + + if ( *len == 0 ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +ber_tag_t +ber_next_element( + BerElement *ber, + ber_len_t *len, + LDAP_CONST char *last ) +{ + assert( ber != NULL ); + assert( last != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_ptr >= last ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +/* VARARGS */ +ber_tag_t +ber_scanf ( BerElement *ber, + LDAP_CONST char *fmt, + ... ) +{ + va_list ap; + LDAP_CONST char *fmt_reset; + char *s, **ss, ***sss; + struct berval data, *bval, **bvp, ***bvpp; + ber_int_t *i; + ber_len_t *l; + ber_tag_t *t; + ber_tag_t rc; + ber_len_t len; + + va_start( ap, fmt ); + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + fmt_reset = fmt; + + if ( ber->ber_debug & (LDAP_DEBUG_TRACE|LDAP_DEBUG_BER)) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_scanf fmt (%s) ber:\n", fmt ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + + for ( rc = 0; *fmt && rc != LBER_DEFAULT; fmt++ ) { + /* When this is modified, remember to update + * the error-cleanup code below accordingly. */ + switch ( *fmt ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + rc = (*f)( ber, p, 0 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa( ber, ss ); + break; + + case 'A': /* octet string - allocate storage as needed, + * but return NULL if len == 0 */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa_null( ber, ss ); + break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_boolean( ber, i ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + l = va_arg( ap, ber_len_t * ); /* for length, in bits */ + rc = ber_get_bitstringa( ber, ss, l ); + break; + + case 'e': /* enumerated */ + case 'i': /* integer */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_int( ber, i ); + break; + + case 'l': /* length of next item */ + l = va_arg( ap, ber_len_t * ); + rc = ber_peek_tag( ber, l ); + break; + + case 'm': /* octet string in berval, in-place */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, 0 ); + break; + + case 'M': /* bvoffarray - must include address of + * a record len, and record offset. + * number of records will be returned thru + * len ptr on finish. parsed in-place. + */ + { + bgbvr cookie = { BvOff, 0 }; + bvp = va_arg( ap, struct berval ** ); + l = va_arg( ap, ber_len_t * ); + cookie.siz = *l; + cookie.off = va_arg( ap, ber_len_t ); + rc = ber_get_stringbvl( ber, &cookie ); + *bvp = cookie.result; + *l = cookie.siz; + break; + } + + case 'n': /* null */ + rc = ber_get_null( ber ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, LBER_BV_ALLOC ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + rc = ber_get_stringal( ber, bvp ); + break; + + case 's': /* octet string - in a buffer */ + s = va_arg( ap, char * ); + l = va_arg( ap, ber_len_t * ); + rc = ber_get_stringb( ber, s, l ); + break; + + case 't': /* tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_peek_tag( ber, &len ); + break; + + case 'T': /* skip tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_skip_tag( ber, &len ); + break; + + case 'v': /* sequence of strings */ + { + bgbvr cookie = { + ChArray, LBER_BV_ALLOC | LBER_BV_STRING, sizeof( char * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, char *** )) = cookie.result; + break; + } + + case 'V': /* sequence of strings + lengths */ + { + bgbvr cookie = { + BvVec, LBER_BV_ALLOC, sizeof( struct berval * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval *** )) = cookie.result; + break; + } + + case 'W': /* bvarray */ + { + bgbvr cookie = { + BvArray, LBER_BV_ALLOC, sizeof( struct berval ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval ** )) = cookie.result; + break; + } + + case 'x': /* skip the next element - whatever it is */ + rc = ber_skip_element( ber, &data ); + break; + + case '{': /* begin sequence */ + case '[': /* begin set */ + switch ( fmt[1] ) { + case 'v': case 'V': case 'W': case 'M': + break; + default: + rc = ber_skip_tag( ber, &len ); + break; + } + break; + + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_scanf: unknown fmt %c\n", *fmt ); + } + rc = LBER_DEFAULT; + break; + } + } + + va_end( ap ); + + if ( rc == LBER_DEFAULT ) { + /* + * Error. Reclaim malloced memory that was given to the caller. + * Set allocated pointers to NULL, "data length" outvalues to 0. + */ + va_start( ap, fmt ); + + for ( ; fmt_reset < fmt; fmt_reset++ ) { + switch ( *fmt_reset ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + (void) (*f)( ber, p, 1 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + case 'A': + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + break; + + case 'b': /* boolean */ + case 'e': /* enumerated */ + case 'i': /* integer */ + (void) va_arg( ap, ber_int_t * ); + break; + + case 'l': /* length of next item */ + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 'm': /* berval in-place */ + bval = va_arg( ap, struct berval * ); + BER_BVZERO( bval ); + break; + + case 'M': /* BVoff array in-place */ + bvp = va_arg( ap, struct berval ** ); + ber_memfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + *(va_arg( ap, ber_len_t * )) = 0; + (void) va_arg( ap, ber_len_t ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + ber_memfree_x( bval->bv_val, ber->ber_memctx ); + BER_BVZERO( bval ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + ber_bvfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 's': /* octet string - in a buffer */ + (void) va_arg( ap, char * ); + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 't': /* tag of next item */ + case 'T': /* skip tag of next item */ + (void) va_arg( ap, ber_tag_t * ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + *(va_arg( ap, ber_len_t * )) = 0; /* for length, in bits */ + break; + + case 'v': /* sequence of strings */ + sss = va_arg( ap, char *** ); + ber_memvfree_x( (void **) *sss, ber->ber_memctx ); + *sss = NULL; + break; + + case 'V': /* sequence of strings + lengths */ + bvpp = va_arg( ap, struct berval *** ); + ber_bvecfree_x( *bvpp, ber->ber_memctx ); + *bvpp = NULL; + break; + + case 'W': /* BerVarray */ + bvp = va_arg( ap, struct berval ** ); + ber_bvarray_free_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 'n': /* null */ + case 'x': /* skip the next element - whatever it is */ + case '{': /* begin sequence */ + case '[': /* begin set */ + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + /* format should be good */ + assert( 0 ); + } + } + + va_end( ap ); + } + + return rc; +} diff --git a/contrib/libs/openldap/libraries/liblber/encode.c b/contrib/libs/openldap/libraries/liblber/encode.c new file mode 100644 index 0000000000..30d28eb603 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/encode.c @@ -0,0 +1,651 @@ +/* encode.c - ber output encoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/stdarg.h> +#include <ac/socket.h> +#include <ac/string.h> + +#include "lber-int.h" + + +#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8) +#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t) +#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t)) +#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE) + +/* + * BER element size constrains: + * + * - We traditionally support a length of max 0xffffffff. However + * some functions return an int length so that is their max. + * MAXINT_BERSIZE is the max for those functions. + * + * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets. + * + * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS: + * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes + * space in the working encoding and DER encoding of a sequence + * or set. Smaller further limits sizes near a sequence/set.) + * + * ber_len_t is mostly unrelated to this. Which may be for the best, + * since it is also used for lengths of data that are never encoded. + */ +#define MAXINT_BERSIZE \ + (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE) +#define MAXINT_BERSIZE_OCTETS 4 +typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */ + + +/* Prepend tag to ptr, which points to the end of a tag buffer */ +static unsigned char * +ber_prepend_tag( unsigned char *ptr, ber_tag_t tag ) +{ + do { + *--ptr = (unsigned char) tag & 0xffU; + } while ( (tag >>= 8) != 0 ); + + return ptr; +} + +/* Prepend ber length to ptr, which points to the end of a length buffer */ +static unsigned char * +ber_prepend_len( unsigned char *ptr, ber_len_t len ) +{ + /* + * short len if it's less than 128 - one byte giving the len, + * with bit 8 0. + * long len otherwise - one byte with bit 8 set, giving the + * length of the length, followed by the length itself. + */ + + *--ptr = (unsigned char) len & 0xffU; + + if ( len >= 0x80 ) { + unsigned char *endptr = ptr--; + + while ( (len >>= 8) != 0 ) { + *ptr-- = (unsigned char) len & 0xffU; + } + *ptr = (unsigned char) (endptr - ptr) + 0x80U; + } + + return ptr; +} + +/* out->bv_len should be the buffer size on input */ +int +ber_encode_oid( BerValue *in, BerValue *out ) +{ + unsigned char *der; + unsigned long val1, val; + int i, j, len; + char *ptr, *end, *inend; + + assert( in != NULL ); + assert( out != NULL ); + + if ( !out->bv_val || out->bv_len < in->bv_len/2 ) + return -1; + + der = (unsigned char *) out->bv_val; + ptr = in->bv_val; + inend = ptr + in->bv_len; + + /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */ + if ( !isdigit( (unsigned char) *ptr )) return -1; + val1 = strtoul( ptr, &end, 10 ); + if ( end == ptr || val1 > 2 ) return -1; + if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1; + val = strtoul( end, &ptr, 10 ); + if ( ptr == end ) return -1; + if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1; + val += val1 * 40; + + for (;;) { + if ( ptr > inend ) return -1; + + /* Write the OID component little-endian, then reverse it */ + len = 0; + do { + der[len++] = (val & 0xff) | 0x80; + } while ( (val >>= 7) != 0 ); + der[0] &= 0x7f; + for ( i = 0, j = len; i < --j; i++ ) { + unsigned char tmp = der[i]; + der[i] = der[j]; + der[j] = tmp; + } + der += len; + + if ( ptr == inend ) + break; + + if ( *ptr++ != '.' ) return -1; + if ( !isdigit( (unsigned char) *ptr )) return -1; + val = strtoul( ptr, &end, 10 ); + if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1; + ptr = end; + } + + out->bv_len = (char *)der - out->bv_val; + return 0; +} + +static int +ber_put_int_or_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + ber_uint_t unum; + unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr; + + sign = 0; + unum = num; /* Bit fiddling should be done with unsigned values */ + if ( num < 0 ) { + sign = 0xffU; + unum = ~unum; + } + for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) { + *ptr-- = (sign ^ (unsigned char) unum) & 0xffU; + if ( unum < 0x80 ) /* top bit at *ptr is sign bit */ + break; + } + + *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */ + ptr = ber_prepend_tag( ptr, tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_ENUMERATED; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_int( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_INTEGER; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_ostring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t len, + ber_tag_t tag ) +{ + int rc; + unsigned char header[HEADER_SIZE], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_OCTETSTRING; + } + + if ( len > MAXINT_BERSIZE ) { + return -1; + } + + ptr = ber_prepend_len( &header[sizeof(header)], len ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + contents) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_berval( + BerElement *ber, + struct berval *bv, + ber_tag_t tag ) +{ + if( bv == NULL || bv->bv_len == 0 ) { + return ber_put_ostring( ber, "", (ber_len_t) 0, tag ); + } + + return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag ); +} + +int +ber_put_string( + BerElement *ber, + LDAP_CONST char *str, + ber_tag_t tag ) +{ + assert( str != NULL ); + + return ber_put_ostring( ber, str, strlen( str ), tag ); +} + +int +ber_put_bitstring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t blen /* in bits */, + ber_tag_t tag ) +{ + int rc; + ber_len_t len; + unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_BITSTRING; + } + + unusedbits = (unsigned char) -blen & 7; + len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */ + if ( len >= MAXINT_BERSIZE ) { + return -1; + } + + header[sizeof(header) - 1] = unusedbits; + ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + unused bit count + bitstring) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_null( BerElement *ber, ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_NULL; + } + + data[sizeof(data) - 1] = 0; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_boolean( + BerElement *ber, + ber_int_t boolval, + ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 2], *ptr; + + if ( tag == LBER_DEFAULT ) + tag = LBER_BOOLEAN; + + data[sizeof(data) - 1] = boolval ? 0xff : 0; + data[sizeof(data) - 2] = 1; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + + +/* Max number of length octets in a sequence or set, normally 5 */ +#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \ + (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS)) + +/* Header of incomplete sequence or set */ +typedef struct seqorset_header { + char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */ + union { + ber_elem_size_t offset; /* enclosing sequence/set */ + char padding[SOS_LENLEN-1]; /* for final length encoding */ + } next_sos; +# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1) +} Seqorset_header; + +/* Start a sequence or set */ +static int +ber_start_seqorset( + BerElement *ber, + ber_tag_t tag ) +{ + /* + * Write the tag and SOS_LENLEN octets reserved for length, to ber. + * For now, length octets = (tag length, previous ber_sos_inner). + * + * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr + * will not move until the outermost sequence or set is complete. + */ + + Seqorset_header header; + unsigned char *headptr; + ber_len_t taglen, headlen; + char *dest, **p; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */ + header.next_sos.offset = 0; + p = &ber->ber_ptr; + } else { + if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) { + if ( ber->ber_sos_inner > (ber_elem_size_t) -1 ) + return -1; + } + header.next_sos.offset = ber->ber_sos_inner; + p = &ber->ber_sos_ptr; + } + headptr = ber_prepend_tag( SOS_TAG_END(header), tag ); + *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr; + headlen = taglen + SOS_LENLEN; + + /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */ + if ( headlen > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, headlen ) != 0 ) + return -1; + } + dest = *p; + AC_MEMCPY( dest, headptr, headlen ); + ber->ber_sos_ptr = dest + headlen; + + ber->ber_sos_inner = dest + taglen - ber->ber_buf; + + /* + * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset() + * should return lenlen - SOS_LENLEN + len, which can be < 0. + */ + return 0; +} + +int +ber_start_seq( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SEQUENCE; + } + + return ber_start_seqorset( ber, tag ); +} + +int +ber_start_set( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SET; + } + + return ber_start_seqorset( ber, tag ); +} + +/* End a sequence or set */ +static int +ber_put_seqorset( BerElement *ber ) +{ + Seqorset_header header; + unsigned char *lenptr; /* length octets in the sequence/set */ + ber_len_t len; /* length(contents) */ + ber_len_t xlen; /* len + length(length) */ + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) return -1; + + lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner; + xlen = ber->ber_sos_ptr - (char *) lenptr; + if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) { + return -1; + } + + /* Extract sequence/set information from length octets */ + memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN ); + + /* Store length, and close gap of leftover reserved length octets */ + len = xlen - SOS_LENLEN; + if ( !(ber->ber_options & LBER_USE_DER) ) { + int i; + lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */ + for( i = SOS_LENLEN; --i > 0; len >>= 8 ) { + lenptr[i] = len & 0xffU; + } + } else { + unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len ); + ber_len_t unused = p - lenptr; + if ( unused != 0 ) { + /* length(length) < the reserved SOS_LENLEN bytes */ + xlen -= unused; + AC_MEMCPY( lenptr, p, xlen ); + ber->ber_sos_ptr = (char *) lenptr + xlen; + } + } + + ber->ber_sos_inner = header.next_sos.offset; + if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */ + /* The ber_ptr is at the set/seq start - move it to the end */ + ber->ber_ptr = ber->ber_sos_ptr; + ber->ber_sos_ptr = NULL; + } + + return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */ +} + +int +ber_put_seq( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +int +ber_put_set( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +/* N tag */ +static ber_tag_t lber_int_null = 0; + +/* VARARGS */ +int +ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... ) +{ + va_list ap; + char *s, **ss; + struct berval *bv, **bvp; + int rc; + ber_int_t i; + ber_len_t len; + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + va_start( ap, fmt ); + + for ( rc = 0; *fmt && rc != -1; fmt++ ) { + switch ( *fmt ) { + case '!': { /* hook */ + BEREncodeCallback *f; + void *p; + + ber->ber_usertag = 0; + + f = va_arg( ap, BEREncodeCallback * ); + p = va_arg( ap, void * ); + rc = (*f)( ber, p ); + + if ( ber->ber_usertag ) { + goto next; + } + } break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_boolean( ber, i, ber->ber_tag ); + break; + + case 'i': /* int */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_int( ber, i, ber->ber_tag ); + break; + + case 'e': /* enumeration */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_enum( ber, i, ber->ber_tag ); + break; + + case 'n': /* null */ + rc = ber_put_null( ber, ber->ber_tag ); + break; + + case 'N': /* Debug NULL */ + rc = 0; + if( lber_int_null != 0 ) { + /* Insert NULL to ensure peer ignores unknown tags */ + rc = ber_put_null( ber, lber_int_null ); + } + break; + + case 'o': /* octet string (non-null terminated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); + rc = ber_put_ostring( ber, s, len, ber->ber_tag ); + break; + + case 'O': /* berval octet string */ + bv = va_arg( ap, struct berval * ); + if( bv == NULL ) break; + rc = ber_put_berval( ber, bv, ber->ber_tag ); + break; + + case 's': /* string */ + s = va_arg( ap, char * ); + rc = ber_put_string( ber, s, ber->ber_tag ); + break; + + case 'B': /* bit string */ + case 'X': /* bit string (deprecated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); /* in bits */ + rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); + break; + + case 't': /* tag for the next element */ + ber->ber_tag = va_arg( ap, ber_tag_t ); + goto next; + + case 'v': /* vector of strings */ + if ( (ss = va_arg( ap, char ** )) == NULL ) + break; + for ( i = 0; ss[i] != NULL; i++ ) { + if ( (rc = ber_put_string( ber, ss[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'V': /* sequences of strings + lengths */ + if ( (bvp = va_arg( ap, struct berval ** )) == NULL ) + break; + for ( i = 0; bvp[i] != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, bvp[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'W': /* BerVarray */ + if ( (bv = va_arg( ap, BerVarray )) == NULL ) + break; + for ( i = 0; bv[i].bv_val != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, &bv[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case '{': /* begin sequence */ + rc = ber_start_seq( ber, ber->ber_tag ); + break; + + case '}': /* end sequence */ + rc = ber_put_seqorset( ber ); + break; + + case '[': /* begin set */ + rc = ber_start_set( ber, ber->ber_tag ); + break; + + case ']': /* end set */ + rc = ber_put_seqorset( ber ); + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_printf: unknown fmt %c\n", *fmt ); + } + rc = -1; + break; + } + + ber->ber_tag = LBER_DEFAULT; + next:; + } + + va_end( ap ); + + return rc; +} diff --git a/contrib/libs/openldap/libraries/liblber/io.c b/contrib/libs/openldap/libraries/liblber/io.c new file mode 100644 index 0000000000..ea5aaa6357 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/io.c @@ -0,0 +1,725 @@ +/* io.c - ber general i/o routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include "lber-int.h" +#include "ldap_log.h" + +ber_slen_t +ber_skip_data( + BerElement *ber, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + ber->ber_ptr += actuallen; + ber->ber_tag = *(unsigned char *)ber->ber_ptr; + + return( (ber_slen_t) actuallen ); +} + +/* + * Read from the ber buffer. The caller must maintain ber->ber_tag. + * Do not use to read whole tags. See ber_get_tag() and ber_skip_data(). + */ +ber_slen_t +ber_read( + BerElement *ber, + char *buf, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + + AC_MEMCPY( buf, ber->ber_ptr, actuallen ); + + ber->ber_ptr += actuallen; + + return( (ber_slen_t) actuallen ); +} + +/* + * Write to the ber buffer. + * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write(). + */ +ber_slen_t +ber_write( + BerElement *ber, + LDAP_CONST char *buf, + ber_len_t len, + int zero ) /* nonzero is unsupported from OpenLDAP 2.4.18 */ +{ + char **p; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + if ( zero != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s", + "ber_write: nonzero 4th argument not supported\n" ); + return( -1 ); + } + + p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr; + if ( len > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, len ) != 0 ) return( -1 ); + } + AC_MEMCPY( *p, buf, len ); + *p += len; + + return( (ber_slen_t) len ); +} + +/* Resize the ber buffer */ +int +ber_realloc( BerElement *ber, ber_len_t len ) +{ + ber_len_t total, offset, sos_offset, rw_offset; + char *buf; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + /* leave room for ber_flatten() to \0-terminate ber_buf */ + if ( ++len == 0 ) { + return( -1 ); + } + + total = ber_pvt_ber_total( ber ); + +#define LBER_EXBUFSIZ 4060 /* a few words less than 2^N for binary buddy */ +#if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0 +# ifndef notdef + /* don't realloc by small amounts */ + total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len; +# else + { /* not sure what value this adds. reduce fragmentation? */ + ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ; + ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ; + total = ( have + need ) * LBER_EXBUFSIZ; + } +# endif +#else + total += len; /* realloc just what's needed */ +#endif + + if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) { + return( -1 ); + } + + buf = ber->ber_buf; + offset = ber->ber_ptr - buf; + sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0; + /* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */ + rw_offset = ber->ber_rwptr ? ber->ber_rwptr - buf : 0; + + buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx ); + if ( buf == NULL ) { + return( -1 ); + } + + ber->ber_buf = buf; + ber->ber_end = buf + total; + ber->ber_ptr = buf + offset; + if ( sos_offset ) + ber->ber_sos_ptr = buf + sos_offset; + if ( ber->ber_rwptr ) + ber->ber_rwptr = buf + rw_offset; + + return( 0 ); +} + +void +ber_free_buf( BerElement *ber ) +{ + assert( LBER_VALID( ber ) ); + + if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx ); + + ber->ber_buf = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_valid = LBER_UNINITIALIZED; +} + +void +ber_free( BerElement *ber, int freebuf ) +{ + if( ber == NULL ) { + LDAP_MEMORY_DEBUG_ASSERT( ber != NULL ); + return; + } + + if( freebuf ) ber_free_buf( ber ); + + ber_memfree_x( (char *) ber, ber->ber_memctx ); +} + +int +ber_flush( Sockbuf *sb, BerElement *ber, int freeit ) +{ + return ber_flush2( sb, ber, + freeit ? LBER_FLUSH_FREE_ON_SUCCESS + : LBER_FLUSH_FREE_NEVER ); +} + +int +ber_flush2( Sockbuf *sb, BerElement *ber, int freeit ) +{ + ber_len_t towrite; + ber_slen_t rc; + + assert( sb != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_rwptr == NULL ) { + ber->ber_rwptr = ber->ber_buf; + } + towrite = ber->ber_ptr - ber->ber_rwptr; + + if ( sb->sb_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug, + "ber_flush2: %ld bytes to sd %ld%s\n", + towrite, (long) sb->sb_fd, + ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" ); + ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug, + ber->ber_rwptr, towrite ); + } + + while ( towrite > 0 ) { +#ifdef LBER_TRICKLE + sleep(1); + rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 ); +#else + rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite ); +#endif + if ( rc <= 0 ) { + if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 ); + return -1; + } + towrite -= rc; + ber->ber_rwptr += rc; + } + + if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 ); + + return 0; +} + +BerElement * +ber_alloc_t( int options ) +{ + BerElement *ber; + + ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) ); + + if ( ber == NULL ) { + return NULL; + } + + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = options; + ber->ber_debug = ber_int_debug; + + assert( LBER_VALID( ber ) ); + return ber; +} + +BerElement * +ber_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( 0 ); +} + +BerElement * +der_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( LBER_USE_DER ); +} + +BerElement * +ber_dup( BerElement *ber ) +{ + BerElement *new; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) { + return NULL; + } + + *new = *ber; + + assert( LBER_VALID( new ) ); + return( new ); +} + + +void +ber_init2( BerElement *ber, struct berval *bv, int options ) +{ + assert( ber != NULL ); + + (void) memset( (char *)ber, '\0', sizeof( BerElement )); + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = (char) options; + ber->ber_debug = ber_int_debug; + + if ( bv != NULL ) { + ber->ber_buf = bv->bv_val; + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + bv->bv_len; + } + + assert( LBER_VALID( ber ) ); +} + +/* OLD U-Mich ber_init() */ +void +ber_init_w_nullc( BerElement *ber, int options ) +{ + ber_init2( ber, NULL, options ); +} + +/* New C-API ber_init() */ +/* This function constructs a BerElement containing a copy +** of the data in the bv argument. +*/ +BerElement * +ber_init( struct berval *bv ) +{ + BerElement *ber; + + assert( bv != NULL ); + + if ( bv == NULL ) { + return NULL; + } + + ber = ber_alloc_t( 0 ); + + if( ber == NULL ) { + /* allocation failed */ + return NULL; + } + + /* copy the data */ + if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) + != bv->bv_len ) + { + /* write failed, so free and return NULL */ + ber_free( ber, 1 ); + return NULL; + } + + ber_reset( ber, 1 ); /* reset the pointer to the start of the buffer */ + return ber; +} + +/* New C-API ber_flatten routine */ +/* This routine allocates a struct berval whose contents are a BER +** encoding taken from the ber argument. The bvPtr pointer points to +** the returned berval. +** +** ber_flatten2 is the same, but uses a struct berval passed by +** the caller. If alloc is 0 the returned bv uses the ber buf directly. +*/ +int ber_flatten2( + BerElement *ber, + struct berval *bv, + int alloc ) +{ + assert( bv != NULL ); + + if ( bv == NULL ) { + return -1; + } + + if ( ber == NULL ) { + /* ber is null, create an empty berval */ + bv->bv_val = NULL; + bv->bv_len = 0; + + } else if ( ber->ber_sos_ptr != NULL ) { + /* unmatched "{" and "}" */ + return -1; + + } else { + /* copy the berval */ + ber_len_t len = ber_pvt_ber_write( ber ); + + if ( alloc ) { + bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return -1; + } + AC_MEMCPY( bv->bv_val, ber->ber_buf, len ); + bv->bv_val[len] = '\0'; + } else if ( ber->ber_buf != NULL ) { + bv->bv_val = ber->ber_buf; + bv->bv_val[len] = '\0'; + } else { + bv->bv_val = ""; + } + bv->bv_len = len; + } + return 0; +} + +int ber_flatten( + BerElement *ber, + struct berval **bvPtr) +{ + struct berval *bv; + int rc; + + assert( bvPtr != NULL ); + + if(bvPtr == NULL) { + return -1; + } + + bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx ); + if ( bv == NULL ) { + return -1; + } + rc = ber_flatten2(ber, bv, 1); + if (rc == -1) { + ber_memfree_x(bv, ber->ber_memctx); + } else { + *bvPtr = bv; + } + return rc; +} + +void +ber_reset( BerElement *ber, int was_writing ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( was_writing ) { + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; + + } else { + ber->ber_ptr = ber->ber_end; + } + + ber->ber_rwptr = NULL; +} + +/* + * A rewrite of ber_get_next that can safely be called multiple times + * for the same packet. It will simply continue where it stopped until + * a full packet is read. + */ + +#define LENSIZE 4 + +ber_tag_t +ber_get_next( + Sockbuf *sb, + ber_len_t *len, + BerElement *ber ) +{ + assert( sb != NULL ); + assert( len != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_debug & LDAP_DEBUG_TRACE ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next\n" ); + } + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag byte (we + * can assume a single byte), return the length in len, + * and the rest of the undecoded element in buf. + * + * Assumptions: + * 1) small tags (less than 128) + * 2) definite lengths + * 3) primitive encodings used whenever possible + * + * The code also handles multi-byte tags. The first few bytes + * of the message are read to check for multi-byte tags and + * lengths. These bytes are temporarily stored in the ber_tag, + * ber_len, and ber_usertag fields of the berelement until + * tag/len parsing is complete. After this parsing, any leftover + * bytes and the rest of the message are copied into the ber_buf. + * + * We expect tag and len to be at most 32 bits wide. + */ + + if (ber->ber_rwptr == NULL) { + assert( ber->ber_buf == NULL ); + ber->ber_rwptr = (char *) &ber->ber_len-1; + ber->ber_ptr = ber->ber_rwptr; + ber->ber_tag = 0; + } + + while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr < + (char *)&ber->ber_len + LENSIZE*2) { + ber_slen_t sblen; + char buf[sizeof(ber->ber_len)-1]; + ber_len_t tlen = 0; + + /* The tag & len can be at most 9 bytes; we try to read up to 8 here */ + sock_errset(0); + sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr; + /* Trying to read the last len byte of a 9 byte tag+len */ + if (sblen<1) + sblen = 1; + sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen ); + if (sblen<=0) return LBER_DEFAULT; + ber->ber_rwptr += sblen; + + /* We got at least one byte, try to parse the tag. */ + if (ber->ber_ptr == (char *)&ber->ber_len-1) { + ber_tag_t tag; + unsigned char *p = (unsigned char *)ber->ber_ptr; + tag = *p++; + if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) { + ber_len_t i; + for (i=1; (char *)p<ber->ber_rwptr; i++) { + tag <<= 8; + tag |= *p++; + if (!(tag & LBER_MORE_TAG_MASK)) + break; + /* Is the tag too big? */ + if (i == sizeof(ber_tag_t)-1) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + } + /* Did we run out of bytes? */ + if ((char *)p == ber->ber_rwptr) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + } + ber->ber_tag = tag; + ber->ber_ptr = (char *)p; + } + + if ( ber->ber_ptr == ber->ber_rwptr ) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + + /* Now look for the length */ + if (*ber->ber_ptr & 0x80) { /* multi-byte */ + int i; + unsigned char *p = (unsigned char *)ber->ber_ptr; + int llen = *p++ & 0x7f; + if (llen > LENSIZE) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + /* Not enough bytes? */ + if (ber->ber_rwptr - (char *)p < llen) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + for (i=0; i<llen; i++) { + tlen <<=8; + tlen |= *p++; + } + ber->ber_ptr = (char *)p; + } else { + tlen = *(unsigned char *)ber->ber_ptr++; + } + + /* Are there leftover data bytes inside ber->ber_len? */ + if (ber->ber_ptr < (char *)&ber->ber_usertag) { + if (ber->ber_rwptr < (char *)&ber->ber_usertag) { + sblen = ber->ber_rwptr - ber->ber_ptr; + } else { + sblen = (char *)&ber->ber_usertag - ber->ber_ptr; + } + AC_MEMCPY(buf, ber->ber_ptr, sblen); + ber->ber_ptr += sblen; + } else { + sblen = 0; + } + ber->ber_len = tlen; + + /* now fill the buffer. */ + + /* make sure length is reasonable */ + if ( ber->ber_len == 0 ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) { + ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug, + "ber_get_next: sockbuf_max_incoming exceeded " + "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming ); + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if (ber->ber_buf==NULL) { + ber_len_t l = ber->ber_rwptr - ber->ber_ptr; + /* ber->ber_ptr is always <= ber->ber->ber_rwptr. + * make sure ber->ber_len agrees with what we've + * already read. + */ + if ( ber->ber_len < sblen + l ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx ); + if (ber->ber_buf==NULL) { + return LBER_DEFAULT; + } + ber->ber_end = ber->ber_buf + ber->ber_len; + if (sblen) { + AC_MEMCPY(ber->ber_buf, buf, sblen); + } + if (l > 0) { + AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l); + sblen += l; + } + *ber->ber_end = '\0'; + ber->ber_ptr = ber->ber_buf; + ber->ber_usertag = 0; + if ((ber_len_t)sblen == ber->ber_len) { + goto done; + } + ber->ber_rwptr = ber->ber_buf + sblen; + } + } + + if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) { + ber_slen_t res; + ber_slen_t to_go; + + to_go = ber->ber_end - ber->ber_rwptr; + /* unsigned/signed overflow */ + if (to_go<0) return LBER_DEFAULT; + + sock_errset(0); + res = ber_int_sb_read( sb, ber->ber_rwptr, to_go ); + if (res<=0) return LBER_DEFAULT; + ber->ber_rwptr+=res; + + if (res<to_go) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } +done: + ber->ber_rwptr = NULL; + *len = ber->ber_len; + if ( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next: tag 0x%lx len %ld contents:\n", + ber->ber_tag, ber->ber_len ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + return (ber->ber_tag); + } + + /* invalid input */ + return LBER_DEFAULT; +} + +char * +ber_start( BerElement* ber ) +{ + return ber->ber_buf; +} + +int +ber_len( BerElement* ber ) +{ + return ( ber->ber_end - ber->ber_buf ); +} + +int +ber_ptrlen( BerElement* ber ) +{ + return ( ber->ber_ptr - ber->ber_buf ); +} + +void +ber_rewind ( BerElement * ber ) +{ + ber->ber_rwptr = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; +#if 0 /* TODO: Should we add this? */ + ber->ber_tag = LBER_DEFAULT; + ber->ber_usertag = 0; +#endif +} + +int +ber_remaining( BerElement * ber ) +{ + return ber_pvt_ber_remaining( ber ); +} diff --git a/contrib/libs/openldap/libraries/liblber/lber-int.h b/contrib/libs/openldap/libraries/liblber/lber-int.h new file mode 100644 index 0000000000..14a110653c --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/lber-int.h @@ -0,0 +1,225 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LBER_INT_H +#define _LBER_INT_H + +#include "lber.h" +#define LDAP_INT_DEBUG +#include "ldap_log.h" +#include "lber_pvt.h" +#include "ldap_queue.h" + +LDAP_BEGIN_DECL + +typedef void (*BER_LOG_FN)(FILE *file, + const char *subsys, int level, const char *fmt, ... ); + +LBER_V (BER_ERRNO_FN) ber_int_errno_fn; + +#ifdef LDAP_MEMORY_TRACE +# ifndef LDAP_MEMORY_DEBUG +# define LDAP_MEMORY_DEBUG 1 +# endif +#endif + +#ifdef LDAP_MEMORY_DEBUG +LBER_V (long) ber_int_meminuse; +#endif +#if defined(LDAP_MEMORY_DEBUG) && ((LDAP_MEMORY_DEBUG +0) & 2) +# define LDAP_MEMORY_DEBUG_ASSERT assert +#else +# define LDAP_MEMORY_DEBUG_ASSERT(expr) ((void) 0) +#endif + +struct lber_options { + short lbo_valid; + unsigned short lbo_options; + int lbo_debug; +}; + +LBER_F( int ) ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, ... ); + +#define LBER_UNINITIALIZED 0x0 +#define LBER_INITIALIZED 0x1 +#define LBER_VALID_BERELEMENT 0x2 +#define LBER_VALID_SOCKBUF 0x3 + +LBER_V (struct lber_options) ber_int_options; +#define ber_int_debug ber_int_options.lbo_debug + +/* Data encoded in ASN.1 BER format */ +struct berelement { + struct lber_options ber_opts; +#define ber_valid ber_opts.lbo_valid +#define ber_options ber_opts.lbo_options +#define ber_debug ber_opts.lbo_debug + + /* + * The members below, when not NULL/LBER_DEFAULT/etc, are: + * ber_buf Data buffer. Other pointers normally point into it. + * ber_rwptr Read/write cursor for Sockbuf I/O. + * ber_memctx Context passed to ber_memalloc() & co. + * When decoding data (reading it from the BerElement): + * ber_end End of BER data. + * ber_ptr Read cursor, except for 1st octet of tags. + * ber_tag 1st octet of next tag, saved from *ber_ptr when + * ber_ptr may be pointing at a tag and is >ber_buf. + * The octet *ber_ptr itself may get overwritten with + * a \0, to terminate the preceding element. + * When encoding data (writing it to the BerElement): + * ber_end End of allocated buffer - 1 (allowing a final \0). + * ber_ptr Last complete BER element (normally write cursor). + * ber_sos_ptr NULL or write cursor for incomplete sequence or set. + * ber_sos_inner offset(seq/set length octets) if ber_sos_ptr!=NULL. + * ber_tag Default tag for next ber_printf() element. + * ber_usertag Boolean set by ber_printf "!" if it sets ber_tag. + * ber_len Reused for ber_sos_inner. + * When output to a Sockbuf: + * ber_ptr End of encoded data to write. + * When input from a Sockbuf: + * See ber_get_next(). + */ + + /* Do not change the order of these 3 fields! see ber_get_next */ + ber_tag_t ber_tag; + ber_len_t ber_len; + ber_tag_t ber_usertag; + + char *ber_buf; + char *ber_ptr; + char *ber_end; + + char *ber_sos_ptr; +# define ber_sos_inner ber_len /* reused for binary compat */ + + char *ber_rwptr; + void *ber_memctx; +}; +#define LBER_VALID(ber) ((ber)->ber_valid==LBER_VALID_BERELEMENT) + +#define ber_pvt_ber_remaining(ber) ((ber)->ber_end - (ber)->ber_ptr) +#define ber_pvt_ber_total(ber) ((ber)->ber_end - (ber)->ber_buf) +#define ber_pvt_ber_write(ber) ((ber)->ber_ptr - (ber)->ber_buf) + +struct sockbuf { + struct lber_options sb_opts; + Sockbuf_IO_Desc *sb_iod; /* I/O functions */ +#define sb_valid sb_opts.lbo_valid +#define sb_options sb_opts.lbo_options +#define sb_debug sb_opts.lbo_debug + ber_socket_t sb_fd; + ber_len_t sb_max_incoming; + unsigned int sb_trans_needs_read:1; + unsigned int sb_trans_needs_write:1; +#ifdef LDAP_PF_LOCAL_SENDMSG + char sb_ungetlen; + char sb_ungetbuf[8]; +#endif +}; + +#define SOCKBUF_VALID( sb ) ( (sb)->sb_valid == LBER_VALID_SOCKBUF ) + + +/* + * decode.c, encode.c + */ + +/* Simplest OID max-DER-component to implement in both decode and encode */ +#define LBER_OID_COMPONENT_MAX ((unsigned long)-1 - 128) + + +/* + * io.c + */ +LBER_F( int ) +ber_realloc LDAP_P(( + BerElement *ber, + ber_len_t len )); + +LBER_F (char *) ber_start LDAP_P(( BerElement * )); +LBER_F (int) ber_len LDAP_P(( BerElement * )); +LBER_F (int) ber_ptrlen LDAP_P(( BerElement * )); +LBER_F (void) ber_rewind LDAP_P(( BerElement * )); + +/* + * bprint.c + */ +#define ber_log_printf ber_pvt_log_printf + +LBER_F( int ) +ber_log_bprint LDAP_P(( + int errlvl, + int loglvl, + const char *data, + ber_len_t len )); + +LBER_F( int ) +ber_log_dump LDAP_P(( + int errlvl, + int loglvl, + BerElement *ber, + int inout )); + +LBER_V (BER_LOG_FN) ber_int_log_proc; +LBER_V (FILE *) ber_pvt_err_file; + +/* memory.c */ + /* simple macros to realloc for now */ +LBER_V (BerMemoryFunctions *) ber_int_memory_fns; +LBER_F (char *) ber_strndup( LDAP_CONST char *, ber_len_t ); +LBER_F (char *) ber_strndup_x( LDAP_CONST char *, ber_len_t, void *ctx ); + +#define LBER_MALLOC(s) ber_memalloc((s)) +#define LBER_CALLOC(n,s) ber_memcalloc((n),(s)) +#define LBER_REALLOC(p,s) ber_memrealloc((p),(s)) +#define LBER_FREE(p) ber_memfree((p)) +#define LBER_VFREE(v) ber_memvfree((void**)(v)) +#define LBER_STRDUP(s) ber_strdup((s)) +#define LBER_STRNDUP(s,l) ber_strndup((s),(l)) + +/* sockbuf.c */ + +LBER_F( int ) +ber_int_sb_init LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_close LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_destroy LDAP_P(( Sockbuf *sb )); + +LBER_F( ber_slen_t ) +ber_int_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LBER_F( ber_slen_t ) +ber_int_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LDAP_END_DECL + +#endif /* _LBER_INT_H */ diff --git a/contrib/libs/openldap/libraries/liblber/memory.c b/contrib/libs/openldap/libraries/liblber/memory.c new file mode 100644 index 0000000000..c5083387dd --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/memory.c @@ -0,0 +1,825 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> + +#include "lber-int.h" + +#ifdef LDAP_MEMORY_TRACE +#include <stdio.h> +#endif + +#ifdef LDAP_MEMORY_DEBUG +/* + * LDAP_MEMORY_DEBUG should only be enabled for the purposes of + * debugging memory management within OpenLDAP libraries and slapd. + * + * It should only be enabled by an experienced developer as it causes + * the inclusion of numerous assert()'s, many of which may be triggered + * by a perfectly valid program. If LDAP_MEMORY_DEBUG & 2 is true, + * that includes asserts known to break both slapd and current clients. + * + * The code behind this macro is subject to change as needed to + * support this testing. + */ + +struct ber_mem_hdr { + ber_int_t bm_top; /* Pattern to detect buf overrun from prev buffer */ + ber_int_t bm_length; /* Length of user allocated area */ +#ifdef LDAP_MEMORY_TRACE + ber_int_t bm_sequence; /* Allocation sequence number */ +#endif + union bmu_align_u { /* Force alignment, pattern to detect back clobber */ + ber_len_t bmu_len_t; + ber_tag_t bmu_tag_t; + ber_int_t bmu_int_t; + + size_t bmu_size_t; + void * bmu_voidp; + double bmu_double; + long bmu_long; + long (*bmu_funcp)( double ); + unsigned char bmu_char[4]; + } ber_align; +#define bm_junk ber_align.bmu_len_t +#define bm_data ber_align.bmu_char[1] +#define bm_char ber_align.bmu_char +}; + +/* Pattern at top of allocated space */ +#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada) + +static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK }; + +/* Note sequence and ber_int_meminuse are counters, but are not + * thread safe. If you want to use these values for multithreaded applications, + * you must put mutexes around them, otherwise they will have incorrect values. + * When debugging, if you sort the debug output, the sequence number will + * put allocations/frees together. It is then a simple matter to write a script + * to find any allocations that don't have a buffer free function. + */ +long ber_int_meminuse = 0; +#ifdef LDAP_MEMORY_TRACE +static ber_int_t sequence = 0; +#endif + +/* Pattern placed just before user data */ +static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde }; +/* Pattern placed just after user data */ +static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca }; + +#define mbu_len sizeof(ber_int_mem_hdr.ber_align) + +/* Test if pattern placed just before user data is good */ +#define testdatatop(val) ( \ + *(val->bm_char+mbu_len-4)==toppattern[0] && \ + *(val->bm_char+mbu_len-3)==toppattern[1] && \ + *(val->bm_char+mbu_len-2)==toppattern[2] && \ + *(val->bm_char+mbu_len-1)==toppattern[3] ) + +/* Place pattern just before user data */ +#define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \ + *(val->bm_char+mbu_len-3)=toppattern[1]; \ + *(val->bm_char+mbu_len-2)=toppattern[2]; \ + *(val->bm_char+mbu_len-1)=toppattern[3]; + +/* Test if pattern placed just after user data is good */ +#define testend(val) ( *((unsigned char *)val+0)==endpattern[0] && \ + *((unsigned char *)val+1)==endpattern[1] && \ + *((unsigned char *)val+2)==endpattern[2] && \ + *((unsigned char *)val+3)==endpattern[3] ) + +/* Place pattern just after user data */ +#define setend(val) *((unsigned char *)val+0)=endpattern[0]; \ + *((unsigned char *)val+1)=endpattern[1]; \ + *((unsigned char *)val+2)=endpattern[2]; \ + *((unsigned char *)val+3)=endpattern[3]; + +#define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data) +#define BER_MEM_VALID(p) do { \ + assert( (p) != BER_MEM_BADADDR ); \ + assert( (p) != (void *) &ber_int_mem_hdr ); \ + } while(0) + +#else +#define BER_MEM_VALID(p) /* no-op */ +#endif + +BerMemoryFunctions *ber_int_memory_fns = NULL; + +void +ber_memfree_x( void *p, void *ctx ) +{ + if( p == NULL ) { + return; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + ber_int_meminuse -= mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* Fill the free space with poison */ + memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)); + free( mh ); +#else + free( p ); +#endif + return; + } + + assert( ber_int_memory_fns->bmf_free != 0 ); + + (*ber_int_memory_fns->bmf_free)( p, ctx ); +} + +void +ber_memfree( void *p ) +{ + ber_memfree_x(p, NULL); +} + +void +ber_memvfree_x( void **vec, void *ctx ) +{ + int i; + + if( vec == NULL ) { + return; + } + + BER_MEM_VALID( vec ); + + for ( i = 0; vec[i] != NULL; i++ ) { + ber_memfree_x( vec[i], ctx ); + } + + ber_memfree_x( vec, ctx ); +} + +void +ber_memvfree( void **vec ) +{ + ber_memvfree_x( vec, NULL ); +} + +void * +ber_memalloc_x( ber_len_t s, void *ctx ) +{ + void *new; + + if( s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( s != 0 ); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t)); + if( new ) + { + struct ber_mem_hdr *mh = new; + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; /* Count mem inuse */ + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* poison new memory */ + memset( (char *)&mh[1], 0xff, s); + + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = malloc( s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_malloc)( s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memalloc( ber_len_t s ) +{ + return ber_memalloc_x( s, NULL ); +} + +void * +ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx ) +{ + void *new; + + if( n == 0 || s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s + ? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)) + : NULL; + if( new ) + { + struct ber_mem_hdr *mh = new; + + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = n*s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = calloc( n, s ); +#endif + + } else { + new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memcalloc( ber_len_t n, ber_len_t s ) +{ + return ber_memcalloc_x( n, s, NULL ); +} + +void * +ber_memrealloc_x( void* p, ber_len_t s, void *ctx ) +{ + void *new = NULL; + + /* realloc(NULL,s) -> malloc(s) */ + if( p == NULL ) { + return ber_memalloc_x( s, ctx ); + } + + /* realloc(p,0) -> free(p) */ + if( s == 0 ) { + ber_memfree_x( p, ctx ); + return NULL; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + ber_int_t oldlen; + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + oldlen = mh->bm_length; + + p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) ); + if( p == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + return NULL; + } + + mh = p; + mh->bm_length = s; + setend( (char *)&mh[1] + mh->bm_length ); + if( s > oldlen ) { + /* poison any new memory */ + memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen); + } + + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + + ber_int_meminuse += s - oldlen; +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + return &mh[1]; +#else + new = realloc( p, s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memrealloc( void* p, ber_len_t s ) +{ + return ber_memrealloc_x( p, s, NULL ); +} + +void +ber_bvfree_x( struct berval *bv, void *ctx ) +{ + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + if ( bv->bv_val != NULL ) { + ber_memfree_x( bv->bv_val, ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvfree( struct berval *bv ) +{ + ber_bvfree_x( bv, NULL ); +} + +void +ber_bvecfree_x( struct berval **bv, void *ctx ) +{ + int i; + + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + /* count elements */ + for ( i = 0; bv[i] != NULL; i++ ) ; + + /* free in reverse order */ + for ( i--; i >= 0; i-- ) { + ber_bvfree_x( bv[i], ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvecfree( struct berval **bv ) +{ + ber_bvecfree_x( bv, NULL ); +} + +int +ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx ) +{ + ber_len_t i; + struct berval **new; + + if( *bvec == NULL ) { + if( bv == NULL ) { + /* nothing to add */ + return 0; + } + + *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx ); + + if( *bvec == NULL ) { + return -1; + } + + (*bvec)[0] = bv; + (*bvec)[1] = NULL; + + return 1; + } + + BER_MEM_VALID( bvec ); + + /* count entries */ + for ( i = 0; (*bvec)[i] != NULL; i++ ) { + /* EMPTY */; + } + + if( bv == NULL ) { + return i; + } + + new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx); + + if( new == NULL ) { + return -1; + } + + *bvec = new; + + (*bvec)[i++] = bv; + (*bvec)[i] = NULL; + + return i; +} + +int +ber_bvecadd( struct berval ***bvec, struct berval *bv ) +{ + return ber_bvecadd_x( bvec, bv, NULL ); +} + +struct berval * +ber_dupbv_x( + struct berval *dst, struct berval *src, void *ctx ) +{ + struct berval *new, tmp; + + if( src == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if ( dst ) { + new = &tmp; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + if ( src->bv_val == NULL ) { + new->bv_val = NULL; + new->bv_len = 0; + } else { + + if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) { + if ( !dst ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len ); + new->bv_val[src->bv_len] = '\0'; + new->bv_len = src->bv_len; + } + + if ( dst ) { + *dst = *new; + new = dst; + } + + return new; +} + +struct berval * +ber_dupbv( + struct berval *dst, struct berval *src ) +{ + return ber_dupbv_x( dst, src, NULL ); +} + +struct berval * +ber_bvdup( + struct berval *src ) +{ + return ber_dupbv_x( NULL, src, NULL ); +} + +struct berval * +ber_str2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len ? len : strlen( s ); + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_str2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_str2bv_x( s, len, dup, bv, NULL ); +} + +struct berval * +ber_mem2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len; + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) { + ber_memfree_x( new, ctx ); + } + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_mem2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_mem2bv_x( s, len, dup, bv, NULL ); +} + +char * +ber_strdup_x( LDAP_CONST char *s, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = strlen( s ) + 1; + if ( (p = ber_memalloc_x( len, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + } + + return p; +} + +char * +ber_strdup( LDAP_CONST char *s ) +{ + return ber_strdup_x( s, NULL ); +} + +ber_len_t +ber_strnlen( LDAP_CONST char *s, ber_len_t len ) +{ + ber_len_t l; + + for ( l = 0; l < len && s[l] != '\0'; l++ ) ; + + return l; +} + +char * +ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = ber_strnlen( s, l ); + if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + p[len] = '\0'; + } + + return p; +} + +char * +ber_strndup( LDAP_CONST char *s, ber_len_t l ) +{ + return ber_strndup_x( s, l, NULL ); +} + +/* + * dst is resized as required by src and the value of src is copied into dst + * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be + * alloc'ed with the context ctx + */ +struct berval * +ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx ) +{ + assert( dst != NULL ); + assert( !BER_BVISNULL( src ) ); + + if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) { + dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx ); + } + + AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 ); + dst->bv_len = src->bv_len; + + return dst; +} + +struct berval * +ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src ) +{ + return ber_bvreplace_x( dst, src, NULL ); +} + +void +ber_bvarray_free_x( BerVarray a, void *ctx ) +{ + int i; + + if (a) { + BER_MEM_VALID( a ); + + /* count elements */ + for (i=0; a[i].bv_val; i++) ; + + /* free in reverse order */ + for (i--; i>=0; i--) { + ber_memfree_x(a[i].bv_val, ctx); + } + + ber_memfree_x(a, ctx); + } +} + +void +ber_bvarray_free( BerVarray a ) +{ + ber_bvarray_free_x(a, NULL); +} + +int +ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx ) +{ + int i, j; + BerVarray new; + + if ( !src ) { + *dst = NULL; + return 0; + } + + for (i=0; !BER_BVISNULL( &src[i] ); i++) ; + new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx ); + if ( !new ) + return -1; + for (j=0; j<i; j++) { + ber_dupbv_x( &new[j], &src[j], ctx ); + if ( BER_BVISNULL( &new[j] )) { + ber_bvarray_free_x( new, ctx ); + return -1; + } + } + BER_BVZERO( &new[j] ); + *dst = new; + return 0; +} + +int +ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx ) +{ + int n; + + if ( *a == NULL ) { + if (bv == NULL) { + return 0; + } + n = 0; + + *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx ); + if ( *a == NULL ) { + return -1; + } + + } else { + BerVarray atmp; + BER_MEM_VALID( a ); + + for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) { + ; /* just count them */ + } + + if (bv == NULL) { + return n; + } + + atmp = (BerValue *) ber_memrealloc_x( (char *) *a, + (n + 2) * sizeof(BerValue), ctx ); + + if( atmp == NULL ) { + return -1; + } + + *a = atmp; + } + + (*a)[n++] = *bv; + (*a)[n].bv_val = NULL; + (*a)[n].bv_len = 0; + + return n; +} + +int +ber_bvarray_add( BerVarray *a, BerValue *bv ) +{ + return ber_bvarray_add_x( a, bv, NULL ); +} diff --git a/contrib/libs/openldap/libraries/liblber/options.c b/contrib/libs/openldap/libraries/liblber/options.c new file mode 100644 index 0000000000..a31c8ecf87 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/options.c @@ -0,0 +1,237 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/stdarg.h> +#include "lber-int.h" + +char ber_pvt_opt_on; /* used to get a non-NULL address for *_OPT_ON */ + +struct lber_options ber_int_options = { + LBER_UNINITIALIZED, 0, 0 }; + +static BerMemoryFunctions ber_int_memory_fns_datum; + +int +ber_get_option( + void *item, + int option, + void *outvalue) +{ + const BerElement *ber; + const Sockbuf *sb; + + if(outvalue == NULL) { + /* no place to get to */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + * (int *) outvalue = ber_int_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernel implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded ldap applications. + */ +#ifdef LDAP_MEMORY_DEBUG + * (int *) outvalue = ber_int_meminuse; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + + case LBER_OPT_LOG_PRINT_FILE: + *((FILE**)outvalue) = (FILE*)ber_pvt_err_file; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FN: + *(BER_LOG_PRINT_FN *)outvalue = ber_pvt_log_print; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_options; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_remaining(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_total(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_write(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + *((void **) outvalue) = ber->ber_memctx; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} + +int +ber_set_option( + void *item, + int option, + LDAP_CONST void *invalue) +{ + BerElement *ber; + Sockbuf *sb; + + if(invalue == NULL) { + /* no place to set from */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + ber_int_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FN: + ber_pvt_log_print = (BER_LOG_PRINT_FN) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FILE: + ber_pvt_err_file = (void *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernel implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded applications. + */ +#ifdef LDAP_MEMORY_DEBUG + ber_int_meminuse = * (int *) invalue; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + case LBER_OPT_MEMORY_FNS: + if ( ber_int_memory_fns == NULL ) + { + const BerMemoryFunctions *f = + (const BerMemoryFunctions *) invalue; + /* make sure all functions are provided */ + if(!( f->bmf_malloc && f->bmf_calloc + && f->bmf_realloc && f->bmf_free )) + { + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber_int_memory_fns = &ber_int_memory_fns_datum; + + AC_MEMCPY(ber_int_memory_fns, f, + sizeof(BerMemoryFunctions)); + + return LBER_OPT_SUCCESS; + } + break; + + case LBER_OPT_LOG_PROC: + ber_int_log_proc = (BER_LOG_FN)invalue; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + ber->ber_options = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + ber->ber_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_ptr[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + ber->ber_ptr = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + ber->ber_memctx = *(void **)invalue; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} diff --git a/contrib/libs/openldap/libraries/liblber/sockbuf.c b/contrib/libs/openldap/libraries/liblber/sockbuf.c new file mode 100644 index 0000000000..7bd322873c --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/sockbuf.c @@ -0,0 +1,988 @@ +/* sockbuf.c - i/o routines with support for adding i/o layers. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ + +#if defined( HAVE_FCNTL_H ) +#include <fcntl.h> +#endif + +#if defined( HAVE_SYS_FILIO_H ) +#include <sys/filio.h> +#elif defined( HAVE_SYS_IOCTL_H ) +#include <sys/ioctl.h> +#endif + +#include "lber-int.h" + +#ifndef LBER_MIN_BUFF_SIZE +#define LBER_MIN_BUFF_SIZE 4096 +#endif +#ifndef LBER_MAX_BUFF_SIZE +#define LBER_MAX_BUFF_SIZE (65536*256) +#endif +#ifndef LBER_DEFAULT_READAHEAD +#define LBER_DEFAULT_READAHEAD 16384 +#endif + +Sockbuf * +ber_sockbuf_alloc( void ) +{ + Sockbuf *sb; + + sb = LBER_CALLOC( 1, sizeof( Sockbuf ) ); + + if( sb == NULL ) return NULL; + + ber_int_sb_init( sb ); + return sb; +} + +void +ber_sockbuf_free( Sockbuf *sb ) +{ + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + ber_int_sb_close( sb ); + ber_int_sb_destroy( sb ); + LBER_FREE( sb ); +} + +/* Return values: -1: error, 0: no operation performed or the answer is false, + * 1: successful operation or the answer is true + */ +int +ber_sockbuf_ctrl( Sockbuf *sb, int opt, void *arg ) +{ + Sockbuf_IO_Desc *p; + int ret = 0; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + switch ( opt ) { + case LBER_SB_OPT_HAS_IO: + p = sb->sb_iod; + while ( p && p->sbiod_io != (Sockbuf_IO *)arg ) { + p = p->sbiod_next; + } + + if ( p ) { + ret = 1; + } + break; + + case LBER_SB_OPT_GET_FD: + if ( arg != NULL ) { + *((ber_socket_t *)arg) = sb->sb_fd; + } + ret = ( sb->sb_fd == AC_SOCKET_INVALID ? -1 : 1); + break; + + case LBER_SB_OPT_SET_FD: + sb->sb_fd = *((ber_socket_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_SET_NONBLOCK: + ret = ber_pvt_socket_set_nonblock( sb->sb_fd, arg != NULL) + ? -1 : 1; + break; + + case LBER_SB_OPT_DRAIN: { + /* Drain the data source to enable possible errors (e.g. + * TLS) to be propagated to the upper layers + */ + char buf[LBER_MIN_BUFF_SIZE]; + + do { + ret = ber_int_sb_read( sb, buf, sizeof( buf ) ); + } while ( ret == sizeof( buf ) ); + + ret = 1; + } break; + + case LBER_SB_OPT_NEEDS_READ: + ret = ( sb->sb_trans_needs_read ? 1 : 0 ); + break; + + case LBER_SB_OPT_NEEDS_WRITE: + ret = ( sb->sb_trans_needs_write ? 1 : 0 ); + break; + + case LBER_SB_OPT_GET_MAX_INCOMING: + if ( arg != NULL ) { + *((ber_len_t *)arg) = sb->sb_max_incoming; + } + ret = 1; + break; + + case LBER_SB_OPT_SET_MAX_INCOMING: + sb->sb_max_incoming = *((ber_len_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_UNGET_BUF: +#ifdef LDAP_PF_LOCAL_SENDMSG + sb->sb_ungetlen = ((struct berval *)arg)->bv_len; + if ( sb->sb_ungetlen <= sizeof( sb->sb_ungetbuf )) { + AC_MEMCPY( sb->sb_ungetbuf, ((struct berval *)arg)->bv_val, + sb->sb_ungetlen ); + ret = 1; + } else { + sb->sb_ungetlen = 0; + ret = -1; + } +#endif + break; + + default: + ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod, opt, arg ); + break; + } + + return ret; +} + +int +ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg ) +{ + Sockbuf_IO_Desc *d, *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sbio == NULL ) { + return -1; + } + + q = &sb->sb_iod; + p = *q; + while ( p && p->sbiod_level > layer ) { + q = &p->sbiod_next; + p = *q; + } + + d = LBER_MALLOC( sizeof( *d ) ); + if ( d == NULL ) { + return -1; + } + + d->sbiod_level = layer; + d->sbiod_sb = sb; + d->sbiod_io = sbio; + memset( &d->sbiod_pvt, '\0', sizeof( d->sbiod_pvt ) ); + d->sbiod_next = p; + *q = d; + + if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) ) { + return -1; + } + + return 0; +} + +int +ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer ) +{ + Sockbuf_IO_Desc *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sb->sb_iod == NULL ) { + return -1; + } + + q = &sb->sb_iod; + while ( *q != NULL ) { + p = *q; + if ( layer == p->sbiod_level && p->sbiod_io == sbio ) { + if ( p->sbiod_io->sbi_remove != NULL && + p->sbiod_io->sbi_remove( p ) < 0 ) + { + return -1; + } + *q = p->sbiod_next; + LBER_FREE( p ); + break; + } + q = &p->sbiod_next; + } + + return 0; +} + +void +ber_pvt_sb_buf_init( Sockbuf_Buf *buf ) +{ + buf->buf_base = NULL; + buf->buf_ptr = 0; + buf->buf_end = 0; + buf->buf_size = 0; +} + +void +ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf ) +{ + assert( buf != NULL); + + if (buf->buf_base) { + LBER_FREE( buf->buf_base ); + } + ber_pvt_sb_buf_init( buf ); +} + +int +ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize ) +{ + ber_len_t pw; + char *p; + + assert( buf != NULL ); + + for ( pw = LBER_MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) { + if (pw > LBER_MAX_BUFF_SIZE) return -1; + } + + if ( buf->buf_size < pw ) { + p = LBER_REALLOC( buf->buf_base, pw ); + if ( p == NULL ) return -1; + buf->buf_base = p; + buf->buf_size = pw; + } + return 0; +} + +ber_len_t +ber_pvt_sb_copy_out( Sockbuf_Buf *sbb, char *buf, ber_len_t len ) +{ + ber_len_t max; + + assert( buf != NULL ); + assert( sbb != NULL ); +#if 0 + assert( sbb->buf_size > 0 ); +#endif + + max = sbb->buf_end - sbb->buf_ptr; + max = ( max < len) ? max : len; + if ( max ) { + AC_MEMCPY( buf, sbb->buf_base + sbb->buf_ptr, max ); + sbb->buf_ptr += max; + if ( sbb->buf_ptr >= sbb->buf_end ) { + sbb->buf_ptr = sbb->buf_end = 0; + } + } + return max; +} + +ber_slen_t +ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out ) +{ + ber_len_t to_go; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + to_go = buf_out->buf_end - buf_out->buf_ptr; + assert( to_go > 0 ); + + for(;;) { + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf_out->buf_base + + buf_out->buf_ptr, to_go ); +#ifdef EINTR + if ((ret<0) && (errno==EINTR)) continue; +#endif + break; + } + + if ( ret <= 0 ) return ret; + + buf_out->buf_ptr += ret; + if (buf_out->buf_ptr == buf_out->buf_end) { + buf_out->buf_end = buf_out->buf_ptr = 0; + } + + return ret; +} + +int +ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb ) +{ +#ifdef HAVE_FCNTL + int flags = fcntl( sd, F_GETFL); + if( nb ) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl( sd, F_SETFL, flags ); + +#elif defined( FIONBIO ) + ioctl_t status = nb ? 1 : 0; + return ioctl( sd, FIONBIO, &status ); +#endif +} + +int +ber_int_sb_init( Sockbuf *sb ) +{ + assert( sb != NULL); + + sb->sb_valid=LBER_VALID_SOCKBUF; + sb->sb_options = 0; + sb->sb_debug = ber_int_debug; + sb->sb_fd = AC_SOCKET_INVALID; + sb->sb_iod = NULL; + sb->sb_trans_needs_read = 0; + sb->sb_trans_needs_write = 0; + + assert( SOCKBUF_VALID( sb ) ); + return 0; +} + +int +ber_int_sb_close( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + + p = sb->sb_iod; + while ( p ) { + if ( p->sbiod_io->sbi_close && p->sbiod_io->sbi_close( p ) < 0 ) { + return -1; + } + p = p->sbiod_next; + } + + sb->sb_fd = AC_SOCKET_INVALID; + + return 0; +} + +int +ber_int_sb_destroy( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + assert( SOCKBUF_VALID( sb ) ); + + while ( sb->sb_iod ) { + p = sb->sb_iod->sbiod_next; + ber_sockbuf_remove_io( sb, sb->sb_iod->sbiod_io, + sb->sb_iod->sbiod_level ); + sb->sb_iod = p; + } + + return ber_int_sb_init( sb ); +} + +ber_slen_t +ber_int_sb_read( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_read( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +ber_slen_t +ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_write( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +/* + * Support for TCP + */ + +static ber_slen_t +sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ + return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf, + len, NULL ); + +#elif defined( HAVE_PCNFS ) || \ + defined( HAVE_WINSOCK ) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined( HAVE_NCSA ) +/* + * NCSA Telnet TCP/IP stack (under DOS) + */ + return nread( sbiod->sbiod_sb->sb_fd, buf, len ); + +#else + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static ber_slen_t +sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ +#define MAX_WRITE 65535 + return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf, + (len<MAX_WRITE) ? len : MAX_WRITE ); + +#elif defined( HAVE_PCNFS) \ + || defined( HAVE_WINSOCK) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return send( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined(HAVE_NCSA) + return netwrite( sbiod->sbiod_sb->sb_fd, buf, len ); + +#elif defined(VMS) +/* + * VMS -- each write must be 64K or smaller + */ +#define MAX_WRITE 65535 + return write( sbiod->sbiod_sb->sb_fd, buf, + (len<MAX_WRITE) ? len : MAX_WRITE); +#else + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static int +sb_stream_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the socket descriptor */ +static int +sb_stream_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) { + sbiod->sbiod_sb->sb_fd = *((int *)arg); + } + return 0; +} + +static int +sb_stream_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_tcp = { + sb_stream_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_stream_ctrl, /* sbi_ctrl */ + sb_stream_read, /* sbi_read */ + sb_stream_write, /* sbi_write */ + sb_stream_close /* sbi_close */ +}; + + +/* + * Support for readahead (UDP needs it) + */ + +static int +sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) return -1; + + ber_pvt_sb_buf_init( p ); + + if ( arg == NULL ) { + ber_pvt_sb_grow_buffer( p, LBER_DEFAULT_READAHEAD ); + } else { + ber_pvt_sb_grow_buffer( p, *((int *)arg) ); + } + + sbiod->sbiod_pvt = p; + return 0; +} + +static int +sb_rdahead_remove( Sockbuf_IO_Desc *sbiod ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( p->buf_ptr != p->buf_end ) return -1; + + ber_pvt_sb_buf_destroy( (Sockbuf_Buf *)(sbiod->sbiod_pvt) ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + + return 0; +} + +static ber_slen_t +sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + Sockbuf_Buf *p; + ber_slen_t bufptr = 0, ret, max; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( sbiod->sbiod_next != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + assert( p->buf_size > 0 ); + + /* Are there anything left in the buffer? */ + ret = ber_pvt_sb_copy_out( p, buf, len ); + bufptr += ret; + len -= ret; + + if ( len == 0 ) return bufptr; + + max = p->buf_size - p->buf_end; + ret = 0; + while ( max > 0 ) { + ret = LBER_SBIOD_READ_NEXT( sbiod, p->buf_base + p->buf_end, + max ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + if ( ret < 0 ) { + return ( bufptr ? bufptr : ret ); + } + + p->buf_end += ret; + bufptr += ber_pvt_sb_copy_out( p, (char *) buf + bufptr, len ); + return bufptr; +} + +static ber_slen_t +sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_next != NULL ); + + return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); +} + +static int +sb_rdahead_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + + /* Just erase the buffer */ + ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt); + return 0; +} + +static int +sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + Sockbuf_Buf *p; + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p->buf_ptr != p->buf_end ) { + return 1; + } + + } else if ( opt == LBER_SB_OPT_SET_READAHEAD ) { + if ( p->buf_size >= *((ber_len_t *)arg) ) { + return 0; + } + return ( ber_pvt_sb_grow_buffer( p, *((int *)arg) ) ? + -1 : 1 ); + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +Sockbuf_IO ber_sockbuf_io_readahead = { + sb_rdahead_setup, /* sbi_setup */ + sb_rdahead_remove, /* sbi_remove */ + sb_rdahead_ctrl, /* sbi_ctrl */ + sb_rdahead_read, /* sbi_read */ + sb_rdahead_write, /* sbi_write */ + sb_rdahead_close /* sbi_close */ +}; + +/* + * Support for simple file IO + */ + +static ber_slen_t +sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#ifdef LDAP_PF_LOCAL_SENDMSG + if ( sbiod->sbiod_sb->sb_ungetlen ) { + ber_len_t blen = sbiod->sbiod_sb->sb_ungetlen; + if ( blen > len ) + blen = len; + AC_MEMCPY( buf, sbiod->sbiod_sb->sb_ungetbuf, blen ); + buf = (char *) buf + blen; + len -= blen; + sbiod->sbiod_sb->sb_ungetlen -= blen; + if ( sbiod->sbiod_sb->sb_ungetlen ) { + AC_MEMCPY( sbiod->sbiod_sb->sb_ungetbuf, + sbiod->sbiod_sb->sb_ungetbuf+blen, + sbiod->sbiod_sb->sb_ungetlen ); + } + if ( len == 0 ) + return blen; + } +#endif + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static ber_slen_t +sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static int +sb_fd_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the file descriptor */ +static int +sb_fd_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) + sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static int +sb_fd_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_fd = { + sb_fd_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_fd_ctrl, /* sbi_ctrl */ + sb_fd_read, /* sbi_read */ + sb_fd_write, /* sbi_write */ + sb_fd_close /* sbi_close */ +}; + +/* + * Debugging layer + */ + +static int +sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL ); + + if ( arg == NULL ) arg = "sockbuf_"; + + sbiod->sbiod_pvt = LBER_MALLOC( strlen( arg ) + 1 ); + if ( sbiod->sbiod_pvt == NULL ) return -1; + + strcpy( (char *)sbiod->sbiod_pvt, (char *)arg ); + return 0; +} + +static int +sb_debug_remove( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +sb_debug_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +sb_debug_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_READ_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld error=%s\n", (char *)sbiod->sbiod_pvt, + (long)len, AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld, got=%ld\n", (char *)sbiod->sbiod_pvt, + (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + return ret; +} + +static ber_slen_t +sb_debug_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld error=%s\n", + (char *)sbiod->sbiod_pvt, (long)len, + AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld, written=%ld\n", + (char *)sbiod->sbiod_pvt, (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + + return ret; +} + +Sockbuf_IO ber_sockbuf_io_debug = { + sb_debug_setup, /* sbi_setup */ + sb_debug_remove, /* sbi_remove */ + sb_debug_ctrl, /* sbi_ctrl */ + sb_debug_read, /* sbi_read */ + sb_debug_write, /* sbi_write */ + NULL /* sbi_close */ +}; + +#ifdef LDAP_CONNECTIONLESS + +/* + * Support for UDP (CLDAP) + * + * All I/O at this level must be atomic. For ease of use, the sb_readahead + * must be used above this module. All data reads and writes are prefixed + * with a sockaddr_storage containing the address of the remote entity. Upper levels + * must read and write this sockaddr_storage before doing the usual ber_printf/scanf + * operations on LDAP messages. + */ + +static int +sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( arg != NULL ) sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static ber_slen_t +sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + ber_socklen_t addrlen; + struct sockaddr *src; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + addrlen = sizeof( struct sockaddr_storage ); + src = buf; + buf = (char *) buf + addrlen; + len -= addrlen; + rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, &addrlen ); + + return rc > 0 ? rc+sizeof(struct sockaddr_storage) : rc; +} + +static ber_slen_t +sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + struct sockaddr *dst; + socklen_t dstsize; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + dst = buf; + buf = (char *) buf + sizeof( struct sockaddr_storage ); + len -= sizeof( struct sockaddr_storage ); + dstsize = dst->sa_family == AF_INET ? sizeof( struct sockaddr_in ) +#ifdef LDAP_PF_INET6 + : dst->sa_family == AF_INET6 ? sizeof( struct sockaddr_in6 ) +#endif + : sizeof( struct sockaddr_storage ); + rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst, dstsize ); + + if ( rc < 0 ) return -1; + + /* fake error if write was not atomic */ + if (rc < len) { +# ifdef EMSGSIZE + errno = EMSGSIZE; +# endif + return -1; + } + rc = len + sizeof(struct sockaddr_storage); + return rc; +} + +static int +sb_dgram_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +static int +sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_udp = +{ + sb_dgram_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_dgram_ctrl, /* sbi_ctrl */ + sb_dgram_read, /* sbi_read */ + sb_dgram_write, /* sbi_write */ + sb_dgram_close /* sbi_close */ +}; + +#endif /* LDAP_CONNECTIONLESS */ diff --git a/contrib/libs/openldap/libraries/liblber/stdio.c b/contrib/libs/openldap/libraries/liblber/stdio.c new file mode 100644 index 0000000000..6550690fad --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/stdio.c @@ -0,0 +1,244 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <lutil.h> +#include <unistd.h> + +#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC) +/* Write at most n characters to the buffer in str, return the + * number of chars written or -1 if the buffer would have been + * overflowed. + * + * This is portable to any POSIX-compliant system. We use pipe() + * to create a valid file descriptor, and then fdopen() it to get + * a valid FILE pointer. The user's buffer and size are assigned + * to the FILE pointer using setvbuf. Then we close the read side + * of the pipe to invalidate the descriptor. + * + * If the write arguments all fit into size n, the write will + * return successfully. If the write is too large, the stdio + * buffer will need to be flushed to the underlying file descriptor. + * The flush will fail because it is attempting to write to a + * broken pipe, and the write will be terminated. + * -- hyc, 2002-07-19 + */ +/* This emulation uses vfprintf; on OS/390 we're also emulating + * that function so it's more efficient just to have a separate + * version of vsnprintf there. + */ +#include <ac/signal.h> +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + int fds[2], res; + FILE *f; + RETSIGTYPE (*sig)(); + + if (pipe( fds )) return -1; + + f = fdopen( fds[1], "w" ); + if ( !f ) { + close( fds[1] ); + close( fds[0] ); + return -1; + } + setvbuf( f, str, _IOFBF, n ); + sig = signal( SIGPIPE, SIG_IGN ); + close( fds[0] ); + + res = vfprintf( f, fmt, ap ); + + fclose( f ); + signal( SIGPIPE, sig ); + if ( res > 0 && res < n ) { + res = vsprintf( str, fmt, ap ); + } + return res; +} +#endif + +#ifndef HAVE_SNPRINTF +int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = vsnprintf( str, n, fmt, ap ); + va_end( ap ); + return res; +} +#endif /* !HAVE_SNPRINTF */ + +#ifdef HAVE_EBCDIC +/* stdio replacements with ASCII/EBCDIC translation for OS/390. + * The OS/390 port depends on the CONVLIT compiler option being + * used to force character and string literals to be compiled in + * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the + * OS/390 ASCII-compatibility library. This library only supplies + * an ASCII version of sprintf, so other needed functions are + * provided here. + * + * All of the internal character manipulation is done in ASCII, + * but file I/O is EBCDIC, so we catch any stdio reading/writing + * of files here and do the translations. + */ + +#undef fputs +#undef fgets + +char *ber_pvt_fgets( char *s, int n, FILE *fp ) +{ + s = (char *)fgets( s, n, fp ); + if ( s ) __etoa( s ); + return s; +} + +int ber_pvt_fputs( const char *str, FILE *fp ) +{ + char buf[8192]; + + strncpy( buf, str, sizeof(buf) ); + __atoe( buf ); + return fputs( buf, fp ); +} + +/* The __LIBASCII doesn't include a working vsprintf, so we make do + * using just sprintf. This is a very simplistic parser that looks for + * format strings and uses sprintf to process them one at a time. + * Literal text is just copied straight to the destination. + * The result is appended to the destination string. The parser + * recognizes field-width specifiers and the 'l' qualifier; it + * may need to be extended to recognize other qualifiers but so + * far this seems to be enough. + */ +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + char *ptr, *pct, *s2, *f2, *end; + char fm2[64]; + int len, rem; + + ptr = (char *)fmt; + s2 = str; + fm2[0] = '%'; + if (n) { + end = str + n; + } else { + end = NULL; + } + + for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) { + len = pct-ptr; + if (end) { + rem = end-s2; + if (rem < 1) return -1; + if (rem < len) len = rem; + } + s2 = lutil_strncopy( s2, ptr, len ); + /* Did we cheat the length above? If so, bail out */ + if (len < pct-ptr) return -1; + for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++; + if (*pct == 'l') *f2++ = *pct++; + if (*pct == '%') { + *s2++ = '%'; + } else { + *f2++ = *pct; + *f2 = '\0'; + if (*pct == 's') { + char *ss = va_arg(ap, char *); + /* Attempt to limit sprintf output. This + * may be thrown off if field widths were + * specified for this string. + * + * If it looks like the string is too + * long for the remaining buffer, bypass + * sprintf and just copy what fits, then + * quit. + */ + if (end && strlen(ss) > (rem=end-s2)) { + strncpy(s2, ss, rem); + return -1; + } else { + s2 += sprintf(s2, fm2, ss); + } + } else { + s2 += sprintf(s2, fm2, va_arg(ap, int)); + } + } + ptr = pct + 1; + } + if (end) { + rem = end-s2; + if (rem > 0) { + len = strlen(ptr); + s2 = lutil_strncopy( s2, ptr, rem ); + rem -= len; + } + if (rem < 0) return -1; + } else { + s2 = lutil_strcopy( s2, ptr ); + } + return s2 - str; +} + +int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap ) +{ + return vsnprintf( str, 0, fmt, ap ); +} + +/* The fixed buffer size here is a problem, we don't know how + * to flush the buffer and keep printing if the msg is too big. + * Hopefully we never try to write something bigger than this + * in a log msg... + */ +int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap ) +{ + char buf[8192]; + int res; + + vsnprintf( buf, sizeof(buf), fmt, ap ); + __atoe( buf ); + res = fputs( buf, fp ); + if (res == EOF) res = -1; + return res; +} + +int ber_pvt_printf( const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( stdout, fmt, ap ); + va_end( ap ); + return res; +} + +int ber_pvt_fprintf( FILE *fp, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( fp, fmt, ap ); + va_end( ap ); + return res; +} +#endif diff --git a/contrib/libs/openldap/libraries/liblber/version.c b/contrib/libs/openldap/libraries/liblber/version.c new file mode 100644 index 0000000000..ee83d02705 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/version.c @@ -0,0 +1,22 @@ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +static const char copyright[] = +"Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.\n" +"COPYING RESTRICTIONS APPLY\n"; + +static const char __Version[] = +"@(#) $OpenLDAP: liblber.la 2.6.6 (" __DATE__ " " __TIME__ ") $\n" +"\topenldap\n"; + diff --git a/contrib/libs/openldap/libraries/liblber/ya.make b/contrib/libs/openldap/libraries/liblber/ya.make new file mode 100644 index 0000000000..1f1aac1fcc --- /dev/null +++ b/contrib/libs/openldap/libraries/liblber/ya.make @@ -0,0 +1,39 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE( + Bsd-Original-Uc-1986 AND + OLDAP-2.8 +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +ADDINCL( + contrib/libs/openldap/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLBER_LIBRARY + -DLDAPI_SOCK=\"/run/openldap/ldapi\" +) + +SRCS( + assert.c + bprint.c + debug.c + decode.c + encode.c + io.c + memory.c + options.c + sockbuf.c + stdio.c + version.c +) + +END() diff --git a/contrib/libs/openldap/libraries/libldap/abandon.c b/contrib/libs/openldap/libraries/libldap/abandon.c new file mode 100644 index 0000000000..6161cc9cb4 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/abandon.c @@ -0,0 +1,458 @@ +/* abandon.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * An abandon request looks like this: + * AbandonRequest ::= [APPLICATION 16] MessageID + * and has no response. (Source: RFC 4511) + */ +#include "lutil.h" + +static int +do_abandon( + LDAP *ld, + ber_int_t origid, + LDAPRequest *lr, + LDAPControl **sctrls, + int sendabandon ); + +/* + * ldap_abandon_ext - perform an ldap extended abandon operation. + * + * Parameters: + * ld LDAP descriptor + * msgid The message id of the operation to abandon + * scntrls Server Controls + * ccntrls Client Controls + * + * ldap_abandon_ext returns a LDAP error code. + * (LDAP_SUCCESS if everything went ok) + * + * Example: + * ldap_abandon_ext( ld, msgid, scntrls, ccntrls ); + */ +int +ldap_abandon_ext( + LDAP *ld, + int msgid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + + Debug1( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid ); + + /* check client controls */ + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + + rc = ldap_int_client_controls( ld, cctrls ); + if ( rc == LDAP_SUCCESS ) { + rc = do_abandon( ld, msgid, NULL, sctrls, 1 ); + } + + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + + return rc; +} + + +/* + * ldap_abandon - perform an ldap abandon operation. Parameters: + * + * ld LDAP descriptor + * msgid The message id of the operation to abandon + * + * ldap_abandon returns 0 if everything went ok, -1 otherwise. + * + * Example: + * ldap_abandon( ld, msgid ); + */ +int +ldap_abandon( LDAP *ld, int msgid ) +{ + Debug1( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid ); + return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS + ? 0 : -1; +} + + +int +ldap_pvt_discard( + LDAP *ld, + ber_int_t msgid ) +{ + int rc; + + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + rc = do_abandon( ld, msgid, NULL, NULL, 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + return rc; +} + +static int +do_abandon( + LDAP *ld, + ber_int_t origid, + LDAPRequest *lr, + LDAPControl **sctrls, + int sendabandon ) +{ + BerElement *ber; + int i, err; + ber_int_t msgid = origid; + Sockbuf *sb; + LDAPRequest needle = {0}; + + needle.lr_msgid = origid; + + if ( lr != NULL ) { + msgid = lr->lr_msgid; + Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n", + origid, msgid ); + } else if ( (lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp )) != NULL ) { + Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n", + origid, msgid ); + if ( lr->lr_parent != NULL ) { + /* don't let caller abandon child requests! */ + ld->ld_errno = LDAP_PARAM_ERROR; + return( LDAP_PARAM_ERROR ); + } + msgid = lr->lr_msgid; + } + + if ( lr != NULL ) { + LDAPRequest **childp = &lr->lr_child; + + needle.lr_msgid = lr->lr_msgid; + + if ( lr->lr_status != LDAP_REQST_INPROGRESS ) { + /* no need to send abandon message */ + sendabandon = 0; + } + + while ( *childp ) { + /* Abandon children */ + LDAPRequest *child = *childp; + + (void)do_abandon( ld, lr->lr_origid, child, sctrls, sendabandon ); + if ( *childp == child ) { + childp = &child->lr_refnext; + } + } + } + + /* ldap_msgdelete locks the res_mutex. Give up the req_mutex + * while we're in there. + */ + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + err = ldap_msgdelete( ld, msgid ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + if ( err == 0 ) { + ld->ld_errno = LDAP_SUCCESS; + return LDAP_SUCCESS; + } + + /* fetch again the request that we are abandoning */ + if ( lr != NULL ) { + lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp ); + } + + err = 0; + if ( sendabandon ) { + if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) { + /* not connected */ + err = -1; + ld->ld_errno = LDAP_SERVER_DOWN; + + } else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) { + /* BER element allocation failed */ + err = -1; + ld->ld_errno = LDAP_NO_MEMORY; + + } else { + /* + * We already have the mutex in LDAP_R_COMPILE, so + * don't try to get it again. + * LDAP_NEXT_MSGID(ld, i); + */ + + LDAP_NEXT_MSGID(ld, i); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage sa = {0}; + /* dummy, filled with ldo_peer in request.c */ + err = ber_write( ber, (char *) &sa, sizeof(sa), 0 ); + } + if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == + LDAP_VERSION2 ) + { + char *dn; + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{isti", /* '}' */ + i, dn, + LDAP_REQ_ABANDON, msgid ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + } else +#endif + { + /* create a message to send */ + err = ber_printf( ber, "{iti", /* '}' */ + i, + LDAP_REQ_ABANDON, msgid ); + } + + if ( err == -1 ) { + /* encoding error */ + ld->ld_errno = LDAP_ENCODING_ERROR; + + } else { + /* Put Server Controls */ + if ( ldap_int_put_controls( ld, sctrls, ber ) + != LDAP_SUCCESS ) + { + err = -1; + + } else { + /* close '{' */ + err = ber_printf( ber, /*{*/ "N}" ); + + if ( err == -1 ) { + /* encoding error */ + ld->ld_errno = LDAP_ENCODING_ERROR; + } + } + } + + if ( err == -1 ) { + ber_free( ber, 1 ); + + } else { + /* send the message */ + if ( lr != NULL ) { + assert( lr->lr_conn != NULL ); + sb = lr->lr_conn->lconn_sb; + } else { + sb = ld->ld_sb; + } + + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) { + ld->ld_errno = LDAP_SERVER_DOWN; + err = -1; + } else { + err = 0; + } + } + } + } + + if ( lr != NULL ) { + LDAPConn *lc; + int freeconn = 0; + if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) { + freeconn = 1; + lc = lr->lr_conn; + } + if ( origid == msgid ) { + ldap_free_request( ld, lr ); + + } else { + lr->lr_abandoned = 1; + } + + if ( freeconn ) { + /* release ld_req_mutex while grabbing ld_conn_mutex to + * prevent deadlock. + */ + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + ldap_free_connection( ld, lc, 0, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + } + } + + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + + /* use bisection */ + i = 0; + if ( ld->ld_nabandoned == 0 || + ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 ) + { + ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i ); + } + + if ( err != -1 ) { + ld->ld_errno = LDAP_SUCCESS; + } + + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return( ld->ld_errno ); +} + +/* + * ldap_int_bisect_find + * + * args: + * v: array of length n (in) + * n: length of array v (in) + * id: value to look for (in) + * idxp: pointer to location of value/insert point + * + * return: + * 0: not found + * 1: found + * -1: error + */ +int +ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp ) +{ + int begin, + end, + rc = 0; + + assert( id >= 0 ); + + begin = 0; + end = n - 1; + + if ( n <= 0 || id < v[ begin ] ) { + *idxp = 0; + + } else if ( id > v[ end ] ) { + *idxp = n; + + } else { + int pos; + ber_int_t curid; + + do { + pos = (begin + end)/2; + curid = v[ pos ]; + + if ( id < curid ) { + end = pos - 1; + + } else if ( id > curid ) { + begin = ++pos; + + } else { + /* already abandoned? */ + rc = 1; + break; + } + } while ( end >= begin ); + + *idxp = pos; + } + + return rc; +} + +/* + * ldap_int_bisect_insert + * + * args: + * vp: pointer to array of length *np (in/out) + * np: pointer to length of array *vp (in/out) + * id: value to insert (in) + * idx: location of insert point (as computed by ldap_int_bisect_find()) + * + * return: + * 0: inserted + * -1: error + */ +int +ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx ) +{ + ber_int_t *v; + ber_len_t n; + int i; + + assert( vp != NULL ); + assert( np != NULL ); + assert( idx >= 0 ); + assert( (unsigned) idx <= *np ); + + n = *np; + + v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) ); + if ( v == NULL ) { + return -1; + } + *vp = v; + + for ( i = n; i > idx; i-- ) { + v[ i ] = v[ i - 1 ]; + } + v[ idx ] = id; + ++(*np); + + return 0; +} + +/* + * ldap_int_bisect_delete + * + * args: + * vp: pointer to array of length *np (in/out) + * np: pointer to length of array *vp (in/out) + * id: value to delete (in) + * idx: location of value to delete (as computed by ldap_int_bisect_find()) + * + * return: + * 0: deleted + */ +int +ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx ) +{ + ber_int_t *v; + ber_len_t i, n; + + assert( vp != NULL ); + assert( np != NULL ); + assert( idx >= 0 ); + assert( (unsigned) idx < *np ); + + v = *vp; + + assert( v[ idx ] == id ); + + --(*np); + n = *np; + + for ( i = idx; i < n; i++ ) { + v[ i ] = v[ i + 1 ]; + } + + return 0; +} diff --git a/contrib/libs/openldap/libraries/libldap/account_usability.c b/contrib/libs/openldap/libraries/libldap/account_usability.c new file mode 100644 index 0000000000..9cac6e6416 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/account_usability.c @@ -0,0 +1,128 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2004-2022 The OpenLDAP Foundation. + * Portions Copyright 2004 Hewlett-Packard Company. + * Portions Copyright 2004 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software, based on prior work by Neil Dunbar (HP). + * This work was sponsored by the Hewlett-Packard Company. + */ + +#include "portable.h" + +#include "ldap-int.h" + +#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY + +int +ldap_create_accountusability_control( LDAP *ld, + LDAPControl **ctrlp ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrlp != NULL ); + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_ACCOUNT_USABILITY, + 0, NULL, 0, ctrlp ); + + return ld->ld_errno; +} + +int +ldap_parse_accountusability_control( + LDAP *ld, + LDAPControl *ctrl, + int *availablep, + LDAPAccountUsability *usabilityp ) +{ + BerElement *ber; + int available = 0; + ber_tag_t tag; + ber_len_t berLen; + char *last; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if ( !ctrl->ldctl_value.bv_val ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + tag = ber_peek_tag( ber, &berLen ); + + if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ) { + available = 1; + + if ( usabilityp != NULL ) { + if (ber_get_int( ber, &usabilityp->seconds_remaining ) == LBER_DEFAULT) goto exit; + } + } else if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ) { + available = 0; + LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 }; + + ber_skip_tag( ber, &berLen ); + while ( (tag = ber_peek_tag( ber, &berLen )) != LBER_DEFAULT ) { + switch (tag) { + case LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE: + if (ber_get_boolean( ber, &more_info.inactive ) == LBER_DEFAULT) goto exit; + break; + case LDAP_TAG_X_ACCOUNT_USABILITY_RESET: + if (ber_get_boolean( ber, &more_info.reset ) == LBER_DEFAULT) goto exit; + break; + case LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED: + if (ber_get_boolean( ber, &more_info.expired ) == LBER_DEFAULT) goto exit; + break; + case LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE: + if (ber_get_int( ber, &more_info.remaining_grace ) == LBER_DEFAULT) goto exit; + break; + case LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK: + if (ber_get_int( ber, &more_info.seconds_before_unlock ) == LBER_DEFAULT) goto exit; + break; + default: + goto exit; + } + } + if ( usabilityp != NULL ) { + usabilityp->more_info = more_info; + } + } else { + goto exit; + } + if ( availablep != NULL ) { + *availablep = available; + } + + ber_free(ber, 1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); + + exit: + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); +} + +#endif /* LDAP_CONTROL_X_ACCOUNT_USABILITY */ diff --git a/contrib/libs/openldap/libraries/libldap/add.c b/contrib/libs/openldap/libraries/libldap/add.c new file mode 100644 index 0000000000..7f63fa2b77 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/add.c @@ -0,0 +1,263 @@ +/* add.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* An LDAP Add Request/Response looks like this: + * AddRequest ::= [APPLICATION 8] SEQUENCE { + * entry LDAPDN, + * attributes AttributeList } + * + * AttributeList ::= SEQUENCE OF attribute Attribute + * + * Attribute ::= PartialAttribute(WITH COMPONENTS { + * ..., + * vals (SIZE(1..MAX))}) + * + * PartialAttribute ::= SEQUENCE { + * type AttributeDescription, + * vals SET OF value AttributeValue } + * + * AttributeDescription ::= LDAPString + * -- Constrained to <attributedescription> [RFC4512] + * + * AttributeValue ::= OCTET STRING + * + * AddResponse ::= [APPLICATION 9] LDAPResult + * (Source: RFC 4511) + */ + +/* + * ldap_add - initiate an ldap add operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the entry to add + * mods List of attributes for the entry. This is a null- + * terminated array of pointers to LDAPMod structures. + * only the type and values in the structures need be + * filled in. + * + * Example: + * LDAPMod *attrs[] = { + * { 0, "cn", { "babs jensen", "babs", 0 } }, + * { 0, "sn", { "jensen", 0 } }, + * { 0, "objectClass", { "person", 0 } }, + * 0 + * } + * msgid = ldap_add( ld, dn, attrs ); + */ +int +ldap_add( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs ) +{ + int rc; + int msgid; + + rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return -1; + + return msgid; +} + + +BerElement * +ldap_build_add_req( + LDAP *ld, + const char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int i, rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID(ld, *msgidp); + rc = ber_printf( ber, "{it{s{", /* '}}}' */ + *msgidp, LDAP_REQ_ADD, dn ); + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* allow attrs to be NULL ("touch"; should fail...) */ + if ( attrs ) { + /* for each attribute in the entry... */ + for ( i = 0; attrs[i] != NULL; i++ ) { + if ( ( attrs[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + int j; + + if ( attrs[i]->mod_bvalues == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + for ( j = 0; attrs[i]->mod_bvalues[ j ] != NULL; j++ ) { + if ( attrs[i]->mod_bvalues[ j ]->bv_val == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + + rc = ber_printf( ber, "{s[V]N}", attrs[i]->mod_type, + attrs[i]->mod_bvalues ); + + } else { + if ( attrs[i]->mod_values == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + rc = ber_printf( ber, "{s[v]N}", attrs[i]->mod_type, + attrs[i]->mod_values ); + } + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + } + + if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_add_ext - initiate an ldap extended add operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the entry to add + * mods List of attributes for the entry. This is a null- + * terminated array of pointers to LDAPMod structures. + * only the type and values in the structures need be + * filled in. + * sctrl Server Controls + * cctrl Client Controls + * msgidp Message ID pointer + * + * Example: + * LDAPMod *attrs[] = { + * { 0, "cn", { "babs jensen", "babs", 0 } }, + * { 0, "sn", { "jensen", 0 } }, + * { 0, "objectClass", { "person", 0 } }, + * 0 + * } + * rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid ); + */ +int +ldap_add_ext( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_add_ext\n" ); + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_add_req( ld, dn, attrs, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_ADD, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_add_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int msgid, rc; + LDAPMessage *res; + + rc = ldap_add_ext( ld, dn, attrs, sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_add_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs ) +{ + return ldap_add_ext_s( ld, dn, attrs, NULL, NULL ); +} + diff --git a/contrib/libs/openldap/libraries/libldap/addentry.c b/contrib/libs/openldap/libraries/libldap/addentry.c new file mode 100644 index 0000000000..14a8879c91 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/addentry.c @@ -0,0 +1,72 @@ +/* addentry.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + LDAPMessage *tmp, *prev = NULL; + + assert( list != NULL ); + assert( e != NULL ); + + for ( tmp = *list; tmp != NULL && tmp != e; tmp = tmp->lm_chain ) + prev = tmp; + + if ( tmp == NULL ) + return( NULL ); + + if ( prev == NULL ) { + if ( tmp->lm_chain ) + tmp->lm_chain->lm_chain_tail = (*list)->lm_chain_tail; + *list = tmp->lm_chain; + } else { + prev->lm_chain = tmp->lm_chain; + if ( prev->lm_chain == NULL ) + (*list)->lm_chain_tail = prev; + } + tmp->lm_chain = NULL; + + return( tmp ); +} + +void +ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + assert( list != NULL ); + assert( e != NULL ); + + e->lm_chain = *list; + if ( *list ) + e->lm_chain_tail = (*list)->lm_chain_tail; + else + e->lm_chain_tail = e; + *list = e; +} diff --git a/contrib/libs/openldap/libraries/libldap/assertion.c b/contrib/libs/openldap/libraries/libldap/assertion.c new file mode 100644 index 0000000000..97ddb61616 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/assertion.c @@ -0,0 +1,100 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_create_assertion_control_value( + LDAP *ld, + char *assertion, + struct berval *value ) +{ + BerElement *ber = NULL; + int err; + + ld->ld_errno = LDAP_SUCCESS; + + if ( assertion == NULL || assertion[ 0 ] == '\0' ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if ( value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + BER_BVZERO( value ); + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + err = ldap_pvt_put_filter( ber, assertion ); + if ( err < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + err = ber_flatten2( ber, value, 1 ); + if ( err < 0 ) { + ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_assertion_control( + LDAP *ld, + char *assertion, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_assertion_control_value( ld, + assertion, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_ASSERT, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + diff --git a/contrib/libs/openldap/libraries/libldap/avl.c b/contrib/libs/openldap/libraries/libldap/avl.c new file mode 100644 index 0000000000..fd22c7f0de --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/avl.c @@ -0,0 +1,671 @@ +/* avl.c - routines to implement an avl tree */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Howard Y. Chu + * Hallvard B. Furuseth + * Kurt D. Zeilenga + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memrealloc realloc +#define ber_memfree free +#else +#include "lber.h" +#endif + +#define AVL_INTERNAL +#include "ldap_avl.h" + +/* Maximum tree depth this host's address space could support */ +#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT) + +static const int avl_bfs[] = {LH, RH}; + +/* + * ldap_avl_insert -- insert a node containing data data into the avl tree + * with root root. fcmp is a function to call to compare the data portion + * of two nodes. it should take two arguments and return <, >, or == 0, + * depending on whether its first argument is <, >, or == its second + * argument (like strcmp, e.g.). fdup is a function to call when a duplicate + * node is inserted. it should return 0, or -1 and its return value + * will be the return value from ldap_avl_insert in the case of a duplicate node. + * the function will be called with the original node's data as its first + * argument and with the incoming duplicate node's data as its second + * argument. this could be used, for example, to keep a count with each + * node. + * + * NOTE: this routine may malloc memory + */ +int +ldap_avl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup ) +{ + Avlnode *t, *p, *s, *q, *r; + int a, cmp, ncmp; + + if ( *root == NULL ) { + if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + r->avl_link[0] = r->avl_link[1] = NULL; + r->avl_data = data; + r->avl_bits[0] = r->avl_bits[1] = AVL_CHILD; + r->avl_bf = EH; + *root = r; + + return( 0 ); + } + + t = NULL; + s = p = *root; + + /* find insertion point */ + while (1) { + cmp = fcmp( data, p->avl_data ); + if ( cmp == 0 ) + return (*fdup)( p->avl_data, data ); + + cmp = (cmp > 0); + q = p->avl_link[cmp]; + if (q == NULL) { + /* insert */ + if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + q->avl_link[0] = q->avl_link[1] = NULL; + q->avl_data = data; + q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD; + q->avl_bf = EH; + + p->avl_link[cmp] = q; + break; + } else if ( q->avl_bf ) { + t = p; + s = q; + } + p = q; + } + + /* adjust balance factors */ + cmp = fcmp( data, s->avl_data ) > 0; + r = p = s->avl_link[cmp]; + a = avl_bfs[cmp]; + + while ( p != q ) { + cmp = fcmp( data, p->avl_data ) > 0; + p->avl_bf = avl_bfs[cmp]; + p = p->avl_link[cmp]; + } + + /* checks and balances */ + + if ( s->avl_bf == EH ) { + s->avl_bf = a; + return 0; + } else if ( s->avl_bf == -a ) { + s->avl_bf = EH; + return 0; + } else if ( s->avl_bf == a ) { + cmp = (a > 0); + ncmp = !cmp; + if ( r->avl_bf == a ) { + /* single rotation */ + p = r; + s->avl_link[cmp] = r->avl_link[ncmp]; + r->avl_link[ncmp] = s; + s->avl_bf = 0; + r->avl_bf = 0; + } else if ( r->avl_bf == -a ) { + /* double rotation */ + p = r->avl_link[ncmp]; + r->avl_link[ncmp] = p->avl_link[cmp]; + p->avl_link[cmp] = r; + s->avl_link[cmp] = p->avl_link[ncmp]; + p->avl_link[ncmp] = s; + + if ( p->avl_bf == a ) { + s->avl_bf = -a; + r->avl_bf = 0; + } else if ( p->avl_bf == -a ) { + s->avl_bf = 0; + r->avl_bf = a; + } else { + s->avl_bf = 0; + r->avl_bf = 0; + } + p->avl_bf = 0; + } + /* Update parent */ + if ( t == NULL ) + *root = p; + else if ( s == t->avl_right ) + t->avl_right = p; + else + t->avl_left = p; + } + + return 0; +} + +void* +ldap_avl_delete( Avlnode **root, void* data, AVL_CMP fcmp ) +{ + Avlnode *p, *q, *r, *top; + int side, side_bf, shorter, nside; + + /* parent stack */ + Avlnode *pptr[MAX_TREE_DEPTH]; + unsigned char pdir[MAX_TREE_DEPTH]; + int depth = 0; + + if ( *root == NULL ) + return NULL; + + p = *root; + + while (1) { + side = fcmp( data, p->avl_data ); + if ( !side ) + break; + side = ( side > 0 ); + pdir[depth] = side; + pptr[depth++] = p; + + p = p->avl_link[side]; + if ( p == NULL ) + return p; + } + data = p->avl_data; + + /* If this node has two children, swap so we are deleting a node with + * at most one child. + */ + if ( p->avl_link[0] && p->avl_link[1] ) { + + /* find the immediate predecessor <q> */ + q = p->avl_link[0]; + side = depth; + pdir[depth++] = 0; + while (q->avl_link[1]) { + pdir[depth] = 1; + pptr[depth++] = q; + q = q->avl_link[1]; + } + /* swap links */ + r = p->avl_link[0]; + p->avl_link[0] = q->avl_link[0]; + q->avl_link[0] = r; + + q->avl_link[1] = p->avl_link[1]; + p->avl_link[1] = NULL; + + q->avl_bf = p->avl_bf; + + /* fix stack positions: old parent of p points to q */ + pptr[side] = q; + if ( side ) { + r = pptr[side-1]; + r->avl_link[pdir[side-1]] = q; + } else { + *root = q; + } + /* new parent of p points to p */ + if ( depth-side > 1 ) { + r = pptr[depth-1]; + r->avl_link[1] = p; + } else { + q->avl_link[0] = p; + } + } + + /* now <p> has at most one child, get it */ + q = p->avl_link[0] ? p->avl_link[0] : p->avl_link[1]; + + ber_memfree( p ); + + if ( !depth ) { + *root = q; + return data; + } + + /* set the child into p's parent */ + depth--; + p = pptr[depth]; + side = pdir[depth]; + p->avl_link[side] = q; + + top = NULL; + shorter = 1; + + while ( shorter ) { + p = pptr[depth]; + side = pdir[depth]; + nside = !side; + side_bf = avl_bfs[side]; + + /* case 1: height unchanged */ + if ( p->avl_bf == EH ) { + /* Tree is now heavier on opposite side */ + p->avl_bf = avl_bfs[nside]; + shorter = 0; + + } else if ( p->avl_bf == side_bf ) { + /* case 2: taller subtree shortened, height reduced */ + p->avl_bf = EH; + } else { + /* case 3: shorter subtree shortened */ + if ( depth ) + top = pptr[depth-1]; /* p->parent; */ + else + top = NULL; + /* set <q> to the taller of the two subtrees of <p> */ + q = p->avl_link[nside]; + if ( q->avl_bf == EH ) { + /* case 3a: height unchanged, single rotate */ + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + shorter = 0; + q->avl_bf = side_bf; + p->avl_bf = (- side_bf); + + } else if ( q->avl_bf == p->avl_bf ) { + /* case 3b: height reduced, single rotate */ + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + shorter = 1; + q->avl_bf = EH; + p->avl_bf = EH; + + } else { + /* case 3c: height reduced, balance factors opposite */ + r = q->avl_link[side]; + q->avl_link[side] = r->avl_link[nside]; + r->avl_link[nside] = q; + + p->avl_link[nside] = r->avl_link[side]; + r->avl_link[side] = p; + + if ( r->avl_bf == side_bf ) { + q->avl_bf = (- side_bf); + p->avl_bf = EH; + } else if ( r->avl_bf == (- side_bf)) { + q->avl_bf = EH; + p->avl_bf = side_bf; + } else { + q->avl_bf = EH; + p->avl_bf = EH; + } + r->avl_bf = EH; + q = r; + } + /* a rotation has caused <q> (or <r> in case 3c) to become + * the root. let <p>'s former parent know this. + */ + if ( top == NULL ) { + *root = q; + } else if (top->avl_link[0] == p) { + top->avl_link[0] = q; + } else { + top->avl_link[1] = q; + } + /* end case 3 */ + p = q; + } + if ( !depth ) + break; + depth--; + } /* end while(shorter) */ + + return data; +} + +static int +avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( root->avl_left != 0 ) + if ( avl_inapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( (*fn)( root->avl_data, arg ) == stopflag ) + return( stopflag ); + + if ( root->avl_right == 0 ) + return( AVL_NOMORE ); + else + return( avl_inapply( root->avl_right, fn, arg, stopflag ) ); +} + +static int +avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( root->avl_left != 0 ) + if ( avl_postapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( root->avl_right != 0 ) + if ( avl_postapply( root->avl_right, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + return( (*fn)( root->avl_data, arg ) ); +} + +static int +avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( (*fn)( root->avl_data, arg ) == stopflag ) + return( stopflag ); + + if ( root->avl_left != 0 ) + if ( avl_preapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( root->avl_right == 0 ) + return( AVL_NOMORE ); + else + return( avl_preapply( root->avl_right, fn, arg, stopflag ) ); +} + +/* + * ldap_avl_apply -- avl tree root is traversed, function fn is called with + * arguments arg and the data portion of each node. if fn returns stopflag, + * the traversal is cut short, otherwise it continues. Do not use -6 as + * a stopflag, as this is what is used to indicate the traversal ran out + * of nodes. + */ + +int +ldap_avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type ) +{ + switch ( type ) { + case AVL_INORDER: + return( avl_inapply( root, fn, arg, stopflag ) ); + case AVL_PREORDER: + return( avl_preapply( root, fn, arg, stopflag ) ); + case AVL_POSTORDER: + return( avl_postapply( root, fn, arg, stopflag ) ); + default: + fprintf( stderr, "Invalid traversal type %d\n", type ); + return( -1 ); + } + + /* NOTREACHED */ +} + +/* + * ldap_avl_prefixapply - traverse avl tree root, applying function fprefix + * to any nodes that match. fcmp is called with data as its first arg + * and the current node's data as its second arg. it should return + * 0 if they match, < 0 if data is less, and > 0 if data is greater. + * the idea is to efficiently find all nodes that are prefixes of + * some key... Like ldap_avl_apply, this routine also takes a stopflag + * and will return prematurely if fmatch returns this value. Otherwise, + * AVL_NOMORE is returned. + */ + +int +ldap_avl_prefixapply( + Avlnode *root, + void* data, + AVL_CMP fmatch, + void* marg, + AVL_CMP fcmp, + void* carg, + int stopflag +) +{ + int cmp; + + if ( root == 0 ) + return( AVL_NOMORE ); + + cmp = (*fcmp)( data, root->avl_data /* , carg */); + if ( cmp == 0 ) { + if ( (*fmatch)( root->avl_data, marg ) == stopflag ) + return( stopflag ); + + if ( root->avl_left != 0 ) + if ( ldap_avl_prefixapply( root->avl_left, data, fmatch, + marg, fcmp, carg, stopflag ) == stopflag ) + return( stopflag ); + + if ( root->avl_right != 0 ) + return( ldap_avl_prefixapply( root->avl_right, data, fmatch, + marg, fcmp, carg, stopflag ) ); + else + return( AVL_NOMORE ); + + } else if ( cmp < 0 ) { + if ( root->avl_left != 0 ) + return( ldap_avl_prefixapply( root->avl_left, data, fmatch, + marg, fcmp, carg, stopflag ) ); + } else { + if ( root->avl_right != 0 ) + return( ldap_avl_prefixapply( root->avl_right, data, fmatch, + marg, fcmp, carg, stopflag ) ); + } + + return( AVL_NOMORE ); +} + +/* + * ldap_avl_free -- traverse avltree root, freeing the memory it is using. + * the dfree() is called to free the data portion of each node. The + * number of items actually freed is returned. + */ + +int +ldap_avl_free( Avlnode *root, AVL_FREE dfree ) +{ + int nleft, nright; + + if ( root == 0 ) + return( 0 ); + + nleft = nright = 0; + if ( root->avl_left != 0 ) + nleft = ldap_avl_free( root->avl_left, dfree ); + + if ( root->avl_right != 0 ) + nright = ldap_avl_free( root->avl_right, dfree ); + + if ( dfree ) + (*dfree)( root->avl_data ); + ber_memfree( root ); + + return( nleft + nright + 1 ); +} + +/* + * ldap_avl_find -- search avltree root for a node with data data. the function + * cmp is used to compare things. it is called with data as its first arg + * and the current node data as its second. it should return 0 if they match, + * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. + */ + +Avlnode * +ldap_avl_find2( Avlnode *root, const void *data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = root->avl_link[cmp]; + } + return root; +} + +void* +ldap_avl_find( Avlnode *root, const void* data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = root->avl_link[cmp]; + } + + return( root ? root->avl_data : 0 ); +} + +/* + * ldap_avl_find_lin -- search avltree root linearly for a node with data data. + * the function cmp is used to compare things. it is called with data as its + * first arg and the current node data as its second. it should return 0 if + * they match, non-zero otherwise. + */ + +void* +ldap_avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp ) +{ + void* res; + + if ( root == 0 ) + return( NULL ); + + if ( (*fcmp)( data, root->avl_data ) == 0 ) + return( root->avl_data ); + + if ( root->avl_left != 0 ) + if ( (res = ldap_avl_find_lin( root->avl_left, data, fcmp )) + != NULL ) + return( res ); + + if ( root->avl_right == 0 ) + return( NULL ); + else + return( ldap_avl_find_lin( root->avl_right, data, fcmp ) ); +} + +/* NON-REENTRANT INTERFACE */ + +static void* *avl_list; +static int avl_maxlist; +static int ldap_avl_nextlist; + +#define AVL_GRABSIZE 100 + +/* ARGSUSED */ +static int +avl_buildlist( void* data, void* arg ) +{ + static int slots; + + if ( avl_list == (void* *) 0 ) { + avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*)); + slots = AVL_GRABSIZE; + avl_maxlist = 0; + } else if ( avl_maxlist == slots ) { + slots += AVL_GRABSIZE; + avl_list = (void* *) ber_memrealloc( (char *) avl_list, + (unsigned) slots * sizeof(void*)); + } + + avl_list[ avl_maxlist++ ] = data; + + return( 0 ); +} + +/* + * ldap_avl_getfirst() and ldap_avl_getnext() are provided as alternate tree + * traversal methods, to be used when a single function cannot be + * provided to be called with every node in the tree. ldap_avl_getfirst() + * traverses the tree and builds a linear list of all the nodes, + * returning the first node. ldap_avl_getnext() returns the next thing + * on the list built by ldap_avl_getfirst(). This means that ldap_avl_getfirst() + * can take a while, and that the tree should not be messed with while + * being traversed in this way, and that multiple traversals (even of + * different trees) cannot be active at once. + */ + +void* +ldap_avl_getfirst( Avlnode *root ) +{ + if ( avl_list ) { + ber_memfree( (char *) avl_list); + avl_list = (void* *) 0; + } + avl_maxlist = 0; + ldap_avl_nextlist = 0; + + if ( root == 0 ) + return( 0 ); + + (void) ldap_avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER ); + + return( avl_list[ ldap_avl_nextlist++ ] ); +} + +void* +ldap_avl_getnext( void ) +{ + if ( avl_list == 0 ) + return( 0 ); + + if ( ldap_avl_nextlist == avl_maxlist ) { + ber_memfree( (void*) avl_list); + avl_list = (void* *) 0; + return( 0 ); + } + + return( avl_list[ ldap_avl_nextlist++ ] ); +} + +/* end non-reentrant code */ + + +int +ldap_avl_dup_error( void* left, void* right ) +{ + return( -1 ); +} + +int +ldap_avl_dup_ok( void* left, void* right ) +{ + return( 0 ); +} diff --git a/contrib/libs/openldap/libraries/libldap/bind.c b/contrib/libs/openldap/libraries/libldap/bind.c new file mode 100644 index 0000000000..ea6fe5ca6b --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/bind.c @@ -0,0 +1,117 @@ +/* bind.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + * (Source: RFC 2251) + */ + +/* + * ldap_bind - bind to the ldap server (and X.500). The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. The msgid of the bind request is returned on success, + * -1 if there's trouble. ldap_result() should be called to find out the + * outcome of the bind request. + * + * Example: + * ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret", + * LDAP_AUTH_SIMPLE ) + */ + +int +ldap_bind( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd, int authmethod ) +{ + Debug0( LDAP_DEBUG_TRACE, "ldap_bind\n" ); + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind( ld, dn, passwd ) ); + + case LDAP_AUTH_SASL: + /* user must use ldap_sasl_bind */ + /* FALL-THRU */ + + default: + ld->ld_errno = LDAP_AUTH_UNKNOWN; + return( -1 ); + } +} + +/* + * ldap_bind_s - bind to the ldap server (and X.500). The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. This routine just calls whichever bind routine is + * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or + * some other error indication). + * + * Examples: + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret", LDAP_AUTH_SIMPLE ) + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * NULL, LDAP_AUTH_KRBV4 ) + */ +int +ldap_bind_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *passwd, + int authmethod ) +{ + Debug0( LDAP_DEBUG_TRACE, "ldap_bind_s\n" ); + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind_s( ld, dn, passwd ) ); + + case LDAP_AUTH_SASL: + /* user must use ldap_sasl_bind */ + /* FALL-THRU */ + + default: + return( ld->ld_errno = LDAP_AUTH_UNKNOWN ); + } +} diff --git a/contrib/libs/openldap/libraries/libldap/cancel.c b/contrib/libs/openldap/libraries/libldap/cancel.c new file mode 100644 index 0000000000..cfb4ba1959 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/cancel.c @@ -0,0 +1,76 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. + */ + +/* + * LDAPv3 Cancel Operation Request + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +int +ldap_cancel( + LDAP *ld, + int cancelid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *cancelidber = NULL; + struct berval cancelidvalp = { 0, NULL }; + int rc; + + cancelidber = ber_alloc_t( LBER_USE_DER ); + ber_printf( cancelidber, "{i}", cancelid ); + ber_flatten2( cancelidber, &cancelidvalp, 0 ); + rc = ldap_extended_operation( ld, LDAP_EXOP_CANCEL, + &cancelidvalp, sctrls, cctrls, msgidp ); + ber_free( cancelidber, 1 ); + return rc; +} + +int +ldap_cancel_s( + LDAP *ld, + int cancelid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + BerElement *cancelidber = NULL; + struct berval cancelidvalp = { 0, NULL }; + int rc; + + cancelidber = ber_alloc_t( LBER_USE_DER ); + ber_printf( cancelidber, "{i}", cancelid ); + ber_flatten2( cancelidber, &cancelidvalp, 0 ); + rc = ldap_extended_operation_s( ld, LDAP_EXOP_CANCEL, + &cancelidvalp, sctrls, cctrls, NULL, NULL ); + ber_free( cancelidber, 1 ); + return rc; +} + diff --git a/contrib/libs/openldap/libraries/libldap/charray.c b/contrib/libs/openldap/libraries/libldap/charray.c new file mode 100644 index 0000000000..856843fed8 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/charray.c @@ -0,0 +1,275 @@ +/* charray.c - routines for dealing with char * arrays */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "ldap-int.h" + +int +ldap_charray_add( + char ***a, + const char *s +) +{ + int n; + + if ( *a == NULL ) { + *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) ); + n = 0; + + if( *a == NULL ) { + return -1; + } + + } else { + char **new; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + + new = (char **) LDAP_REALLOC( (char *) *a, + (n + 2) * sizeof(char *) ); + + if( new == NULL ) { + /* caller is required to call ldap_charray_free(*a) */ + return -1; + } + + *a = new; + } + + (*a)[n] = LDAP_STRDUP(s); + + if( (*a)[n] == NULL ) { + return 1; + } + + (*a)[++n] = NULL; + + return 0; +} + +int +ldap_charray_merge( + char ***a, + char **s +) +{ + int i, n, nn; + char **aa; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + for ( nn = 0; s[nn] != NULL; nn++ ) { + ; /* NULL */ + } + + aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) ); + + if( aa == NULL ) { + return -1; + } + + *a = aa; + + for ( i = 0; i < nn; i++ ) { + (*a)[n + i] = LDAP_STRDUP(s[i]); + + if( (*a)[n + i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( (*a)[n + i] ); + (*a)[n + i] = NULL; + } + return -1; + } + } + + (*a)[n + nn] = NULL; + return 0; +} + +void +ldap_charray_free( char **a ) +{ + char **p; + + if ( a == NULL ) { + return; + } + + for ( p = a; *p != NULL; p++ ) { + if ( *p != NULL ) { + LDAP_FREE( *p ); + } + } + + LDAP_FREE( (char *) a ); +} + +int +ldap_charray_inlist( + char **a, + const char *s +) +{ + int i; + + if( a == NULL ) return 0; + + for ( i=0; a[i] != NULL; i++ ) { + if ( strcasecmp( s, a[i] ) == 0 ) { + return 1; + } + } + + return 0; +} + +char ** +ldap_charray_dup( char **a ) +{ + int i; + char **new; + + for ( i = 0; a[i] != NULL; i++ ) + ; /* NULL */ + + new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( new == NULL ) { + return NULL; + } + + for ( i = 0; a[i] != NULL; i++ ) { + new[i] = LDAP_STRDUP( a[i] ); + + if( new[i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( new[i] ); + } + LDAP_FREE( new ); + return NULL; + } + } + new[i] = NULL; + + return( new ); +} + +char ** +ldap_str2charray( const char *str_in, const char *brkstr ) +{ + char **res; + char *str, *s; + char *lasts; + int i; + + /* protect the input string from strtok */ + str = LDAP_STRDUP( str_in ); + if( str == NULL ) { + return NULL; + } + + i = 1; + for ( s = str; ; LDAP_UTF8_INCR(s) ) { + s = ldap_utf8_strpbrk( s, brkstr ); + if ( !s ) break; + i++; + } + + res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( res == NULL ) { + LDAP_FREE( str ); + return NULL; + } + + i = 0; + + for ( s = ldap_utf8_strtok( str, brkstr, &lasts ); + s != NULL; + s = ldap_utf8_strtok( NULL, brkstr, &lasts ) ) + { + res[i] = LDAP_STRDUP( s ); + + if(res[i] == NULL) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( res[i] ); + } + LDAP_FREE( res ); + LDAP_FREE( str ); + return NULL; + } + + i++; + } + + res[i] = NULL; + + LDAP_FREE( str ); + return( res ); +} + +char * ldap_charray2str( char **a, const char *sep ) +{ + char *s, **v, *p; + int len; + int slen; + + if( sep == NULL ) sep = " "; + + slen = strlen( sep ); + len = 0; + + for ( v = a; *v != NULL; v++ ) { + len += strlen( *v ) + slen; + } + + if ( len == 0 ) { + return NULL; + } + + /* trim extra sep len */ + len -= slen; + + s = LDAP_MALLOC ( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + p = s; + for ( v = a; *v != NULL; v++ ) { + if ( v != a ) { + strncpy( p, sep, slen ); + p += slen; + } + + len = strlen( *v ); + strncpy( p, *v, len ); + p += len; + } + + *p = '\0'; + return s; +} diff --git a/contrib/libs/openldap/libraries/libldap/compare.c b/contrib/libs/openldap/libraries/libldap/compare.c new file mode 100644 index 0000000000..86285c685a --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/compare.c @@ -0,0 +1,197 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* The compare request looks like this: + * CompareRequest ::= SEQUENCE { + * entry DistinguishedName, + * ava SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * } + */ + +BerElement * +ldap_build_compare_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID(ld, *msgidp); + rc = ber_printf( ber, "{it{s{sON}N}", /* '}' */ + *msgidp, + LDAP_REQ_COMPARE, dn, attr, bvalue ); + if ( rc == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_compare_ext - perform an ldap extended compare operation. The dn + * of the entry to compare to and the attribute and value to compare (in + * attr and value) are supplied. The msgid of the response is returned. + * + * Example: + * struct berval bvalue = { "secret", sizeof("secret")-1 }; + * rc = ldap_compare( ld, "c=us@cn=bob", + * "userPassword", &bvalue, + * sctrl, cctrl, &msgid ) + */ +int +ldap_compare_ext( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *ber; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_compare\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( attr != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_compare_req( + ld, dn, attr, bvalue, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_COMPARE, dn, ber, id ); + return ( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +/* + * ldap_compare_ext - perform an ldap extended compare operation. The dn + * of the entry to compare to and the attribute and value to compare (in + * attr and value) are supplied. The msgid of the response is returned. + * + * Example: + * msgid = ldap_compare( ld, "c=us@cn=bob", "userPassword", "secret" ) + */ +int +ldap_compare( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value ) +{ + int msgid; + struct berval bvalue; + + assert( value != NULL ); + + bvalue.bv_val = (char *) value; + bvalue.bv_len = (value == NULL) ? 0 : strlen( value ); + + return ldap_compare_ext( ld, dn, attr, &bvalue, NULL, NULL, &msgid ) == LDAP_SUCCESS + ? msgid : -1; +} + +int +ldap_compare_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrl, + LDAPControl **cctrl ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_compare_ext( ld, dn, attr, bvalue, sctrl, cctrl, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_compare_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value ) +{ + struct berval bvalue; + + assert( value != NULL ); + + bvalue.bv_val = (char *) value; + bvalue.bv_len = (value == NULL) ? 0 : strlen( value ); + + return ldap_compare_ext_s( ld, dn, attr, &bvalue, NULL, NULL ); +} diff --git a/contrib/libs/openldap/libraries/libldap/controls.c b/contrib/libs/openldap/libraries/libldap/controls.c new file mode 100644 index 0000000000..125707e272 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/controls.c @@ -0,0 +1,552 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/time.h> +#include <ac/string.h> + +#include "ldap-int.h" + +/* LDAPv3 Controls (RFC 4511) + * + * Controls ::= SEQUENCE OF control Control + * + * Control ::= SEQUENCE { + * controlType LDAPOID, + * criticality BOOLEAN DEFAULT FALSE, + * controlValue OCTET STRING OPTIONAL + * } + */ + +int +ldap_pvt_put_control( + const LDAPControl *c, + BerElement *ber ) +{ + if ( ber_printf( ber, "{s" /*}*/, c->ldctl_oid ) == -1 ) { + return LDAP_ENCODING_ERROR; + } + + if ( c->ldctl_iscritical /* only if true */ + && ( ber_printf( ber, "b", + (ber_int_t) c->ldctl_iscritical ) == -1 ) ) + { + return LDAP_ENCODING_ERROR; + } + + if ( !BER_BVISNULL( &c->ldctl_value ) /* only if we have a value */ + && ( ber_printf( ber, "O", &c->ldctl_value ) == -1 ) ) + { + return LDAP_ENCODING_ERROR; + } + + if ( ber_printf( ber, /*{*/"N}" ) == -1 ) { + return LDAP_ENCODING_ERROR; + } + + return LDAP_SUCCESS; +} + + +/* + * ldap_int_put_controls + */ + +int +ldap_int_put_controls( + LDAP *ld, + LDAPControl *const *ctrls, + BerElement *ber ) +{ + LDAPControl *const *c; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ber != NULL ); + + if( ctrls == NULL ) { + /* use default server controls */ + ctrls = ld->ld_sctrls; + } + + if( ctrls == NULL || *ctrls == NULL ) { + return LDAP_SUCCESS; + } + + if ( ld->ld_version < LDAP_VERSION3 ) { + /* LDAPv2 doesn't support controls, + * error if any control is critical + */ + for( c = ctrls ; *c != NULL; c++ ) { + if( (*c)->ldctl_iscritical ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; + } + + /* Controls are encoded as a sequence of sequences */ + if( ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + for( c = ctrls ; *c != NULL; c++ ) { + ld->ld_errno = ldap_pvt_put_control( *c, ber ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + return ld->ld_errno; + } + } + + + if( ber_printf( ber, /*{*/ "}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + return LDAP_SUCCESS; +} + +int ldap_pvt_get_controls( + BerElement *ber, + LDAPControl ***ctrls ) +{ + int nctrls; + ber_tag_t tag; + ber_len_t len; + char *opaque; + + assert( ber != NULL ); + + if( ctrls == NULL ) { + return LDAP_SUCCESS; + } + *ctrls = NULL; + + len = ber_pvt_ber_remaining( ber ); + + if( len == 0) { + /* no controls */ + return LDAP_SUCCESS; + } + + if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) { + if( tag == LBER_ERROR ) { + /* decoding error */ + return LDAP_DECODING_ERROR; + } + + /* ignore unexpected input */ + return LDAP_SUCCESS; + } + + /* set through each element */ + nctrls = 0; + *ctrls = LDAP_MALLOC( 1 * sizeof(LDAPControl *) ); + + if( *ctrls == NULL ) { + return LDAP_NO_MEMORY; + } + + *ctrls[nctrls] = NULL; + + for( tag = ber_first_element( ber, &len, &opaque ); + tag != LBER_ERROR; + tag = ber_next_element( ber, &len, opaque ) ) + { + LDAPControl *tctrl; + LDAPControl **tctrls; + + tctrl = LDAP_CALLOC( 1, sizeof(LDAPControl) ); + + /* allocate pointer space for current controls (nctrls) + * + this control + extra NULL + */ + tctrls = (tctrl == NULL) ? NULL : + LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *)); + + if( tctrls == NULL ) { + /* one of the above allocation failed */ + + if( tctrl != NULL ) { + LDAP_FREE( tctrl ); + } + + ldap_controls_free(*ctrls); + *ctrls = NULL; + + return LDAP_NO_MEMORY; + } + + + tctrls[nctrls++] = tctrl; + tctrls[nctrls] = NULL; + + tag = ber_scanf( ber, "{a" /*}*/, &tctrl->ldctl_oid ); + + if( tag == LBER_ERROR ) { + *ctrls = NULL; + ldap_controls_free( tctrls ); + return LDAP_DECODING_ERROR; + } + + tag = ber_peek_tag( ber, &len ); + + if( tag == LBER_BOOLEAN ) { + ber_int_t crit; + tag = ber_scanf( ber, "b", &crit ); + tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0; + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LBER_OCTETSTRING ) { + tag = ber_scanf( ber, "o", &tctrl->ldctl_value ); + } else { + BER_BVZERO( &tctrl->ldctl_value ); + } + + *ctrls = tctrls; + } + + return LDAP_SUCCESS; +} + +/* + * Free a LDAPControl + */ +void +ldap_control_free( LDAPControl *c ) +{ + LDAP_MEMORY_DEBUG_ASSERT( c != NULL ); + + if ( c != NULL ) { + if( c->ldctl_oid != NULL) { + LDAP_FREE( c->ldctl_oid ); + } + + if( c->ldctl_value.bv_val != NULL ) { + LDAP_FREE( c->ldctl_value.bv_val ); + } + + LDAP_FREE( c ); + } +} + +/* + * Free an array of LDAPControl's + */ +void +ldap_controls_free( LDAPControl **controls ) +{ + LDAP_MEMORY_DEBUG_ASSERT( controls != NULL ); + + if ( controls != NULL ) { + int i; + + for( i=0; controls[i] != NULL; i++) { + ldap_control_free( controls[i] ); + } + + LDAP_FREE( controls ); + } +} + +/* + * Duplicate an array of LDAPControl + */ +LDAPControl ** +ldap_controls_dup( LDAPControl *const *controls ) +{ + LDAPControl **new; + int i; + + if ( controls == NULL ) { + return NULL; + } + + /* count the controls */ + for(i=0; controls[i] != NULL; i++) /* empty */ ; + + if( i < 1 ) { + /* no controls to duplicate */ + return NULL; + } + + new = (LDAPControl **) LDAP_MALLOC( (i+1) * sizeof(LDAPControl *) ); + + if( new == NULL ) { + /* memory allocation failure */ + return NULL; + } + + /* duplicate the controls */ + for(i=0; controls[i] != NULL; i++) { + new[i] = ldap_control_dup( controls[i] ); + + if( new[i] == NULL ) { + ldap_controls_free( new ); + return NULL; + } + } + + new[i] = NULL; + + return new; +} + +/* + * Duplicate a LDAPControl + */ +LDAPControl * +ldap_control_dup( const LDAPControl *c ) +{ + LDAPControl *new; + + if ( c == NULL || c->ldctl_oid == NULL ) { + return NULL; + } + + new = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) ); + + if( new == NULL ) { + return NULL; + } + + new->ldctl_oid = LDAP_STRDUP( c->ldctl_oid ); + + if(new->ldctl_oid == NULL) { + LDAP_FREE( new ); + return NULL; + } + + if( c->ldctl_value.bv_val != NULL ) { + new->ldctl_value.bv_val = + (char *) LDAP_MALLOC( c->ldctl_value.bv_len + 1 ); + + if(new->ldctl_value.bv_val == NULL) { + if(new->ldctl_oid != NULL) { + LDAP_FREE( new->ldctl_oid ); + } + LDAP_FREE( new ); + return NULL; + } + + new->ldctl_value.bv_len = c->ldctl_value.bv_len; + + AC_MEMCPY( new->ldctl_value.bv_val, c->ldctl_value.bv_val, + c->ldctl_value.bv_len ); + + new->ldctl_value.bv_val[new->ldctl_value.bv_len] = '\0'; + + } else { + new->ldctl_value.bv_len = 0; + new->ldctl_value.bv_val = NULL; + } + + new->ldctl_iscritical = c->ldctl_iscritical; + return new; +} + +/* + * Find a LDAPControl - deprecated + */ +LDAPControl * +ldap_find_control( + LDAP_CONST char *oid, + LDAPControl **ctrls ) +{ + if( ctrls == NULL || *ctrls == NULL ) { + return NULL; + } + + for( ; *ctrls != NULL; ctrls++ ) { + if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) { + return *ctrls; + } + } + + return NULL; +} + +/* + * Find a LDAPControl + */ +LDAPControl * +ldap_control_find( + LDAP_CONST char *oid, + LDAPControl **ctrls, + LDAPControl ***nextctrlp ) +{ + if ( oid == NULL || ctrls == NULL || *ctrls == NULL ) { + return NULL; + } + + for( ; *ctrls != NULL; ctrls++ ) { + if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) { + if ( nextctrlp != NULL ) { + *nextctrlp = ctrls + 1; + } + + return *ctrls; + } + } + + if ( nextctrlp != NULL ) { + *nextctrlp = NULL; + } + + return NULL; +} + +/* + * Create a LDAPControl, optionally from ber - deprecated + */ +int +ldap_create_control( + LDAP_CONST char *requestOID, + BerElement *ber, + int iscritical, + LDAPControl **ctrlp ) +{ + LDAPControl *ctrl; + + assert( requestOID != NULL ); + assert( ctrlp != NULL ); + + ctrl = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) ); + if ( ctrl == NULL ) { + return LDAP_NO_MEMORY; + } + + BER_BVZERO(&ctrl->ldctl_value); + if ( ber && ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 )) { + LDAP_FREE( ctrl ); + return LDAP_NO_MEMORY; + } + + ctrl->ldctl_oid = LDAP_STRDUP( requestOID ); + ctrl->ldctl_iscritical = iscritical; + + if ( requestOID != NULL && ctrl->ldctl_oid == NULL ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + + *ctrlp = ctrl; + return LDAP_SUCCESS; +} + +/* + * Create a LDAPControl, optionally from value + */ +int +ldap_control_create( + LDAP_CONST char *requestOID, + int iscritical, + struct berval *value, + int dupval, + LDAPControl **ctrlp ) +{ + LDAPControl *ctrl; + + assert( requestOID != NULL ); + assert( ctrlp != NULL ); + + ctrl = (LDAPControl *) LDAP_CALLOC( sizeof(LDAPControl), 1 ); + if ( ctrl == NULL ) { + return LDAP_NO_MEMORY; + } + + ctrl->ldctl_iscritical = iscritical; + if ( requestOID != NULL ) { + ctrl->ldctl_oid = LDAP_STRDUP( requestOID ); + if ( ctrl->ldctl_oid == NULL ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + } + + if ( value && !BER_BVISNULL( value ) ) { + if ( dupval ) { + ber_dupbv( &ctrl->ldctl_value, value ); + if ( BER_BVISNULL( &ctrl->ldctl_value ) ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + + } else { + ctrl->ldctl_value = *value; + } + } + + *ctrlp = ctrl; + + return LDAP_SUCCESS; +} + +/* + * check for critical client controls and bitch if present + * if we ever support critical controls, we'll have to + * find a means for maintaining per API call control + * information. + */ +int ldap_int_client_controls( LDAP *ld, LDAPControl **ctrls ) +{ + LDAPControl *const *c; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if( ctrls == NULL ) { + /* use default client controls */ + ctrls = ld->ld_cctrls; + } + + if( ctrls == NULL || *ctrls == NULL ) { + return LDAP_SUCCESS; + } + + for( c = ctrls ; *c != NULL; c++ ) { + if( (*c)->ldctl_iscritical ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; +} diff --git a/contrib/libs/openldap/libraries/libldap/cyrus.c b/contrib/libs/openldap/libraries/libldap/cyrus.c new file mode 100644 index 0000000000..e713e58dcc --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/cyrus.c @@ -0,0 +1,1335 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include "ldap-int.h" + +#ifdef HAVE_CYRUS_SASL + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> +#include <ac/ctype.h> +#include <ac/unistd.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifndef INT_MAX +#define INT_MAX 2147483647 /* 32 bit signed max */ +#endif + +#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX) +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#if SASL_VERSION_MAJOR >= 2 +#define SASL_CONST const +#else +#define SASL_CONST +#endif + +/* +* Various Cyrus SASL related stuff. +*/ + +static const sasl_callback_t client_callbacks[] = { +#ifdef SASL_CB_GETREALM + { SASL_CB_GETREALM, NULL, NULL }, +#endif + { SASL_CB_USER, NULL, NULL }, + { SASL_CB_AUTHNAME, NULL, NULL }, + { SASL_CB_PASS, NULL, NULL }, + { SASL_CB_ECHOPROMPT, NULL, NULL }, + { SASL_CB_NOECHOPROMPT, NULL, NULL }, + { SASL_CB_LIST_END, NULL, NULL } +}; + +/* + * ldap_int_initialize is responsible for calling this only once. + */ +int ldap_int_sasl_init( void ) +{ +#ifdef HAVE_SASL_VERSION + /* stringify the version number, sasl.h doesn't do it for us */ +#define VSTR0(maj, min, pat) #maj "." #min "." #pat +#define VSTR(maj, min, pat) VSTR0(maj, min, pat) +#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ + SASL_VERSION_STEP) + { int rc; + sasl_version( NULL, &rc ); + if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) || + (rc & 0xffff) < SASL_VERSION_STEP) { + char version[sizeof("xxx.xxx.xxxxx")]; + sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff, + rc & 0xffff ); + + Debug1( LDAP_DEBUG_ANY, + "ldap_int_sasl_init: SASL library version mismatch:" + " expected " SASL_VERSION_STRING "," + " got %s\n", version ); + return -1; + } + } +#endif + +/* SASL 2 takes care of its own memory completely internally */ +#if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC) + sasl_set_alloc( + ber_memalloc, + ber_memcalloc, + ber_memrealloc, + ber_memfree ); +#endif /* CSRIMALLOC */ + +#ifdef LDAP_R_COMPILE + sasl_set_mutex( + ldap_pvt_sasl_mutex_new, + ldap_pvt_sasl_mutex_lock, + ldap_pvt_sasl_mutex_unlock, + ldap_pvt_sasl_mutex_dispose ); +#endif + + if ( sasl_client_init( NULL ) == SASL_OK ) { + return 0; + } + +#if SASL_VERSION_MAJOR < 2 + /* A no-op to make sure we link with Cyrus 1.5 */ + sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL ); +#endif + return -1; +} + +static void +sb_sasl_cyrus_init( + struct sb_sasl_generic_data *p, + ber_len_t *min_send, + ber_len_t *max_send, + ber_len_t *max_recv) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_len_t maxbuf; + + sasl_getprop( sasl_context, SASL_MAXOUTBUF, + (SASL_CONST void **)(char *) &maxbuf ); + + *min_send = SASL_MIN_BUFF_SIZE; + *max_send = maxbuf; + *max_recv = SASL_MAX_BUFF_SIZE; +} + +static ber_int_t +sb_sasl_cyrus_encode( + struct sb_sasl_generic_data *p, + unsigned char *buf, + ber_len_t len, + Sockbuf_Buf *dst) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; + + ret = sasl_encode( sasl_context, (char *)buf, len, + (SASL_CONST char **)&dst->buf_base, + &tmpsize ); + + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; + + if ( ret != SASL_OK ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_encode: failed to encode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); + return -1; + } + + return 0; +} + +static ber_int_t +sb_sasl_cyrus_decode( + struct sb_sasl_generic_data *p, + const Sockbuf_Buf *src, + Sockbuf_Buf *dst) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; + + ret = sasl_decode( sasl_context, + src->buf_base, src->buf_end, + (SASL_CONST char **)&dst->buf_base, + (unsigned *)&tmpsize ); + + + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; + + if ( ret != SASL_OK ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_decode: failed to decode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); + return -1; + } + + return 0; +} + +static void +sb_sasl_cyrus_reset_buf( + struct sb_sasl_generic_data *p, + Sockbuf_Buf *buf) +{ +#if SASL_VERSION_MAJOR >= 2 + ber_pvt_sb_buf_init( buf ); +#else + ber_pvt_sb_buf_destroy( buf ); +#endif +} + +static void +sb_sasl_cyrus_fini( + struct sb_sasl_generic_data *p) +{ +#if SASL_VERSION_MAJOR >= 2 + /* + * SASLv2 encode/decode buffers are managed by + * libsasl2. Ensure they are not freed by liblber. + */ + p->buf_in.buf_base = NULL; + p->buf_out.buf_base = NULL; +#endif +} + +static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = { + sb_sasl_cyrus_init, + sb_sasl_cyrus_encode, + sb_sasl_cyrus_decode, + sb_sasl_cyrus_reset_buf, + sb_sasl_cyrus_fini + }; + +int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg ) +{ + struct sb_sasl_generic_install install_arg; + + install_arg.ops = &sb_sasl_cyrus_ops; + install_arg.ops_private = ctx_arg; + + return ldap_pvt_sasl_generic_install( sb, &install_arg ); +} + +void ldap_pvt_sasl_remove( Sockbuf *sb ) +{ + ldap_pvt_sasl_generic_remove( sb ); +} + +static int +sasl_err2ldap( int saslerr ) +{ + int rc; + + /* map SASL errors to LDAP API errors returned by: + * sasl_client_new() + * SASL_OK, SASL_NOMECH, SASL_NOMEM + * sasl_client_start() + * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT + * sasl_client_step() + * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV + */ + + switch (saslerr) { + case SASL_CONTINUE: + rc = LDAP_MORE_RESULTS_TO_RETURN; + break; + case SASL_INTERACT: + rc = LDAP_LOCAL_ERROR; + break; + case SASL_OK: + rc = LDAP_SUCCESS; + break; + case SASL_NOMEM: + rc = LDAP_NO_MEMORY; + break; + case SASL_NOMECH: + rc = LDAP_AUTH_UNKNOWN; + break; + case SASL_BADPROT: + rc = LDAP_DECODING_ERROR; + break; + case SASL_BADSERV: + rc = LDAP_AUTH_UNKNOWN; + break; + + /* other codes */ + case SASL_BADAUTH: + rc = LDAP_AUTH_UNKNOWN; + break; + case SASL_NOAUTHZ: + rc = LDAP_PARAM_ERROR; + break; + case SASL_FAIL: + rc = LDAP_LOCAL_ERROR; + break; + case SASL_TOOWEAK: + case SASL_ENCRYPT: + rc = LDAP_AUTH_UNKNOWN; + break; + default: + rc = LDAP_LOCAL_ERROR; + break; + } + + assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) ); + return rc; +} + +int +ldap_int_sasl_open( + LDAP *ld, + LDAPConn *lc, + const char * host ) +{ + int rc; + sasl_conn_t *ctx; + + assert( lc->lconn_sasl_authctx == NULL ); + + if ( host == NULL ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + return ld->ld_errno; + } + +#if SASL_VERSION_MAJOR >= 2 + rc = sasl_client_new( "ldap", host, NULL, NULL, + client_callbacks, 0, &ctx ); +#else + rc = sasl_client_new( "ldap", host, client_callbacks, + SASL_SECURITY_LAYER, &ctx ); +#endif + + if ( rc != SASL_OK ) { + ld->ld_errno = sasl_err2ldap( rc ); + return ld->ld_errno; + } + + Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n", + host ); + + lc->lconn_sasl_authctx = ctx; + + return LDAP_SUCCESS; +} + +int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) +{ + sasl_conn_t *ctx = lc->lconn_sasl_authctx; + + if( ctx != NULL ) { + sasl_dispose( &ctx ); + if ( lc->lconn_sasl_sockctx && + lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) { + ctx = lc->lconn_sasl_sockctx; + sasl_dispose( &ctx ); + } + lc->lconn_sasl_sockctx = NULL; + lc->lconn_sasl_authctx = NULL; + } + if( lc->lconn_sasl_cbind ) { + ldap_memfree( lc->lconn_sasl_cbind ); + lc->lconn_sasl_cbind = NULL; + } + + return LDAP_SUCCESS; +} + +int ldap_pvt_sasl_cbinding_parse( const char *arg ) +{ + int i = -1; + + if ( strcasecmp(arg, "none") == 0 ) + i = LDAP_OPT_X_SASL_CBINDING_NONE; + else if ( strcasecmp(arg, "tls-unique") == 0 ) + i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE; + else if ( strcasecmp(arg, "tls-endpoint") == 0 ) + i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT; + + return i; +} + +void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server ) +{ +#if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS) + char unique_prefix[] = "tls-unique:"; + char endpoint_prefix[] = "tls-server-end-point:"; + char cbinding[ 64 ]; + struct berval cbv = { 64, cbinding }; + unsigned char *cb_data; /* used since cb->data is const* */ + sasl_channel_binding_t *cb; + char *prefix; + int plen; + + switch (type) { + case LDAP_OPT_X_SASL_CBINDING_NONE: + return NULL; + case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE: + if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server )) + return NULL; + prefix = unique_prefix; + plen = sizeof(unique_prefix) -1; + break; + case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT: + if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server )) + return NULL; + prefix = endpoint_prefix; + plen = sizeof(endpoint_prefix) -1; + break; + default: + return NULL; + } + + cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len ); + cb->len = plen + cbv.bv_len; + cb->data = cb_data = (unsigned char *)(cb+1); + memcpy( cb_data, prefix, plen ); + memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len ); + cb->name = "ldap"; + cb->critical = 0; + + return cb; +#else + return NULL; +#endif +} + +int +ldap_int_sasl_bind( + LDAP *ld, + const char *dn, + const char *mechs, + LDAPControl **sctrls, + LDAPControl **cctrls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ + const char *mech; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; + sasl_interact_t *prompts = NULL; + struct berval ccred = BER_BVNULL; + int saslrc, rc; + unsigned credlen; +#if !defined(_WIN32) + char my_hostname[HOST_NAME_MAX + 1]; +#endif + int free_saslhost = 0; + + Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", + mechs ? mechs : "<null>" ); + + /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ + if (ld->ld_version < LDAP_VERSION3) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + /* Starting a Bind */ + if ( !result ) { + const char *pmech = NULL; + sasl_conn_t *oldctx; + ber_socket_t sd; + void *ssl; + + rc = 0; + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); + + if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) { + /* not connected yet */ + + rc = ldap_open_defconn( ld ); + + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); + + if( sd == AC_SOCKET_INVALID ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + rc = ld->ld_errno; + } + } + } + if ( rc == 0 && ld->ld_defconn && + ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) { + rc = ldap_int_check_async_open( ld, sd ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + if( rc != 0 ) return ld->ld_errno; + + oldctx = ld->ld_defconn->lconn_sasl_authctx; + + /* If we already have an authentication context, clear it out */ + if( oldctx ) { + if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { + sasl_dispose( &oldctx ); + } + ld->ld_defconn->lconn_sasl_authctx = NULL; + } + + { + char *saslhost; + int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, + LDAP_BOOL_SASL_NOCANON ); + + /* If we don't need to canonicalize just use the host + * from the LDAP URI. + * Always use the result of gethostname() for LDAPI. + * Skip for Windows which doesn't support LDAPI. + */ +#if !defined(_WIN32) + if (ld->ld_defconn->lconn_server->lud_scheme != NULL && + strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) { + rc = gethostname(my_hostname, HOST_NAME_MAX + 1); + if (rc == 0) { + saslhost = my_hostname; + } else { + saslhost = "localhost"; + } + } else +#endif + if ( nocanon ) + saslhost = ld->ld_defconn->lconn_server->lud_host; + else { + saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, + "localhost" ); + free_saslhost = 1; + } + rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); + if ( free_saslhost ) + LDAP_FREE( saslhost ); + } + + if ( rc != LDAP_SUCCESS ) return rc; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + +#ifdef HAVE_TLS + /* Check for TLS */ + ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); + if ( ssl ) { + struct berval authid = BER_BVNULL; + ber_len_t fac; + + fac = ldap_pvt_tls_get_strength( ssl ); + /* failure is OK, we just can't use SASL EXTERNAL */ + (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); + + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); + LDAP_FREE( authid.bv_val ); +#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */ + if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) { + void *cb; + cb = ldap_pvt_sasl_cbinding( ssl, + ld->ld_options.ldo_sasl_cbinding, + 0 ); + if ( cb != NULL ) { + sasl_setprop( ld->ld_defconn->lconn_sasl_authctx, + SASL_CHANNEL_BINDING, cb ); + ld->ld_defconn->lconn_sasl_cbind = cb; + } + } +#endif + } +#endif + +#if !defined(_WIN32) + /* Check for local */ + if ( ldap_pvt_url_scheme2proto( + ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) + { + char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," + "cn=peercred,cn=external,cn=auth")]; + sprintf( authid, "gidNumber=%u+uidNumber=%u," + "cn=peercred,cn=external,cn=auth", + getegid(), geteuid() ); + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, + LDAP_PVT_SASL_LOCAL_SSF ); + } +#endif + + /* (re)set security properties */ + sasl_setprop( ctx, SASL_SEC_PROPS, + &ld->ld_options.ldo_sasl_secprops ); + + mech = NULL; + + do { + saslrc = sasl_client_start( ctx, + mechs, +#if SASL_VERSION_MAJOR < 2 + NULL, +#endif + &prompts, + (SASL_CONST char **)&ccred.bv_val, + &credlen, + &mech ); + + if( pmech == NULL && mech != NULL ) { + pmech = mech; + *rmech = mech; + + if( flags != LDAP_SASL_QUIET ) { + fprintf(stderr, + "SASL/%s authentication started\n", + pmech ); + } + } + + if( saslrc == SASL_INTERACT ) { + int res; + if( !interact ) break; + res = (interact)( ld, flags, defaults, prompts ); + + if( res != LDAP_SUCCESS ) break; + } + } while ( saslrc == SASL_INTERACT ); + rc = LDAP_SASL_BIND_IN_PROGRESS; + + } else { + /* continuing an in-progress Bind */ + struct berval *scred = NULL; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 ); + if ( rc != LDAP_SUCCESS ) { + if ( scred ) + ber_bvfree( scred ); + goto done; + } + + rc = ldap_result2error( ld, result, 0 ); + if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { + if( scred ) { + /* and server provided us with data? */ + Debug2( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: rc=%d len=%ld\n", + rc, scred ? (long) scred->bv_len : -1L ); + ber_bvfree( scred ); + scred = NULL; + } + goto done; + } + + mech = *rmech; + if ( rc == LDAP_SUCCESS && mech == NULL ) { + if ( scred ) + ber_bvfree( scred ); + goto success; + } + + do { + if( ! scred ) { + /* no data! */ + Debug0( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: no data in step!\n" ); + } + + saslrc = sasl_client_step( ctx, + (scred == NULL) ? NULL : scred->bv_val, + (scred == NULL) ? 0 : scred->bv_len, + &prompts, + (SASL_CONST char **)&ccred.bv_val, + &credlen ); + + Debug1( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n", + saslrc ); + + if( saslrc == SASL_INTERACT ) { + int res; + if( !interact ) break; + res = (interact)( ld, flags, defaults, prompts ); + if( res != LDAP_SUCCESS ) break; + } + } while ( saslrc == SASL_INTERACT ); + + ber_bvfree( scred ); + } + + if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { + rc = ld->ld_errno = sasl_err2ldap( saslrc ); +#if SASL_VERSION_MAJOR >= 2 + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); +#endif + goto done; + } + + if ( saslrc == SASL_OK ) + *rmech = NULL; + + ccred.bv_len = credlen; + + if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) { + rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid ); + + if ( ccred.bv_val != NULL ) { +#if SASL_VERSION_MAJOR < 2 + LDAP_FREE( ccred.bv_val ); +#endif + ccred.bv_val = NULL; + } + if ( rc == LDAP_SUCCESS ) + rc = LDAP_SASL_BIND_IN_PROGRESS; + goto done; + } + +success: + /* Conversation was completed successfully by now */ + if( flags != LDAP_SASL_QUIET ) { + char *data; + saslrc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char *) &data ); + if( saslrc == SASL_OK && data && *data ) { + fprintf( stderr, "SASL username: %s\n", data ); + } + +#if SASL_VERSION_MAJOR < 2 + saslrc = sasl_getprop( ctx, SASL_REALM, + (SASL_CONST void **) &data ); + if( saslrc == SASL_OK && data && *data ) { + fprintf( stderr, "SASL realm: %s\n", data ); + } +#endif + } + + ssf = NULL; + saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); + if( saslrc == SASL_OK ) { + if( flags != LDAP_SASL_QUIET ) { + fprintf( stderr, "SASL SSF: %lu\n", + (unsigned long) *ssf ); + } + + if( ssf && *ssf ) { + if ( ld->ld_defconn->lconn_sasl_sockctx ) { + sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx; + sasl_dispose( &oldctx ); + ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); + } + ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx ); + ld->ld_defconn->lconn_sasl_sockctx = ctx; + + if( flags != LDAP_SASL_QUIET ) { + fprintf( stderr, "SASL data security layer installed.\n" ); + } + } + } + ld->ld_defconn->lconn_sasl_authctx = ctx; + +done: + return rc; +} + +int +ldap_int_sasl_external( + LDAP *ld, + LDAPConn *conn, + const char * authid, + ber_len_t ssf ) +{ + int sc; + sasl_conn_t *ctx; +#if SASL_VERSION_MAJOR < 2 + sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf = ssf; +#endif + + ctx = conn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return LDAP_LOCAL_ERROR; + } + +#if SASL_VERSION_MAJOR >= 2 + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); + if ( sc == SASL_OK ) + sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); +#else + memset( &extprops, '\0', sizeof(extprops) ); + extprops.ssf = ssf; + extprops.auth_id = (char *) authid; + + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); +#endif + + if ( sc != SASL_OK ) { + return LDAP_LOCAL_ERROR; + } + + return LDAP_SUCCESS; +} + + +#define GOT_MINSSF 1 +#define GOT_MAXSSF 2 +#define GOT_MAXBUF 4 + +static struct { + struct berval key; + int sflag; + int ival; + int idef; +} sprops[] = { + { BER_BVC("none"), 0, 0, 0 }, + { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 }, + { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 }, + { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 }, + { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 }, + { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 }, + { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 }, + { BER_BVC("minssf="), 0, GOT_MINSSF, 0 }, + { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX }, + { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 }, + { BER_BVNULL, 0, 0, 0 } +}; + +void ldap_pvt_sasl_secprops_unparse( + sasl_security_properties_t *secprops, + struct berval *out ) +{ + int i, l = 0; + int comma; + char *ptr; + + if ( secprops == NULL || out == NULL ) { + return; + } + + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + l += sprops[i].key.bv_len + 24; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + l += sprops[i].key.bv_len; + } + } else if ( secprops->security_flags == 0 ) { + l += sprops[i].key.bv_len; + } + if ( comma ) l++; + comma = 1; + } + l++; + + out->bv_val = LDAP_MALLOC( l ); + if ( out->bv_val == NULL ) { + out->bv_len = 0; + return; + } + + ptr = out->bv_val; + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v ); + comma = 1; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } else if ( secprops->security_flags == 0 ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } + out->bv_len = ptr - out->bv_val; +} + +int ldap_pvt_sasl_secprops( + const char *in, + sasl_security_properties_t *secprops ) +{ + unsigned i, j, l; + char **props; + unsigned sflags = 0; + int got_sflags = 0; + sasl_ssf_t max_ssf = 0; + int got_max_ssf = 0; + sasl_ssf_t min_ssf = 0; + int got_min_ssf = 0; + unsigned maxbufsize = 0; + int got_maxbufsize = 0; + + if( secprops == NULL ) { + return LDAP_PARAM_ERROR; + } + props = ldap_str2charray( in, "," ); + if( props == NULL ) { + return LDAP_PARAM_ERROR; + } + + for( i=0; props[i]; i++ ) { + l = strlen( props[i] ); + for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) { + if ( l < sprops[j].key.bv_len ) continue; + if ( strncasecmp( props[i], sprops[j].key.bv_val, + sprops[j].key.bv_len )) continue; + if ( sprops[j].ival ) { + unsigned v; + char *next = NULL; + if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] )) + continue; + v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 ); + if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue; + switch( sprops[j].ival ) { + case GOT_MINSSF: + min_ssf = v; got_min_ssf++; break; + case GOT_MAXSSF: + max_ssf = v; got_max_ssf++; break; + case GOT_MAXBUF: + maxbufsize = v; got_maxbufsize++; break; + } + } else { + if ( props[i][sprops[j].key.bv_len] ) continue; + if ( sprops[j].sflag ) + sflags |= sprops[j].sflag; + else + sflags = 0; + got_sflags++; + } + break; + } + if ( BER_BVISNULL( &sprops[j].key )) { + ldap_charray_free( props ); + return LDAP_NOT_SUPPORTED; + } + } + + if(got_sflags) { + secprops->security_flags = sflags; + } + if(got_min_ssf) { + secprops->min_ssf = min_ssf; + } + if(got_max_ssf) { + secprops->max_ssf = max_ssf; + } + if(got_maxbufsize) { + secprops->maxbufsize = maxbufsize; + } + + ldap_charray_free( props ); + return LDAP_SUCCESS; +} + +int +ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg ) +{ + int rc, i; + + switch( option ) { + case LDAP_OPT_X_SASL_SECPROPS: + rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops ); + if( rc == LDAP_SUCCESS ) return 0; + break; + case LDAP_OPT_X_SASL_CBINDING: + i = ldap_pvt_sasl_cbinding_parse( arg ); + if ( i >= 0 ) { + lo->ldo_sasl_cbinding = i; + return 0; + } + break; + } + + return -1; +} + +int +ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) +{ + if ( option == LDAP_OPT_X_SASL_MECHLIST ) { + *(char ***)arg = (char **)sasl_global_listmech(); + return 0; + } + + if ( ld == NULL ) + return -1; + + switch ( option ) { + case LDAP_OPT_X_SASL_MECH: { + *(char **)arg = ld->ld_options.ldo_def_sasl_mech + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL; + } break; + case LDAP_OPT_X_SASL_REALM: { + *(char **)arg = ld->ld_options.ldo_def_sasl_realm + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL; + } break; + case LDAP_OPT_X_SASL_AUTHCID: { + *(char **)arg = ld->ld_options.ldo_def_sasl_authcid + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL; + } break; + case LDAP_OPT_X_SASL_AUTHZID: { + *(char **)arg = ld->ld_options.ldo_def_sasl_authzid + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL; + } break; + + case LDAP_OPT_X_SASL_SSF: { + int sc; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_sockctx; + + if ( ctx == NULL ) { + return -1; + } + + sc = sasl_getprop( ctx, SASL_SSF, + (SASL_CONST void **)(char *) &ssf ); + + if ( sc != SASL_OK ) { + return -1; + } + + *(ber_len_t *)arg = *ssf; + } break; + + case LDAP_OPT_X_SASL_SSF_EXTERNAL: + /* this option is write only */ + return -1; + + case LDAP_OPT_X_SASL_SSF_MIN: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf; + break; + case LDAP_OPT_X_SASL_SSF_MAX: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf; + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize; + break; + case LDAP_OPT_X_SASL_NOCANON: + *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + break; + + case LDAP_OPT_X_SASL_USERNAME: { + int sc; + char *username; + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return -1; + } + + sc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char **) &username ); + + if ( sc != SASL_OK ) { + return -1; + } + + *(char **)arg = username ? LDAP_STRDUP( username ) : NULL; + } break; + + case LDAP_OPT_X_SASL_SECPROPS: + /* this option is write only */ + return -1; + + case LDAP_OPT_X_SASL_CBINDING: + *(int *)arg = ld->ld_options.ldo_sasl_cbinding; + break; + +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + + default: + return -1; + } + return 0; +} + +int +ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) +{ + if ( ld == NULL ) + return -1; + + if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON ) + return -1; + + switch ( option ) { + case LDAP_OPT_X_SASL_SSF: + case LDAP_OPT_X_SASL_USERNAME: + /* This option is read-only */ + return -1; + + case LDAP_OPT_X_SASL_SSF_EXTERNAL: { + int sc; +#if SASL_VERSION_MAJOR < 2 + sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf; +#endif + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return -1; + } + +#if SASL_VERSION_MAJOR >= 2 + sasl_ssf = * (ber_len_t *)arg; + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf); +#else + memset(&extprops, 0L, sizeof(extprops)); + + extprops.ssf = * (ber_len_t *) arg; + + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); +#endif + + if ( sc != SASL_OK ) { + return -1; + } + } break; + + case LDAP_OPT_X_SASL_SSF_MIN: + ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_SSF_MAX: + ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_NOCANON: + if ( arg == LDAP_OPT_OFF ) { + LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } else { + LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } + break; + + case LDAP_OPT_X_SASL_SECPROPS: { + int sc; + sc = ldap_pvt_sasl_secprops( (char *) arg, + &ld->ld_options.ldo_sasl_secprops ); + + return sc == LDAP_SUCCESS ? 0 : -1; + } + + case LDAP_OPT_X_SASL_CBINDING: + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_SASL_CBINDING_NONE: + case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE: + case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT: + ld->ld_options.ldo_sasl_cbinding = *(int *) arg; + return 0; + } + return -1; + +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + + default: + return -1; + } + return 0; +} + +#ifdef LDAP_R_COMPILE +#define LDAP_DEBUG_R_SASL +void *ldap_pvt_sasl_mutex_new(void) +{ + ldap_pvt_thread_mutex_t *mutex; + + mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1, + sizeof(ldap_pvt_thread_mutex_t) ); + + if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) { + return mutex; + } + LDAP_FREE( mutex ); +#ifndef LDAP_DEBUG_R_SASL + assert( 0 ); +#endif /* !LDAP_DEBUG_R_SASL */ + return NULL; +} + +int ldap_pvt_sasl_mutex_lock(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return SASL_OK; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex ) + ? SASL_FAIL : SASL_OK; +} + +int ldap_pvt_sasl_mutex_unlock(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return SASL_OK; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex ) + ? SASL_FAIL : SASL_OK; +} + +void ldap_pvt_sasl_mutex_dispose(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex ); + LDAP_FREE( mutex ); +} +#endif + +#else +int ldap_int_sasl_init( void ) +{ return LDAP_SUCCESS; } + +int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) +{ return LDAP_SUCCESS; } + +int +ldap_int_sasl_bind( + LDAP *ld, + const char *dn, + const char *mechs, + LDAPControl **sctrls, + LDAPControl **cctrls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ return LDAP_NOT_SUPPORTED; } + +int +ldap_int_sasl_external( + LDAP *ld, + LDAPConn *conn, + const char * authid, + ber_len_t ssf ) +{ return LDAP_SUCCESS; } + +#endif /* HAVE_CYRUS_SASL */ diff --git a/contrib/libs/openldap/libraries/libldap/dds.c b/contrib/libs/openldap/libraries/libldap/dds.c new file mode 100644 index 0000000000..7244d05f9a --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/dds.c @@ -0,0 +1,156 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2022 The OpenLDAP Foundation. + * Portions Copyright 2005-2006 SysNet s.n.c. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Pierangelo Masarati for inclusion + * in OpenLDAP Software */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_parse_refresh( LDAP *ld, LDAPMessage *res, ber_int_t *newttl ) +{ + int rc; + struct berval *retdata = NULL; + ber_tag_t tag; + BerElement *ber; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( newttl != NULL ); + + *newttl = 0; + + rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 ); + + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ld->ld_errno != LDAP_SUCCESS ) { + return ld->ld_errno; + } + + if ( retdata == NULL ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + return rc; + } + + ber = ber_init( retdata ); + if ( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + /* check the tag */ + tag = ber_scanf( ber, "{i}", newttl ); + ber_free( ber, 1 ); + + if ( tag != LDAP_TAG_EXOP_REFRESH_RES_TTL ) { + *newttl = 0; + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + +done:; + if ( retdata ) { + ber_bvfree( retdata ); + } + + return rc; +} + +int +ldap_refresh( + LDAP *ld, + struct berval *dn, + ber_int_t ttl, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + struct berval bv = { 0, NULL }; + BerElement *ber = NULL; + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + *msgidp = -1; + + ber = ber_alloc_t( LBER_USE_DER ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ber_printf( ber, "{tOtiN}", + LDAP_TAG_EXOP_REFRESH_REQ_DN, dn, + LDAP_TAG_EXOP_REFRESH_REQ_TTL, ttl ); + + rc = ber_flatten2( ber, &bv, 0 ); + + if ( rc < 0 ) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_REFRESH, &bv, + sctrls, cctrls, msgidp ); + +done:; + ber_free( ber, 1 ); + + return rc; +} + +int +ldap_refresh_s( + LDAP *ld, + struct berval *dn, + ber_int_t ttl, + ber_int_t *newttl, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_refresh( ld, dn, ttl, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) return rc; + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *)NULL, &res ); + if( rc == -1 || !res ) return ld->ld_errno; + + rc = ldap_parse_refresh( ld, res, newttl ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return ldap_result2error( ld, res, 1 ); +} + diff --git a/contrib/libs/openldap/libraries/libldap/delete.c b/contrib/libs/openldap/libraries/libldap/delete.c new file mode 100644 index 0000000000..b086f97e23 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/delete.c @@ -0,0 +1,174 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * A delete request looks like this: + * DelRequest ::= DistinguishedName, + */ + +BerElement * +ldap_build_delete_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + rc = ber_printf( ber, "{its", /* '}' */ + *msgidp, LDAP_REQ_DELETE, dn ); + if ( rc == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_delete_ext - initiate an ldap extended delete operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to delete + * sctrls Server Controls + * cctrls Client Controls + * msgidp Message Id Pointer + * + * Example: + * rc = ldap_delete( ld, dn, sctrls, cctrls, msgidp ); + */ +int +ldap_delete_ext( + LDAP *ld, + LDAP_CONST char* dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *ber; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_delete_ext\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_delete_req( ld, dn, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_DELETE, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_delete_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int msgid; + int rc; + LDAPMessage *res; + + rc = ldap_delete_ext( ld, dn, sctrls, cctrls, &msgid ); + + if( rc != LDAP_SUCCESS ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} +/* + * ldap_delete - initiate an ldap (and X.500) delete operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to delete + * + * Example: + * msgid = ldap_delete( ld, dn ); + */ +int +ldap_delete( LDAP *ld, LDAP_CONST char *dn ) +{ + int msgid; + + /* + * A delete request looks like this: + * DelRequest ::= DistinguishedName, + */ + + Debug0( LDAP_DEBUG_TRACE, "ldap_delete\n" ); + + return ldap_delete_ext( ld, dn, NULL, NULL, &msgid ) == LDAP_SUCCESS + ? msgid : -1 ; +} + + +int +ldap_delete_s( LDAP *ld, LDAP_CONST char *dn ) +{ + return ldap_delete_ext_s( ld, dn, NULL, NULL ); +} diff --git a/contrib/libs/openldap/libraries/libldap/deref.c b/contrib/libs/openldap/libraries/libldap/deref.c new file mode 100644 index 0000000000..f187a9fd44 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/deref.c @@ -0,0 +1,289 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 2008 Pierangelo Masarati. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_create_deref_control_value( + LDAP *ld, + LDAPDerefSpec *ds, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + int i; + + if ( ld == NULL || value == NULL || ds == NULL ) + { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{" /*}*/ ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( i = 0; ds[i].derefAttr != NULL; i++ ) { + int j; + + tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( j = 0; ds[i].attributes[j] != NULL; j++ ) { + tag = ber_printf( ber, "s", ds[i].attributes[ j ] ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{{*/ "}N}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{*/ "}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_deref_control( + LDAP *ld, + LDAPDerefSpec *ds, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DEREF, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +void +ldap_derefresponse_free( LDAPDerefRes *dr ) +{ + for ( ; dr; ) { + LDAPDerefRes *drnext = dr->next; + LDAPDerefVal *dv; + + LDAP_FREE( dr->derefAttr ); + LDAP_FREE( dr->derefVal.bv_val ); + + for ( dv = dr->attrVals; dv; ) { + LDAPDerefVal *dvnext = dv->next; + LDAP_FREE( dv->type ); + ber_bvarray_free( dv->vals ); + LDAP_FREE( dv ); + dv = dvnext; + } + + LDAP_FREE( dr ); + + dr = drnext; + } +} + +int +ldap_parse_derefresponse_control( + LDAP *ld, + LDAPControl *ctrl, + LDAPDerefRes **drp2 ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + char *last; + LDAPDerefRes *drhead = NULL, **drp; + + if ( ld == NULL || ctrl == NULL || drp2 == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + /* Set up a BerElement from the berval returned in the control. */ + ber_init2( ber, &ctrl->ldctl_value, 0 ); + + /* Extract the count and cookie from the control. */ + drp = &drhead; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + LDAPDerefRes *dr; + LDAPDerefVal **dvp; + char *last2; + + dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) ); + if ( dr == NULL ) { + ldap_derefresponse_free( drhead ); + *drp2 = NULL; + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + dvp = &dr->attrVals; + + tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal ); + if ( tag == LBER_ERROR ) { + goto done; + } + + tag = ber_peek_tag( ber, &len ); + if ( tag == (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ) { + for ( tag = ber_first_element( ber, &len, &last2 ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last2 ) ) + { + LDAPDerefVal *dv; + + dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) ); + if ( dv == NULL ) { + ldap_derefresponse_free( drhead ); + LDAP_FREE( dr ); + *drp2 = NULL; + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *dvp = dv; + dvp = &dv->next; + } + } + + tag = ber_scanf( ber, "}" ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *drp = dr; + drp = &dr->next; + } + + tag = 0; + +done:; + if ( tag == LBER_ERROR ) { + if ( drhead != NULL ) { + ldap_derefresponse_free( drhead ); + } + + *drp2 = NULL; + ld->ld_errno = LDAP_DECODING_ERROR; + + } else { + *drp2 = drhead; + ld->ld_errno = LDAP_SUCCESS; + } + + return ld->ld_errno; +} + +int +ldap_parse_deref_control( + LDAP *ld, + LDAPControl **ctrls, + LDAPDerefRes **drp ) +{ + LDAPControl *c; + + if ( drp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + *drp = NULL; + + if ( ctrls == NULL ) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL ); + if ( c == NULL ) { + /* No deref control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp ); + + return ld->ld_errno; +} + diff --git a/contrib/libs/openldap/libraries/libldap/dnssrv.c b/contrib/libs/openldap/libraries/libldap/dnssrv.c new file mode 100644 index 0000000000..433c37f5e0 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/dnssrv.c @@ -0,0 +1,422 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * locate LDAP servers using DNS SRV records. + * Location code based on MIT Kerberos KDC location code. + */ +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif +#ifdef HAVE_RESOLV_H +#include <resolv.h> +#endif + +int ldap_dn2domain( + LDAP_CONST char *dn_in, + char **domainp) +{ + int i, j; + char *ndomain; + LDAPDN dn = NULL; + LDAPRDN rdn = NULL; + LDAPAVA *ava = NULL; + struct berval domain = BER_BVNULL; + static const struct berval DC = BER_BVC("DC"); + static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25"); + + assert( dn_in != NULL ); + assert( domainp != NULL ); + + *domainp = NULL; + + if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) { + return -2; + } + + if( dn ) for( i=0; dn[i] != NULL; i++ ) { + rdn = dn[i]; + + for( j=0; rdn[j] != NULL; j++ ) { + ava = rdn[j]; + + if( rdn[j+1] == NULL && + (ava->la_flags & LDAP_AVA_STRING) && + ava->la_value.bv_len && + ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0 + || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) ) + { + if( domain.bv_len == 0 ) { + ndomain = LDAP_REALLOC( domain.bv_val, + ava->la_value.bv_len + 1); + + if( ndomain == NULL ) { + goto return_error; + } + + domain.bv_val = ndomain; + + AC_MEMCPY( domain.bv_val, ava->la_value.bv_val, + ava->la_value.bv_len ); + + domain.bv_len = ava->la_value.bv_len; + domain.bv_val[domain.bv_len] = '\0'; + + } else { + ndomain = LDAP_REALLOC( domain.bv_val, + ava->la_value.bv_len + sizeof(".") + domain.bv_len ); + + if( ndomain == NULL ) { + goto return_error; + } + + domain.bv_val = ndomain; + domain.bv_val[domain.bv_len++] = '.'; + AC_MEMCPY( &domain.bv_val[domain.bv_len], + ava->la_value.bv_val, ava->la_value.bv_len ); + domain.bv_len += ava->la_value.bv_len; + domain.bv_val[domain.bv_len] = '\0'; + } + } else { + domain.bv_len = 0; + } + } + } + + + if( domain.bv_len == 0 && domain.bv_val != NULL ) { + LDAP_FREE( domain.bv_val ); + domain.bv_val = NULL; + } + + ldap_dnfree( dn ); + *domainp = domain.bv_val; + return 0; + +return_error: + ldap_dnfree( dn ); + LDAP_FREE( domain.bv_val ); + return -1; +} + +int ldap_domain2dn( + LDAP_CONST char *domain_in, + char **dnp) +{ + char *domain, *s, *tok_r, *dn, *dntmp; + size_t loc; + + assert( domain_in != NULL ); + assert( dnp != NULL ); + + domain = LDAP_STRDUP(domain_in); + if (domain == NULL) { + return LDAP_NO_MEMORY; + } + dn = NULL; + loc = 0; + + for (s = ldap_pvt_strtok(domain, ".", &tok_r); + s != NULL; + s = ldap_pvt_strtok(NULL, ".", &tok_r)) + { + size_t len = strlen(s); + + dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len ); + if (dntmp == NULL) { + if (dn != NULL) + LDAP_FREE(dn); + LDAP_FREE(domain); + return LDAP_NO_MEMORY; + } + + dn = dntmp; + + if (loc > 0) { + /* not first time. */ + strcpy(dn + loc, ","); + loc++; + } + strcpy(dn + loc, "dc="); + loc += sizeof("dc=")-1; + + strcpy(dn + loc, s); + loc += len; + } + + LDAP_FREE(domain); + *dnp = dn; + return LDAP_SUCCESS; +} + +#ifdef HAVE_RES_QUERY +#define DNSBUFSIZ (64*1024) +#define MAXHOST 254 /* RFC 1034, max length is 253 chars */ +typedef struct srv_record { + u_short priority; + u_short weight; + u_short port; + char hostname[MAXHOST]; +} srv_record; + +/* Linear Congruential Generator - we don't need + * high quality randomness, and we don't want to + * interfere with anyone else's use of srand(). + * + * The PRNG here cycles thru 941,955 numbers. + */ +static float srv_seed; + +static void srv_srand(int seed) { + srv_seed = (float)seed / (float)RAND_MAX; +} + +static float srv_rand() { + float val = 9821.0 * srv_seed + .211327; + srv_seed = val - (int)val; + return srv_seed; +} + +static int srv_cmp(const void *aa, const void *bb){ + srv_record *a=(srv_record *)aa; + srv_record *b=(srv_record *)bb; + int i = a->priority - b->priority; + if (i) return i; + return b->weight - a->weight; +} + +static void srv_shuffle(srv_record *a, int n) { + int i, j, total = 0, r, p; + + for (i=0; i<n; i++) + total += a[i].weight; + + /* Do a shuffle per RFC2782 Page 4 */ + for (p=n; p>1; a++, p--) { + if (!total) { + /* all remaining weights are zero, + do a straight Fisher-Yates shuffle */ + j = srv_rand() * p; + } else { + r = srv_rand() * total; + for (j=0; j<p; j++) { + r -= a[j].weight; + if (r < 0) { + total -= a[j].weight; + break; + } + } + } + if (j && j<p) { + srv_record t = a[0]; + a[0] = a[j]; + a[j] = t; + } + } +} +#endif /* HAVE_RES_QUERY */ + +/* + * Lookup and return LDAP servers for domain (using the DNS + * SRV record _ldap._tcp.domain). + */ +int ldap_domain2hostlist( + LDAP_CONST char *domain, + char **list ) +{ +#ifdef HAVE_RES_QUERY + char *request; + char *hostlist = NULL; + srv_record *hostent_head=NULL; + int i, j; + int rc, len, cur = 0; + unsigned char reply[DNSBUFSIZ]; + int hostent_count=0; + + assert( domain != NULL ); + assert( list != NULL ); + if( *domain == '\0' ) { + return LDAP_PARAM_ERROR; + } + + request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp.")); + if (request == NULL) { + return LDAP_NO_MEMORY; + } + sprintf(request, "_ldap._tcp.%s", domain); + + LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); + + rc = LDAP_UNAVAILABLE; +#ifdef NS_HFIXEDSZ + /* Bind 8/9 interface */ + len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply)); +# ifndef T_SRV +# define T_SRV ns_t_srv +# endif +#else + /* Bind 4 interface */ +# ifndef T_SRV +# define T_SRV 33 +# endif + + len = res_query(request, C_IN, T_SRV, reply, sizeof(reply)); +#endif + if (len >= 0) { + unsigned char *p; + char host[DNSBUFSIZ]; + int status; + u_short port, priority, weight; + + /* Parse out query */ + p = reply; + +#ifdef NS_HFIXEDSZ + /* Bind 8/9 interface */ + p += NS_HFIXEDSZ; +#elif defined(HFIXEDSZ) + /* Bind 4 interface w/ HFIXEDSZ */ + p += HFIXEDSZ; +#else + /* Bind 4 interface w/o HFIXEDSZ */ + p += sizeof(HEADER); +#endif + + status = dn_expand(reply, reply + len, p, host, sizeof(host)); + if (status < 0) { + goto out; + } + p += status; + p += 4; + + while (p < reply + len) { + int type, class, ttl, size; + status = dn_expand(reply, reply + len, p, host, sizeof(host)); + if (status < 0) { + goto out; + } + p += status; + type = (p[0] << 8) | p[1]; + p += 2; + class = (p[0] << 8) | p[1]; + p += 2; + ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + p += 4; + size = (p[0] << 8) | p[1]; + p += 2; + if (type == T_SRV) { + status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); + if (status < 0) { + goto out; + } + + /* Get priority weight and port */ + priority = (p[0] << 8) | p[1]; + weight = (p[2] << 8) | p[3]; + port = (p[4] << 8) | p[5]; + + if ( port == 0 || host[ 0 ] == '\0' ) { + goto add_size; + } + + hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record))); + if(hostent_head==NULL){ + rc=LDAP_NO_MEMORY; + goto out; + } + hostent_head[hostent_count].priority=priority; + hostent_head[hostent_count].weight=weight; + hostent_head[hostent_count].port=port; + strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1); + hostent_head[hostent_count].hostname[MAXHOST-1] = '\0'; + hostent_count++; + } +add_size:; + p += size; + } + if (!hostent_head) goto out; + qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp); + + if (!srv_seed) + srv_srand(time(0L)); + + /* shuffle records of same priority */ + j = 0; + priority = hostent_head[0].priority; + for (i=1; i<hostent_count; i++) { + if (hostent_head[i].priority != priority) { + priority = hostent_head[i].priority; + if (i-j > 1) + srv_shuffle(hostent_head+j, i-j); + j = i; + } + } + if (i-j > 1) + srv_shuffle(hostent_head+j, i-j); + + for(i=0; i<hostent_count; i++){ + int buflen; + buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 "); + hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1); + if (hostlist == NULL) { + rc = LDAP_NO_MEMORY; + goto out; + } + if(cur>0){ + hostlist[cur++]=' '; + } + cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port); + } + } + + if (hostlist == NULL) { + /* No LDAP servers found in DNS. */ + rc = LDAP_UNAVAILABLE; + goto out; + } + + rc = LDAP_SUCCESS; + *list = hostlist; + + out: + LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); + + if (request != NULL) { + LDAP_FREE(request); + } + if (hostent_head != NULL) { + LDAP_FREE(hostent_head); + } + if (rc != LDAP_SUCCESS && hostlist != NULL) { + LDAP_FREE(hostlist); + } + return rc; +#else + return LDAP_NOT_SUPPORTED; +#endif /* HAVE_RES_QUERY */ +} diff --git a/contrib/libs/openldap/libraries/libldap/error.c b/contrib/libs/openldap/libraries/libldap/error.c new file mode 100644 index 0000000000..444a0244bf --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/error.c @@ -0,0 +1,395 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +void ldap_int_error_init( void ) { +} + +char * +ldap_err2string( int err ) +{ + char *m; + + Debug0( LDAP_DEBUG_TRACE, "ldap_err2string\n" ); + + switch ( err ) { +# define C(code, message) case code: m = message; break + + /* LDAPv3 (RFC 4511) codes */ + C(LDAP_SUCCESS, N_("Success")); + C(LDAP_OPERATIONS_ERROR, N_("Operations error")); + C(LDAP_PROTOCOL_ERROR, N_("Protocol error")); + C(LDAP_TIMELIMIT_EXCEEDED, N_("Time limit exceeded")); + C(LDAP_SIZELIMIT_EXCEEDED, N_("Size limit exceeded")); + C(LDAP_COMPARE_FALSE, N_("Compare False")); + C(LDAP_COMPARE_TRUE, N_("Compare True")); + C(LDAP_STRONG_AUTH_NOT_SUPPORTED,N_("Authentication method not supported")); + C(LDAP_STRONG_AUTH_REQUIRED, N_("Strong(er) authentication required")); + + C(LDAP_REFERRAL, N_("Referral")); + C(LDAP_ADMINLIMIT_EXCEEDED, N_("Administrative limit exceeded")); + C(LDAP_UNAVAILABLE_CRITICAL_EXTENSION, + N_("Critical extension is unavailable")); + C(LDAP_CONFIDENTIALITY_REQUIRED,N_("Confidentiality required")); + C(LDAP_SASL_BIND_IN_PROGRESS, N_("SASL bind in progress")); + + C(LDAP_NO_SUCH_ATTRIBUTE, N_("No such attribute")); + C(LDAP_UNDEFINED_TYPE, N_("Undefined attribute type")); + C(LDAP_INAPPROPRIATE_MATCHING, N_("Inappropriate matching")); + C(LDAP_CONSTRAINT_VIOLATION, N_("Constraint violation")); + C(LDAP_TYPE_OR_VALUE_EXISTS, N_("Type or value exists")); + C(LDAP_INVALID_SYNTAX, N_("Invalid syntax")); + + C(LDAP_NO_SUCH_OBJECT, N_("No such object")); + C(LDAP_ALIAS_PROBLEM, N_("Alias problem")); + C(LDAP_INVALID_DN_SYNTAX, N_("Invalid DN syntax")); + + C(LDAP_ALIAS_DEREF_PROBLEM, N_("Alias dereferencing problem")); + + C(LDAP_INAPPROPRIATE_AUTH, N_("Inappropriate authentication")); + C(LDAP_INVALID_CREDENTIALS, N_("Invalid credentials")); + C(LDAP_INSUFFICIENT_ACCESS, N_("Insufficient access")); + C(LDAP_BUSY, N_("Server is busy")); + C(LDAP_UNAVAILABLE, N_("Server is unavailable")); + C(LDAP_UNWILLING_TO_PERFORM, N_("Server is unwilling to perform")); + C(LDAP_LOOP_DETECT, N_("Loop detected")); + + C(LDAP_NAMING_VIOLATION, N_("Naming violation")); + C(LDAP_OBJECT_CLASS_VIOLATION, N_("Object class violation")); + C(LDAP_NOT_ALLOWED_ON_NONLEAF, N_("Operation not allowed on non-leaf")); + C(LDAP_NOT_ALLOWED_ON_RDN, N_("Operation not allowed on RDN")); + C(LDAP_ALREADY_EXISTS, N_("Already exists")); + C(LDAP_NO_OBJECT_CLASS_MODS, N_("Cannot modify object class")); + + C(LDAP_AFFECTS_MULTIPLE_DSAS, N_("Operation affects multiple DSAs")); + + /* Virtual List View draft */ + C(LDAP_VLV_ERROR, N_("Virtual List View error")); + + C(LDAP_OTHER, N_("Other (e.g., implementation specific) error")); + + /* LDAPv2 (RFC 1777) codes */ + C(LDAP_PARTIAL_RESULTS, N_("Partial results and referral received")); + C(LDAP_IS_LEAF, N_("Entry is a leaf")); + + /* Connection-less LDAP (CLDAP - RFC 1798) code */ + C(LDAP_RESULTS_TOO_LARGE, N_("Results too large")); + + /* Cancel Operation (RFC 3909) codes */ + C(LDAP_CANCELLED, N_("Cancelled")); + C(LDAP_NO_SUCH_OPERATION, N_("No Operation to Cancel")); + C(LDAP_TOO_LATE, N_("Too Late to Cancel")); + C(LDAP_CANNOT_CANCEL, N_("Cannot Cancel")); + + /* Assert Control (RFC 4528 and old internet-draft) codes */ + C(LDAP_ASSERTION_FAILED, N_("Assertion Failed")); + C(LDAP_X_ASSERTION_FAILED, N_("Assertion Failed (X)")); + + /* Proxied Authorization Control (RFC 4370 and I-D) codes */ + C(LDAP_PROXIED_AUTHORIZATION_DENIED, N_("Proxied Authorization Denied")); + C(LDAP_X_PROXY_AUTHZ_FAILURE, N_("Proxy Authorization Failure (X)")); + + /* Content Sync Operation (RFC 4533 and I-D) codes */ + C(LDAP_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required")); + C(LDAP_X_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required (X)")); + + /* No-Op Control (draft-zeilenga-ldap-noop) code */ + C(LDAP_X_NO_OPERATION, N_("No Operation (X)")); + + /* Client Update Protocol (RFC 3928) codes */ + C(LDAP_CUP_RESOURCES_EXHAUSTED, N_("LCUP Resources Exhausted")); + C(LDAP_CUP_SECURITY_VIOLATION, N_("LCUP Security Violation")); + C(LDAP_CUP_INVALID_DATA, N_("LCUP Invalid Data")); + C(LDAP_CUP_UNSUPPORTED_SCHEME, N_("LCUP Unsupported Scheme")); + C(LDAP_CUP_RELOAD_REQUIRED, N_("LCUP Reload Required")); + + C(LDAP_TXN_SPECIFY_OKAY, N_("TXN specify okay")); + C(LDAP_TXN_ID_INVALID, N_("TXN ID is invalid")); + + /* API codes - renumbered since draft-ietf-ldapext-ldap-c-api */ + C(LDAP_SERVER_DOWN, N_("Can't contact LDAP server")); + C(LDAP_LOCAL_ERROR, N_("Local error")); + C(LDAP_ENCODING_ERROR, N_("Encoding error")); + C(LDAP_DECODING_ERROR, N_("Decoding error")); + C(LDAP_TIMEOUT, N_("Timed out")); + C(LDAP_AUTH_UNKNOWN, N_("Unknown authentication method")); + C(LDAP_FILTER_ERROR, N_("Bad search filter")); + C(LDAP_USER_CANCELLED, N_("User cancelled operation")); + C(LDAP_PARAM_ERROR, N_("Bad parameter to an ldap routine")); + C(LDAP_NO_MEMORY, N_("Out of memory")); + C(LDAP_CONNECT_ERROR, N_("Connect error")); + C(LDAP_NOT_SUPPORTED, N_("Not Supported")); + C(LDAP_CONTROL_NOT_FOUND, N_("Control not found")); + C(LDAP_NO_RESULTS_RETURNED, N_("No results returned")); + C(LDAP_MORE_RESULTS_TO_RETURN, N_("More results to return")); + C(LDAP_CLIENT_LOOP, N_("Client Loop")); + C(LDAP_REFERRAL_LIMIT_EXCEEDED, N_("Referral Limit Exceeded")); + C(LDAP_X_CONNECTING, N_("Connecting (X)")); +# undef C + + default: + m = (LDAP_API_ERROR(err) ? N_("Unknown API error") + : LDAP_E_ERROR(err) ? N_("Unknown (extension) error") + : LDAP_X_ERROR(err) ? N_("Unknown (private extension) error") + : N_("Unknown error")); + break; + } + + return _(m); +} + +/* deprecated */ +void +ldap_perror( LDAP *ld, LDAP_CONST char *str ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( str != NULL ); + + fprintf( stderr, "%s: %s (%d)\n", + str ? str : "ldap_perror", + ldap_err2string( ld->ld_errno ), + ld->ld_errno ); + + if ( ld->ld_matched != NULL && ld->ld_matched[0] != '\0' ) { + fprintf( stderr, _("\tmatched DN: %s\n"), ld->ld_matched ); + } + + if ( ld->ld_error != NULL && ld->ld_error[0] != '\0' ) { + fprintf( stderr, _("\tadditional info: %s\n"), ld->ld_error ); + } + + if ( ld->ld_referrals != NULL && ld->ld_referrals[0] != NULL) { + fprintf( stderr, _("\treferrals:\n") ); + for (i=0; ld->ld_referrals[i]; i++) { + fprintf( stderr, _("\t\t%s\n"), ld->ld_referrals[i] ); + } + } + + fflush( stderr ); +} + +/* deprecated */ +int +ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit ) +{ + int rc, err; + + rc = ldap_parse_result( ld, r, &err, + NULL, NULL, NULL, NULL, freeit ); + + return err != LDAP_SUCCESS ? err : rc; +} + +/* + * Parse LDAPResult Messages: + * + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED, + * matchedDN LDAPDN, + * errorMessage LDAPString, + * referral [3] Referral OPTIONAL } + * + * including Bind results: + * + * BindResponse ::= [APPLICATION 1] SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds [7] OCTET STRING OPTIONAL } + * + * and ExtendedOp results: + * + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * response [11] OCTET STRING OPTIONAL } + * + */ +int +ldap_parse_result( + LDAP *ld, + LDAPMessage *r, + int *errcodep, + char **matcheddnp, + char **errmsgp, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit ) +{ + LDAPMessage *lm; + ber_int_t errcode = LDAP_SUCCESS; + + ber_tag_t tag; + BerElement *ber; + + Debug0( LDAP_DEBUG_TRACE, "ldap_parse_result\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( r != NULL ); + + if(errcodep != NULL) *errcodep = LDAP_SUCCESS; + if(matcheddnp != NULL) *matcheddnp = NULL; + if(errmsgp != NULL) *errmsgp = NULL; + if(referralsp != NULL) *referralsp = NULL; + if(serverctrls != NULL) *serverctrls = NULL; + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + /* Find the result, last msg in chain... */ + lm = r->lm_chain_tail; + /* FIXME: either this is not possible (assert?) + * or it should be handled */ + if ( lm != NULL ) { + switch ( lm->lm_msgtype ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + lm = NULL; + break; + + default: + break; + } + } + + if( lm == NULL ) { + errcode = ld->ld_errno = LDAP_NO_RESULTS_RETURNED; + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + goto done; + } + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + if ( ld->ld_referrals ) { + LDAP_VFREE( ld->ld_referrals ); + ld->ld_referrals = NULL; + } + + /* parse results */ + + ber = ber_dup( lm->lm_ber ); + + if ( ld->ld_version < LDAP_VERSION2 ) { + tag = ber_scanf( ber, "{iA}", + &ld->ld_errno, &ld->ld_error ); + + } else { + ber_len_t len; + + tag = ber_scanf( ber, "{iAA" /*}*/, + &ld->ld_errno, &ld->ld_matched, &ld->ld_error ); + + if( tag != LBER_ERROR ) { + /* peek for referrals */ + if( ber_peek_tag(ber, &len) == LDAP_TAG_REFERRAL ) { + tag = ber_scanf( ber, "v", &ld->ld_referrals ); + } + } + + /* need to clean out misc items */ + if( tag != LBER_ERROR ) { + if( lm->lm_msgtype == LDAP_RES_BIND ) { + /* look for sasl result credentials */ + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + + } else if( lm->lm_msgtype == LDAP_RES_EXTENDED ) { + /* look for exop result oid or value */ + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_OID ) { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + + if ( tag != LBER_ERROR && + ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_VALUE ) + { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + } + } + + if( tag != LBER_ERROR ) { + int rc = ldap_pvt_get_controls( ber, serverctrls ); + + if( rc != LDAP_SUCCESS ) { + tag = LBER_ERROR; + } + } + + if( tag != LBER_ERROR ) { + tag = ber_scanf( ber, /*{*/"}" ); + } + } + + if ( tag == LBER_ERROR ) { + ld->ld_errno = errcode = LDAP_DECODING_ERROR; + } + + if( ber != NULL ) { + ber_free( ber, 0 ); + } + + /* return */ + if( errcodep != NULL ) { + *errcodep = ld->ld_errno; + } + if ( errcode == LDAP_SUCCESS ) { + if( matcheddnp != NULL ) { + if ( ld->ld_matched ) + { + *matcheddnp = LDAP_STRDUP( ld->ld_matched ); + } + } + if( errmsgp != NULL ) { + if ( ld->ld_error ) + { + *errmsgp = LDAP_STRDUP( ld->ld_error ); + } + } + + if( referralsp != NULL) { + *referralsp = ldap_value_dup( ld->ld_referrals ); + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + +done: + if ( freeit ) { + ldap_msgfree( r ); + } + + return errcode; +} diff --git a/contrib/libs/openldap/libraries/libldap/extended.c b/contrib/libs/openldap/libraries/libldap/extended.c new file mode 100644 index 0000000000..b492fcdfc7 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/extended.c @@ -0,0 +1,419 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +BerElement * +ldap_build_extended_req( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if ( reqdata != NULL ) { + rc = ber_printf( ber, "{it{tstON}", /* '}' */ + *msgidp, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, reqoid, + LDAP_TAG_EXOP_REQ_VALUE, reqdata ); + + } else { + rc = ber_printf( ber, "{it{tsN}", /* '}' */ + *msgidp, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, reqoid ); + } + + if( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * LDAPv3 Extended Operation Request + * ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + * requestName [0] LDAPOID, + * requestValue [1] OCTET STRING OPTIONAL + * } + * + * LDAPv3 Extended Operation Response + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * response [11] OCTET STRING OPTIONAL + * } + * + * (Source RFC 4511) + */ + +int +ldap_extended_operation( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( reqoid != NULL && *reqoid != '\0' ); + assert( msgidp != NULL ); + + /* must be version 3 (or greater) */ + if ( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return( ld->ld_errno ); + } + + ber = ldap_build_extended_req( ld, reqoid, reqdata, + sctrls, cctrls, &id ); + if ( !ber ) + return( ld->ld_errno ); + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id ); + + return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +int +ldap_extended_operation_s( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + char **retoidp, + struct berval **retdatap ) +{ + int rc; + int msgid; + LDAPMessage *res; + + Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( reqoid != NULL && *reqoid != '\0' ); + + rc = ldap_extended_operation( ld, reqoid, reqdata, + sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return( ld->ld_errno ); + } + + if ( retoidp != NULL ) *retoidp = NULL; + if ( retdatap != NULL ) *retdatap = NULL; + + rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 ); + + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} + +/* Parse an extended result */ +int +ldap_parse_extended_result ( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + int freeit ) +{ + BerElement *ber; + ber_tag_t rc; + ber_tag_t tag; + ber_len_t len; + struct berval *resdata; + ber_int_t errcode; + char *resoid; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + Debug0( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n" ); + + if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + if( res->lm_msgtype != LDAP_RES_EXTENDED ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if( retoidp != NULL ) *retoidp = NULL; + if( retdatap != NULL ) *retdatap = NULL; + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + ber = ber_dup( res->lm_ber ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + rc = ber_scanf( ber, "{eAA" /*}*/, &errcode, + &ld->ld_matched, &ld->ld_error ); + + if( rc == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + resoid = NULL; + resdata = NULL; + + tag = ber_peek_tag( ber, &len ); + + if( tag == LDAP_TAG_REFERRAL ) { + /* skip over referral */ + if( ber_scanf( ber, "x" ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_EXOP_RES_OID ) { + /* we have a resoid */ + if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + assert( resoid[ 0 ] != '\0' ); + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_EXOP_RES_VALUE ) { + /* we have a resdata */ + if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + if( resoid != NULL ) LDAP_FREE( resoid ); + return ld->ld_errno; + } + } + + ber_free( ber, 0 ); + + if( retoidp != NULL ) { + *retoidp = resoid; + } else { + LDAP_FREE( resoid ); + } + + if( retdatap != NULL ) { + *retdatap = resdata; + } else { + ber_bvfree( resdata ); + } + + ld->ld_errno = errcode; + + if( freeit ) { + ldap_msgfree( res ); + } + + return LDAP_SUCCESS; +} + + +/* Parse an extended partial */ +int +ldap_parse_intermediate ( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + LDAPControl ***serverctrls, + int freeit ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + struct berval *resdata; + char *resoid; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + Debug0( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n" ); + + if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if( retoidp != NULL ) *retoidp = NULL; + if( retdatap != NULL ) *retdatap = NULL; + if( serverctrls != NULL ) *serverctrls = NULL; + + ber = ber_dup( res->lm_ber ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_scanf( ber, "{" /*}*/ ); + + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + resoid = NULL; + resdata = NULL; + + tag = ber_peek_tag( ber, &len ); + + /* + * NOTE: accept intermediate and extended response tag values + * as older versions of slapd(8) incorrectly used extended + * response tags. + * Should be removed when 2.2 is moved to Historic. + */ + if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) { + /* we have a resoid */ + if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + assert( resoid[ 0 ] != '\0' ); + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) { + /* we have a resdata */ + if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + if( resoid != NULL ) LDAP_FREE( resoid ); + return ld->ld_errno; + } + } + + if ( serverctrls == NULL ) { + ld->ld_errno = LDAP_SUCCESS; + goto free_and_return; + } + + if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + goto free_and_return; + } + + ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls ); + +free_and_return: + ber_free( ber, 0 ); + + if( retoidp != NULL ) { + *retoidp = resoid; + } else { + LDAP_FREE( resoid ); + } + + if( retdatap != NULL ) { + *retdatap = resdata; + } else { + ber_bvfree( resdata ); + } + + if( freeit ) { + ldap_msgfree( res ); + } + + return ld->ld_errno; +} + diff --git a/contrib/libs/openldap/libraries/libldap/fetch.c b/contrib/libs/openldap/libraries/libldap/fetch.c new file mode 100644 index 0000000000..536871bcfe --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/fetch.c @@ -0,0 +1,148 @@ +/* fetch.c - routines for fetching data at URLs */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * Portions Copyright 1999-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for + * inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/time.h> + +#ifdef HAVE_FETCH +#include <fetch.h> +#endif + +#include "lber_pvt.h" +#include "ldap_pvt.h" +#include "ldap_config.h" +#include "ldif.h" + +FILE * +ldif_open_url( + LDAP_CONST char *urlstr ) +{ + FILE *url; + + if( strncasecmp( "file:", urlstr, sizeof("file:")-1 ) == 0 ) { + char *p; + urlstr += sizeof("file:")-1; + + /* we don't check for LDAP_DIRSEP since URLs should contain '/' */ + if ( urlstr[0] == '/' && urlstr[1] == '/' ) { + urlstr += 2; + /* path must be absolute if authority is present + * technically, file://hostname/path is also legal but we don't + * accept a non-empty hostname + */ + if ( urlstr[0] != '/' ) { +#ifdef _WIN32 + /* An absolute path in improper file://C:/foo/bar format */ + if ( urlstr[1] != ':' ) +#endif + return NULL; + } +#ifdef _WIN32 + /* An absolute path in proper file:///C:/foo/bar format */ + if ( urlstr[2] == ':' ) + urlstr++; +#endif + } + + p = ber_strdup( urlstr ); + if ( p == NULL ) + return NULL; + + /* But we should convert to LDAP_DIRSEP before use */ + if ( LDAP_DIRSEP[0] != '/' ) { + char *s = p; + while (( s = strchr( s, '/' ))) + *s++ = LDAP_DIRSEP[0]; + } + + ldap_pvt_hex_unescape( p ); + + url = fopen( p, "rb" ); + + ber_memfree( p ); + } else { +#ifdef HAVE_FETCH + url = fetchGetURL( (char*) urlstr, "" ); +#else + url = NULL; +#endif + } + return url; +} + +int +ldif_fetch_url( + LDAP_CONST char *urlstr, + char **valuep, + ber_len_t *vlenp ) +{ + FILE *url; + char buffer[1024]; + char *p = NULL; + size_t total; + size_t bytes; + + *valuep = NULL; + *vlenp = 0; + + url = ldif_open_url( urlstr ); + + if( url == NULL ) { + return -1; + } + + total = 0; + + while( (bytes = fread( buffer, 1, sizeof(buffer), url )) != 0 ) { + char *newp = ber_memrealloc( p, total + bytes + 1 ); + if( newp == NULL ) { + ber_memfree( p ); + fclose( url ); + return -1; + } + p = newp; + AC_MEMCPY( &p[total], buffer, bytes ); + total += bytes; + } + + fclose( url ); + + if( total == 0 ) { + char *newp = ber_memrealloc( p, 1 ); + if( newp == NULL ) { + ber_memfree( p ); + return -1; + } + p = newp; + } + + p[total] = '\0'; + *valuep = p; + *vlenp = total; + + return 0; +} diff --git a/contrib/libs/openldap/libraries/libldap/filter.c b/contrib/libs/openldap/libraries/libldap/filter.c new file mode 100644 index 0000000000..e158fd1fe3 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/filter.c @@ -0,0 +1,1117 @@ +/* search.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +static int put_simple_vrFilter LDAP_P(( + BerElement *ber, + char *str )); + +static int put_vrFilter_list LDAP_P(( + BerElement *ber, + char *str )); + +static char *put_complex_filter LDAP_P(( + BerElement *ber, + char *str, + ber_tag_t tag, + int not )); + +static int put_simple_filter LDAP_P(( + BerElement *ber, + char *str )); + +static int put_substring_filter LDAP_P(( + BerElement *ber, + char *type, + char *str, + char *nextstar )); + +static int put_filter_list LDAP_P(( + BerElement *ber, + char *str, + ber_tag_t tag )); + +static int ldap_is_oid ( const char *str ) +{ + int i; + + if( LDAP_ALPHA( str[0] )) { + for( i=1; str[i]; i++ ) { + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; + + } else if LDAP_DIGIT( str[0] ) { + int dot=0; + for( i=1; str[i]; i++ ) { + if( LDAP_DIGIT( str[i] )) { + dot=0; + + } else if ( str[i] == '.' ) { + if( ++dot > 1 ) return 0; + + } else { + return 0; + } + } + return !dot; + } + + return 0; +} + +static int ldap_is_desc ( const char *str ) +{ + int i; + + if( LDAP_ALPHA( str[0] )) { + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + str = &str[i+1]; + goto options; + } + + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; + + } else if LDAP_DIGIT( str[0] ) { + int dot=0; + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + if( dot ) return 0; + str = &str[i+1]; + goto options; + } + + if( LDAP_DIGIT( str[i] )) { + dot=0; + + } else if ( str[i] == '.' ) { + if( ++dot > 1 ) return 0; + + } else { + return 0; + } + } + return !dot; + } + + return 0; + +options: + if( !LDAP_LDH( str[0] )) { + return 0; + } + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + str = &str[i+1]; + goto options; + } + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; +} + +static char * +find_right_paren( char *s ) +{ + int balance, escape; + + balance = 1; + escape = 0; + while ( *s && balance ) { + if ( !escape ) { + if ( *s == '(' ) { + balance++; + } else if ( *s == ')' ) { + balance--; + } + } + + escape = ( *s == '\\' && !escape ); + + if ( balance ) s++; + } + + return *s ? s : NULL; +} + +static int hex2value( int c ) +{ + if( c >= '0' && c <= '9' ) { + return c - '0'; + } + + if( c >= 'A' && c <= 'F' ) { + return c + (10 - (int) 'A'); + } + + if( c >= 'a' && c <= 'f' ) { + return c + (10 - (int) 'a'); + } + + return -1; +} + +char * +ldap_pvt_find_wildcard( const char *s ) +{ + for( ; *s; s++ ) { + switch( *s ) { + case '*': /* found wildcard */ + return (char *) s; + + case '(': + case ')': + return NULL; + + case '\\': + if( s[1] == '\0' ) return NULL; + + if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) { + s+=2; + + } else switch( s[1] ) { + default: + return NULL; + + /* allow RFC 1960 escapes */ + case '*': + case '(': + case ')': + case '\\': + s++; + } + } + } + + return (char *) s; +} + +/* unescape filter value */ +/* support both LDAP v2 and v3 escapes */ +/* output can include nul characters! */ +ber_slen_t +ldap_pvt_filter_value_unescape( char *fval ) +{ + ber_slen_t r, v; + int v1, v2; + + for( r=v=0; fval[v] != '\0'; v++ ) { + switch( fval[v] ) { + case '(': + case ')': + case '*': + return -1; + + case '\\': + /* escape */ + v++; + + if ( fval[v] == '\0' ) { + /* escape at end of string */ + return -1; + } + + if (( v1 = hex2value( fval[v] )) >= 0 ) { + /* LDAPv3 escape */ + if (( v2 = hex2value( fval[v+1] )) < 0 ) { + /* must be two digit code */ + return -1; + } + + fval[r++] = v1 * 16 + v2; + v++; + + } else { + /* LDAPv2 escape */ + switch( fval[v] ) { + case '(': + case ')': + case '*': + case '\\': + fval[r++] = fval[v]; + break; + default: + /* illegal escape */ + return -1; + } + } + break; + + default: + fval[r++] = fval[v]; + } + } + + fval[r] = '\0'; + return r; +} + +static char * +put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not ) +{ + char *next; + + /* + * We have (x(filter)...) with str sitting on + * the x. We have to find the paren matching + * the one before the x and put the intervening + * filters by calling put_filter_list(). + */ + + /* put explicit tag */ + if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) { + return NULL; + } + + str++; + if ( (next = find_right_paren( str )) == NULL ) { + return NULL; + } + + *next = '\0'; + if ( put_filter_list( ber, str, tag ) == -1 ) { + return NULL; + } + + /* close the '(' */ + *next++ = ')'; + + /* flush explicit tagged thang */ + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + return NULL; + } + + return next; +} + +int +ldap_pvt_put_filter( BerElement *ber, const char *str_in ) +{ + int rc; + char *freeme; + char *str; + char *next; + int parens, balance, escape; + + /* + * A Filter looks like this (RFC 4511 as extended by RFC 4526): + * Filter ::= CHOICE { + * and [0] SET SIZE (0..MAX) OF filter Filter, + * or [1] SET SIZE (0..MAX) OF filter Filter, + * not [2] Filter, + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeDescription, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] MatchingRuleAssertion, + * ... } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeDescription, + * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { + * initial [0] AssertionValue, -- only once + * any [1] AssertionValue, + * final [2] AssertionValue -- only once + * } + * } + * + * MatchingRuleAssertion ::= SEQUENCE { + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue, + * dnAttributes [4] BOOLEAN DEFAULT FALSE } + * + * Note: tags in a CHOICE are always explicit + */ + + Debug1( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': /*')'*/ + str++; + parens++; + + /* skip spaces */ + while( LDAP_SPACE( *str ) ) str++; + + switch ( *str ) { + case '&': + Debug0( LDAP_DEBUG_TRACE, "put_filter: AND\n" ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_AND, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '|': + Debug0( LDAP_DEBUG_TRACE, "put_filter: OR\n" ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_OR, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '!': + Debug0( LDAP_DEBUG_TRACE, "put_filter: NOT\n" ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_NOT, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '(': + rc = -1; + goto done; + + default: + Debug0( LDAP_DEBUG_TRACE, "put_filter: simple\n" ); + + balance = 1; + escape = 0; + next = str; + + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) { + balance++; + } else if ( *next == ')' ) { + balance--; + } + } + + if ( *next == '\\' && ! escape ) { + escape = 1; + } else { + escape = 0; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_simple_filter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + *next++ = /*'('*/ ')'; + + str = next; + parens--; + break; + } + break; + + case /*'('*/ ')': + Debug0( LDAP_DEBUG_TRACE, "put_filter: end\n" ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + Debug0( LDAP_DEBUG_TRACE, "put_filter: default\n" ); + next = strchr( str, '\0' ); + if ( put_simple_filter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + if ( !parens ) + break; + } + + rc = ( parens || *str ) ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +/* + * Put a list of filters like this "(filter1)(filter2)..." + */ + +static int +put_filter_list( BerElement *ber, char *str, ber_tag_t tag ) +{ + char *next = NULL; + char save; + + Debug1( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", + str ); + + while ( *str ) { + while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { + str++; + } + if ( *str == '\0' ) break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) { + return -1; + } + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1; + *next = save; + str = next; + + if( tag == LDAP_FILTER_NOT ) break; + } + + if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) { + return -1; + } + + return 0; +} + +static int +put_simple_filter( + BerElement *ber, + char *str ) +{ + char *s; + char *value; + ber_tag_t ftype; + int rc = -1; + + Debug1( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n", + str ); + + if ( str[0] == '=' ) return -1; + + str = LDAP_STRDUP( str ); + if( str == NULL ) return -1; + + if ( (s = strchr( str, '=' )) == NULL ) { + goto done; + } + + value = s + 1; + *s-- = '\0'; + + switch ( *s ) { + case '<': + ftype = LDAP_FILTER_LE; + *s = '\0'; + break; + + case '>': + ftype = LDAP_FILTER_GE; + *s = '\0'; + break; + + case '~': + ftype = LDAP_FILTER_APPROX; + *s = '\0'; + break; + + case ':': + /* RFC 4515 extensible filters are off the form: + * type [:dn] [:rule] := value + * or [:dn]:rule := value + */ + ftype = LDAP_FILTER_EXT; + *s = '\0'; + + { + char *dn = strchr( str, ':' ); + char *rule = NULL; + + if( dn != NULL ) { + *dn++ = '\0'; + rule = strchr( dn, ':' ); + + if( rule == NULL ) { + /* one colon */ + if ( strcasecmp(dn, "dn") == 0 ) { + /* must have attribute */ + if( !ldap_is_desc( str ) ) { + goto done; + } + + rule = ""; + + } else { + rule = dn; + dn = NULL; + } + + } else { + /* two colons */ + *rule++ = '\0'; + + if ( strcasecmp(dn, "dn") != 0 ) { + /* must have "dn" */ + goto done; + } + } + + } + + if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { + /* must have either type or rule */ + goto done; + } + + if ( *str != '\0' && !ldap_is_desc( str ) ) { + goto done; + } + + if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { + goto done; + } + + rc = ber_printf( ber, "t{" /*"}"*/, ftype ); + + if( rc != -1 && rule && *rule != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); + } + + if( rc != -1 && *str != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); + } + + if( rc != -1 ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "to", + LDAP_FILTER_EXT_VALUE, value, len ); + } else { + rc = -1; + } + } + + if( rc != -1 && dn ) { + rc = ber_printf( ber, "tb", + LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); + } + + if( rc != -1 ) { + rc = ber_printf( ber, /*"{"*/ "N}" ); + } + } + goto done; + + default: + if( !ldap_is_desc( str ) ) { + goto done; + + } else { + char *nextstar = ldap_pvt_find_wildcard( value ); + + if ( nextstar == NULL ) { + goto done; + + } else if ( *nextstar == '\0' ) { + ftype = LDAP_FILTER_EQUALITY; + + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + + } else { + rc = put_substring_filter( ber, str, value, nextstar ); + goto done; + } + } break; + } + + if( !ldap_is_desc( str ) ) goto done; + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + + } else { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "t{soN}", + ftype, str, value, len ); + } + } + +done: + if( rc != -1 ) rc = 0; + LDAP_FREE( str ); + return rc; +} + +static int +put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) +{ + int gotstar = 0; + ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; + + Debug2( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", + type, val ); + + if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { + return -1; + } + + for( ; *val; val=nextstar ) { + if ( gotstar ) + nextstar = ldap_pvt_find_wildcard( val ); + + if ( nextstar == NULL ) { + return -1; + } + + if ( *nextstar == '\0' ) { + ftype = LDAP_SUBSTRING_FINAL; + } else { + *nextstar++ = '\0'; + if ( gotstar++ == 0 ) { + ftype = LDAP_SUBSTRING_INITIAL; + } else { + ftype = LDAP_SUBSTRING_ANY; + } + } + + if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( val ); + + if ( len <= 0 ) { + return -1; + } + + if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) { + return -1; + } + } + } + + if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { + return -1; + } + + return 0; +} + +static int +put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc; + char *freeme; + char *str; + char *next; + int parens, balance, escape; + + /* + * A ValuesReturnFilter looks like this: + * + * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem + * SimpleFilterItem ::= CHOICE { + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeType, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 + * } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeType, + * SEQUENCE OF CHOICE { + * initial [0] IA5String, + * any [1] IA5String, + * final [2] IA5String + * } + * } + * + * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue } + * + * (Source: RFC 3876) + */ + + Debug1( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': /*')'*/ + str++; + parens++; + + /* skip spaces */ + while( LDAP_SPACE( *str ) ) str++; + + switch ( *str ) { + case '(': + if ( (next = find_right_paren( str )) == NULL ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_vrFilter_list( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + /* close the '(' */ + *next++ = ')'; + + str = next; + + parens--; + break; + + + default: + Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n" ); + + balance = 1; + escape = 0; + next = str; + + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) { + balance++; + } else if ( *next == ')' ) { + balance--; + } + } + + if ( *next == '\\' && ! escape ) { + escape = 1; + } else { + escape = 0; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + *next++ = /*'('*/ ')'; + + str = next; + parens--; + break; + } + break; + + case /*'('*/ ')': + Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: end\n" ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: default\n" ); + next = strchr( str, '\0' ); + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + } + + rc = parens ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +int +ldap_put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc =0; + + if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { + return -1; + } + + rc = put_vrFilter( ber, str_in ); + + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + rc = -1; + } + + return rc; +} + +static int +put_vrFilter_list( BerElement *ber, char *str ) +{ + char *next = NULL; + char save; + + Debug1( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", + str ); + + while ( *str ) { + while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { + str++; + } + if ( *str == '\0' ) break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) { + return -1; + } + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( put_vrFilter( ber, str ) == -1 ) return -1; + *next = save; + str = next; + } + + return 0; +} + +static int +put_simple_vrFilter( + BerElement *ber, + char *str ) +{ + char *s; + char *value; + ber_tag_t ftype; + int rc = -1; + + Debug1( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", + str ); + + str = LDAP_STRDUP( str ); + if( str == NULL ) return -1; + + if ( (s = strchr( str, '=' )) == NULL ) { + goto done; + } + + value = s + 1; + *s-- = '\0'; + + switch ( *s ) { + case '<': + ftype = LDAP_FILTER_LE; + *s = '\0'; + break; + + case '>': + ftype = LDAP_FILTER_GE; + *s = '\0'; + break; + + case '~': + ftype = LDAP_FILTER_APPROX; + *s = '\0'; + break; + + case ':': + /* According to ValuesReturnFilter control definition + * extensible filters are off the form: + * type [:rule] := value + * or :rule := value + */ + ftype = LDAP_FILTER_EXT; + *s = '\0'; + + { + char *rule = strchr( str, ':' ); + + if( rule == NULL ) { + /* must have attribute */ + if( !ldap_is_desc( str ) ) { + goto done; + } + rule = ""; + } else { + *rule++ = '\0'; + } + + if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { + /* must have either type or rule */ + goto done; + } + + if ( *str != '\0' && !ldap_is_desc( str ) ) { + goto done; + } + + if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { + goto done; + } + + rc = ber_printf( ber, "t{" /*"}"*/, ftype ); + + if( rc != -1 && rule && *rule != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); + } + + if( rc != -1 && *str != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); + } + + if( rc != -1 ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "to", + LDAP_FILTER_EXT_VALUE, value, len ); + } else { + rc = -1; + } + } + + if( rc != -1 ) { + rc = ber_printf( ber, /*"{"*/ "N}" ); + } + } + goto done; + + default: + if( !ldap_is_desc( str ) ) { + goto done; + + } else { + char *nextstar = ldap_pvt_find_wildcard( value ); + + if ( nextstar == NULL ) { + goto done; + + } else if ( *nextstar == '\0' ) { + ftype = LDAP_FILTER_EQUALITY; + + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + + } else { + rc = put_substring_filter( ber, str, value, nextstar ); + goto done; + } + } break; + } + + if( !ldap_is_desc( str ) ) goto done; + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + + } else { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "t{soN}", + ftype, str, value, len ); + } + } + +done: + if( rc != -1 ) rc = 0; + LDAP_FREE( str ); + return rc; +} + diff --git a/contrib/libs/openldap/libraries/libldap/free.c b/contrib/libs/openldap/libraries/libldap/free.c new file mode 100644 index 0000000000..4d09eeef45 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/free.c @@ -0,0 +1,107 @@ +/* free.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 The Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * free.c - some free routines are included here to avoid having to + * link in lots of extra code when not using certain features + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * C-API deallocator + */ +void +ldap_memfree( void *p ) +{ + LDAP_FREE( p ); +} + +void +ldap_memvfree( void **v ) +{ + LDAP_VFREE( v ); +} + +void * +ldap_memalloc( ber_len_t s ) +{ + return LDAP_MALLOC( s ); +} + +void * +ldap_memcalloc( ber_len_t n, ber_len_t s ) +{ + return LDAP_CALLOC( n, s ); +} + +void * +ldap_memrealloc( void* p, ber_len_t s ) +{ + return LDAP_REALLOC( p, s ); +} + +char * +ldap_strdup( LDAP_CONST char *p ) +{ + return LDAP_STRDUP( p ); +} + +/* + * free a null-terminated array of pointers to mod structures. the + * structures are freed, not the array itself, unless the freemods + * flag is set. + */ + +void +ldap_mods_free( LDAPMod **mods, int freemods ) +{ + int i; + + if ( mods == NULL ) + return; + + for ( i = 0; mods[i] != NULL; i++ ) { + if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) { + if( mods[i]->mod_bvalues != NULL ) + ber_bvecfree( mods[i]->mod_bvalues ); + + } else if( mods[i]->mod_values != NULL ) { + LDAP_VFREE( mods[i]->mod_values ); + } + + if ( mods[i]->mod_type != NULL ) { + LDAP_FREE( mods[i]->mod_type ); + } + + LDAP_FREE( (char *) mods[i] ); + } + + if ( freemods ) { + LDAP_FREE( (char *) mods ); + } +} diff --git a/contrib/libs/openldap/libraries/libldap/getattr.c b/contrib/libs/openldap/libraries/libldap/getattr.c new file mode 100644 index 0000000000..07398dffb5 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/getattr.c @@ -0,0 +1,157 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +char * +ldap_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **berout ) +{ + int rc; + ber_tag_t tag; + ber_len_t len = 0; + char *attr; + BerElement *ber; + + Debug0( LDAP_DEBUG_TRACE, "ldap_first_attribute\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( berout != NULL ); + + *berout = NULL; + + ber = ldap_alloc_ber_with_options( ld ); + if( ber == NULL ) { + return NULL; + } + + *ber = *entry->lm_ber; + + /* + * Skip past the sequence, dn, sequence of sequence leaving + * us at the first attribute. + */ + + tag = ber_scanf( ber, "{xl{" /*}}*/, &len ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + /* set the length to avoid overrun */ + rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len ); + if( rc != LBER_OPT_SUCCESS ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + if ( ber_pvt_ber_remaining( ber ) == 0 ) { + assert( len == 0 ); + ber_free( ber, 0 ); + return NULL; + } + assert( len != 0 ); + + /* snatch the first attribute */ + tag = ber_scanf( ber, "{ax}", &attr ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + *berout = ber; + return attr; +} + +/* ARGSUSED */ +char * +ldap_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber ) +{ + ber_tag_t tag; + char *attr; + + Debug0( LDAP_DEBUG_TRACE, "ldap_next_attribute\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( ber != NULL ); + + if ( ber_pvt_ber_remaining( ber ) == 0 ) { + return NULL; + } + + /* skip sequence, snarf attribute type, skip values */ + tag = ber_scanf( ber, "{ax}", &attr ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return NULL; + } + + return attr; +} + +/* Fetch attribute type and optionally fetch values. The type + * and values are referenced in-place from the BerElement, they are + * not dup'd into malloc'd memory. + */ +/* ARGSUSED */ +int +ldap_get_attribute_ber( LDAP *ld, LDAPMessage *entry, BerElement *ber, + BerValue *attr, BerVarray *vals ) +{ + ber_tag_t tag; + int rc = LDAP_SUCCESS; + + Debug0( LDAP_DEBUG_TRACE, "ldap_get_attribute_ber\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( ber != NULL ); + assert( attr != NULL ); + + attr->bv_val = NULL; + attr->bv_len = 0; + + if ( ber_pvt_ber_remaining( ber ) ) { + ber_len_t siz = sizeof( BerValue ); + + /* skip sequence, snarf attribute type */ + tag = ber_scanf( ber, vals ? "{mM}" : "{mx}", attr, vals, + &siz, (ber_len_t)0 ); + if( tag == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + } + + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/getdn.c b/contrib/libs/openldap/libraries/libldap/getdn.c new file mode 100644 index 0000000000..617059621f --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/getdn.c @@ -0,0 +1,3334 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_schema.h" +#include "ldif.h" + +/* extension to UFN that turns trailing "dc=value" rdns in DNS style, + * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */ +#define DC_IN_UFN + +/* parsing/printing routines */ +static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, + const char **next, unsigned flags, int *retFlags, void *ctx ); +static int DCE2strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int IA52strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int quotedIA52strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int hexstr2binval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int hexstr2bin( const char *str, char *c ); +static int byte2hexpair( const char *val, char *pair ); +static int binval2hexstr( struct berval *val, char *str ); +static int strval2strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2IA5strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2IA5str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2DCEstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2DCEstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2ADstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2ADstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN ); + +/* AVA helpers */ +static LDAPAVA * ldapava_new( + const struct berval *attr, const struct berval *val, unsigned flags, void *ctx ); + +/* Higher level helpers */ +static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *, unsigned, ber_len_t * ) ); +static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * )); +static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len ); +static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first ); +static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ); + +/* + * RFC 1823 ldap_get_dn + */ +char * +ldap_get_dn( LDAP *ld, LDAPMessage *entry ) +{ + char *dn; + BerElement tmp; + + Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID(ld) ); + assert( entry != NULL ); + + tmp = *entry->lm_ber; /* struct copy */ + if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( dn ); +} + +int +ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout, + BerValue *dn ) +{ + BerElement tmp, *ber; + ber_len_t len = 0; + int rc = LDAP_SUCCESS; + + Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID(ld) ); + assert( entry != NULL ); + assert( dn != NULL ); + + dn->bv_val = NULL; + dn->bv_len = 0; + + if ( berout ) { + *berout = NULL; + ber = ldap_alloc_ber_with_options( ld ); + if( ber == NULL ) { + return LDAP_NO_MEMORY; + } + *berout = ber; + } else { + ber = &tmp; + } + + *ber = *entry->lm_ber; /* struct copy */ + if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + if ( rc == LDAP_SUCCESS ) { + /* set the length to avoid overrun */ + rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len ); + if( rc != LBER_OPT_SUCCESS ) { + rc = ld->ld_errno = LDAP_LOCAL_ERROR; + } + } + if ( rc != LDAP_SUCCESS && berout ) { + ber_free( ber, 0 ); + *berout = NULL; + } + return rc; +} + +/* + * RFC 1823 ldap_dn2ufn + */ +char * +ldap_dn2ufn( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n" ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_UFN ); + + return( out ); +} + +/* + * RFC 1823 ldap_explode_dn + */ +char ** +ldap_explode_dn( LDAP_CONST char *dn, int notypes ) +{ + LDAPDN tmpDN; + char **values = NULL; + int iRDN; + unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3; + + Debug0( LDAP_DEBUG_TRACE, "ldap_explode_dn\n" ); + + if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { + return NULL; + } + + if( tmpDN == NULL ) { + values = LDAP_MALLOC( sizeof( char * ) ); + if( values == NULL ) return NULL; + + values[0] = NULL; + return values; + } + + for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ); + + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) ); + if ( values == NULL ) { + ldap_dnfree( tmpDN ); + return NULL; + } + + for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) { + ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag ); + } + ldap_dnfree( tmpDN ); + values[ iRDN ] = NULL; + + return values; +} + +char ** +ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) +{ + LDAPRDN tmpRDN; + char **values = NULL; + const char *p; + int iAVA; + + Debug0( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n" ); + + /* + * we only parse the first rdn + * FIXME: we prefer efficiency over checking if the _ENTIRE_ + * dn can be parsed + */ + if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { + return( NULL ); + } + + for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ; + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) ); + if ( values == NULL ) { + ldap_rdnfree( tmpRDN ); + return( NULL ); + } + + for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) { + ber_len_t l = 0, vl, al = 0; + char *str; + LDAPAVA *ava = tmpRDN[ iAVA ]; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + vl = 1 + 2 * ava->la_value.bv_len; + + } else { + if ( strval2strlen( &ava->la_value, + ava->la_flags, &vl ) ) { + goto error_return; + } + } + + if ( !notypes ) { + al = ava->la_attr.bv_len; + l = vl + ava->la_attr.bv_len + 1; + + str = LDAP_MALLOC( l + 1 ); + if ( str == NULL ) { + goto error_return; + } + AC_MEMCPY( str, ava->la_attr.bv_val, + ava->la_attr.bv_len ); + str[ al++ ] = '='; + + } else { + l = vl; + str = LDAP_MALLOC( l + 1 ); + if ( str == NULL ) { + goto error_return; + } + } + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ al++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) { + goto error_return; + } + + } else { + if ( strval2str( &ava->la_value, &str[ al ], + ava->la_flags, &vl ) ) { + goto error_return; + } + } + + str[ l ] = '\0'; + values[ iAVA ] = str; + } + values[ iAVA ] = NULL; + + ldap_rdnfree( tmpRDN ); + + return( values ); + +error_return:; + LBER_VFREE( values ); + ldap_rdnfree( tmpRDN ); + return( NULL ); +} + +char * +ldap_dn2dcedn( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n" ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_DCE ); + + return( out ); +} + +char * +ldap_dcedn2dn( LDAP_CONST char *dce ) +{ + char *out = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n" ); + + ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 ); + + return( out ); +} + +char * +ldap_dn2ad_canonical( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n" ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_AD_CANONICAL ); + + return( out ); +} + +/* + * function that changes the string representation of dnin + * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK ) + * + * fin can be one of: + * LDAP_DN_FORMAT_LDAP (RFC 4514 liberal, plus some RFC 1779) + * LDAP_DN_FORMAT_LDAPV3 (RFC 4514) + * LDAP_DN_FORMAT_LDAPV2 (RFC 1779) + * LDAP_DN_FORMAT_DCE (?) + * + * fout can be any of the above except + * LDAP_DN_FORMAT_LDAP + * plus: + * LDAP_DN_FORMAT_UFN (RFC 1781, partial and with extensions) + * LDAP_DN_FORMAT_AD_CANONICAL (?) + */ +int +ldap_dn_normalize( LDAP_CONST char *dnin, + unsigned fin, char **dnout, unsigned fout ) +{ + int rc; + LDAPDN tmpDN = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n" ); + + assert( dnout != NULL ); + + *dnout = NULL; + + if ( dnin == NULL ) { + return( LDAP_SUCCESS ); + } + + rc = ldap_str2dn( dnin , &tmpDN, fin ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_dn2str( tmpDN, dnout, fout ); + + ldap_dnfree( tmpDN ); + + return( rc ); +} + +/* States */ +#define B4AVA 0x0000 + +/* #define B4ATTRTYPE 0x0001 */ +#define B4OIDATTRTYPE 0x0002 +#define B4STRINGATTRTYPE 0x0003 + +#define B4AVAEQUALS 0x0100 +#define B4AVASEP 0x0200 +#define B4RDNSEP 0x0300 +#define GOTAVA 0x0400 + +#define B4ATTRVALUE 0x0010 +#define B4STRINGVALUE 0x0020 +#define B4IA5VALUEQUOTED 0x0030 +#define B4IA5VALUE 0x0040 +#define B4BINARYVALUE 0x0050 + +/* + * Helpers (mostly from slap.h) + * c is assumed to Unicode in an ASCII compatible format (UTF-8) + * Macros assume "C" Locale (ASCII) + */ +#define LDAP_DN_ASCII_SPACE(c) \ + ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' ) +#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c) +#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c) +#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c) + +#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c) +#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c) +#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c) +#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c) +#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c) +#define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' ) + +/* attribute type */ +#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c) +#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c) +#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c) +#define LDAP_DN_LANG_SEP(c) ( (c) == ';' ) +#define LDAP_DN_ATTRDESC_CHAR(c) \ + ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) ) + +/* special symbols */ +#define LDAP_DN_AVA_EQUALS(c) ( (c) == '=' ) +#define LDAP_DN_AVA_SEP(c) ( (c) == '+' ) +#define LDAP_DN_RDN_SEP(c) ( (c) == ',' ) +#define LDAP_DN_RDN_SEP_V2(c) ( LDAP_DN_RDN_SEP(c) || (c) == ';' ) +#define LDAP_DN_OCTOTHORPE(c) ( (c) == '#' ) +#define LDAP_DN_QUOTES(c) ( (c) == '\"' ) +#define LDAP_DN_ESCAPE(c) ( (c) == '\\' ) +#define LDAP_DN_VALUE_END(c) \ + ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) ) + +/* NOTE: according to RFC 4514, '=' can be escaped and treated as special, + * i.e. escaped both as "\<hexpair>" and * as "\=", but it is treated as + * a regular char, i.e. it can also appear as '='. + * + * As such, in 2.2 we used to allow reading unescaped '=', but we always + * produced escaped '\3D'; this changes since 2.3, if compatibility issues + * do not arise + */ +#define LDAP_DN_NE(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \ + || LDAP_DN_QUOTES(c) \ + || (c) == '<' || (c) == '>' ) +#define LDAP_DN_MAYESCAPE(c) \ + ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \ + || LDAP_DN_AVA_EQUALS(c) \ + || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) ) +#define LDAP_DN_SHOULDESCAPE(c) ( LDAP_DN_AVA_EQUALS(c) ) + +#define LDAP_DN_NEEDESCAPE(c) \ + ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) ) +#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c) +#define LDAP_DN_NEEDESCAPE_TRAIL(c) \ + ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) ) +#define LDAP_DN_WILLESCAPE_CHAR(c) \ + ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY ) +#define LDAP_DN_WILLESCAPE_HEX(f, c) \ + ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) ) + +/* LDAPv2 */ +#define LDAP_DN_VALUE_END_V2(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) ) +/* RFC 1779 */ +#define LDAP_DN_V2_SPECIAL(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \ + || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \ + || LDAP_DN_OCTOTHORPE(c) ) +#define LDAP_DN_V2_PAIR(c) \ + ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) ) + +/* + * DCE (mostly from Luke Howard and IBM implementation for AIX) + * + * From: "Application Development Guide - Directory Services" (FIXME: add link?) + * Here escapes and valid chars for GDS are considered; as soon as more + * specific info is found, the macros will be updated. + * + * Chars: 'a'-'z', 'A'-'Z', '0'-'9', + * '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '. + * + * Metachars: '/', ',', '=', '\'. + * + * the '\' is used to escape other metachars. + * + * Assertion: '=' + * RDN separator: '/' + * AVA separator: ',' + * + * Attribute types must start with alphabetic chars and can contain + * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed. + */ +#define LDAP_DN_RDN_SEP_DCE(c) ( (c) == '/' ) +#define LDAP_DN_AVA_SEP_DCE(c) ( (c) == ',' ) +#define LDAP_DN_ESCAPE_DCE(c) ( LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_VALUE_END_DCE(c) \ + ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) ) +#define LDAP_DN_NEEDESCAPE_DCE(c) \ + ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) ) + +/* AD Canonical */ +#define LDAP_DN_RDN_SEP_AD(c) ( (c) == '/' ) +#define LDAP_DN_ESCAPE_AD(c) ( LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_AVA_SEP_AD(c) ( (c) == ',' ) /* assume same as DCE */ +#define LDAP_DN_VALUE_END_AD(c) \ + ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) ) +#define LDAP_DN_NEEDESCAPE_AD(c) \ + ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) ) + +/* generics */ +#define LDAP_DN_HEXPAIR(s) \ + ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) ) +/* better look at the AttributeDescription? */ + +/* FIXME: no composite rdn or non-"dc" types, right? + * (what about "dc" in OID form?) */ +/* FIXME: we do not allow binary values in domain, right? */ +/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */ +/* NOTE: don't use strcasecmp() as it is locale specific! */ +#define LDAP_DC_ATTR "dc" +#define LDAP_DC_ATTRU "DC" +#define LDAP_DN_IS_RDN_DC( r ) \ + ( (r) && (r)[0] && !(r)[1] \ + && ((r)[0]->la_flags & LDAP_AVA_STRING) \ + && ((r)[0]->la_attr.bv_len == 2) \ + && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \ + || ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \ + && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \ + || ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1]))) + +/* Composite rules */ +#define LDAP_DN_ALLOW_ONE_SPACE(f) \ + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) ) +#define LDAP_DN_ALLOW_SPACES(f) \ + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) ) +#define LDAP_DN_LDAP(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP ) +#define LDAP_DN_LDAPV3(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 ) +#define LDAP_DN_LDAPV2(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 ) +#define LDAP_DN_DCE(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE ) +#define LDAP_DN_UFN(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN ) +#define LDAP_DN_ADC(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL ) +#define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK ) + +/* + * LDAPAVA helpers (will become part of the API for operations + * on structural representations of DNs). + */ +static LDAPAVA * +ldapava_new( const struct berval *attr, const struct berval *val, + unsigned flags, void *ctx ) +{ + LDAPAVA *ava; + + assert( attr != NULL ); + assert( val != NULL ); + + ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx ); + + if ( ava ) { + ava->la_attr.bv_len = attr->bv_len; + ava->la_attr.bv_val = (char *)(ava+1); + AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len ); + ava->la_attr.bv_val[attr->bv_len] = '\0'; + + ava->la_value = *val; + ava->la_flags = flags | LDAP_AVA_FREE_VALUE; + + ava->la_private = NULL; + } + + return( ava ); +} + +static void +ldapava_free( LDAPAVA *ava, void *ctx ) +{ + assert( ava != NULL ); + +#if 0 + /* ava's private must be freed by caller + * (at present let's skip this check because la_private + * basically holds static data) */ + assert( ava->la_private == NULL ); +#endif + + if (ava->la_flags & LDAP_AVA_FREE_VALUE) + LDAP_FREEX( ava->la_value.bv_val, ctx ); + + LDAP_FREEX( ava, ctx ); +} + +void +ldap_rdnfree( LDAPRDN rdn ) +{ + ldap_rdnfree_x( rdn, NULL ); +} + +void +ldap_rdnfree_x( LDAPRDN rdn, void *ctx ) +{ + int iAVA; + + if ( rdn == NULL ) { + return; + } + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + ldapava_free( rdn[ iAVA ], ctx ); + } + + LDAP_FREEX( rdn, ctx ); +} + +void +ldap_dnfree( LDAPDN dn ) +{ + ldap_dnfree_x( dn, NULL ); +} + +void +ldap_dnfree_x( LDAPDN dn, void *ctx ) +{ + int iRDN; + + if ( dn == NULL ) { + return; + } + + for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ldap_rdnfree_x( dn[ iRDN ], ctx ); + } + + LDAP_FREEX( dn, ctx ); +} + +/* + * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE) + * into a structural representation of the DN, by separating attribute + * types and values encoded in the more appropriate form, which is + * string or OID for attribute types and binary form of the BER encoded + * value or Unicode string. Formats different from LDAPv3 are parsed + * according to their own rules and turned into the more appropriate + * form according to LDAPv3. + * + * NOTE: I realize the code is getting spaghettish; it is rather + * experimental and will hopefully turn into something more simple + * and readable as soon as it works as expected. + */ + +/* + * Default sizes of AVA and RDN static working arrays; if required + * the are dynamically resized. The values can be tuned in case + * of special requirements (e.g. very deep DN trees or high number + * of AVAs per RDN). + */ +#define TMP_AVA_SLOTS 8 +#define TMP_RDN_SLOTS 32 + +int +ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags ) +{ + struct berval bv; + + assert( str != NULL ); + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2dn_x( &bv, dn, flags, NULL ); +} + +int +ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags ) +{ + return ldap_bv2dn_x( bv, dn, flags, NULL ); +} + +int +ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx ) +{ + const char *p; + int rc = LDAP_DECODING_ERROR; + int nrdns = 0; + + LDAPDN newDN = NULL; + LDAPRDN newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_; + int num_slots = TMP_RDN_SLOTS; + char *str, *end; + struct berval bvtmp, *bv = &bvtmp; + + assert( bvin != NULL ); + assert( bvin->bv_val != NULL ); + assert( dn != NULL ); + + *bv = *bvin; + str = bv->bv_val; + end = str + bv->bv_len; + + Debug2( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags ); + + *dn = NULL; + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_DCE: + break; + + /* allow DN enclosed in brackets */ + case LDAP_DN_FORMAT_LDAPV2: + if ( str[0] == '<' ) { + if ( bv->bv_len < 2 || end[ -1 ] != '>' ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + bv->bv_val++; + bv->bv_len -= 2; + str++; + end--; + } + break; + + /* unsupported in str2dn */ + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return LDAP_PARAM_ERROR; + + case LDAP_DN_FORMAT_LBER: + default: + return LDAP_PARAM_ERROR; + } + + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; + } + + p = str; + if ( LDAP_DN_DCE( flags ) ) { + + /* + * (from Luke Howard: thnx) A RDN separator is required + * at the beginning of an (absolute) DN. + */ + if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + goto parsing_error; + } + p++; + + /* + * actually we do not want to accept by default the DCE form, + * we do not want to auto-detect it + */ +#if 0 + } else if ( LDAP_DN_LDAP( flags ) ) { + /* + * if dn starts with '/' let's make it a DCE dn + */ + if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + flags |= LDAP_DN_FORMAT_DCE; + p++; + } +#endif + } + + for ( ; p < end; p++ ) { + int err; + struct berval tmpbv; + tmpbv.bv_len = bv->bv_len - ( p - str ); + tmpbv.bv_val = (char *)p; + + err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx); + if ( err != LDAP_SUCCESS ) { + goto parsing_error; + } + + /* + * We expect a rdn separator + */ + if ( p < end && p[ 0 ] ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + } + } + + + tmpDN[nrdns++] = newRDN; + newRDN = NULL; + + /* + * make the static RDN array dynamically rescalable + */ + if ( nrdns == num_slots ) { + LDAPRDN *tmp; + + if ( tmpDN == tmpDN_ ) { + tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) ); + + } else { + tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpDN = tmp; + num_slots *= 2; + } + + if ( p >= end || p[ 0 ] == '\0' ) { + /* + * the DN is over, phew + */ + newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx ); + if ( newDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + int i; + + if ( LDAP_DN_DCE( flags ) ) { + /* add in reversed order */ + for ( i=0; i<nrdns; i++ ) + newDN[i] = tmpDN[nrdns-1-i]; + } else { + for ( i=0; i<nrdns; i++ ) + newDN[i] = tmpDN[i]; + } + newDN[nrdns] = NULL; + rc = LDAP_SUCCESS; + } + goto return_result; + } + } + +parsing_error:; + if ( newRDN ) { + ldap_rdnfree_x( newRDN, ctx ); + } + + for ( nrdns-- ;nrdns >= 0; nrdns-- ) { + ldap_rdnfree_x( tmpDN[nrdns], ctx ); + } + +return_result:; + + if ( tmpDN != tmpDN_ ) { + LDAP_FREEX( tmpDN, ctx ); + } + + Debug3( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc, + rc ? ldap_err2string( rc ) : "" ); + *dn = newDN; + + return( rc ); +} + +/* + * ldap_str2rdn + * + * Parses a relative DN according to flags up to a rdn separator + * or to the end of str. + * Returns the rdn and a pointer to the string continuation, which + * corresponds to the rdn separator or to '\0' in case the string is over. + */ +int +ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn, + char **n_in, unsigned flags ) +{ + struct berval bv; + + assert( str != NULL ); + assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */ + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL ); +} + +int +ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn, + char **n_in, unsigned flags ) +{ + return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL ); +} + +int +ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn, + char **n_in, unsigned flags, void *ctx ) +{ + const char **n = (const char **) n_in; + const char *p; + int navas = 0; + int state = B4AVA; + int rc = LDAP_DECODING_ERROR; + int attrTypeEncoding = LDAP_AVA_STRING, + attrValueEncoding = LDAP_AVA_STRING; + + struct berval attrType = BER_BVNULL; + struct berval attrValue = BER_BVNULL; + + LDAPRDN newRDN = NULL; + LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_; + int num_slots = TMP_AVA_SLOTS; + + char *str; + ber_len_t stoplen; + + assert( bv != NULL ); + assert( bv->bv_len != 0 ); + assert( bv->bv_val != NULL ); + assert( rdn || flags & LDAP_DN_SKIP ); + assert( n != NULL ); + + str = bv->bv_val; + stoplen = bv->bv_len; + + if ( rdn ) { + *rdn = NULL; + } + *n = NULL; + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + case LDAP_DN_FORMAT_DCE: + break; + + /* unsupported in str2dn */ + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return LDAP_PARAM_ERROR; + + case LDAP_DN_FORMAT_LBER: + default: + return LDAP_PARAM_ERROR; + } + + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; + } + + p = str; + for ( ; p[ 0 ] || state == GOTAVA; ) { + + /* + * The parser in principle advances one token a time, + * or toggles state if preferable. + */ + switch (state) { + + /* + * an AttributeType can be encoded as: + * - its string representation; in detail, implementations + * MUST recognize AttributeType string type names listed + * in Section 3 of RFC 4514, and MAY recognize other names. + * - its numeric OID (a dotted decimal string) + */ + case B4AVA: + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) { + /* error */ + goto parsing_error; + } + p++; + } + + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + /* error */ + goto parsing_error; + } + + /* whitespace is allowed (and trimmed) */ + p++; + while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + p++; + } + + if ( !p[ 0 ] ) { + /* error: we expected an AVA */ + goto parsing_error; + } + } + + /* oid */ + if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) { + state = B4OIDATTRTYPE; + break; + } + + /* else must be alpha */ + if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) { + goto parsing_error; + } + + /* LDAPv2 "oid." prefix */ + if ( LDAP_DN_LDAPV2( flags ) ) { + /* + * to be overly pedantic, we only accept + * "OID." or "oid." + */ + if ( flags & LDAP_DN_PEDANTIC ) { + if ( !strncmp( p, "OID.", 4 ) + || !strncmp( p, "oid.", 4 ) ) { + p += 4; + state = B4OIDATTRTYPE; + break; + } + } else { + if ( !strncasecmp( p, "oid.", 4 ) ) { + p += 4; + state = B4OIDATTRTYPE; + break; + } + } + } + + state = B4STRINGATTRTYPE; + break; + + case B4OIDATTRTYPE: { + int err = LDAP_SUCCESS; + + attrType.bv_val = ldap_int_parse_numericoid( &p, &err, + LDAP_SCHEMA_SKIP); + + if ( err != LDAP_SUCCESS ) { + goto parsing_error; + } + attrType.bv_len = p - attrType.bv_val; + + attrTypeEncoding = LDAP_AVA_BINARY; + + state = B4AVAEQUALS; + break; + } + + case B4STRINGATTRTYPE: { + const char *startPos, *endPos = NULL; + ber_len_t len; + + /* + * the starting char has been found to be + * a LDAP_DN_DESC_LEADCHAR so we don't re-check it + * FIXME: DCE attr types seem to have a more + * restrictive syntax (no '-' ...) + */ + for ( startPos = p++; p[ 0 ]; p++ ) { + if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) { + continue; + } + + if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) { + + /* + * RFC 4514 explicitly does not allow attribute + * description options, such as language tags. + */ + if ( flags & LDAP_DN_PEDANTIC ) { + goto parsing_error; + } + + /* + * we trim ';' and following lang + * and so from attribute types + */ + endPos = p; + for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] ) + || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) { + /* no op */ ; + } + break; + } + break; + } + + len = ( endPos ? endPos : p ) - startPos; + if ( len == 0 ) { + goto parsing_error; + } + + attrTypeEncoding = LDAP_AVA_STRING; + + /* + * here we need to decide whether to use it as is + * or turn it in OID form; as a consequence, we + * need to decide whether to binary encode the value + */ + + state = B4AVAEQUALS; + + if ( flags & LDAP_DN_SKIP ) { + break; + } + + attrType.bv_val = (char *)startPos; + attrType.bv_len = len; + + break; + } + + case B4AVAEQUALS: + /* spaces may not be allowed */ + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + goto parsing_error; + } + + /* trim spaces */ + for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + } + + /* need equal sign */ + if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) { + goto parsing_error; + } + p++; + + /* spaces may not be allowed */ + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + goto parsing_error; + } + + /* trim spaces */ + for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + } + + /* + * octothorpe means a BER encoded value will follow + * FIXME: I don't think DCE will allow it + */ + if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) { + p++; + attrValueEncoding = LDAP_AVA_BINARY; + state = B4BINARYVALUE; + break; + } + + /* STRING value expected */ + + /* + * if we're pedantic, an attribute type in OID form + * SHOULD imply a BER encoded attribute value; we + * should at least issue a warning + */ + if ( ( flags & LDAP_DN_PEDANTIC ) + && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) { + /* OID attrType SHOULD use binary encoding */ + goto parsing_error; + } + + attrValueEncoding = LDAP_AVA_STRING; + + /* + * LDAPv2 allows the attribute value to be quoted; + * also, IA5 values are expected, in principle + */ + if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) { + if ( LDAP_DN_QUOTES( p[ 0 ] ) ) { + p++; + state = B4IA5VALUEQUOTED; + break; + } + + if ( LDAP_DN_LDAPV2( flags ) ) { + state = B4IA5VALUE; + break; + } + } + + /* + * here STRING means RFC 4514 string + * FIXME: what about DCE strings? + */ + if ( !p[ 0 ] ) { + /* empty value */ + state = GOTAVA; + } else { + state = B4STRINGVALUE; + } + break; + + case B4BINARYVALUE: + if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case B4STRINGVALUE: + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + if ( str2strval( p, stoplen - ( p - str ), + &attrValue, &p, flags, + &attrValueEncoding, ctx ) ) { + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + break; + + default: + assert( 0 ); + } + + state = GOTAVA; + break; + + case B4IA5VALUE: + if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case B4IA5VALUEQUOTED: + + /* lead quote already stripped */ + if ( quotedIA52strval( p, &attrValue, + &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case GOTAVA: { + int rdnsep = 0; + + if ( !( flags & LDAP_DN_SKIP ) ) { + LDAPAVA *ava; + + /* + * we accept empty values + */ + ava = ldapava_new( &attrType, &attrValue, + attrValueEncoding, ctx ); + if ( ava == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + tmpRDN[navas++] = ava; + + attrValue.bv_val = NULL; + attrValue.bv_len = 0; + + /* + * prepare room for new AVAs if needed + */ + if (navas == num_slots) { + LDAPAVA **tmp; + + if ( tmpRDN == tmpRDN_ ) { + tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) ); + + } else { + tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpRDN = tmp; + num_slots *= 2; + } + } + + /* + * if we got an AVA separator ('+', or ',' for DCE ) + * we expect a new AVA for this RDN; otherwise + * we add the RDN to the DN + */ + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) { + rdnsep = 1; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) { + rdnsep = 1; + } + break; + } + + if ( rdnsep ) { + /* + * the RDN is over, phew + */ + *n = p; + if ( !( flags & LDAP_DN_SKIP ) ) { + newRDN = (LDAPRDN)LDAP_MALLOCX( + sizeof(LDAPAVA) * (navas+1), ctx ); + if ( newRDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas); + newRDN[navas] = NULL; + } + + } + rc = LDAP_SUCCESS; + goto return_result; + } + + /* they should have been used in an AVA */ + attrType.bv_val = NULL; + attrValue.bv_val = NULL; + + p++; + state = B4AVA; + break; + } + + default: + assert( 0 ); + goto parsing_error; + } + } + *n = p; + +parsing_error:; + /* They are set to NULL after they're used in an AVA */ + + if ( attrValue.bv_val ) { + LDAP_FREEX( attrValue.bv_val, ctx ); + } + + for ( navas-- ; navas >= 0; navas-- ) { + ldapava_free( tmpRDN[navas], ctx ); + } + +return_result:; + + if ( tmpRDN != tmpRDN_ ) { + LDAP_FREEX( tmpRDN, ctx ); + } + + if ( rdn ) { + *rdn = newRDN; + } + + return( rc ); +} + +/* + * reads in a UTF-8 string value, unescaping stuff: + * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c' + * '\' + HEXPAIR(p) -> unhex(p) + */ +static int +str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx ) +{ + const char *p, *end, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + end = str + stoplen; + for ( startPos = p = str, escapes = 0; p < end; p++ ) { + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + p++; + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) { + escapes++; + continue; + } + + if ( LDAP_DN_HEXPAIR( p ) ) { + char c; + + hexstr2bin( p, &c ); + escapes += 2; + + if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) { + + /* + * we assume the string is UTF-8 + */ + *retFlags = LDAP_AVA_NONPRINTABLE; + } + p++; + + continue; + } + + if ( LDAP_DN_PEDANTIC & flags ) { + return( 1 ); + } + /* + * we do not allow escaping + * of chars that don't need + * to and do not belong to + * HEXDIGITS + */ + return( 1 ); + + } else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) { + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + *retFlags = LDAP_AVA_NONPRINTABLE; + + } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) + || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) { + break; + + } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { + /* + * FIXME: maybe we can add + * escapes if not pedantic? + */ + return( 1 ); + } + } + + /* + * we do allow unescaped spaces at the end + * of the value only in non-pedantic mode + */ + if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) && + !LDAP_DN_ESCAPE( p[ -2 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p - 1; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + /* + * FIXME: test memory? + */ + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + + if ( escapes == 0 ) { + if ( *retFlags & LDAP_AVA_NONPRINTABLE ) { + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( 1 ); + } + + AC_MEMCPY( val->bv_val, startPos, len ); + val->bv_val[ len ] = '\0'; + } else { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + } + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( 1 ); + } + + for ( s = 0, d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { + s++; + if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) { + val->bv_val[ d++ ] = + startPos[ s++ ]; + + } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) { + char c; + + hexstr2bin( &startPos[ s ], &c ); + val->bv_val[ d++ ] = c; + s += 2; + + } else { + /* we should never get here */ + assert( 0 ); + } + + } else { + val->bv_val[ d++ ] = startPos[ s++ ]; + } + } + + val->bv_val[ d ] = '\0'; + assert( d == len ); + } + + return( 0 ); +} + +static int +DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { + if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) { + p++; + if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { + escapes++; + + } else { + return( 1 ); + } + + } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + /* + * (unescaped) trailing spaces are trimmed must be silently ignored; + * so we eat them + */ + if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) && + !LDAP_DN_ESCAPE( p[ -2 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p - 1; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + if ( escapes == 0 ){ + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( 1 ); + } + + for ( s = 0, d = 0; d < len; ) { + /* + * This point is reached only if escapes + * are properly used, so all we need to + * do is eat them + */ + if ( LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) { + s++; + + } + val->bv_val[ d++ ] = startPos[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + /* + * LDAPv2 (RFC 1779) + */ + + for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + p++; + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + + if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] ) + && ( LDAP_DN_PEDANTIC & flags ) ) { + return( 1 ); + } + escapes++; + + } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + if ( escapes == 0 ) { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( 1 ); + } + + for ( s = 0, d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { + s++; + } + val->bv_val[ d++ ] = startPos[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len; + unsigned escapes = 0; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + /* initial quote already eaten */ + for ( startPos = p = str; p[ 0 ]; p++ ) { + /* + * According to RFC 1779, the quoted value can + * contain escaped as well as unescaped special values; + * as a consequence we tolerate escaped values + * (e.g. '"\,"' -> '\,') and escape unescaped specials + * (e.g. '","' -> '\,'). + */ + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + if ( p[ 1 ] == '\0' ) { + return( 1 ); + } + p++; + + if ( !LDAP_DN_V2_PAIR( p[ 0 ] ) + && ( LDAP_DN_PEDANTIC & flags ) ) { + /* + * do we allow to escape normal chars? + * LDAPv2 does not allow any mechanism + * for escaping chars with '\' and hex + * pair + */ + return( 1 ); + } + escapes++; + + } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) { + endPos = p; + /* eat closing quotes */ + p++; + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + if ( endPos == NULL ) { + return( 1 ); + } + + /* Strip trailing (unescaped) spaces */ + for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = endPos - startPos - escapes; + assert( endPos >= startPos + escapes ); + val->bv_len = len; + if ( escapes == 0 ) { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( 1 ); + } + + val->bv_len = len; + + for ( s = d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( str[ s ] ) ) { + s++; + } + val->bv_val[ d++ ] = str[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +hexstr2bin( const char *str, char *c ) +{ + char c1, c2; + + assert( str != NULL ); + assert( c != NULL ); + + c1 = str[ 0 ]; + c2 = str[ 1 ]; + + if ( LDAP_DN_ASCII_DIGIT( c1 ) ) { + *c = c1 - '0'; + + } else { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) { + *c = c1 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) ); + *c = c1 - 'a' + 10; + } + } + + *c <<= 4; + + if ( LDAP_DN_ASCII_DIGIT( c2 ) ) { + *c += c2 - '0'; + + } else { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) { + *c += c2 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) ); + *c += c2 - 'a' + 10; + } + } + + return( 0 ); +} + +static int +hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len; + ber_len_t s, d; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + for ( startPos = p = str; p[ 0 ]; p += 2 ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } + break; + } + + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + endPos = p; + + for ( ; p[ 0 ]; p++ ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } + break; + } + } + break; + } + + if ( !LDAP_DN_HEXPAIR( p ) ) { + return( 1 ); + } + } + +end_of_value:; + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( ( endPos ? endPos : p ) - startPos ) / 2; + /* must be even! */ + assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos )); + + val->bv_len = len; + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( LDAP_NO_MEMORY ); + } + + for ( s = 0, d = 0; d < len; s += 2, d++ ) { + char c; + + hexstr2bin( &startPos[ s ], &c ); + + val->bv_val[ d ] = c; + } + + val->bv_val[ d ] = '\0'; + + return( 0 ); +} + +/* + * convert a byte in a hexadecimal pair + */ +static int +byte2hexpair( const char *val, char *pair ) +{ + static const char hexdig[] = "0123456789ABCDEF"; + + assert( val != NULL ); + assert( pair != NULL ); + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ]; + pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ]; + + return( 0 ); +} + +/* + * convert a binary value in hexadecimal pairs + */ +static int +binval2hexstr( struct berval *val, char *str ) +{ + ber_len_t s, d; + + assert( val != NULL ); + assert( str != NULL ); + + if ( val->bv_len == 0 ) { + return( 0 ); + } + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) { + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + } + + return( 0 ); +} + +/* + * Length of the string representation, accounting for escaped hex + * of UTF-8 chars + */ +static int +strval2strlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l, cl = 1; + char *p, *end; + int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3; +#ifdef PRETTY_ESCAPE + int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3; +#endif /* PRETTY_ESCAPE */ + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + end = val->bv_val + val->bv_len - 1; + for ( l = 0, p = val->bv_val; p <= end; p += cl ) { + + /* + * escape '%x00' + */ + if ( p[ 0 ] == '\0' ) { + cl = 1; + l += 3; + continue; + } + + cl = LDAP_UTF8_CHARLEN2( p, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char! */ + return( -1 ); + + } else if ( cl > 1 ) { + ber_len_t cnt; + + for ( cnt = 1; cnt < cl; cnt++ ) { + if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) { + return( -1 ); + } + } + l += escaped_byte_len * cl; + + } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || LDAP_DN_SHOULDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { +#ifdef PRETTY_ESCAPE +#if 0 + if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) { +#else + if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) { +#endif + + /* + * there might be some chars we want + * to escape in form of a couple + * of hexdigits for optimization purposes + */ + l += 3; + + } else { + l += escaped_ascii_len; + } +#else /* ! PRETTY_ESCAPE */ + l += 3; +#endif /* ! PRETTY_ESCAPE */ + + } else { + l++; + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to string representation, escaping with hex the UTF-8 stuff; + * assume the destination has enough room for escaping + */ +static int +strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, end; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { + ber_len_t cl; + + /* + * escape '%x00' + */ + if ( val->bv_val[ s ] == '\0' ) { + cl = 1; + str[ d++ ] = '\\'; + str[ d++ ] = '0'; + str[ d++ ] = '0'; + s++; + continue; + } + + /* + * The length was checked in strval2strlen(); + */ + cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] ); + + /* + * there might be some chars we want to escape in form + * of a couple of hexdigits for optimization purposes + */ + if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) +#ifdef PRETTY_ESCAPE +#if 0 + || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) +#else + || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) +#endif +#else /* ! PRETTY_ESCAPE */ + || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) + +#endif /* ! PRETTY_ESCAPE */ + ) { + for ( ; cl--; ) { + str[ d++ ] = '\\'; + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + s++; + d += 2; + } + + } else if ( cl > 1 ) { + for ( ; cl--; ) { + str[ d++ ] = val->bv_val[ s++ ]; + } + + } else { +#ifdef PRETTY_ESCAPE + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { + str[ d++ ] = '\\'; + if ( !LDAP_DN_IS_PRETTY( flags ) ) { + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + s++; + d += 2; + continue; + } + } +#endif /* PRETTY_ESCAPE */ + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the IA5 string representation (no UTF-8 allowed) + */ +static int +strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * Turn value into a binary encoded BER + */ + return( -1 ); + + } else { + for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { + if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || LDAP_DN_SHOULDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { + l += 2; + + } else { + l++; + } + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to string representation (np UTF-8) + * assume the destination has enough room for escaping + */ +static int +strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, end; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * Turn value into a binary encoded BER + */ + *len = 0; + return( -1 ); + + } else { + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { + str[ d++ ] = '\\'; + } + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the (supposedly) DCE string representation, + * accounting for escaped hex of UTF-8 chars + */ +static int +strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * FIXME: Turn the value into a binary encoded BER? + */ + return( -1 ); + + } else { + for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { + if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { + l += 2; + + } else { + l++; + } + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to (supposedly) DCE string representation, + * escaping with hex the UTF-8 stuff; + * assume the destination has enough room for escaping + */ +static int +strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * FIXME: Turn the value into a binary encoded BER? + */ + *len = 0; + return( -1 ); + + } else { + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; ) { + if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) { + str[ d++ ] = '\\'; + } + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the (supposedly) AD canonical string representation, + * accounting for chars that need to be escaped + */ +static int +strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l, cl; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) { + cl = LDAP_UTF8_CHARLEN2( p, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char */ + return -1; + } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) { + l += 2; + } else { + l += cl; + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to (supposedly) AD string representation, + * assume the destination has enough room for escaping + */ +static int +strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, cl; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + /* + * we assume the string has enough room for the escaping + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; ) { + cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char */ + return -1; + } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) { + str[ d++ ] = '\\'; + } + for (; cl--;) { + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * If the DN is terminated by single-AVA RDNs with attribute type of "dc", + * the first part of the AD representation of the DN is written in DNS + * form, i.e. dot separated domain name components (as suggested + * by Luke Howard, http://www.padl.com/~lukeh) + */ +static int +dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN ) +{ + int i; + int domain = 0, first = 1; + ber_len_t l = 1; /* we move the null also */ + char *str; + + /* we are guaranteed there's enough memory in str */ + + /* sanity */ + assert( dn != NULL ); + assert( bv != NULL ); + assert( iRDN != NULL ); + assert( *iRDN >= 0 ); + + str = bv->bv_val + pos; + + for ( i = *iRDN; i >= 0; i-- ) { + LDAPRDN rdn; + LDAPAVA *ava; + + assert( dn[ i ] != NULL ); + rdn = dn[ i ]; + + assert( rdn[ 0 ] != NULL ); + ava = rdn[ 0 ]; + + if ( !LDAP_DN_IS_RDN_DC( rdn ) ) { + break; + } + + if ( ldif_is_not_printable( ava->la_value.bv_val, ava->la_value.bv_len ) ) { + domain = 0; + break; + } + + domain = 1; + + if ( first ) { + first = 0; + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len + 1); + l += ava->la_value.bv_len; + + } else { + AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l); + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len ); + str[ ava->la_value.bv_len ] = '.'; + l += ava->la_value.bv_len + 1; + } + } + + *iRDN = i; + bv->bv_len = pos + l - 1; + + return( domain ); +} + +static int +rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) ) +{ + int iAVA; + ber_len_t l = 0; + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* len(type) + '=' + '+' | ',' */ + l += ava->la_attr.bv_len + 2; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( ( *s2l )( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; + + str[ l++ ] = '='; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' ); + } + + *len = l; + + return( 0 ); +} + +static int +rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* len(type) + '=' + ',' | '/' */ + l += ava->la_attr.bv_len + 2; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); + } + + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; + + str[ l++ ] = '='; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn != NULL ); + assert( len != NULL ); + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* ' + ' | ', ' */ + l += ( rdn[ iAVA + 1 ] ? 3 : 2 ); + + /* FIXME: are binary values allowed in UFN? */ + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2strlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + + if ( rdn[ iAVA + 1 ] ) { + AC_MEMCPY( &str[ l ], " + ", 3 ); + l += 3; + + } else { + AC_MEMCPY( &str[ l ], ", ", 2 ); + l += 2; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn != NULL ); + assert( len != NULL ); + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* ',' | '/' */ + l++; + + /* FIXME: are binary values allowed in UFN? */ + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); + } + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +/* + * ldap_rdn2str + * + * Returns in str a string representation of rdn based on flags. + * There is some duplication of code between this and ldap_dn2str; + * this is wanted to reduce the allocation of temporary buffers. + */ +int +ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str != NULL ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } + + rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL ); + *str = bv.bv_val; + return rc; +} + +int +ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags ) +{ + return ldap_rdn2bv_x( rdn, bv, flags, NULL ); +} + +int +ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx ) +{ + int rc, back; + ber_len_t l; + + assert( bv != NULL ); + + bv->bv_len = 0; + bv->bv_val = NULL; + + if ( rdn == NULL ) { + bv->bv_val = LDAP_STRDUPX( "", ctx ); + return( LDAP_SUCCESS ); + } + + /* + * This routine wastes "back" bytes at the end of the string + */ + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_LDAPV2: + if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_UFN: + if ( rdn2UFNstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( rdn2DCEstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + if ( rdn2ADstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + default: + return LDAP_PARAM_ERROR; + } + + bv->bv_val = LDAP_MALLOCX( l + 1, ctx ); + if ( bv->bv_val == NULL ) { + return LDAP_NO_MEMORY; + } + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str ); + back = 1; + break; + + case LDAP_DN_FORMAT_LDAPV2: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str ); + back = 1; + break; + + case LDAP_DN_FORMAT_UFN: + rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l ); + back = 2; + break; + + case LDAP_DN_FORMAT_DCE: + rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + default: + /* need at least one of the previous */ + return LDAP_PARAM_ERROR; + } + + if ( rc ) { + LDAP_FREEX( bv->bv_val, ctx ); + return rc; + } + + bv->bv_len = l - back; + bv->bv_val[ bv->bv_len ] = '\0'; + + return LDAP_SUCCESS; +} + +/* + * Very bulk implementation; many optimizations can be performed + * - a NULL dn results in an empty string "" + * + * FIXME: doubts + * a) what do we do if a UTF-8 string must be converted in LDAPv2? + * we must encode it in binary form ('#' + HEXPAIRs) + * b) does DCE/AD support UTF-8? + * no clue; don't think so. + * c) what do we do when binary values must be converted in UTF/DCE/AD? + * use binary encoded BER + */ +int ldap_dn2str( LDAPDN dn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str != NULL ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } + + rc = ldap_dn2bv_x( dn, &bv, flags, NULL ); + *str = bv.bv_val; + return rc; +} + +int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags ) +{ + return ldap_dn2bv_x( dn, bv, flags, NULL ); +} + +int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx ) +{ + int iRDN; + int rc = LDAP_ENCODING_ERROR; + ber_len_t len, l; + + /* stringifying helpers for LDAPv3/LDAPv2 */ + int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l ); + int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l ); + + assert( bv != NULL ); + bv->bv_len = 0; + bv->bv_val = NULL; + + Debug1( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags ); + + /* + * a null dn means an empty dn string + * FIXME: better raise an error? + */ + if ( dn == NULL || dn[0] == NULL ) { + bv->bv_val = LDAP_STRDUPX( "", ctx ); + return( LDAP_SUCCESS ); + } + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + sv2l = strval2strlen; + sv2s = strval2str; + + if( 0 ) { + case LDAP_DN_FORMAT_LDAPV2: + sv2l = strval2IA5strlen; + sv2s = strval2IA5str; + } + + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags, + &rdnl, sv2s ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + assert( l == len ); + + /* + * trim the last ',' (the allocated memory + * is one byte longer than required) + */ + bv->bv_len = len - 1; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + break; + + case LDAP_DN_FORMAT_UFN: { + /* + * FIXME: quoting from RFC 1781: + * + To take a distinguished name, and generate a name of this format with + attribute types omitted, the following steps are followed. + + 1. If the first attribute is of type CommonName, the type may be + omitted. + + 2. If the last attribute is of type Country, the type may be + omitted. + + 3. If the last attribute is of type Country, the last + Organisation attribute may have the type omitted. + + 4. All attributes of type OrganisationalUnit may have the type + omitted, unless they are after an Organisation attribute or + the first attribute is of type OrganisationalUnit. + + * this should be the pedantic implementation. + * + * Here the standard implementation reflects + * the one historically provided by OpenLDAP + * (and UMIch, I presume), with the variant + * of spaces and plusses (' + ') separating + * rdn components. + * + * A non-standard but nice implementation could + * be to turn the final "dc" attributes into a + * dot-separated domain. + * + * Other improvements could involve the use of + * friendly country names and so. + */ +#ifdef DC_IN_UFN + int leftmost_dc = -1; + int last_iRDN = -1; +#endif /* DC_IN_UFN */ + + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + len += rdnl; + +#ifdef DC_IN_UFN + if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) { + if ( leftmost_dc == -1 ) { + leftmost_dc = iRDN; + } + } else { + leftmost_dc = -1; + } +#endif /* DC_IN_UFN */ + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + +#ifdef DC_IN_UFN + if ( leftmost_dc == -1 ) { +#endif /* DC_IN_UFN */ + for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t vl; + + if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += vl; + } + + /* + * trim the last ', ' (the allocated memory + * is two bytes longer than required) + */ + bv->bv_len = len - 2; + bv->bv_val[ bv->bv_len ] = '\0'; +#ifdef DC_IN_UFN + } else { + last_iRDN = iRDN - 1; + + for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) { + ber_len_t vl; + + if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += vl; + } + + if ( !dn2domain( dn, bv, l, &last_iRDN ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + + /* the string is correctly terminated by dn2domain */ + } +#endif /* DC_IN_UFN */ + + rc = LDAP_SUCCESS; + + } break; + + case LDAP_DN_FORMAT_DCE: + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0; iRDN--; ) { + ber_len_t rdnl; + + if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags, + &rdnl, 0 ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + assert( l == len ); + + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: { + int trailing_slash = 1; + + /* + * Sort of UFN for DCE DNs: a slash ('/') separated + * global->local DN with no types; strictly speaking, + * the naming context should be a domain, which is + * written in DNS-style, e.g. dot-separated. + * + * Example: + * + * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com" + * + * will read + * + * "microsoft.com/People/Bill,Gates" + */ + for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + + len += rdnl; + } + + /* reserve room for trailing '/' in case the DN + * is exactly a domain */ + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL ) + { + rc = LDAP_NO_MEMORY; + break; + } + + iRDN--; + if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) { + for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) { + ber_len_t rdnl; + + trailing_slash = 0; + + if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &rdnl, 0 ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + } else { + int first = 1; + + /* + * Strictly speaking, AD canonical requires + * a DN to be in the form "..., dc=smtg", + * i.e. terminated by a domain component + */ + if ( flags & LDAP_DN_PEDANTIC ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + rc = LDAP_ENCODING_ERROR; + break; + } + + for ( l = 0; iRDN >= 0 ; iRDN-- ) { + ber_len_t rdnl; + + if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &rdnl, first ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + if ( first ) { + first = 0; + } + l += rdnl; + } + } + + if ( trailing_slash ) { + /* the DN is exactly a domain -- need a trailing + * slash; room was reserved in advance */ + bv->bv_val[ len ] = '/'; + len++; + } + + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + } break; + + default: + return LDAP_PARAM_ERROR; + } + + Debug3( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n", + bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" ); + +return_results:; + return( rc ); +} + diff --git a/contrib/libs/openldap/libraries/libldap/getentry.c b/contrib/libs/openldap/libraries/libldap/getentry.c new file mode 100644 index 0000000000..b812189e6e --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/getentry.c @@ -0,0 +1,124 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* ARGSUSED */ +LDAPMessage * +ldap_first_entry( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY + ? chain + : ldap_next_entry( ld, chain ); +} + +LDAPMessage * +ldap_next_entry( LDAP *ld, LDAPMessage *entry ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + + for( + entry = entry->lm_chain; + entry != NULL; + entry = entry->lm_chain ) + { + if( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + return( entry ); + } + } + + return( NULL ); +} + +int +ldap_count_entries( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + if( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + i++; + } + } + + return( i ); +} + +int +ldap_get_entry_controls( + LDAP *ld, + LDAPMessage *entry, + LDAPControl ***sctrls ) +{ + int rc; + BerElement be; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( sctrls != NULL ); + + if ( entry->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) { + return LDAP_PARAM_ERROR; + } + + /* make a local copy of the BerElement */ + AC_MEMCPY(&be, entry->lm_ber, sizeof(be)); + + if ( ber_scanf( &be, "{xx" /*}*/ ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto cleanup_and_return; + } + + rc = ldap_pvt_get_controls( &be, sctrls ); + +cleanup_and_return: + if( rc != LDAP_SUCCESS ) { + ld->ld_errno = rc; + + if( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + } + + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/getvalues.c b/contrib/libs/openldap/libraries/libldap/getvalues.c new file mode 100644 index 0000000000..b3ac1907f8 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/getvalues.c @@ -0,0 +1,211 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +char ** +ldap_get_values( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target ) +{ + BerElement ber; + char *attr; + int found = 0; + char **vals; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( target != NULL ); + + Debug0( LDAP_DEBUG_TRACE, "ldap_get_values\n" ); + + ber = *entry->lm_ber; + + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a" /*}}}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + found = 1; + + /* break out on success, return out on error */ + while ( ! found ) { + LDAP_FREE(attr); + attr = NULL; + + if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + break; + + } + + LDAP_FREE(attr); + attr = NULL; + + /* + * if we get this far, we've found the attribute and are sitting + * just before the set of values. + */ + + if ( ber_scanf( &ber, "[v]", &vals ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( vals ); +} + +struct berval ** +ldap_get_values_len( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target ) +{ + BerElement ber; + char *attr; + int found = 0; + struct berval **vals; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( target != NULL ); + + Debug0( LDAP_DEBUG_TRACE, "ldap_get_values_len\n" ); + + ber = *entry->lm_ber; + + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a" /* }}} */, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + found = 1; + + /* break out on success, return out on error */ + while ( ! found ) { + LDAP_FREE( attr ); + attr = NULL; + + if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + break; + } + + LDAP_FREE( attr ); + attr = NULL; + + /* + * if we get this far, we've found the attribute and are sitting + * just before the set of values. + */ + + if ( ber_scanf( &ber, "[V]", &vals ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( vals ); +} + +int +ldap_count_values( char **vals ) +{ + int i; + + if ( vals == NULL ) + return( 0 ); + + for ( i = 0; vals[i] != NULL; i++ ) + ; /* NULL */ + + return( i ); +} + +int +ldap_count_values_len( struct berval **vals ) +{ + return( ldap_count_values( (char **) vals ) ); +} + +void +ldap_value_free( char **vals ) +{ + LDAP_VFREE( vals ); +} + +void +ldap_value_free_len( struct berval **vals ) +{ + ber_bvecfree( vals ); +} + +char ** +ldap_value_dup( char *const *vals ) +{ + char **new; + int i; + + if( vals == NULL ) { + return NULL; + } + + for( i=0; vals[i]; i++ ) { + ; /* Count the number of values */ + } + + if( i == 0 ) { + return NULL; + } + + new = LDAP_MALLOC( (i+1)*sizeof(char *) ); /* Alloc array of pointers */ + if( new == NULL ) { + return NULL; + } + + for( i=0; vals[i]; i++ ) { + new[i] = LDAP_STRDUP( vals[i] ); /* Dup each value */ + if( new[i] == NULL ) { + LDAP_VFREE( new ); + return NULL; + } + } + new[i] = NULL; + + return new; +} + diff --git a/contrib/libs/openldap/libraries/libldap/init.c b/contrib/libs/openldap/libraries/libldap/init.c new file mode 100644 index 0000000000..3a81790dcf --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/init.c @@ -0,0 +1,782 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef HAVE_GETEUID +#include <ac/unistd.h> +#endif + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "ldap-int.h" +#include "ldap_defaults.h" +#include "lutil.h" + +struct ldapoptions ldap_int_global_options = + { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE + LDAP_LDO_NULLARG + LDAP_LDO_SOURCEIP_NULLARG + LDAP_LDO_CONNECTIONLESS_NULLARG + LDAP_LDO_TLS_NULLARG + LDAP_LDO_SASL_NULLARG + LDAP_LDO_MUTEX_NULLARG }; + +#define ATTR_NONE 0 +#define ATTR_BOOL 1 +#define ATTR_INT 2 +#define ATTR_KV 3 +#define ATTR_STRING 4 +#define ATTR_OPTION 5 + +#define ATTR_SASL 6 +#define ATTR_TLS 7 + +#define ATTR_OPT_TV 8 +#define ATTR_OPT_INT 9 + +struct ol_keyvalue { + const char * key; + int value; +}; + +static const struct ol_keyvalue deref_kv[] = { + {"never", LDAP_DEREF_NEVER}, + {"searching", LDAP_DEREF_SEARCHING}, + {"finding", LDAP_DEREF_FINDING}, + {"always", LDAP_DEREF_ALWAYS}, + {NULL, 0} +}; + +static const struct ol_attribute { + int useronly; + int type; + const char * name; + const void * data; + size_t offset; +} attrs[] = { + {0, ATTR_OPT_TV, "TIMEOUT", NULL, LDAP_OPT_TIMEOUT}, + {0, ATTR_OPT_TV, "NETWORK_TIMEOUT", NULL, LDAP_OPT_NETWORK_TIMEOUT}, + {0, ATTR_OPT_INT, "VERSION", NULL, LDAP_OPT_PROTOCOL_VERSION}, + {0, ATTR_KV, "DEREF", deref_kv, /* or &deref_kv[0] */ + offsetof(struct ldapoptions, ldo_deref)}, + {0, ATTR_INT, "SIZELIMIT", NULL, + offsetof(struct ldapoptions, ldo_sizelimit)}, + {0, ATTR_INT, "TIMELIMIT", NULL, + offsetof(struct ldapoptions, ldo_timelimit)}, + {1, ATTR_STRING, "BINDDN", NULL, + offsetof(struct ldapoptions, ldo_defbinddn)}, + {0, ATTR_STRING, "BASE", NULL, + offsetof(struct ldapoptions, ldo_defbase)}, + {0, ATTR_INT, "PORT", NULL, /* deprecated */ + offsetof(struct ldapoptions, ldo_defport)}, + {0, ATTR_OPTION, "HOST", NULL, LDAP_OPT_HOST_NAME}, /* deprecated */ + {0, ATTR_OPTION, "URI", NULL, LDAP_OPT_URI}, /* replaces HOST/PORT */ + {0, ATTR_OPTION, "SOCKET_BIND_ADDRESSES", NULL, LDAP_OPT_SOCKET_BIND_ADDRESSES}, + {0, ATTR_BOOL, "REFERRALS", NULL, LDAP_BOOL_REFERRALS}, + {0, ATTR_OPT_INT, "KEEPALIVE_IDLE", NULL, LDAP_OPT_X_KEEPALIVE_IDLE}, + {0, ATTR_OPT_INT, "KEEPALIVE_PROBES", NULL, LDAP_OPT_X_KEEPALIVE_PROBES}, + {0, ATTR_OPT_INT, "KEEPALIVE_INTERVAL", NULL, LDAP_OPT_X_KEEPALIVE_INTERVAL}, + +#if 0 + /* This should only be allowed via ldap_set_option(3) */ + {0, ATTR_BOOL, "RESTART", NULL, LDAP_BOOL_RESTART}, +#endif + +#ifdef HAVE_CYRUS_SASL + {0, ATTR_STRING, "SASL_MECH", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_mech)}, + {0, ATTR_STRING, "SASL_REALM", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_realm)}, + {1, ATTR_STRING, "SASL_AUTHCID", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_authcid)}, + {1, ATTR_STRING, "SASL_AUTHZID", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_authzid)}, + {0, ATTR_SASL, "SASL_SECPROPS", NULL, LDAP_OPT_X_SASL_SECPROPS}, + {0, ATTR_BOOL, "SASL_NOCANON", NULL, LDAP_BOOL_SASL_NOCANON}, + {0, ATTR_SASL, "SASL_CBINDING", NULL, LDAP_OPT_X_SASL_CBINDING}, +#endif + +#ifdef HAVE_TLS + {1, ATTR_TLS, "TLS_CERT", NULL, LDAP_OPT_X_TLS_CERTFILE}, + {1, ATTR_TLS, "TLS_KEY", NULL, LDAP_OPT_X_TLS_KEYFILE}, + {0, ATTR_TLS, "TLS_CACERT", NULL, LDAP_OPT_X_TLS_CACERTFILE}, + {0, ATTR_TLS, "TLS_CACERTDIR", NULL, LDAP_OPT_X_TLS_CACERTDIR}, + {0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT}, + {0, ATTR_TLS, "TLS_REQSAN", NULL, LDAP_OPT_X_TLS_REQUIRE_SAN}, + {0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE}, + {0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE}, + {0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN}, + {0, ATTR_TLS, "TLS_PROTOCOL_MAX", NULL, LDAP_OPT_X_TLS_PROTOCOL_MAX}, + {0, ATTR_TLS, "TLS_PEERKEY_HASH", NULL, LDAP_OPT_X_TLS_PEERKEY_HASH}, + {0, ATTR_TLS, "TLS_ECNAME", NULL, LDAP_OPT_X_TLS_ECNAME}, + +#ifdef HAVE_OPENSSL + {0, ATTR_TLS, "TLS_CRLCHECK", NULL, LDAP_OPT_X_TLS_CRLCHECK}, +#endif +#ifdef HAVE_GNUTLS + {0, ATTR_TLS, "TLS_CRLFILE", NULL, LDAP_OPT_X_TLS_CRLFILE}, +#endif + +#endif + + {0, ATTR_NONE, NULL, NULL, 0} +}; + +#define MAX_LDAP_ATTR_LEN sizeof("SOCKET_BIND_ADDRESSES") +#define MAX_LDAP_ENV_PREFIX_LEN 8 + +static int +ldap_int_conf_option( + struct ldapoptions *gopts, + char *cmd, char *opt, int userconf ) +{ + int i; + + for(i=0; attrs[i].type != ATTR_NONE; i++) { + void *p; + + if( !userconf && attrs[i].useronly ) { + continue; + } + + if(strcasecmp(cmd, attrs[i].name) != 0) { + continue; + } + + switch(attrs[i].type) { + case ATTR_BOOL: + if((strcasecmp(opt, "on") == 0) + || (strcasecmp(opt, "yes") == 0) + || (strcasecmp(opt, "true") == 0)) + { + LDAP_BOOL_SET(gopts, attrs[i].offset); + + } else { + LDAP_BOOL_CLR(gopts, attrs[i].offset); + } + + break; + + case ATTR_INT: { + char *next; + long l; + p = &((char *) gopts)[attrs[i].offset]; + l = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' ) { + * (int*) p = l; + } + } break; + + case ATTR_KV: { + const struct ol_keyvalue *kv; + + for(kv = attrs[i].data; + kv->key != NULL; + kv++) { + + if(strcasecmp(opt, kv->key) == 0) { + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = kv->value; + break; + } + } + } break; + + case ATTR_STRING: + p = &((char *) gopts)[attrs[i].offset]; + if (* (char**) p != NULL) LDAP_FREE(* (char**) p); + * (char**) p = LDAP_STRDUP(opt); + break; + case ATTR_OPTION: + ldap_set_option( NULL, attrs[i].offset, opt ); + break; + case ATTR_SASL: +#ifdef HAVE_CYRUS_SASL + ldap_int_sasl_config( gopts, attrs[i].offset, opt ); +#endif + break; + case ATTR_TLS: +#ifdef HAVE_TLS + ldap_pvt_tls_config( NULL, attrs[i].offset, opt ); +#endif + break; + case ATTR_OPT_TV: { + struct timeval tv; + char *next; + tv.tv_usec = 0; + tv.tv_sec = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' && tv.tv_sec > 0 ) { + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv ); + } + } break; + case ATTR_OPT_INT: { + long l; + char *next; + l = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) { + int v = (int)l; + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v ); + } + } break; + } + + break; + } + + if ( attrs[i].type == ATTR_NONE ) { + Debug1( LDAP_DEBUG_TRACE, "ldap_pvt_tls_config: " + "unknown option '%s'", + cmd ); + return 1; + } + + return 0; +} + +int +ldap_pvt_conf_option( + char *cmd, char *opt, int userconf ) +{ + struct ldapoptions *gopts; + int rc = LDAP_OPT_ERROR; + + /* Get pointer to global option structure */ + gopts = LDAP_INT_GLOBAL_OPT(); + if (NULL == gopts) { + return LDAP_NO_MEMORY; + } + + if ( gopts->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(gopts, NULL); + if ( gopts->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + return ldap_int_conf_option( gopts, cmd, opt, userconf ); +} + +static void openldap_ldap_init_w_conf( + const char *file, int userconf ) +{ + char linebuf[ AC_LINE_MAX ]; + FILE *fp; + int i; + char *cmd, *opt; + char *start, *end; + struct ldapoptions *gopts; + + if ((gopts = LDAP_INT_GLOBAL_OPT()) == NULL) { + return; /* Could not allocate mem for global options */ + } + + if (file == NULL) { + /* no file name */ + return; + } + + Debug1(LDAP_DEBUG_TRACE, "ldap_init: trying %s\n", file ); + + fp = fopen(file, "r"); + if(fp == NULL) { + /* could not open file */ + return; + } + + Debug1(LDAP_DEBUG_TRACE, "ldap_init: using %s\n", file ); + + while((start = fgets(linebuf, sizeof(linebuf), fp)) != NULL) { + /* skip lines starting with '#' */ + if(*start == '#') continue; + + /* trim leading white space */ + while((*start != '\0') && isspace((unsigned char) *start)) + start++; + + /* anything left? */ + if(*start == '\0') continue; + + /* trim trailing white space */ + end = &start[strlen(start)-1]; + while(isspace((unsigned char)*end)) end--; + end[1] = '\0'; + + /* anything left? */ + if(*start == '\0') continue; + + + /* parse the command */ + cmd=start; + while((*start != '\0') && !isspace((unsigned char)*start)) { + start++; + } + if(*start == '\0') { + /* command has no argument */ + continue; + } + + *start++ = '\0'; + + /* we must have some whitespace to skip */ + while(isspace((unsigned char)*start)) start++; + opt = start; + + ldap_int_conf_option( gopts, cmd, opt, userconf ); + } + + fclose(fp); +} + +static void openldap_ldap_init_w_sysconf(const char *file) +{ + openldap_ldap_init_w_conf( file, 0 ); +} + +static void openldap_ldap_init_w_userconf(const char *file) +{ + char *home; + char *path = NULL; + + if (file == NULL) { + /* no file name */ + return; + } + + home = getenv("HOME"); + + if (home != NULL) { + Debug1(LDAP_DEBUG_TRACE, "ldap_init: HOME env is %s\n", + home ); + path = LDAP_MALLOC(strlen(home) + strlen(file) + sizeof( LDAP_DIRSEP ".")); + } else { + Debug0(LDAP_DEBUG_TRACE, "ldap_init: HOME env is NULL\n" ); + } + + if(home != NULL && path != NULL) { + /* we assume UNIX path syntax is used... */ + + /* try ~/file */ + sprintf(path, "%s" LDAP_DIRSEP "%s", home, file); + openldap_ldap_init_w_conf(path, 1); + + /* try ~/.file */ + sprintf(path, "%s" LDAP_DIRSEP ".%s", home, file); + openldap_ldap_init_w_conf(path, 1); + } + + if(path != NULL) { + LDAP_FREE(path); + } + + /* try file */ + openldap_ldap_init_w_conf(file, 1); +} + +static void openldap_ldap_init_w_env( + struct ldapoptions *gopts, + const char *prefix) +{ + char buf[MAX_LDAP_ATTR_LEN+MAX_LDAP_ENV_PREFIX_LEN]; + int len; + int i; + void *p; + char *value; + + if (prefix == NULL) { + prefix = LDAP_ENV_PREFIX; + } + + strncpy(buf, prefix, MAX_LDAP_ENV_PREFIX_LEN); + buf[MAX_LDAP_ENV_PREFIX_LEN] = '\0'; + len = strlen(buf); + + for(i=0; attrs[i].type != ATTR_NONE; i++) { + strcpy(&buf[len], attrs[i].name); + value = getenv(buf); + + if(value == NULL) { + continue; + } + + switch(attrs[i].type) { + case ATTR_BOOL: + if((strcasecmp(value, "on") == 0) + || (strcasecmp(value, "yes") == 0) + || (strcasecmp(value, "true") == 0)) + { + LDAP_BOOL_SET(gopts, attrs[i].offset); + + } else { + LDAP_BOOL_CLR(gopts, attrs[i].offset); + } + break; + + case ATTR_INT: + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = atoi(value); + break; + + case ATTR_KV: { + const struct ol_keyvalue *kv; + + for(kv = attrs[i].data; + kv->key != NULL; + kv++) { + + if(strcasecmp(value, kv->key) == 0) { + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = kv->value; + break; + } + } + } break; + + case ATTR_STRING: + p = &((char *) gopts)[attrs[i].offset]; + if (* (char**) p != NULL) LDAP_FREE(* (char**) p); + if (*value == '\0') { + * (char**) p = NULL; + } else { + * (char**) p = LDAP_STRDUP(value); + } + break; + case ATTR_OPTION: + ldap_set_option( NULL, attrs[i].offset, value ); + break; + case ATTR_SASL: +#ifdef HAVE_CYRUS_SASL + ldap_int_sasl_config( gopts, attrs[i].offset, value ); +#endif + break; + case ATTR_TLS: +#ifdef HAVE_TLS + ldap_pvt_tls_config( NULL, attrs[i].offset, value ); +#endif + break; + case ATTR_OPT_TV: { + struct timeval tv; + char *next; + tv.tv_usec = 0; + tv.tv_sec = strtol( value, &next, 10 ); + if ( next != value && next[ 0 ] == '\0' && tv.tv_sec > 0 ) { + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv ); + } + } break; + case ATTR_OPT_INT: { + long l; + char *next; + l = strtol( value, &next, 10 ); + if ( next != value && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) { + int v = (int)l; + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v ); + } + } break; + } + } +} + +#if defined(__GNUC__) +/* Declare this function as a destructor so that it will automatically be + * invoked either at program exit (if libldap is a static library) or + * at unload time (if libldap is a dynamic library). + * + * Sorry, don't know how to handle this for non-GCC environments. + */ +static void ldap_int_destroy_global_options(void) + __attribute__ ((destructor)); +#endif + +static void +ldap_int_destroy_global_options(void) +{ + struct ldapoptions *gopts = LDAP_INT_GLOBAL_OPT(); + + if ( gopts == NULL ) + return; + + gopts->ldo_valid = LDAP_UNINITIALIZED; + + if ( gopts->ldo_defludp ) { + ldap_free_urllist( gopts->ldo_defludp ); + gopts->ldo_defludp = NULL; + } + + if ( gopts->ldo_local_ip_addrs.local_ip_addrs ) { + LDAP_FREE( gopts->ldo_local_ip_addrs.local_ip_addrs ); + gopts->ldo_local_ip_addrs.local_ip_addrs = NULL; + } + +#if defined(HAVE_WINSOCK) || defined(HAVE_WINSOCK2) + WSACleanup( ); +#endif + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) + if ( ldap_int_hostname ) { + LDAP_FREE( ldap_int_hostname ); + ldap_int_hostname = NULL; + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( gopts->ldo_def_sasl_authcid ) { + LDAP_FREE( gopts->ldo_def_sasl_authcid ); + gopts->ldo_def_sasl_authcid = NULL; + } +#endif +#ifdef HAVE_TLS + ldap_int_tls_destroy( gopts ); +#endif +} + +/* + * Initialize the global options structure with default values. + */ +void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl ) +{ + if (dbglvl) + gopts->ldo_debug = *dbglvl; + else + gopts->ldo_debug = 0; + + gopts->ldo_version = LDAP_VERSION2; + gopts->ldo_deref = LDAP_DEREF_NEVER; + gopts->ldo_timelimit = LDAP_NO_LIMIT; + gopts->ldo_sizelimit = LDAP_NO_LIMIT; + + gopts->ldo_tm_api.tv_sec = -1; + gopts->ldo_tm_net.tv_sec = -1; + + memset( &gopts->ldo_local_ip_addrs, 0, + sizeof( gopts->ldo_local_ip_addrs ) ); + + /* ldo_defludp will be freed by the termination handler + */ + ldap_url_parselist(&gopts->ldo_defludp, "ldap://localhost/"); + gopts->ldo_defport = LDAP_PORT; +#if !defined(__GNUC__) && !defined(PIC) + /* Do this only for a static library, and only if we can't + * arrange for it to be executed as a library destructor + */ + atexit(ldap_int_destroy_global_options); +#endif + + gopts->ldo_refhoplimit = LDAP_DEFAULT_REFHOPLIMIT; + gopts->ldo_rebind_proc = NULL; + gopts->ldo_rebind_params = NULL; + + LDAP_BOOL_ZERO(gopts); + + LDAP_BOOL_SET(gopts, LDAP_BOOL_REFERRALS); + +#ifdef LDAP_CONNECTIONLESS + gopts->ldo_peer = NULL; + gopts->ldo_cldapdn = NULL; + gopts->ldo_is_udp = 0; +#endif + +#ifdef HAVE_CYRUS_SASL + gopts->ldo_def_sasl_mech = NULL; + gopts->ldo_def_sasl_realm = NULL; + gopts->ldo_def_sasl_authcid = NULL; + gopts->ldo_def_sasl_authzid = NULL; + + memset( &gopts->ldo_sasl_secprops, + '\0', sizeof(gopts->ldo_sasl_secprops) ); + + gopts->ldo_sasl_secprops.max_ssf = INT_MAX; + gopts->ldo_sasl_secprops.maxbufsize = SASL_MAX_BUFF_SIZE; + gopts->ldo_sasl_secprops.security_flags = + SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS; +#endif + +#ifdef HAVE_TLS + gopts->ldo_tls_connect_cb = NULL; + gopts->ldo_tls_connect_arg = NULL; + gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND; + gopts->ldo_tls_require_san = LDAP_OPT_X_TLS_ALLOW; +#endif + gopts->ldo_keepalive_probes = 0; + gopts->ldo_keepalive_interval = 0; + gopts->ldo_keepalive_idle = 0; + + gopts->ldo_tcp_user_timeout = 0; + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &gopts->ldo_mutex ); +#endif + gopts->ldo_valid = LDAP_INITIALIZED; + return; +} + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) +char * ldap_int_hostname = NULL; +#endif + +#ifdef LDAP_R_COMPILE +int ldap_int_stackguard; +#endif + +void ldap_int_initialize( struct ldapoptions *gopts, int *dbglvl ) +{ +#ifdef LDAP_R_COMPILE + static ldap_pvt_thread_mutex_t init_mutex; + LDAP_PVT_MUTEX_FIRSTCREATE( init_mutex ); + + LDAP_MUTEX_LOCK( &init_mutex ); +#endif + if ( gopts->ldo_valid == LDAP_INITIALIZED ) { + /* someone else got here first */ + goto done; + } + + ldap_int_error_init(); + + ldap_int_utils_init(); + +#ifdef HAVE_WINSOCK2 +{ WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD( 2, 0 ); + if ( WSAStartup( wVersionRequested, &wsaData ) != 0 ) { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + goto done; + } + + /* Confirm that the WinSock DLL supports 2.0.*/ + /* Note that if the DLL supports versions greater */ + /* than 2.0 in addition to 2.0, it will still return */ + /* 2.0 in wVersion since that is the version we */ + /* requested. */ + + if ( LOBYTE( wsaData.wVersion ) != 2 || + HIBYTE( wsaData.wVersion ) != 0 ) + { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + WSACleanup( ); + goto done; + } +} /* The WinSock DLL is acceptable. Proceed. */ +#elif defined(HAVE_WINSOCK) +{ WSADATA wsaData; + if ( WSAStartup( 0x0101, &wsaData ) != 0 ) { + goto done; + } +} +#endif + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) + LDAP_MUTEX_LOCK( &ldap_int_hostname_mutex ); + { + char *name = ldap_int_hostname; + + ldap_int_hostname = ldap_pvt_get_fqdn( name ); + + if ( name != NULL && name != ldap_int_hostname ) { + LDAP_FREE( name ); + } + } + LDAP_MUTEX_UNLOCK( &ldap_int_hostname_mutex ); +#endif + +#ifndef HAVE_POLL + if ( ldap_int_tblsize == 0 ) ldap_int_ip_init(); +#endif + +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_init() != 0 ) { + goto done; + } +#endif + + ldap_int_initialize_global_options(gopts, dbglvl); + + if( getenv("LDAPNOINIT") != NULL ) { + goto done; + } + +#ifdef LDAP_R_COMPILE + if( getenv("LDAPSTACKGUARD") != NULL ) { + ldap_int_stackguard = 1; + } +#endif + +#ifdef HAVE_CYRUS_SASL + { + /* set authentication identity to current user name */ + char *user = getenv("USER"); + + if( user == NULL ) user = getenv("USERNAME"); + if( user == NULL ) user = getenv("LOGNAME"); + + if( user != NULL ) { + gopts->ldo_def_sasl_authcid = LDAP_STRDUP( user ); + } + } +#endif + + openldap_ldap_init_w_sysconf(LDAP_CONF_FILE); + +#ifdef HAVE_GETEUID + if ( geteuid() != getuid() ) + goto done; +#endif + + openldap_ldap_init_w_userconf(LDAP_USERRC_FILE); + + { + char *altfile = getenv(LDAP_ENV_PREFIX "CONF"); + + if( altfile != NULL ) { + Debug2(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n", + LDAP_ENV_PREFIX "CONF", altfile ); + openldap_ldap_init_w_sysconf( altfile ); + } + else + Debug1(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n", + LDAP_ENV_PREFIX "CONF" ); + } + + { + char *altfile = getenv(LDAP_ENV_PREFIX "RC"); + + if( altfile != NULL ) { + Debug2(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n", + LDAP_ENV_PREFIX "RC", altfile ); + openldap_ldap_init_w_userconf( altfile ); + } + else + Debug1(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n", + LDAP_ENV_PREFIX "RC" ); + } + + openldap_ldap_init_w_env(gopts, NULL); + +done:; +#ifdef LDAP_R_COMPILE + LDAP_MUTEX_UNLOCK( &init_mutex ); +#endif +} diff --git a/contrib/libs/openldap/libraries/libldap/lbase64.c b/contrib/libs/openldap/libraries/libldap/lbase64.c new file mode 100644 index 0000000000..aa4a622fd0 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/lbase64.c @@ -0,0 +1,108 @@ +/* lbase64.c - routines for dealing with base64 strings */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. This + * software is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. + */ + +#include "portable.h" + +#include "ldap-int.h" + +#define RIGHT2 0x03 +#define RIGHT4 0x0f + +static const unsigned char b642nib[0x80] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +int +ldap_int_decode_b64_inplace( struct berval *value ) +{ + char *p, *end, *byte; + char nib; + + byte = value->bv_val; + end = value->bv_val + value->bv_len; + + for ( p = value->bv_val, value->bv_len = 0; + p < end; + p += 4, value->bv_len += 3 ) + { + int i; + for ( i = 0; i < 4; i++ ) { + if ( p[i] != '=' && (p[i] & 0x80 || + b642nib[ p[i] & 0x7f ] > 0x3f) ) { + Debug2( LDAP_DEBUG_ANY, + _("ldap_pvt_decode_b64_inplace: invalid base64 encoding" + " char (%c) 0x%x\n"), p[i], p[i] ); + return( -1 ); + } + } + + /* first digit */ + nib = b642nib[ p[0] & 0x7f ]; + byte[0] = nib << 2; + /* second digit */ + nib = b642nib[ p[1] & 0x7f ]; + byte[0] |= nib >> 4; + byte[1] = (nib & RIGHT4) << 4; + /* third digit */ + if ( p[2] == '=' ) { + value->bv_len += 1; + break; + } + nib = b642nib[ p[2] & 0x7f ]; + byte[1] |= nib >> 2; + byte[2] = (nib & RIGHT2) << 6; + /* fourth digit */ + if ( p[3] == '=' ) { + value->bv_len += 2; + break; + } + nib = b642nib[ p[3] & 0x7f ]; + byte[2] |= nib; + + byte += 3; + } + value->bv_val[ value->bv_len ] = '\0'; + + return LDAP_SUCCESS; +} diff --git a/contrib/libs/openldap/libraries/libldap/ldap-int.h b/contrib/libs/openldap/libraries/libldap/ldap-int.h new file mode 100644 index 0000000000..a401f27341 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldap-int.h @@ -0,0 +1,925 @@ +/* ldap-int.h - defines & prototypes internal to the LDAP library */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ + +#ifndef _LDAP_INT_H +#define _LDAP_INT_H 1 + +#ifndef NO_THREADS +#define LDAP_R_COMPILE 1 +#endif + +#include "../liblber/lber-int.h" +#include "lutil.h" +#include "ldap_avl.h" + +#ifdef LDAP_R_COMPILE +#include <ldap_pvt_thread.h> +#endif + +#ifdef HAVE_CYRUS_SASL + /* the need for this should be removed */ +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#define SASL_MAX_BUFF_SIZE (0xffffff) +#define SASL_MIN_BUFF_SIZE 4096 +#endif + +/* for struct timeval */ +#include <ac/time.h> +#include <ac/socket.h> + +#undef TV2MILLISEC +#define TV2MILLISEC(tv) (((tv)->tv_sec * 1000) + ((tv)->tv_usec/1000)) + +/* + * Support needed if the library is running in the kernel + */ +#if LDAP_INT_IN_KERNEL + /* + * Platform specific function to return a pointer to the + * process-specific global options. + * + * This function should perform the following functions: + * Allocate and initialize a global options struct on a per process basis + * Use callers process identifier to return its global options struct + * Note: Deallocate structure when the process exits + */ +# define LDAP_INT_GLOBAL_OPT() ldap_int_global_opt() + struct ldapoptions *ldap_int_global_opt(void); +#else +# define LDAP_INT_GLOBAL_OPT() (&ldap_int_global_options) +#endif + +/* if used from server code, ldap_debug already points elsewhere */ +#ifndef ldap_debug +#define ldap_debug ((LDAP_INT_GLOBAL_OPT())->ldo_debug) +#endif /* !ldap_debug */ + +#define LDAP_INT_DEBUG +#include "ldap_log.h" + +#ifdef LDAP_DEBUG + +#define DebugTest( level ) \ + ( ldap_debug & level ) + +#define Debug0( level, fmt ) \ + do { if ( DebugTest( (level) ) ) \ + ldap_log_printf( NULL, (level), fmt ); \ + } while ( 0 ) + +#define Debug1( level, fmt, arg1 ) \ + do { if ( DebugTest( (level) ) ) \ + ldap_log_printf( NULL, (level), fmt, arg1 ); \ + } while ( 0 ) + +#define Debug2( level, fmt, arg1, arg2 ) \ + do { if ( DebugTest( (level) ) ) \ + ldap_log_printf( NULL, (level), fmt, arg1, arg2 ); \ + } while ( 0 ) + +#define Debug3( level, fmt, arg1, arg2, arg3 ) \ + do { if ( DebugTest( (level) ) ) \ + ldap_log_printf( NULL, (level), fmt, arg1, arg2, arg3 ); \ + } while ( 0 ) + +#else + +#define DebugTest( level ) (0 == 1) +#define Debug0( level, fmt ) ((void)0) +#define Debug1( level, fmt, arg1 ) ((void)0) +#define Debug2( level, fmt, arg1, arg2 ) ((void)0) +#define Debug3( level, fmt, arg1, arg2, arg3 ) ((void)0) + +#endif /* LDAP_DEBUG */ + +#define LDAP_DEPRECATED 1 +#include "ldap.h" + +#include "ldap_pvt.h" + +LDAP_BEGIN_DECL + +#define LDAP_URL_PREFIX "ldap://" +#define LDAP_URL_PREFIX_LEN STRLENOF(LDAP_URL_PREFIX) +#define PLDAP_URL_PREFIX "pldap://" +#define PLDAP_URL_PREFIX_LEN STRLENOF(PLDAP_URL_PREFIX) +#define LDAPS_URL_PREFIX "ldaps://" +#define LDAPS_URL_PREFIX_LEN STRLENOF(LDAPS_URL_PREFIX) +#define PLDAPS_URL_PREFIX "pldaps://" +#define PLDAPS_URL_PREFIX_LEN STRLENOF(PLDAPS_URL_PREFIX) +#define LDAPI_URL_PREFIX "ldapi://" +#define LDAPI_URL_PREFIX_LEN STRLENOF(LDAPI_URL_PREFIX) +#ifdef LDAP_CONNECTIONLESS +#define LDAPC_URL_PREFIX "cldap://" +#define LDAPC_URL_PREFIX_LEN STRLENOF(LDAPC_URL_PREFIX) +#endif +#define LDAP_URL_URLCOLON "URL:" +#define LDAP_URL_URLCOLON_LEN STRLENOF(LDAP_URL_URLCOLON) + +#define LDAP_REF_STR "Referral:\n" +#define LDAP_REF_STR_LEN STRLENOF(LDAP_REF_STR) +#define LDAP_LDAP_REF_STR LDAP_URL_PREFIX +#define LDAP_LDAP_REF_STR_LEN LDAP_URL_PREFIX_LEN + +#define LDAP_DEFAULT_REFHOPLIMIT 5 + +#define LDAP_BOOL_REFERRALS 0 +#define LDAP_BOOL_RESTART 1 +#define LDAP_BOOL_TLS 3 +#define LDAP_BOOL_CONNECT_ASYNC 4 +#define LDAP_BOOL_SASL_NOCANON 5 +#define LDAP_BOOL_KEEPCONN 6 + +#define LDAP_BOOLEANS unsigned long +#define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n)) +#define LDAP_BOOL_GET(lo, bool) \ + ((lo)->ldo_booleans & LDAP_BOOL(bool) ? -1 : 0) +#define LDAP_BOOL_SET(lo, bool) ((lo)->ldo_booleans |= LDAP_BOOL(bool)) +#define LDAP_BOOL_CLR(lo, bool) ((lo)->ldo_booleans &= ~LDAP_BOOL(bool)) +#define LDAP_BOOL_ZERO(lo) ((lo)->ldo_booleans = 0) + +/* + * This structure represents both ldap messages and ldap responses. + * These are really the same, except in the case of search responses, + * where a response has multiple messages. + */ + +struct ldapmsg { + ber_int_t lm_msgid; /* the message id */ + ber_tag_t lm_msgtype; /* the message type */ + BerElement *lm_ber; /* the ber encoded message contents */ + struct ldapmsg *lm_chain; /* for search - next msg in the resp */ + struct ldapmsg *lm_chain_tail; + struct ldapmsg *lm_next; /* next response */ + time_t lm_time; /* used to maintain cache */ +}; + +#ifdef HAVE_TLS +struct ldaptls { + char *lt_certfile; + char *lt_keyfile; + char *lt_dhfile; + char *lt_cacertfile; + char *lt_cacertdir; + char *lt_ciphersuite; + char *lt_crlfile; + char *lt_randfile; /* OpenSSL only */ + char *lt_ecname; /* OpenSSL only */ + int lt_protocol_min; + int lt_protocol_max; + struct berval lt_cacert; + struct berval lt_cert; + struct berval lt_key; +}; +#endif + +typedef struct ldaplist { + struct ldaplist *ll_next; + void *ll_data; +} ldaplist; + +/* + * LDAP Client Source IP structure + */ +typedef struct ldapsourceip { + char *local_ip_addrs; + struct in_addr ip4_addr; + unsigned short has_ipv4; +#ifdef LDAP_PF_INET6 + struct in6_addr ip6_addr; + unsigned short has_ipv6; +#endif +} ldapsourceip; + +/* + * structure representing get/set'able options + * which have global defaults. + * Protect access to this struct with ldo_mutex + * ldap_log.h:ldapoptions_prefix must match the head of this struct. + */ +struct ldapoptions { + short ldo_valid; +#define LDAP_UNINITIALIZED 0x0 +#define LDAP_INITIALIZED 0x1 +#define LDAP_VALID_SESSION 0x2 +#define LDAP_TRASHED_SESSION 0xFF + int ldo_debug; + + ber_int_t ldo_version; + ber_int_t ldo_deref; + ber_int_t ldo_timelimit; + ber_int_t ldo_sizelimit; + + /* per API call timeout */ + struct timeval ldo_tm_api; + struct timeval ldo_tm_net; + + LDAPURLDesc *ldo_defludp; + int ldo_defport; + char* ldo_defbase; + char* ldo_defbinddn; /* bind dn */ + + /* + * Per connection tcp-keepalive settings (Linux only, + * ignored where unsupported) + */ + ber_int_t ldo_keepalive_idle; + ber_int_t ldo_keepalive_probes; + ber_int_t ldo_keepalive_interval; + + /* + * Per connection tcp user timeout (Linux >= 2.6.37 only, + * ignored where unsupported) + */ + ber_uint_t ldo_tcp_user_timeout; + + int ldo_refhoplimit; /* limit on referral nesting */ + + /* LDAPv3 server and client controls */ + LDAPControl **ldo_sctrls; + LDAPControl **ldo_cctrls; + + /* LDAP rebind callback function */ + LDAP_REBIND_PROC *ldo_rebind_proc; + void *ldo_rebind_params; + LDAP_NEXTREF_PROC *ldo_nextref_proc; + void *ldo_nextref_params; + LDAP_URLLIST_PROC *ldo_urllist_proc; + void *ldo_urllist_params; + + /* LDAP connection callback stack */ + ldaplist *ldo_conn_cbs; + + LDAP_BOOLEANS ldo_booleans; /* boolean options */ + +#define LDAP_LDO_NULLARG ,0,0,0,0 ,{0},{0} ,0,0,0,0, 0,0,0,0,0, 0,0, 0,0,0,0,0,0, 0, 0 + + /* LDAP user configured bind IPs */ + struct ldapsourceip ldo_local_ip_addrs; + +#ifdef LDAP_PF_INET6 +#define LDAP_LDO_SOURCEIP_NULLARG ,{0,0,0,0,0} +#else +#define LDAP_LDO_SOURCEIP_NULLARG ,{0,0,0} +#endif + +#ifdef LDAP_CONNECTIONLESS +#define LDAP_IS_UDP(ld) ((ld)->ld_options.ldo_is_udp) + void* ldo_peer; /* struct sockaddr* */ + char* ldo_cldapdn; + int ldo_is_udp; +#define LDAP_LDO_CONNECTIONLESS_NULLARG ,0,0,0 +#else +#define LDAP_LDO_CONNECTIONLESS_NULLARG +#endif + +#ifdef HAVE_TLS + /* tls context */ + void *ldo_tls_ctx; + LDAP_TLS_CONNECT_CB *ldo_tls_connect_cb; + void* ldo_tls_connect_arg; + struct ldaptls ldo_tls_info; +#define ldo_tls_certfile ldo_tls_info.lt_certfile +#define ldo_tls_keyfile ldo_tls_info.lt_keyfile +#define ldo_tls_dhfile ldo_tls_info.lt_dhfile +#define ldo_tls_ecname ldo_tls_info.lt_ecname +#define ldo_tls_cacertfile ldo_tls_info.lt_cacertfile +#define ldo_tls_cacertdir ldo_tls_info.lt_cacertdir +#define ldo_tls_ciphersuite ldo_tls_info.lt_ciphersuite +#define ldo_tls_protocol_min ldo_tls_info.lt_protocol_min +#define ldo_tls_protocol_max ldo_tls_info.lt_protocol_max +#define ldo_tls_crlfile ldo_tls_info.lt_crlfile +#define ldo_tls_randfile ldo_tls_info.lt_randfile +#define ldo_tls_cacert ldo_tls_info.lt_cacert +#define ldo_tls_cert ldo_tls_info.lt_cert +#define ldo_tls_key ldo_tls_info.lt_key + int ldo_tls_mode; + int ldo_tls_require_cert; + int ldo_tls_impl; + int ldo_tls_crlcheck; + int ldo_tls_require_san; + char *ldo_tls_pin_hashalg; + struct berval ldo_tls_pin; +#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0,0,{0,0} +#else +#define LDAP_LDO_TLS_NULLARG +#endif + +#ifdef HAVE_CYRUS_SASL + char* ldo_def_sasl_mech; /* SASL Mechanism(s) */ + char* ldo_def_sasl_realm; /* SASL realm */ + char* ldo_def_sasl_authcid; /* SASL authentication identity */ + char* ldo_def_sasl_authzid; /* SASL authorization identity */ + + /* SASL Security Properties */ + struct sasl_security_properties ldo_sasl_secprops; + int ldo_sasl_cbinding; +#define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0},0 +#else +#define LDAP_LDO_SASL_NULLARG +#endif + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldo_mutex; +#define LDAP_LDO_MUTEX_NULLARG , LDAP_PVT_MUTEX_NULL +#else +#define LDAP_LDO_MUTEX_NULLARG +#endif +}; + + +/* + * structure for representing an LDAP server connection + */ +typedef struct ldap_conn { + Sockbuf *lconn_sb; +#ifdef HAVE_CYRUS_SASL + void *lconn_sasl_authctx; /* context for bind */ + void *lconn_sasl_sockctx; /* for security layer */ + void *lconn_sasl_cbind; /* for channel binding */ +#endif + int lconn_refcnt; + time_t lconn_created; /* time */ + time_t lconn_lastused; /* time */ + int lconn_rebind_inprogress; /* set if rebind in progress */ + char ***lconn_rebind_queue; /* used if rebind in progress */ + int lconn_status; +#define LDAP_CONNST_NEEDSOCKET 1 +#define LDAP_CONNST_CONNECTING 2 +#define LDAP_CONNST_CONNECTED 3 + LDAPURLDesc *lconn_server; + BerElement *lconn_ber; /* ber receiving on this conn. */ + + struct ldap_conn *lconn_next; +} LDAPConn; + + +/* + * structure used to track outstanding requests + */ +typedef struct ldapreq { + ber_int_t lr_msgid; /* the message id */ + int lr_status; /* status of request */ +#define LDAP_REQST_COMPLETED 0 +#define LDAP_REQST_INPROGRESS 1 +#define LDAP_REQST_CHASINGREFS 2 +#define LDAP_REQST_NOTCONNECTED 3 +#define LDAP_REQST_WRITING 4 + int lr_refcnt; /* count of references */ + int lr_outrefcnt; /* count of outstanding referrals */ + int lr_abandoned; /* the request has been abandoned */ + ber_int_t lr_origid; /* original request's message id */ + int lr_parentcnt; /* count of parent requests */ + ber_tag_t lr_res_msgtype; /* result message type */ + ber_int_t lr_res_errno; /* result LDAP errno */ + char *lr_res_error; /* result error string */ + char *lr_res_matched;/* result matched DN string */ + BerElement *lr_ber; /* ber encoded request contents */ + LDAPConn *lr_conn; /* connection used to send request */ + struct berval lr_dn; /* DN of request, in lr_ber */ + struct ldapreq *lr_parent; /* request that spawned this referral */ + struct ldapreq *lr_child; /* first child request */ + struct ldapreq *lr_refnext; /* next referral spawned */ + struct ldapreq *lr_prev; /* previous request */ + struct ldapreq *lr_next; /* next request */ +} LDAPRequest; + +/* + * structure for client cache + */ +#define LDAP_CACHE_BUCKETS 31 /* cache hash table size */ +typedef struct ldapcache { + LDAPMessage *lc_buckets[LDAP_CACHE_BUCKETS];/* hash table */ + LDAPMessage *lc_requests; /* unfulfilled reqs */ + long lc_timeout; /* request timeout */ + ber_len_t lc_maxmem; /* memory to use */ + ber_len_t lc_memused; /* memory in use */ + int lc_enabled; /* enabled? */ + unsigned long lc_options; /* options */ +#define LDAP_CACHE_OPT_CACHENOERRS 0x00000001 +#define LDAP_CACHE_OPT_CACHEALLERRS 0x00000002 +} LDAPCache; + +/* + * structure containing referral request info for rebind procedure + */ +typedef struct ldapreqinfo { + ber_len_t ri_msgid; + int ri_request; + char *ri_url; +} LDAPreqinfo; + +/* + * structure representing an ldap connection + */ + +struct ldap_common { + Sockbuf *ldc_sb; /* socket descriptor & buffer */ +#define ld_sb ldc->ldc_sb + + unsigned short ldc_lberoptions; +#define ld_lberoptions ldc->ldc_lberoptions + + /* protected by msgid_mutex */ + ber_len_t ldc_msgid; +#define ld_msgid ldc->ldc_msgid + + /* do not mess with these */ + /* protected by req_mutex */ + TAvlnode *ldc_requests; /* list of outstanding requests */ + /* protected by res_mutex */ + LDAPMessage *ldc_responses; /* list of outstanding responses */ +#define ld_requests ldc->ldc_requests +#define ld_responses ldc->ldc_responses + + /* protected by abandon_mutex */ + ber_len_t ldc_nabandoned; + ber_int_t *ldc_abandoned; /* array of abandoned requests */ +#define ld_nabandoned ldc->ldc_nabandoned +#define ld_abandoned ldc->ldc_abandoned + + /* unused by libldap */ + LDAPCache *ldc_cache; /* non-null if cache is initialized */ +#define ld_cache ldc->ldc_cache + + /* do not mess with the rest though */ + + /* protected by conn_mutex */ + LDAPConn *ldc_defconn; /* default connection */ +#define ld_defconn ldc->ldc_defconn + LDAPConn *ldc_conns; /* list of server connections */ +#define ld_conns ldc->ldc_conns + void *ldc_selectinfo;/* platform specifics for select */ +#define ld_selectinfo ldc->ldc_selectinfo + + /* ldap_common refcnt - free only if 0 */ + /* protected by ldc_mutex */ + unsigned int ldc_refcnt; +#define ld_ldcrefcnt ldc->ldc_refcnt + + /* protected by ldo_mutex */ + struct ldapoptions ldc_options; +#define ld_options ldc->ldc_options + +#define ld_valid ld_options.ldo_valid +#define ld_debug ld_options.ldo_debug + +#define ld_deref ld_options.ldo_deref +#define ld_timelimit ld_options.ldo_timelimit +#define ld_sizelimit ld_options.ldo_sizelimit + +#define ld_defbinddn ld_options.ldo_defbinddn +#define ld_defbase ld_options.ldo_defbase +#define ld_defhost ld_options.ldo_defhost +#define ld_defport ld_options.ldo_defport + +#define ld_refhoplimit ld_options.ldo_refhoplimit + +#define ld_sctrls ld_options.ldo_sctrls +#define ld_cctrls ld_options.ldo_cctrls +#define ld_rebind_proc ld_options.ldo_rebind_proc +#define ld_rebind_params ld_options.ldo_rebind_params +#define ld_nextref_proc ld_options.ldo_nextref_proc +#define ld_nextref_params ld_options.ldo_nextref_params +#define ld_urllist_proc ld_options.ldo_urllist_proc +#define ld_urllist_params ld_options.ldo_urllist_params + +#define ld_version ld_options.ldo_version + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldc_mutex; + ldap_pvt_thread_mutex_t ldc_msgid_mutex; + ldap_pvt_thread_mutex_t ldc_conn_mutex; + ldap_pvt_thread_mutex_t ldc_req_mutex; + ldap_pvt_thread_mutex_t ldc_res_mutex; + ldap_pvt_thread_mutex_t ldc_abandon_mutex; +#define ld_ldopts_mutex ld_options.ldo_mutex +#define ld_ldcmutex ldc->ldc_mutex +#define ld_msgid_mutex ldc->ldc_msgid_mutex +#define ld_conn_mutex ldc->ldc_conn_mutex +#define ld_req_mutex ldc->ldc_req_mutex +#define ld_res_mutex ldc->ldc_res_mutex +#define ld_abandon_mutex ldc->ldc_abandon_mutex +#endif +}; + +struct ldap { + /* thread shared */ + struct ldap_common *ldc; + + /* thread specific */ + ber_int_t ld_errno; + char *ld_error; + char *ld_matched; + char **ld_referrals; +}; + +#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION ) +#define LDAP_TRASHED(ld) ( (ld)->ld_valid == LDAP_TRASHED_SESSION ) +#define LDAP_TRASH(ld) ( (ld)->ld_valid = LDAP_TRASHED_SESSION ) + +#ifdef LDAP_R_COMPILE +LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_resolv_mutex; +LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_hostname_mutex; +LDAP_V ( int ) ldap_int_stackguard; + +#endif + +#ifdef LDAP_R_COMPILE +#define LDAP_MUTEX_LOCK(mutex) ldap_pvt_thread_mutex_lock( mutex ) +#define LDAP_MUTEX_UNLOCK(mutex) ldap_pvt_thread_mutex_unlock( mutex ) +#define LDAP_ASSERT_MUTEX_OWNER(mutex) \ + LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex) +#else +#define LDAP_MUTEX_LOCK(mutex) ((void) 0) +#define LDAP_MUTEX_UNLOCK(mutex) ((void) 0) +#define LDAP_ASSERT_MUTEX_OWNER(mutex) ((void) 0) +#endif + +#define LDAP_NEXT_MSGID(ld, id) do { \ + LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \ + (id) = ++(ld)->ld_msgid; \ + LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex ); \ +} while (0) + +/* + * in abandon.c + */ + +LDAP_F (int) +ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp ); +LDAP_F (int) +ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx ); +LDAP_F (int) +ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx ); + +/* + * in add.c + */ + +LDAP_F (BerElement *) ldap_build_add_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in lbase64.c + */ + +LDAP_F (int) ldap_int_decode_b64_inplace LDAP_P(( + struct berval *value )); + +/* + * in compare.c + */ + +LDAP_F (BerElement *) ldap_build_compare_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in delete.c + */ + +LDAP_F (BerElement *) ldap_build_delete_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in extended.c + */ + +LDAP_F (BerElement *) ldap_build_extended_req LDAP_P(( + LDAP *ld, + const char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in init.c + */ + +LDAP_V ( struct ldapoptions ) ldap_int_global_options; + +LDAP_F ( void ) ldap_int_initialize LDAP_P((struct ldapoptions *, int *)); +LDAP_F ( void ) ldap_int_initialize_global_options LDAP_P(( + struct ldapoptions *, int *)); + +/* memory.c */ + /* simple macros to realloc for now */ +#define LDAP_MALLOC(s) (ber_memalloc_x((s),NULL)) +#define LDAP_CALLOC(n,s) (ber_memcalloc_x((n),(s),NULL)) +#define LDAP_REALLOC(p,s) (ber_memrealloc_x((p),(s),NULL)) +#define LDAP_FREE(p) (ber_memfree_x((p),NULL)) +#define LDAP_VFREE(v) (ber_memvfree_x((void **)(v),NULL)) +#define LDAP_STRDUP(s) (ber_strdup_x((s),NULL)) +#define LDAP_STRNDUP(s,l) (ber_strndup_x((s),(l),NULL)) + +#define LDAP_MALLOCX(s,x) (ber_memalloc_x((s),(x))) +#define LDAP_CALLOCX(n,s,x) (ber_memcalloc_x((n),(s),(x))) +#define LDAP_REALLOCX(p,s,x) (ber_memrealloc_x((p),(s),(x))) +#define LDAP_FREEX(p,x) (ber_memfree_x((p),(x))) +#define LDAP_VFREEX(v,x) (ber_memvfree_x((void **)(v),(x))) +#define LDAP_STRDUPX(s,x) (ber_strdup_x((s),(x))) +#define LDAP_STRNDUPX(s,l,x) (ber_strndup_x((s),(l),(x))) + +/* + * in error.c + */ +LDAP_F (void) ldap_int_error_init( void ); + +/* + * in modify.c + */ + +LDAP_F (BerElement *) ldap_build_modify_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in modrdn.c + */ + +LDAP_F (BerElement *) ldap_build_moddn_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *newrdn, + const char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in unit-int.c + */ +LDAP_F (void) ldap_int_utils_init LDAP_P(( void )); + + +/* + * in print.c + */ +LDAP_F (int) ldap_log_printf LDAP_P((LDAP *ld, int level, const char *fmt, ...)) LDAP_GCCATTR((format(printf, 3, 4))); + +/* + * in controls.c + */ +LDAP_F (int) ldap_int_put_controls LDAP_P(( + LDAP *ld, + LDAPControl *const *ctrls, + BerElement *ber )); + +LDAP_F (int) ldap_int_client_controls LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); + +/* + * in dsparse.c + */ +LDAP_F (int) ldap_int_next_line_tokens LDAP_P(( char **bufp, ber_len_t *blenp, char ***toksp )); + + +/* + * in open.c + */ +LDAP_F (int) ldap_open_defconn( LDAP *ld ); +LDAP_F (int) ldap_int_open_connection( LDAP *ld, + LDAPConn *conn, LDAPURLDesc *srvlist, int async ); +LDAP_F (int) ldap_int_check_async_open( LDAP *ld, ber_socket_t sd ); + +/* + * in os-ip.c + */ +#ifndef HAVE_POLL +LDAP_V (int) ldap_int_tblsize; +LDAP_F (void) ldap_int_ip_init( void ); +#endif + +LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest, + const struct timeval *tm ); +LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb, + int proto, LDAPURLDesc *srv, int async ); +LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s, + struct timeval *tvp, int wr ); + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) +LDAP_V (char *) ldap_int_hostname; +LDAP_F (char *) ldap_host_connected_to( Sockbuf *sb, + const char *host ); +#endif + +LDAP_F (int) ldap_int_select( LDAP *ld, struct timeval *timeout ); +LDAP_F (void *) ldap_new_select_info( void ); +LDAP_F (void) ldap_free_select_info( void *sip ); +LDAP_F (void) ldap_mark_select_write( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_mark_select_read( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_clear_select_write( LDAP *ld, Sockbuf *sb ); +LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb ); +LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb ); + +LDAP_F (int) ldap_validate_and_fill_sourceip ( char** source_ip_lst, + ldapsourceip* temp_source_ip ); + +LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb, + ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr ); + +/* + * in os-local.c + */ +#ifdef LDAP_PF_LOCAL +LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb, + LDAPURLDesc *srv, int async ); +#endif /* LDAP_PF_LOCAL */ + +/* + * in request.c + */ +LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype, + const char *dn, BerElement *ber, ber_int_t msgid ); +LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld ); +LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber ); + +LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber, + ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, + LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res ); +LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, + int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res ); +LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ); +LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit ); +LDAP_F (int) ldap_req_cmp( const void *l, const void *r ); +LDAP_F (void) ldap_do_free_request( void *arg ); +LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr ); +LDAP_F (void) ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ); +LDAP_F (void) ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ); +LDAP_F (void) ldap_dump_requests_and_responses( LDAP *ld ); +LDAP_F (int) ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, + char **errstrp, int sref, int *hadrefp ); +LDAP_F (int) ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, + char **refs, int sref, char **referralsp, int *hadrefp ); +LDAP_F (int) ldap_append_referral( LDAP *ld, char **referralsp, char *s ); +LDAP_F (int) ldap_int_flush_request( LDAP *ld, LDAPRequest *lr ); + +/* + * in result.c: + */ +LDAP_F (const char *) ldap_int_msgtype2str( ber_tag_t tag ); + +/* + * in search.c + */ +LDAP_F (BerElement *) ldap_build_search_req LDAP_P(( + LDAP *ld, + const char *base, + ber_int_t scope, + const char *filter, + char **attrs, + ber_int_t attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t timelimit, + ber_int_t sizelimit, + ber_int_t deref, + ber_int_t *msgidp)); + + +/* + * in unbind.c + */ +LDAP_F (int) ldap_ld_free LDAP_P(( + LDAP *ld, + int close, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +LDAP_F (int) ldap_send_unbind LDAP_P(( + LDAP *ld, + Sockbuf *sb, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +/* + * in url.c + */ +LDAP_F (LDAPURLDesc *) ldap_url_dup LDAP_P(( + LDAPURLDesc *ludp )); + +LDAP_F (LDAPURLDesc *) ldap_url_duplist LDAP_P(( + LDAPURLDesc *ludlist )); + +LDAP_F (int) ldap_url_parsehosts LDAP_P(( + LDAPURLDesc **ludlist, + const char *hosts, + int port )); + +LDAP_F (char *) ldap_url_list2hosts LDAP_P(( + LDAPURLDesc *ludlist )); + +/* + * in cyrus.c + */ + +LDAP_F (int) ldap_int_sasl_init LDAP_P(( void )); + +LDAP_F (int) ldap_int_sasl_open LDAP_P(( + LDAP *ld, LDAPConn *conn, + const char* host )); +LDAP_F (int) ldap_int_sasl_close LDAP_P(( LDAP *ld, LDAPConn *conn )); + +LDAP_F (int) ldap_int_sasl_external LDAP_P(( + LDAP *ld, LDAPConn *conn, + const char* authid, ber_len_t ssf )); + +LDAP_F (int) ldap_int_sasl_get_option LDAP_P(( LDAP *ld, + int option, void *arg )); +LDAP_F (int) ldap_int_sasl_set_option LDAP_P(( LDAP *ld, + int option, void *arg )); +LDAP_F (int) ldap_int_sasl_config LDAP_P(( struct ldapoptions *lo, + int option, const char *arg )); + +LDAP_F (int) ldap_int_sasl_bind LDAP_P(( + LDAP *ld, + const char *, + const char *, + LDAPControl **, LDAPControl **, + + /* should be passed in client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid )); + +/* in sasl.c */ + +LDAP_F (BerElement *) ldap_build_bind_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *mech, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* in schema.c */ +LDAP_F (char *) ldap_int_parse_numericoid LDAP_P(( + const char **sp, + int *code, + const int flags )); + +/* + * in tls.c + */ +LDAP_F (int) ldap_int_tls_start LDAP_P(( LDAP *ld, + LDAPConn *conn, LDAPURLDesc *srv )); + +LDAP_F (void) ldap_int_tls_destroy LDAP_P(( struct ldapoptions *lo )); + +/* + * in getvalues.c + */ +LDAP_F (char **) ldap_value_dup LDAP_P(( + char *const *vals )); + +LDAP_END_DECL + +#endif /* _LDAP_INT_H */ diff --git a/contrib/libs/openldap/libraries/libldap/ldap-tls.h b/contrib/libs/openldap/libraries/libldap/ldap-tls.h new file mode 100644 index 0000000000..e2d7efc9cf --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldap-tls.h @@ -0,0 +1,91 @@ +/* ldap-tls.h - TLS defines & prototypes internal to the LDAP library */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _LDAP_TLS_H +#define _LDAP_TLS_H 1 + +struct tls_impl; + +struct tls_ctx; +struct tls_session; + +#define CERTPATHSEP ";" + +typedef struct tls_ctx tls_ctx; +typedef struct tls_session tls_session; + +typedef int (TI_tls_init)(void); +typedef void (TI_tls_destroy)(void); + +typedef tls_ctx *(TI_ctx_new)(struct ldapoptions *lo); +typedef void (TI_ctx_ref)(tls_ctx *ctx); +typedef void (TI_ctx_free)(tls_ctx *ctx); +#define ERRBUFSIZE 256 +typedef int (TI_ctx_init)(struct ldapoptions *lo, struct ldaptls *lt, int is_server, char *errmsg); + +typedef tls_session *(TI_session_new)(tls_ctx *ctx, int is_server); +typedef int (TI_session_connect)(LDAP *ld, tls_session *s, const char *name_in); +typedef int (TI_session_accept)(tls_session *s); +typedef int (TI_session_upflags)(Sockbuf *sb, tls_session *s, int rc); +typedef char *(TI_session_errmsg)(tls_session *s, int rc, char *buf, size_t len ); +typedef int (TI_session_dn)(tls_session *sess, struct berval *dn); +typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in); +typedef int (TI_session_strength)(tls_session *sess); +typedef int (TI_session_unique)(tls_session *sess, struct berval *buf, int is_server); +typedef int (TI_session_endpoint)(tls_session *sess, struct berval *buf, int is_server); +typedef const char *(TI_session_name)(tls_session *s); +typedef int (TI_session_peercert)(tls_session *s, struct berval *der); +typedef int (TI_session_pinning)(LDAP *ld, tls_session *s, char *hashalg, struct berval *hash); + +typedef void (TI_thr_init)(void); + +typedef struct tls_impl { + const char *ti_name; + + TI_tls_init *ti_tls_init; /* library initialization */ + TI_tls_destroy *ti_tls_destroy; + + TI_ctx_new *ti_ctx_new; + TI_ctx_ref *ti_ctx_ref; + TI_ctx_free *ti_ctx_free; + TI_ctx_init *ti_ctx_init; + + TI_session_new *ti_session_new; + TI_session_connect *ti_session_connect; + TI_session_accept *ti_session_accept; + TI_session_upflags *ti_session_upflags; + TI_session_errmsg *ti_session_errmsg; + TI_session_dn *ti_session_my_dn; + TI_session_dn *ti_session_peer_dn; + TI_session_chkhost *ti_session_chkhost; + TI_session_strength *ti_session_strength; + TI_session_unique *ti_session_unique; + TI_session_endpoint *ti_session_endpoint; + TI_session_name *ti_session_version; + TI_session_name *ti_session_cipher; + TI_session_peercert *ti_session_peercert; + TI_session_pinning *ti_session_pinning; + + Sockbuf_IO *ti_sbio; + + TI_thr_init *ti_thr_init; + + int ti_inited; +} tls_impl; + +extern tls_impl ldap_int_tls_impl; + +#endif /* _LDAP_TLS_H */ diff --git a/contrib/libs/openldap/libraries/libldap/ldap_sync.c b/contrib/libs/openldap/libraries/libldap/ldap_sync.c new file mode 100644 index 0000000000..6c99e3a895 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldap_sync.c @@ -0,0 +1,928 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +/* + * Proof-of-concept API that implement the client-side + * of the "LDAP Content Sync Operation" (RFC 4533) + */ + +#include "portable.h" + +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_SYNC_TRACE +static const char * +ldap_sync_state2str( int state ) +{ + switch ( state ) { + case LDAP_SYNC_PRESENT: + return "LDAP_SYNC_PRESENT"; + + case LDAP_SYNC_ADD: + return "LDAP_SYNC_ADD"; + + case LDAP_SYNC_MODIFY: + return "LDAP_SYNC_MODIFY"; + + case LDAP_SYNC_DELETE: + return "LDAP_SYNC_DELETE"; + + default: + return "(unknown)"; + } +} +#endif + +/* + * initialize the persistent search structure + */ +ldap_sync_t * +ldap_sync_initialize( ldap_sync_t *ls_in ) +{ + ldap_sync_t *ls = ls_in; + + if ( ls == NULL ) { + ls = ldap_memalloc( sizeof( ldap_sync_t ) ); + if ( ls == NULL ) { + return NULL; + } + } + memset( ls, 0, sizeof( ldap_sync_t ) ); + + ls->ls_scope = LDAP_SCOPE_SUBTREE; + ls->ls_timeout = -1; + + return ls; +} + +/* + * destroy the persistent search structure + */ +void +ldap_sync_destroy( ldap_sync_t *ls, int freeit ) +{ + assert( ls != NULL ); + + if ( ls->ls_base != NULL ) { + ldap_memfree( ls->ls_base ); + ls->ls_base = NULL; + } + + if ( ls->ls_filter != NULL ) { + ldap_memfree( ls->ls_filter ); + ls->ls_filter = NULL; + } + + if ( ls->ls_attrs != NULL ) { + int i; + + for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) { + ldap_memfree( ls->ls_attrs[ i ] ); + } + ldap_memfree( ls->ls_attrs ); + ls->ls_attrs = NULL; + } + + if ( ls->ls_ld != NULL ) { + (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_unbind_ext()\n" ); +#endif /* LDAP_SYNC_TRACE */ + ls->ls_ld = NULL; + } + + if ( ls->ls_cookie.bv_val != NULL ) { + ldap_memfree( ls->ls_cookie.bv_val ); + ls->ls_cookie.bv_val = NULL; + } + + if ( freeit ) { + ldap_memfree( ls ); + } +} + +/* + * handle the LDAP_RES_SEARCH_ENTRY response + */ +static int +ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res ) +{ + LDAPControl **ctrls = NULL; + int rc = LDAP_OTHER, + i; + BerElement *ber = NULL; + struct berval entryUUID = { 0 }, + cookie = { 0 }; + int state = -1; + ber_len_t len; + ldap_sync_refresh_t phase; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + phase = ls->ls_refreshPhase; + + /* OK */ + + /* extract: + * - data + * - entryUUID + * + * check that: + * - Sync State Control is "add" + */ + + /* the control MUST be present */ + + /* extract controls */ + ldap_get_entry_controls( ls->ls_ld, res, &ctrls ); + if ( ctrls == NULL ) { + goto done; + } + + /* lookup the sync state control */ + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) { + break; + } + } + + /* control must be present; there might be other... */ + if ( ctrls[ i ] == NULL ) { + goto done; + } + + /* extract data */ + ber = ber_init( &ctrls[ i ]->ldctl_value ); + if ( ber == NULL ) { + goto done; + } + /* scan entryUUID in-place ("m") */ + if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR + || entryUUID.bv_len == 0 ) + { + goto done; + } + + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + /* scan cookie in-place ("m") */ + if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + switch ( state ) { + case LDAP_SYNC_PRESENT: + case LDAP_SYNC_DELETE: + case LDAP_SYNC_ADD: + case LDAP_SYNC_MODIFY: + /* NOTE: ldap_sync_refresh_t is defined + * as the corresponding LDAP_SYNC_* + * for the 4 above cases */ + phase = state; +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) ); +#endif /* LDAP_SYNC_TRACE */ + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot unknown syncState=%d\n", state ); +#endif /* LDAP_SYNC_TRACE */ + goto done; + } + + rc = ls->ls_search_entry + ? ls->ls_search_entry( ls, res, &entryUUID, phase ) + : LDAP_SUCCESS; + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + if ( ctrls != NULL ) { + ldap_controls_free( ctrls ); + } + + return rc; +} + +/* + * handle the LDAP_RES_SEARCH_REFERENCE response + * (to be implemented yet) + */ +static int +ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res ) +{ + int rc = 0; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + if ( ls->ls_search_reference ) { + rc = ls->ls_search_reference( ls, res ); + } + + return rc; +} + +/* + * handle the LDAP_RES_SEARCH_RESULT response + */ +static int +ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res ) +{ + int err; + char *matched = NULL, + *msg = NULL; + LDAPControl **ctrls = NULL; + int rc; + int refreshDeletes = -1; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + /* should not happen in refreshAndPersist... */ + rc = ldap_parse_result( ls->ls_ld, + res, &err, &matched, &msg, NULL, &ctrls, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n", + err, + matched ? matched : "", + msg ? msg : "", + rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc == LDAP_SUCCESS ) { + rc = err; + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + + switch ( rc ) { + case LDAP_SUCCESS: { + int i; + BerElement *ber = NULL; + ber_len_t len; + struct berval cookie = { 0 }; + + rc = LDAP_OTHER; + + /* deal with control; then fallthru to handler */ + if ( ctrls == NULL ) { + goto done; + } + + /* lookup the sync state control */ + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + if ( strcmp( ctrls[ i ]->ldctl_oid, + LDAP_CONTROL_SYNC_DONE ) == 0 ) + { + break; + } + } + + /* control must be present; there might be other... */ + if ( ctrls[ i ] == NULL ) { + goto done; + } + + /* extract data */ + ber = ber_init( &ctrls[ i ]->ldctl_value ); + if ( ber == NULL ) { + goto done; + } + + if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) { + goto ber_done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto ber_done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + refreshDeletes = 0; + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) { + if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) { + goto ber_done; + } + if ( refreshDeletes ) { + refreshDeletes = 1; + } + } + + if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) { + rc = LDAP_SUCCESS; + } + + ber_done:; + ber_free( ber, 1 ); + if ( rc != LDAP_SUCCESS ) { + break; + } + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDeletes=%s\n", + refreshDeletes ? "TRUE" : "FALSE" ); +#endif /* LDAP_SYNC_TRACE */ + + /* FIXME: what should we do with the refreshDelete? */ + switch ( refreshDeletes ) { + case 0: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS; + break; + + default: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES; + break; + } + + } /* fallthru */ + + case LDAP_SYNC_REFRESH_REQUIRED: + /* TODO: check for Sync Done Control */ + /* FIXME: perhaps the handler should be called + * also in case of failure; we'll deal with this + * later when implementing refreshOnly */ + if ( ls->ls_search_result ) { + err = ls->ls_search_result( ls, res, refreshDeletes ); + } + break; + } + +done:; + if ( matched != NULL ) { + ldap_memfree( matched ); + } + + if ( msg != NULL ) { + ldap_memfree( msg ); + } + + if ( ctrls != NULL ) { + ldap_controls_free( ctrls ); + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + + return rc; +} + +/* + * handle the LDAP_RES_INTERMEDIATE response + */ +static int +ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone ) +{ + int rc; + char *retoid = NULL; + struct berval *retdata = NULL; + BerElement *ber = NULL; + ber_len_t len; + ber_tag_t syncinfo_tag; + struct berval cookie; + int refreshDeletes = 0; + BerVarray syncUUIDs = NULL; + ldap_sync_refresh_t phase; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + assert( refreshDone != NULL ); + + *refreshDone = 0; + + rc = ldap_parse_intermediate( ls->ls_ld, res, + &retoid, &retdata, NULL, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n", + rc != LDAP_SUCCESS ? "!!! " : "", + retoid == NULL ? "\"\"" : retoid, + rc ); +#endif /* LDAP_SYNC_TRACE */ + /* parsing must be successful, and yield the OID + * of the sync info intermediate response */ + if ( rc != LDAP_SUCCESS ) { + goto done; + } + + rc = LDAP_OTHER; + + if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) { + goto done; + } + + /* init ber using the value in the response */ + ber = ber_init( retdata ); + if ( ber == NULL ) { + goto done; + } + + syncinfo_tag = ber_peek_tag( ber, &len ); + switch ( syncinfo_tag ) { + case LDAP_TAG_SYNC_NEW_COOKIE: + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + break; + + case LDAP_TAG_SYNC_REFRESH_DELETE: + case LDAP_TAG_SYNC_REFRESH_PRESENT: + if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) { +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDelete\n" ); +#endif /* LDAP_SYNC_TRACE */ + switch ( ls->ls_refreshPhase ) { + case LDAP_SYNC_CAPI_NONE: + case LDAP_SYNC_CAPI_PRESENTS: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES; + break; + + default: + /* TODO: impossible; handle */ + goto done; + } + + } else { +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshPresent\n" ); +#endif /* LDAP_SYNC_TRACE */ + switch ( ls->ls_refreshPhase ) { + case LDAP_SYNC_CAPI_NONE: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS; + break; + + default: + /* TODO: impossible; handle */ + goto done; + } + } + + if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { + goto done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + *refreshDone = 1; + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) { + if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) { + goto done; + } + } + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDone=%s\n", + *refreshDone ? "TRUE" : "FALSE" ); +#endif /* LDAP_SYNC_TRACE */ + + if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) { + goto done; + } + + if ( *refreshDone ) { + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + } + + if ( ls->ls_intermediate ) { + ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase ); + } + + break; + + case LDAP_TAG_SYNC_ID_SET: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot syncIdSet\n" ); +#endif /* LDAP_SYNC_TRACE */ + if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { + goto done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) { + if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) { + goto done; + } + } + + if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR + || syncUUIDs == NULL ) + { + goto done; + } + +#ifdef LDAP_SYNC_TRACE + { + int i; + + fprintf( stderr, "\t\tgot refreshDeletes=%s\n", + refreshDeletes ? "TRUE" : "FALSE" ); + for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) { + char buf[ BUFSIZ ]; + fprintf( stderr, "\t\t%s\n", + lutil_uuidstr_from_normalized( + syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len, + buf, sizeof( buf ) ) ); + } + } +#endif /* LDAP_SYNC_TRACE */ + + if ( refreshDeletes ) { + phase = LDAP_SYNC_CAPI_DELETES_IDSET; + + } else { + phase = LDAP_SYNC_CAPI_PRESENTS_IDSET; + } + + /* FIXME: should touch ls->ls_refreshPhase? */ + if ( ls->ls_intermediate ) { + ls->ls_intermediate( ls, res, syncUUIDs, phase ); + } + + ber_bvarray_free( syncUUIDs ); + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tunknown tag!\n" ); +#endif /* LDAP_SYNC_TRACE */ + goto done; + } + + rc = LDAP_SUCCESS; + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + if ( retoid != NULL ) { + ldap_memfree( retoid ); + } + + if ( retdata != NULL ) { + ber_bvfree( retdata ); + } + + return rc; +} + +/* + * initialize the sync + */ +int +ldap_sync_init( ldap_sync_t *ls, int mode ) +{ + LDAPControl ctrl = { 0 }, + *ctrls[ 2 ]; + BerElement *ber = NULL; + int rc; + struct timeval tv = { 0 }, + *tvp = NULL; + LDAPMessage *res = NULL; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_sync_init(%s)...\n", + mode == LDAP_SYNC_REFRESH_AND_PERSIST ? + "LDAP_SYNC_REFRESH_AND_PERSIST" : + ( mode == LDAP_SYNC_REFRESH_ONLY ? + "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( ls->ls_ld != NULL ); + + /* support both refreshOnly and refreshAndPersist */ + switch ( mode ) { + case LDAP_SYNC_REFRESH_AND_PERSIST: + case LDAP_SYNC_REFRESH_ONLY: + break; + + default: + fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode ); + return LDAP_PARAM_ERROR; + } + + /* check consistency of cookie and reloadHint at initial refresh */ + if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) { + fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" ); + return LDAP_PARAM_ERROR; + } + + ctrls[ 0 ] = &ctrl; + ctrls[ 1 ] = NULL; + + /* prepare the Sync Request control */ + ber = ber_alloc_t( LBER_USE_DER ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "%sber_alloc_t() %s= NULL\n", + ber == NULL ? "!!! " : "", + ber == NULL ? "=" : "!" ); +#endif /* LDAP_SYNC_TRACE */ + if ( ber == NULL ) { + rc = LDAP_NO_MEMORY; + goto done; + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE; + + if ( ls->ls_cookie.bv_val != NULL ) { + ber_printf( ber, "{eOb}", mode, + &ls->ls_cookie, ls->ls_reloadHint ); + + } else { + ber_printf( ber, "{eb}", mode, ls->ls_reloadHint ); + } + + rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "%sber_flatten2() == %d\n", + rc ? "!!! " : "", + rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc < 0 ) { + rc = LDAP_OTHER; + goto done; + } + + /* make the control critical, as we cannot proceed without */ + ctrl.ldctl_oid = LDAP_CONTROL_SYNC; + ctrl.ldctl_iscritical = 1; + + /* timelimit? */ + if ( ls->ls_timelimit ) { + tv.tv_sec = ls->ls_timelimit; + tvp = &tv; + } + + /* actually run the search */ + rc = ldap_search_ext( ls->ls_ld, + ls->ls_base, ls->ls_scope, ls->ls_filter, + ls->ls_attrs, 0, ctrls, NULL, + tvp, ls->ls_sizelimit, &ls->ls_msgid ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n", + rc ? "!!! " : "", + ls->ls_base, ls->ls_scope, ls->ls_filter, rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc != LDAP_SUCCESS ) { + goto done; + } + + /* initial content/content update phase */ + for ( ; ; ) { + LDAPMessage *msg = NULL; + + /* NOTE: this very short timeout is just to let + * ldap_result() yield long enough to get something */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ls->ls_ld, ls->ls_msgid, + LDAP_MSG_RECEIVED, &tv, &res ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "\t%sldap_result(%d) == %d\n", + rc == -1 ? "!!! " : "", + ls->ls_msgid, rc ); +#endif /* LDAP_SYNC_TRACE */ + switch ( rc ) { + case 0: + /* + * timeout + * + * TODO: can do something else in the meanwhile) + */ + break; + + case -1: + /* smtg bad! */ + goto done; + + default: + for ( msg = ldap_first_message( ls->ls_ld, res ); + msg != NULL; + msg = ldap_next_message( ls->ls_ld, msg ) ) + { + int refreshDone; + + switch ( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_sync_search_entry( ls, res ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + rc = ldap_sync_search_reference( ls, res ); + break; + + case LDAP_RES_SEARCH_RESULT: + rc = ldap_sync_search_result( ls, res ); + goto done_search; + + case LDAP_RES_INTERMEDIATE: + rc = ldap_sync_search_intermediate( ls, res, &refreshDone ); + if ( rc != LDAP_SUCCESS || refreshDone ) { + goto done_search; + } + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot something unexpected...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + ldap_msgfree( res ); + + rc = LDAP_OTHER; + goto done; + } + } + ldap_msgfree( res ); + res = NULL; + break; + } + } + +done_search:; + ldap_msgfree( res ); + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return rc; +} + +/* + * initialize the refreshOnly sync + */ +int +ldap_sync_init_refresh_only( ldap_sync_t *ls ) +{ + return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY ); +} + +/* + * initialize the refreshAndPersist sync + */ +int +ldap_sync_init_refresh_and_persist( ldap_sync_t *ls ) +{ + return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST ); +} + +/* + * poll for new responses + */ +int +ldap_sync_poll( ldap_sync_t *ls ) +{ + struct timeval tv, + *tvp = NULL; + LDAPMessage *res = NULL, + *msg; + int rc = 0; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_sync_poll...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( ls->ls_ld != NULL ); + + if ( ls->ls_timeout != -1 ) { + tv.tv_sec = ls->ls_timeout; + tv.tv_usec = 0; + tvp = &tv; + } + + rc = ldap_result( ls->ls_ld, ls->ls_msgid, + LDAP_MSG_RECEIVED, tvp, &res ); + if ( rc <= 0 ) { + return rc; + } + + for ( msg = ldap_first_message( ls->ls_ld, res ); + msg; + msg = ldap_next_message( ls->ls_ld, msg ) ) + { + int refreshDone; + + switch ( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_sync_search_entry( ls, res ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + rc = ldap_sync_search_reference( ls, res ); + break; + + case LDAP_RES_SEARCH_RESULT: + rc = ldap_sync_search_result( ls, res ); + goto done_search; + + case LDAP_RES_INTERMEDIATE: + rc = ldap_sync_search_intermediate( ls, res, &refreshDone ); + if ( rc != LDAP_SUCCESS || refreshDone ) { + goto done_search; + } + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot something unexpected...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + ldap_msgfree( res ); + + rc = LDAP_OTHER; + goto done; + } + } + +done_search:; + ldap_msgfree( res ); + +done:; + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/ldap_thr_debug.h b/contrib/libs/openldap/libraries/libldap/ldap_thr_debug.h new file mode 100644 index 0000000000..db6a052185 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldap_thr_debug.h @@ -0,0 +1,197 @@ +/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifdef LDAP_THREAD_DEBUG + +/* + * libldap .c files should include this file after ldap_pvt_thread.h, + * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro(s) defined. + */ + +#ifndef _LDAP_PVT_THREAD_H +#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h" +#endif + +/* + * Support for thr_debug.c: + * + * thr_debug.c defines ldap_pvt_thread_* as wrappers around the real + * ldap_pvt_thread_* implementation, which this file renames to + * ldap_int_thread_*. + * + * Implementation: + * + * This file re#defines selected ldap_pvt_thread_* names to + * ldap_int_thread_*, which will be used from wrappers in thr_debug.c. + * Two ldap_int_*() calls are redirected to call ldap_debug_*(): These + * are wrappers around the originals, whose definitions are not renamed. + * This file then #includes ldap_pvt_thread.h to declare the renamed + * functions/types. If #included from thr_debug.c it finally #undefines + * the macros again. + * + * include/ldap_pvt_thread.h declares the typedefs ldap_pvt_thread*_t as + * either wrapper types ldap_debug_thread*_t or their usual definitions + * ldap_int_thread*_t, depending on the LDAP_THREAD_DEBUG_WRAP option. + * When defining the underlying implementation, this file then redirects + * the type names back to the original ldap_int_thread*_t types. + * include/ldap_<int,pvt>_thread.h also do some thr_debug magic. + * + * So, + * libldap/<not thr_debug.c> thus define ldap_int_thread_*() instead + * of ldap_pvt_thread_*(). + * thr_debug.c defines the ldap_pvt_*() and ldap_debug_*() functions. + * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call + * ldap_debug_thread_*() instead of ldap_int_thread_*(). + * In tpool.c, ldap_int_thread_pool_shutdown() has explicit thr_debug.c + * support which treats ldap_pvt_thread_pool_destroy() the same way. + */ + +#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */ +#define ldap_int_thread_initialize ldap_debug_thread_initialize +#define ldap_int_thread_destroy ldap_debug_thread_destroy +#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */ +#undef ldap_int_thread_initialize +#undef ldap_int_thread_destroy +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t +#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t +#endif +#define ldap_pvt_thread_sleep ldap_int_thread_sleep +#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency +#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency +#define ldap_pvt_thread_create ldap_int_thread_create +#define ldap_pvt_thread_exit ldap_int_thread_exit +#define ldap_pvt_thread_join ldap_int_thread_join +#define ldap_pvt_thread_kill ldap_int_thread_kill +#define ldap_pvt_thread_yield ldap_int_thread_yield +#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init +#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy +#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal +#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast +#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait +#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init +#define ldap_pvt_thread_mutex_recursive_init ldap_int_thread_mutex_recursive_init +#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy +#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock +#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock +#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock +#define ldap_pvt_thread_self ldap_int_thread_self +#endif /* LDAP_THREAD_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t +#endif +#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init +#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy +#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock +#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock +#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock +#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock +#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock +#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock +#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers +#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers +#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active +#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t +#endif +#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init +#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit +#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads +#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload +#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause +#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume +#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy +#define ldap_pvt_thread_pool_close ldap_int_thread_pool_close +#define ldap_pvt_thread_pool_free ldap_int_thread_pool_free +#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey +#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey +#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey +#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context +#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset +#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ + +#undef _LDAP_PVT_THREAD_H +#include "ldap_pvt_thread.h" + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */ +/* + * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not + * use it for ldap_pvt_thread.h above because of its use of LDAP_P(). + */ +#undef ldap_pvt_thread_pool_destroy +#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r) +#endif + +#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */ +#undef ldap_pvt_thread_mutex_t +#undef ldap_pvt_thread_cond_t +#undef ldap_pvt_thread_sleep +#undef ldap_pvt_thread_get_concurrency +#undef ldap_pvt_thread_set_concurrency +#undef ldap_pvt_thread_create +#undef ldap_pvt_thread_exit +#undef ldap_pvt_thread_join +#undef ldap_pvt_thread_kill +#undef ldap_pvt_thread_yield +#undef ldap_pvt_thread_cond_init +#undef ldap_pvt_thread_cond_destroy +#undef ldap_pvt_thread_cond_signal +#undef ldap_pvt_thread_cond_broadcast +#undef ldap_pvt_thread_cond_wait +#undef ldap_pvt_thread_mutex_init +#undef ldap_pvt_thread_mutex_recursive_init +#undef ldap_pvt_thread_mutex_destroy +#undef ldap_pvt_thread_mutex_lock +#undef ldap_pvt_thread_mutex_trylock +#undef ldap_pvt_thread_mutex_unlock +#undef ldap_pvt_thread_self +/* LDAP_THREAD_RDWR_IMPLEMENTATION: */ +#undef ldap_pvt_thread_rdwr_t +#undef ldap_pvt_thread_rdwr_init +#undef ldap_pvt_thread_rdwr_destroy +#undef ldap_pvt_thread_rdwr_rlock +#undef ldap_pvt_thread_rdwr_rtrylock +#undef ldap_pvt_thread_rdwr_runlock +#undef ldap_pvt_thread_rdwr_wlock +#undef ldap_pvt_thread_rdwr_wtrylock +#undef ldap_pvt_thread_rdwr_wunlock +#undef ldap_pvt_thread_rdwr_readers +#undef ldap_pvt_thread_rdwr_writers +#undef ldap_pvt_thread_rdwr_active +/* LDAP_THREAD_POOL_IMPLEMENTATION: */ +#undef ldap_pvt_thread_pool_t +#undef ldap_pvt_thread_pool_init +#undef ldap_pvt_thread_pool_submit +#undef ldap_pvt_thread_pool_maxthreads +#undef ldap_pvt_thread_pool_backload +#undef ldap_pvt_thread_pool_pause +#undef ldap_pvt_thread_pool_resume +#undef ldap_pvt_thread_pool_destroy +#undef ldap_pvt_thread_pool_close +#undef ldap_pvt_thread_pool_free +#undef ldap_pvt_thread_pool_getkey +#undef ldap_pvt_thread_pool_setkey +#undef ldap_pvt_thread_pool_purgekey +#undef ldap_pvt_thread_pool_context +#undef ldap_pvt_thread_pool_context_reset +#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */ + +#endif /* LDAP_THREAD_DEBUG */ diff --git a/contrib/libs/openldap/libraries/libldap/ldif.c b/contrib/libs/openldap/libraries/libldap/ldif.c new file mode 100644 index 0000000000..57e44f8c7e --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldif.c @@ -0,0 +1,919 @@ +/* ldif.c - routines for dealing with LDIF files */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. This + * software is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> + +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/time.h> + +int ldif_debug = 0; + +#include "ldap-int.h" +#include "ldif.h" + +#define CONTINUED_LINE_MARKER '\r' + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memcalloc calloc +#define ber_memrealloc realloc +#define ber_strdup strdup +#endif + +static const char nib2b64[0x40] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* + * ldif_parse_line - takes a line of the form "type:[:] value" and splits it + * into components "type" and "value". if a double colon separates type from + * value, then value is encoded in base 64, and parse_line un-decodes it + * (in place) before returning. The type and value are stored in malloc'd + * memory which must be freed by the caller. + * + * ldif_parse_line2 - operates in-place on input buffer, returning type + * in-place. Will return value in-place if possible, (must malloc for + * fetched URLs). If freeval is NULL, all return data will be malloc'd + * and the input line will be unmodified. Otherwise freeval is set to + * True if the value was malloc'd. + */ + +int +ldif_parse_line( + LDAP_CONST char *line, + char **typep, + char **valuep, + ber_len_t *vlenp +) +{ + struct berval type, value; + int rc = ldif_parse_line2( (char *)line, &type, &value, NULL ); + + *typep = type.bv_val; + *valuep = value.bv_val; + *vlenp = value.bv_len; + return rc; +} + +int +ldif_parse_line2( + char *line, + struct berval *type, + struct berval *value, + int *freeval +) +{ + char *s, *p, *d; + int b64, url; + + BER_BVZERO( type ); + BER_BVZERO( value ); + + /* skip any leading space */ + while ( isspace( (unsigned char) *line ) ) { + line++; + } + + if ( freeval ) { + *freeval = 0; + } else { + line = ber_strdup( line ); + + if( line == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: line malloc failed\n")); + return( -1 ); + } + } + + type->bv_val = line; + + s = strchr( type->bv_val, ':' ); + + if ( s == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: missing ':' after %s\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + /* trim any space between type and : */ + for ( p = &s[-1]; p > type->bv_val && isspace( * (unsigned char *) p ); p-- ) { + *p = '\0'; + } + *s++ = '\0'; + type->bv_len = s - type->bv_val - 1; + + url = 0; + b64 = 0; + + if ( *s == '<' ) { + s++; + url = 1; + + } else if ( *s == ':' ) { + /* base 64 encoded value */ + s++; + b64 = 1; + } + + /* skip space between : and value */ + while ( isspace( (unsigned char) *s ) ) { + s++; + } + + /* check for continued line markers that should be deleted */ + for ( p = s, d = s; *p; p++ ) { + if ( *p != CONTINUED_LINE_MARKER ) + *d++ = *p; + } + *d = '\0'; + + if ( b64 ) { + char *byte = s; + + if ( *s == '\0' ) { + /* no value is present, error out */ + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: %s missing base64 value\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + value->bv_val = s; + value->bv_len = d - s; + if ( ldap_int_decode_b64_inplace( value ) != LDAP_SUCCESS ) { + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: %s base64 decode failed\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + } else if ( url ) { + if ( *s == '\0' ) { + /* no value is present, error out */ + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: %s missing URL value\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + if( ldif_fetch_url( s, &value->bv_val, &value->bv_len ) ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: %s: URL \"%s\" fetch failed\n"), + type->bv_val, s ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + if ( freeval ) *freeval = 1; + + } else { + value->bv_val = s; + value->bv_len = (int) (d - s); + } + + if ( !freeval ) { + struct berval bv = *type; + + ber_dupbv( type, &bv ); + + if( BER_BVISNULL( type )) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: type malloc failed\n")); + if( url ) ber_memfree( value->bv_val ); + ber_memfree( line ); + return( -1 ); + } + + if( !url ) { + bv = *value; + ber_dupbv( value, &bv ); + if( BER_BVISNULL( value )) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: value malloc failed\n")); + ber_memfree( type->bv_val ); + ber_memfree( line ); + return( -1 ); + } + } + + ber_memfree( line ); + } + + return( 0 ); +} + +/* + * ldif_getline - return the next "line" (minus newline) of input from a + * string buffer of lines separated by newlines, terminated by \n\n + * or \0. this routine handles continued lines, bundling them into + * a single big line before returning. if a line begins with a white + * space character, it is a continuation of the previous line. the white + * space character (nb: only one char), and preceding newline are changed + * into CONTINUED_LINE_MARKER chars, to be deleted later by the + * ldif_parse_line() routine above. + * + * ldif_getline will skip over any line which starts '#'. + * + * ldif_getline takes a pointer to a pointer to the buffer on the first call, + * which it updates and must be supplied on subsequent calls. + */ + +int +ldif_countlines( LDAP_CONST char *buf ) +{ + char *nl; + int ret = 0; + + if ( !buf ) return ret; + + for ( nl = strchr(buf, '\n'); nl; nl = strchr(nl, '\n') ) { + nl++; + if ( *nl != ' ' ) ret++; + } + return ret; +} + +char * +ldif_getline( char **next ) +{ + char *line; + + do { + if ( *next == NULL || **next == '\n' || **next == '\0' ) { + return( NULL ); + } + + line = *next; + + while ( (*next = strchr( *next, '\n' )) != NULL ) { +#if CONTINUED_LINE_MARKER != '\r' + if ( (*next)[-1] == '\r' ) { + (*next)[-1] = CONTINUED_LINE_MARKER; + } +#endif + + if ( (*next)[1] != ' ' ) { + if ( (*next)[1] == '\r' && (*next)[2] == '\n' ) { + *(*next)++ = '\0'; + } + *(*next)++ = '\0'; + break; + } + + **next = CONTINUED_LINE_MARKER; + (*next)[1] = CONTINUED_LINE_MARKER; + (*next)++; + } + } while( *line == '#' ); + + return( line ); +} + +/* + * name and OID of attributeTypes that must be base64 encoded in any case + */ +typedef struct must_b64_encode_s { + struct berval name; + struct berval oid; +} must_b64_encode_s; + +static must_b64_encode_s default_must_b64_encode[] = { + { BER_BVC( "userPassword" ), BER_BVC( "2.5.4.35" ) }, + { BER_BVNULL, BER_BVNULL } +}; + +static must_b64_encode_s *must_b64_encode = default_must_b64_encode; + +/* + * register name and OID of attributeTypes that must always be base64 + * encoded + * + * NOTE: this routine mallocs memory in a static struct which must + * be explicitly freed when no longer required + */ +int +ldif_must_b64_encode_register( LDAP_CONST char *name, LDAP_CONST char *oid ) +{ + int i; + ber_len_t len; + + assert( must_b64_encode != NULL ); + assert( name != NULL ); + assert( oid != NULL ); + + len = strlen( name ); + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + if ( len != must_b64_encode[i].name.bv_len ) { + continue; + } + + if ( strcasecmp( name, must_b64_encode[i].name.bv_val ) == 0 ) { + break; + } + } + + if ( !BER_BVISNULL( &must_b64_encode[i].name ) ) { + return 1; + } + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) + /* just count */ ; + + if ( must_b64_encode == default_must_b64_encode ) { + must_b64_encode = ber_memalloc( sizeof( must_b64_encode_s ) * ( i + 2 ) ); + if ( must_b64_encode == NULL ) { + return 1; + } + + for ( i = 0; !BER_BVISNULL( &default_must_b64_encode[i].name ); i++ ) { + ber_dupbv( &must_b64_encode[i].name, &default_must_b64_encode[i].name ); + ber_dupbv( &must_b64_encode[i].oid, &default_must_b64_encode[i].oid ); + } + + } else { + must_b64_encode_s *tmp; + + tmp = ber_memrealloc( must_b64_encode, + sizeof( must_b64_encode_s ) * ( i + 2 ) ); + if ( tmp == NULL ) { + return 1; + } + must_b64_encode = tmp; + } + + ber_str2bv( name, len, 1, &must_b64_encode[i].name ); + ber_str2bv( oid, 0, 1, &must_b64_encode[i].oid ); + + BER_BVZERO( &must_b64_encode[i + 1].name ); + + return 0; +} + +void +ldif_must_b64_encode_release( void ) +{ + int i; + + assert( must_b64_encode != NULL ); + + if ( must_b64_encode == default_must_b64_encode ) { + return; + } + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + ber_memfree( must_b64_encode[i].name.bv_val ); + ber_memfree( must_b64_encode[i].oid.bv_val ); + } + + ber_memfree( must_b64_encode ); + + must_b64_encode = default_must_b64_encode; +} + +/* + * returns 1 iff the string corresponds to the name or the OID of any + * of the attributeTypes listed in must_b64_encode + */ +static int +ldif_must_b64_encode( LDAP_CONST char *s ) +{ + int i; + struct berval bv; + + assert( must_b64_encode != NULL ); + assert( s != NULL ); + + ber_str2bv( s, 0, 0, &bv ); + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + if ( ber_bvstrcasecmp( &must_b64_encode[i].name, &bv ) == 0 + || ber_bvcmp( &must_b64_encode[i].oid, &bv ) == 0 ) + { + return 1; + } + } + + return 0; +} + +/* NOTE: only preserved for binary compatibility */ +void +ldif_sput( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen ) +{ + ldif_sput_wrap( out, type, name, val, vlen, 0 ); +} + +void +ldif_sput_wrap( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap ) +{ + const unsigned char *byte, *stop; + unsigned char buf[3]; + unsigned long bits; + char *save; + int pad; + int namelen = 0; + + ber_len_t savelen; + ber_len_t len=0; + ber_len_t i; + + if ( !wrap ) + wrap = LDIF_LINE_WIDTH; + + /* prefix */ + switch( type ) { + case LDIF_PUT_COMMENT: + *(*out)++ = '#'; + len++; + + if( vlen ) { + *(*out)++ = ' '; + len++; + } + + break; + + case LDIF_PUT_SEP: + *(*out)++ = '\n'; + return; + } + + /* name (attribute type) */ + if( name != NULL ) { + /* put the name + ":" */ + namelen = strlen(name); + strcpy(*out, name); + *out += namelen; + len += namelen; + + if( type != LDIF_PUT_COMMENT ) { + *(*out)++ = ':'; + len++; + } + + } +#ifdef LDAP_DEBUG + else { + assert( type == LDIF_PUT_COMMENT ); + } +#endif + + if( vlen == 0 ) { + *(*out)++ = '\n'; + return; + } + + switch( type ) { + case LDIF_PUT_NOVALUE: + *(*out)++ = '\n'; + return; + + case LDIF_PUT_URL: /* url value */ + *(*out)++ = '<'; + len++; + break; + + case LDIF_PUT_B64: /* base64 value */ + *(*out)++ = ':'; + len++; + break; + } + + switch( type ) { + case LDIF_PUT_TEXT: + case LDIF_PUT_URL: + case LDIF_PUT_B64: + *(*out)++ = ' '; + len++; + /* fall-thru */ + + case LDIF_PUT_COMMENT: + /* pre-encoded names */ + for ( i=0; i < vlen; i++ ) { + if ( len > wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + *(*out)++ = val[i]; + len++; + } + *(*out)++ = '\n'; + return; + } + + save = *out; + savelen = len; + + *(*out)++ = ' '; + len++; + + stop = (const unsigned char *) (val + vlen); + + if ( type == LDIF_PUT_VALUE + && isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' + && isgraph( (unsigned char) val[vlen-1] ) +#ifndef LDAP_BINARY_DEBUG + && strstr( name, ";binary" ) == NULL +#endif +#ifndef LDAP_PASSWD_DEBUG + && !ldif_must_b64_encode( name ) +#endif + ) { + int b64 = 0; + + for ( byte = (const unsigned char *) val; byte < stop; + byte++, len++ ) + { + if ( !isascii( *byte ) || !isprint( *byte ) ) { + b64 = 1; + break; + } + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + *(*out)++ = *byte; + } + + if( !b64 ) { + *(*out)++ = '\n'; + return; + } + } + + *out = save; + *(*out)++ = ':'; + *(*out)++ = ' '; + len = savelen + 2; + + /* convert to base 64 (3 bytes => 4 base 64 digits) */ + for ( byte = (const unsigned char *) val; + byte < stop - 2; + byte += 3 ) + { + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, len++, bits <<= 6 ) { + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + /* get b64 digit from high order 6 bits */ + *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } + } + + /* add padding if necessary */ + if ( byte < stop ) { + for ( i = 0; byte + i < stop; i++ ) { + buf[i] = byte[i]; + } + for ( pad = 0; i < 3; i++, pad++ ) { + buf[i] = '\0'; + } + byte = buf; + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, len++, bits <<= 6 ) { + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + if( i + pad < 4 ) { + /* get b64 digit from low order 6 bits */ + *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } else { + *(*out)++ = '='; + } + } + } + *(*out)++ = '\n'; +} + + +/* + * ldif_type_and_value return BER malloc'd, zero-terminated LDIF line + */ + +/* NOTE: only preserved for binary compatibility */ +char * +ldif_put( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen ) +{ + return ldif_put_wrap( type, name, val, vlen, 0 ); +} + +char * +ldif_put_wrap( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap ) +{ + char *buf, *p; + ber_len_t nlen; + + nlen = ( name != NULL ) ? strlen( name ) : 0; + + buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED_WRAP( nlen, vlen, wrap ) + 1 ); + + if ( buf == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_type_and_value: malloc failed!")); + return NULL; + } + + p = buf; + ldif_sput_wrap( &p, type, name, val, vlen, wrap ); + *p = '\0'; + + return( buf ); +} + +int ldif_is_not_printable( + LDAP_CONST char *val, + ber_len_t vlen ) +{ + if( vlen == 0 || val == NULL ) { + return -1; + } + + if( isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' && + isgraph( (unsigned char) val[vlen-1] ) ) + { + ber_len_t i; + + for ( i = 0; val[i]; i++ ) { + if ( !isascii( val[i] ) || !isprint( (unsigned char) val[i] ) ) { + return 1; + } + } + + return 0; + } + + return 1; +} + +LDIFFP * +ldif_open( + LDAP_CONST char *file, + LDAP_CONST char *mode +) +{ + FILE *fp = fopen( file, mode ); + LDIFFP *lfp = NULL; + + if ( fp ) { + lfp = ber_memalloc( sizeof( LDIFFP )); + if ( lfp == NULL ) { + fclose( fp ); + return NULL; + } + lfp->fp = fp; + lfp->prev = NULL; + } + return lfp; +} + +LDIFFP * +ldif_open_mem( + char *ldif, + size_t size, + LDAP_CONST char *mode +) +{ +#ifdef HAVE_FMEMOPEN + FILE *fp = fmemopen( ldif, size, mode ); + LDIFFP *lfp = NULL; + + if ( fp ) { + lfp = ber_memalloc( sizeof( LDIFFP )); + lfp->fp = fp; + lfp->prev = NULL; + } + return lfp; +#else /* !HAVE_FMEMOPEN */ + return NULL; +#endif /* !HAVE_FMEMOPEN */ +} + +void +ldif_close( + LDIFFP *lfp +) +{ + LDIFFP *prev; + + while ( lfp ) { + fclose( lfp->fp ); + prev = lfp->prev; + ber_memfree( lfp ); + lfp = prev; + } +} + +#define LDIF_MAXLINE 4096 + +/* + * ldif_read_record - read an ldif record. Return 1 for success, 0 for EOF, + * -1 for error. + */ +int +ldif_read_record( + LDIFFP *lfp, + unsigned long *lno, /* ptr to line number counter */ + char **bufp, /* ptr to malloced output buffer */ + int *buflenp ) /* ptr to length of *bufp */ +{ + char line[LDIF_MAXLINE], *nbufp; + ber_len_t lcur = 0, len; + int last_ch = '\n', found_entry = 0, stop, top_comment = 0; + + for ( stop = 0; !stop; last_ch = line[len-1] ) { + /* If we're at the end of this file, see if we should pop + * back to a previous file. (return from an include) + */ + while ( feof( lfp->fp )) { +pop: + if ( lfp->prev ) { + LDIFFP *tmp = lfp->prev; + fclose( lfp->fp ); + *lfp = *tmp; + ber_memfree( tmp ); + } else { + stop = 1; + break; + } + } + if ( !stop ) { + if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) { + if ( !found_entry && !ferror( lfp->fp ) ) { + /* ITS#9811 Reached the end looking for an entry, try again */ + goto pop; + } + stop = 1; + len = 0; + } else { + len = strlen( line ); + } + } + + if ( stop ) { + /* Add \n in case the file does not end with newline */ + if (last_ch != '\n') { + len = 1; + line[0] = '\n'; + line[1] = '\0'; + goto last; + } + break; + } + + /* Squash \r\n to \n */ + if ( len > 1 && line[len-2] == '\r' ) { + len--; + line[len] = '\0'; + line[len-1] = '\n'; + } + + if ( last_ch == '\n' ) { + (*lno)++; + + if ( line[0] == '\n' ) { + if ( !found_entry ) { + lcur = 0; + top_comment = 0; + continue; + } + break; + } + + if ( !found_entry ) { + if ( line[0] == '#' ) { + top_comment = 1; + } else if ( ! ( top_comment && line[0] == ' ' ) ) { + /* Found a new entry */ + found_entry = 1; + + if ( isdigit( (unsigned char) line[0] ) ) { + /* skip index */ + continue; + } + if ( !strncasecmp( line, "include:", + STRLENOF("include:"))) { + FILE *fp2; + char *ptr; + found_entry = 0; + + if ( line[len-1] == '\n' ) { + len--; + line[len] = '\0'; + } + + ptr = line + STRLENOF("include:"); + while (isspace((unsigned char) *ptr)) ptr++; + fp2 = ldif_open_url( ptr ); + if ( fp2 ) { + LDIFFP *lnew = ber_memalloc( sizeof( LDIFFP )); + if ( lnew == NULL ) { + fclose( fp2 ); + return 0; + } + lnew->prev = lfp->prev; + lnew->fp = lfp->fp; + lfp->prev = lnew; + lfp->fp = fp2; + line[len] = '\n'; + len++; + continue; + } else { + /* We failed to open the file, this should + * be reported as an error somehow. + */ + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_read_record: include %s failed\n"), ptr ); + return -1; + } + } + } + } + } + +last: + if ( *buflenp - lcur <= len ) { + *buflenp += len + LDIF_MAXLINE; + nbufp = ber_memrealloc( *bufp, *buflenp ); + if( nbufp == NULL ) { + return 0; + } + *bufp = nbufp; + } + strcpy( *bufp + lcur, line ); + lcur += len; + } + + return( found_entry ); +} diff --git a/contrib/libs/openldap/libraries/libldap/ldifutil.c b/contrib/libs/openldap/libraries/libldap/ldifutil.c new file mode 100644 index 0000000000..df557e590c --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ldifutil.c @@ -0,0 +1,724 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * This file contains public API to help with parsing LDIF + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/socket.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldif.h" + +#define M_SEP 0x7f + +/* strings found in LDIF entries */ +static struct berval BV_VERSION = BER_BVC("version"); +static struct berval BV_DN = BER_BVC("dn"); +static struct berval BV_CONTROL = BER_BVC("control"); +static struct berval BV_CHANGETYPE = BER_BVC("changetype"); +static struct berval BV_ADDCT = BER_BVC("add"); +static struct berval BV_MODIFYCT = BER_BVC("modify"); +static struct berval BV_DELETECT = BER_BVC("delete"); +static struct berval BV_MODRDNCT = BER_BVC("modrdn"); +static struct berval BV_MODDNCT = BER_BVC("moddn"); +static struct berval BV_RENAMECT = BER_BVC("rename"); +static struct berval BV_MODOPADD = BER_BVC("add"); +static struct berval BV_MODOPREPLACE = BER_BVC("replace"); +static struct berval BV_MODOPDELETE = BER_BVC("delete"); +static struct berval BV_MODOPINCREMENT = BER_BVC("increment"); +static struct berval BV_NEWRDN = BER_BVC("newrdn"); +static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn"); +static struct berval BV_NEWSUP = BER_BVC("newsuperior"); + +#define BV_CASEMATCH(a, b) \ + ((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val)) + +static int parse_ldif_control LDAP_P(( struct berval *bval, LDAPControl ***ppctrls )); + +void +ldap_ldif_record_done( LDIFRecord *lr ) +{ + int i; + + /* the LDAPControl stuff does not allow the use of memory contexts */ + if (lr->lr_ctrls != NULL) { + ldap_controls_free( lr->lr_ctrls ); + } + if ( lr->lr_lm != NULL ) { + ber_memfree_x( lr->lr_lm, lr->lr_ctx ); + } + if ( lr->lr_mops != NULL ) { + ber_memfree_x( lr->lr_mops, lr->lr_ctx ); + } + for (i=lr->lr_lines-1; i>=0; i--) + if ( lr->lr_freeval[i] ) ber_memfree_x( lr->lr_vals[i].bv_val, lr->lr_ctx ); + ber_memfree_x( lr->lr_btype, lr->lr_ctx ); + + memset( lr, 0, sizeof(LDIFRecord) ); +} + +/* + * ldap_parse_ldif_record_x() will convert an LDIF record read with ldif_read_record() + * into an array of LDAPMod* and an array of LDAPControl*, suitable for passing + * directly to any other LDAP API function that takes LDAPMod** and LDAPControl** + * arguments, such as ldap_modify_s(). + * + * rbuf - the ldif record buffer returned from ldif_read_record - rbuf.bv_val must be + * writable - will use ldif_getline to read from it + * linenum - the ldif line number returned from ldif_read_record + * - used for logging errors (e.g. error at line N) + * lr - holds the data to return + * errstr - a string used for logging (usually the program name e.g. "ldapmodify" + * flags - 0 or some combination of LDIF_DEFAULT_ADD LDIF_ENTRIES_ONLY LDIF_NO_CONTROLS + * ctx is the memory allocation context - if NULL, use the standard memory allocator + */ +int +ldap_parse_ldif_record_x( + struct berval *rbuf, + unsigned long linenum, + LDIFRecord *lr, + const char *errstr, + unsigned int flags, + void *ctx ) +{ + char *line, *dn; + int rc, modop; + int expect_modop, expect_sep; + int ldapadd, new_entry, delete_entry, got_all, no_dn; + LDAPMod **pmods; + int version; + LDAPControl **pctrls; + int i, j, k, idn, nmods; + struct berval **bvl, bv; + + assert( lr != NULL ); + assert( rbuf != NULL ); + memset( lr, 0, sizeof(LDIFRecord) ); + lr->lr_ctx = ctx; /* save memory context for later */ + ldapadd = flags & LDIF_DEFAULT_ADD; + no_dn = flags & LDIF_NO_DN; + expect_modop = flags & LDIF_MODS_ONLY; + new_entry = ldapadd; + + rc = got_all = delete_entry = modop = 0; + expect_sep = 0; + version = 0; + pmods = NULL; + pctrls = NULL; + dn = NULL; + + lr->lr_lines = ldif_countlines( rbuf->bv_val ); + lr->lr_btype = ber_memcalloc_x( 1, (lr->lr_lines+1)*2*sizeof(struct berval)+lr->lr_lines, ctx ); + if ( !lr->lr_btype ) + return LDAP_NO_MEMORY; + + lr->lr_vals = lr->lr_btype+lr->lr_lines+1; + lr->lr_freeval = (char *)(lr->lr_vals+lr->lr_lines+1); + i = -1; + + while ( rc == 0 && ( line = ldif_getline( &rbuf->bv_val )) != NULL ) { + int freev; + + if ( *line == '\n' || *line == '\0' ) { + break; + } + + ++i; + + if ( line[0] == '-' && !line[1] ) { + BER_BVZERO( lr->lr_btype+i ); + lr->lr_freeval[i] = 0; + continue; + } + + if ( ( rc = ldif_parse_line2( line, lr->lr_btype+i, lr->lr_vals+i, &freev ) ) < 0 ) { + fprintf( stderr, _("%s: invalid format (line %lu) entry: \"%s\"\n"), + errstr, linenum+i, dn == NULL ? "" : dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + lr->lr_freeval[i] = freev; + + if ( dn == NULL && !no_dn ) { + if ( linenum+i == 1 && BV_CASEMATCH( lr->lr_btype+i, &BV_VERSION )) { + /* lutil_atoi() introduces a dependence of libldap + * on liblutil; we only allow version 1 by now (ITS#6654) + */ +#if 0 + int v; + if( lr->lr_vals[i].bv_len == 0 || lutil_atoi( &v, lr->lr_vals[i].bv_val) != 0 || v != 1 ) +#endif + static const struct berval version1 = { 1, "1" }; + if ( lr->lr_vals[i].bv_len != version1.bv_len || strncmp( lr->lr_vals[i].bv_val, version1.bv_val, version1.bv_len ) != 0 ) + { + fprintf( stderr, + _("%s: invalid version %s, line %lu (ignored)\n"), + errstr, lr->lr_vals[i].bv_val, linenum ); + } + version++; + + } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) { + lr->lr_dn = lr->lr_vals[i]; + dn = lr->lr_dn.bv_val; /* primarily for logging */ + idn = i; + } + /* skip all lines until we see "dn:" */ + } + } + + /* check to make sure there was a dn: line */ + if ( !dn && !no_dn ) { + rc = 0; + goto leave; + } + + lr->lr_lines = i+1; + + if( lr->lr_lines == 0 ) { + rc = 0; + goto leave; + } + + if( version && lr->lr_lines == 1 ) { + rc = 0; + goto leave; + } + + if ( no_dn ) { + i = 0; + } else { + i = idn+1; + /* Check for "control" tag after dn and before changetype. */ + if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CONTROL )) { + /* Parse and add it to the list of controls */ + if ( !( flags & LDIF_NO_CONTROLS ) ) { + rc = parse_ldif_control( lr->lr_vals+i, &pctrls ); + if (rc != 0) { + fprintf( stderr, + _("%s: Error processing %s line, line %lu: %s\n"), + errstr, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) ); + } + } + i++; + if ( i>= lr->lr_lines ) { +short_input: + fprintf( stderr, + _("%s: Expecting more input after %s line, line %lu\n"), + errstr, lr->lr_btype[i-1].bv_val, linenum+i ); + + rc = LDAP_PARAM_ERROR; + goto leave; + } + } + } + + /* Check for changetype */ + if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CHANGETYPE )) { +#ifdef LIBERAL_CHANGETYPE_MODOP + /* trim trailing spaces (and log warning ...) */ + int icnt; + for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) { + if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) { + break; + } + } + + if ( ++icnt != lr->lr_vals[i].bv_len ) { + fprintf( stderr, _("%s: illegal trailing space after" + " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), + errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn ); + lr->lr_vals[i].bv_val[icnt] = '\0'; + } +#endif /* LIBERAL_CHANGETYPE_MODOP */ + + /* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or + there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */ + if ( flags & LDIF_ENTRIES_ONLY ) { + if ( !( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("%s: skipping LDIF record beginning at line %lu: " + "changetype '%.*s' found but entries only was requested\n"), + errstr, linenum, + (int)lr->lr_vals[i].bv_len, + (const char *)lr->lr_vals[i].bv_val ); + goto leave; + } + } + + if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODIFYCT )) { + new_entry = 0; + expect_modop = 1; + } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) { + new_entry = 1; + modop = LDAP_MOD_ADD; + } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODRDNCT ) + || BV_CASEMATCH( lr->lr_vals+i, &BV_MODDNCT ) + || BV_CASEMATCH( lr->lr_vals+i, &BV_RENAMECT )) + { + i++; + if ( i >= lr->lr_lines ) + goto short_input; + if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWRDN )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + errstr, BV_NEWRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + lr->lrop_newrdn = lr->lr_vals[i]; + i++; + if ( i >= lr->lr_lines ) + goto short_input; + if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_DELETEOLDRDN )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + errstr, BV_DELETEOLDRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + lr->lrop_delold = ( lr->lr_vals[i].bv_val[0] == '0' ) ? 0 : 1; + i++; + if ( i < lr->lr_lines ) { + if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWSUP )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + errstr, BV_NEWSUP.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + lr->lrop_newsup = lr->lr_vals[i]; + i++; + } + got_all = 1; + } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_DELETECT )) { + got_all = delete_entry = 1; + } else { + fprintf( stderr, + _("%s: unknown %s \"%s\" (line %lu, entry \"%s\")\n"), + errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + i++; + } else if ( ldapadd ) { /* missing changetype => add */ + new_entry = 1; + modop = LDAP_MOD_ADD; + } else { + /* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or + there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */ + if ( flags & LDIF_ENTRIES_ONLY ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("%s: skipping LDIF record beginning at line %lu: " + "no changetype found but entries only was requested and " + "the default setting for missing changetype is modify\n"), + errstr, linenum ); + goto leave; + } + expect_modop = 1; /* missing changetype => modify */ + } + + if ( got_all ) { + if ( i < lr->lr_lines ) { + fprintf( stderr, + _("%s: extra lines at end (line %lu, entry \"%s\")\n"), + errstr, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + goto doit; + } + + nmods = lr->lr_lines - i; + idn = i; + + if ( new_entry ) { + int fv; + + /* Make sure all attributes with multiple values are contiguous */ + for (; i<lr->lr_lines; i++) { + for (j=i+1; j<lr->lr_lines; j++) { + if ( !lr->lr_btype[j].bv_val ) { + fprintf( stderr, + _("%s: missing attributeDescription (line %lu, entry \"%s\")\n"), + errstr, linenum+j, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + if ( BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+j )) { + nmods--; + /* out of order, move intervening attributes down */ + if ( j != i+1 ) { + bv = lr->lr_vals[j]; + fv = lr->lr_freeval[j]; + for (k=j; k>i; k--) { + lr->lr_btype[k] = lr->lr_btype[k-1]; + lr->lr_vals[k] = lr->lr_vals[k-1]; + lr->lr_freeval[k] = lr->lr_freeval[k-1]; + } + k++; + lr->lr_btype[k] = lr->lr_btype[i]; + lr->lr_vals[k] = bv; + lr->lr_freeval[k] = fv; + } + i++; + } + } + } + /* Allocate space for array of mods, array of pointers to mods, + * and array of pointers to values, allowing for NULL terminators + * for the pointer arrays... + */ + lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) + + (nmods+1) * sizeof(LDAPMod*) + + (lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx ); + if ( lr->lr_lm == NULL ) { + rc = LDAP_NO_MEMORY; + goto leave; + } + + pmods = (LDAPMod **)(lr->lr_lm+nmods); + bvl = (struct berval **)(pmods+nmods+1); + + j = 0; + k = -1; + BER_BVZERO(&bv); + for (i=idn; i<lr->lr_lines; i++) { + if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) { + fprintf( stderr, _("%s: attributeDescription \"%s\":" + " (possible missing newline" + " after line %lu, entry \"%s\"?)\n"), + errstr, lr->lr_btype[i].bv_val, linenum+i - 1, dn ); + } + if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) { + bvl[k++] = NULL; + bv = lr->lr_btype[i]; + lr->lr_lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + lr->lr_lm[j].mod_type = bv.bv_val; + lr->lr_lm[j].mod_bvalues = bvl+k; + pmods[j] = lr->lr_lm+j; + j++; + } + bvl[k++] = lr->lr_vals+i; + } + bvl[k] = NULL; + pmods[j] = NULL; + goto doit; + } + + lr->lr_mops = ber_memalloc_x( lr->lr_lines+1, ctx ); + if ( lr->lr_mops == NULL ) { + rc = LDAP_NO_MEMORY; + goto leave; + } + + lr->lr_mops[lr->lr_lines] = M_SEP; + if ( i > 0 ) + lr->lr_mops[i-1] = M_SEP; + + for ( ; i<lr->lr_lines; i++ ) { + if ( expect_modop ) { +#ifdef LIBERAL_CHANGETYPE_MODOP + /* trim trailing spaces (and log warning ...) */ + int icnt; + for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) { + if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) break; + } + + if ( ++icnt != lr->lr_vals[i].bv_len ) { + fprintf( stderr, _("%s: illegal trailing space after" + " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), + errstr, type, lr->lr_vals[i].bv_val, linenum+i, dn ); + lr->lr_vals[i].bv_val[icnt] = '\0'; + } +#endif /* LIBERAL_CHANGETYPE_MODOP */ + + expect_modop = 0; + expect_sep = 1; + if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPADD )) { + modop = LDAP_MOD_ADD; + lr->lr_mops[i] = M_SEP; + nmods--; + } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPREPLACE )) { + /* defer handling these since they might have no values. + * Use the BVALUES flag to signal that these were + * deferred. If values are provided later, this + * flag will be switched off. + */ + modop = LDAP_MOD_REPLACE; + lr->lr_mops[i] = modop | LDAP_MOD_BVALUES; + lr->lr_btype[i] = lr->lr_vals[i]; + } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPDELETE )) { + modop = LDAP_MOD_DELETE; + lr->lr_mops[i] = modop | LDAP_MOD_BVALUES; + lr->lr_btype[i] = lr->lr_vals[i]; + } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPINCREMENT )) { + modop = LDAP_MOD_INCREMENT; + lr->lr_mops[i] = M_SEP; + nmods--; + } else { /* no modify op: invalid LDIF */ + fprintf( stderr, _("%s: modify operation type is missing at" + " line %lu, entry \"%s\"\n"), + errstr, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + bv = lr->lr_vals[i]; + } else if ( expect_sep && BER_BVISEMPTY( lr->lr_btype+i )) { + lr->lr_mops[i] = M_SEP; + expect_sep = 0; + expect_modop = 1; + nmods--; + } else { + if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) { + fprintf( stderr, _("%s: wrong attributeType at" + " line %lu, entry \"%s\"\n"), + errstr, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + lr->lr_mops[i] = modop; + /* If prev op was deferred and matches this type, + * clear the flag + */ + if ( (lr->lr_mops[i-1] & LDAP_MOD_BVALUES) + && BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+i-1 )) + { + lr->lr_mops[i-1] = M_SEP; + nmods--; + } + } + } + + /* Allocate space for array of mods, array of pointers to mods, + * and array of pointers to values, allowing for NULL terminators + * for the pointer arrays... + */ + lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) + + (nmods+1) * sizeof(LDAPMod*) + + (lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx ); + if ( lr->lr_lm == NULL ) { + rc = LDAP_NO_MEMORY; + goto leave; + } + + pmods = (LDAPMod **)(lr->lr_lm+nmods); + bvl = (struct berval **)(pmods+nmods+1); + + j = 0; + k = -1; + BER_BVZERO(&bv); + if ( idn > 0 ) + lr->lr_mops[idn-1] = M_SEP; + for (i=idn; i<lr->lr_lines; i++) { + if ( lr->lr_mops[i] == M_SEP ) + continue; + if ( lr->lr_mops[i] != lr->lr_mops[i-1] || !BV_CASEMATCH( lr->lr_btype+i, &bv )) { + bvl[k++] = NULL; + bv = lr->lr_btype[i]; + lr->lr_lm[j].mod_op = lr->lr_mops[i] | LDAP_MOD_BVALUES; + lr->lr_lm[j].mod_type = bv.bv_val; + if ( lr->lr_mops[i] & LDAP_MOD_BVALUES ) { + lr->lr_lm[j].mod_bvalues = NULL; + } else { + lr->lr_lm[j].mod_bvalues = bvl+k; + } + pmods[j] = lr->lr_lm+j; + j++; + } + bvl[k++] = lr->lr_vals+i; + } + bvl[k] = NULL; + pmods[j] = NULL; + +doit: + /* first, set the common fields */ + lr->lr_ctrls = pctrls; + /* next, set the op */ + if ( delete_entry ) { + lr->lr_op = LDAP_REQ_DELETE; + } else if ( lr->lrop_newrdn.bv_val != NULL ) { + lr->lr_op = LDAP_REQ_MODDN; + } else { + /* for now, either add or modify */ + lr->lrop_mods = pmods; + if ( new_entry ) { + lr->lr_op = LDAP_REQ_ADD; + } else { + lr->lr_op = LDAP_REQ_MODIFY; + } + } + +leave: + if ( rc != LDAP_SUCCESS ) { + ldap_ldif_record_done( lr ); + } + + return( rc ); +} + +/* Same as ldap_parse_ldif_record_x() + * public API does not expose memory context + */ +int +ldap_parse_ldif_record( + struct berval *rbuf, + unsigned long linenum, + LDIFRecord *lr, + const char *errstr, + unsigned int flags ) +{ + return ldap_parse_ldif_record_x( rbuf, linenum, lr, errstr, flags, NULL ); +} + +/* Parse an LDIF control line of the form + control: oid [true/false] [: value] or + control: oid [true/false] [:: base64-value] or + control: oid [true/false] [:< url] + The control is added to the list of controls in *ppctrls. +*/ +static int +parse_ldif_control( + struct berval *bval, + LDAPControl ***ppctrls) +{ + char *oid = NULL; + int criticality = 0; /* Default is false if not present */ + int i, rc=0; + char *s, *oidStart; + LDAPControl *newctrl = NULL; + LDAPControl **pctrls = NULL; + struct berval type, bv = BER_BVNULL; + int freeval = 0; + + if (ppctrls) pctrls = *ppctrls; + /* OID should come first. Validate and extract it. */ + s = bval->bv_val; + if (*s == 0) return ( LDAP_PARAM_ERROR ); + oidStart = s; + while (isdigit((unsigned char)*s) || *s == '.') { + s++; /* OID should be digits or . */ + } + if (s == oidStart) { + return ( LDAP_PARAM_ERROR ); /* OID was not present */ + } + if (*s) { /* End of OID should be space or NULL */ + if (!isspace((unsigned char)*s)) { + return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */ + } + *s++ = 0; /* Replace space with null to terminate */ + } + + oid = ber_strdup(oidStart); + if (oid == NULL) return ( LDAP_NO_MEMORY ); + + /* Optional Criticality field is next. */ + while (*s && isspace((unsigned char)*s)) { + s++; /* Skip white space before criticality */ + } + if (strncasecmp(s, "true", 4) == 0) { + criticality = 1; + s += 4; + } + else if (strncasecmp(s, "false", 5) == 0) { + criticality = 0; + s += 5; + } + + /* Optional value field is next */ + while (*s && isspace((unsigned char)*s)) { + s++; /* Skip white space before value */ + } + if (*s) { + if (*s != ':') { /* If value is present, must start with : */ + rc = LDAP_PARAM_ERROR; + goto cleanup; + } + + /* Back up so value is in the form + a: value + a:: base64-value + a:< url + Then we can use ldif_parse_line2 to extract and decode the value + */ + s--; + *s = 'a'; + + rc = ldif_parse_line2(s, &type, &bv, &freeval); + if (rc < 0) { + rc = LDAP_PARAM_ERROR; + goto cleanup; + } + } + + /* Create a new LDAPControl structure. */ + newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl)); + if ( newctrl == NULL ) { + rc = LDAP_NO_MEMORY; + goto cleanup; + } + newctrl->ldctl_oid = oid; + oid = NULL; + newctrl->ldctl_iscritical = criticality; + if ( freeval ) + newctrl->ldctl_value = bv; + else + ber_dupbv( &newctrl->ldctl_value, &bv ); + + /* Add the new control to the passed-in list of controls. */ + i = 0; + if (pctrls) { + while ( pctrls[i] ) { /* Count the # of controls passed in */ + i++; + } + } + /* Allocate 1 more slot for the new control and 1 for the NULL. */ + pctrls = (LDAPControl **) ber_memrealloc(pctrls, + (i+2)*(sizeof(LDAPControl *))); + if (pctrls == NULL) { + rc = LDAP_NO_MEMORY; + goto cleanup; + } + pctrls[i] = newctrl; + newctrl = NULL; + pctrls[i+1] = NULL; + *ppctrls = pctrls; + +cleanup: + if (newctrl) { + if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid); + if (newctrl->ldctl_value.bv_val) { + ber_memfree(newctrl->ldctl_value.bv_val); + } + ber_memfree(newctrl); + } + if (oid) ber_memfree(oid); + + return( rc ); +} + + diff --git a/contrib/libs/openldap/libraries/libldap/messages.c b/contrib/libs/openldap/libraries/libldap/messages.c new file mode 100644 index 0000000000..c4bf7325a6 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/messages.c @@ -0,0 +1,68 @@ +/* messages.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_first_message( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain; +} + +LDAPMessage * +ldap_next_message( LDAP *ld, LDAPMessage *msg ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msg != NULL ); + + return msg->lm_chain; +} + +int +ldap_count_messages( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + i++; + } + + return( i ); +} + +BerElement* +ldap_get_message_ber( LDAPMessage *ld ) +{ + return ld->lm_ber; +} diff --git a/contrib/libs/openldap/libraries/libldap/modify.c b/contrib/libs/openldap/libraries/libldap/modify.c new file mode 100644 index 0000000000..cb8d85a613 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/modify.c @@ -0,0 +1,233 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* A modify request/response looks like this: + * ModifyRequest ::= [APPLICATION 6] SEQUENCE { + * object LDAPDN, + * changes SEQUENCE OF change SEQUENCE { + * operation ENUMERATED { + * add (0), + * delete (1), + * replace (2), + * ... }, + * modification PartialAttribute } } + * + * PartialAttribute ::= SEQUENCE { + * type AttributeDescription, + * vals SET OF value AttributeValue } + * + * AttributeDescription ::= LDAPString + * -- Constrained to <attributedescription> [RFC4512] + * + * AttributeValue ::= OCTET STRING + * + * ModifyResponse ::= [APPLICATION 7] LDAPResult + * + * (Source: RFC 4511) + */ + +BerElement * +ldap_build_modify_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int i, rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + rc = ber_printf( ber, "{it{s{" /*}}}*/, *msgidp, LDAP_REQ_MODIFY, dn ); + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* allow mods to be NULL ("touch") */ + if ( mods ) { + /* for each modification to be performed... */ + for ( i = 0; mods[i] != NULL; i++ ) { + if (( mods[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + rc = ber_printf( ber, "{e{s[V]N}N}", + (ber_int_t) ( mods[i]->mod_op & ~LDAP_MOD_BVALUES ), + mods[i]->mod_type, mods[i]->mod_bvalues ); + } else { + rc = ber_printf( ber, "{e{s[v]N}N}", + (ber_int_t) mods[i]->mod_op, + mods[i]->mod_type, mods[i]->mod_values ); + } + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + } + + if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_modify_ext - initiate an ldap extended modify operation. + * + * Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * mods List of modifications to make. This is null-terminated + * array of struct ldapmod's, specifying the modifications + * to perform. + * sctrls Server Controls + * cctrls Client Controls + * msgidp Message ID pointer + * + * Example: + * LDAPMod *mods[] = { + * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_DELETE, "ou", 0 }, + * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } } + * 0 + * } + * rc= ldap_modify_ext( ld, dn, mods, sctrls, cctrls, &msgid ); + */ +int +ldap_modify_ext( LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_modify_ext\n" ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_modify_req( ld, dn, mods, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODIFY, dn, ber, id ); + return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +/* + * ldap_modify - initiate an ldap modify operation. + * + * Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * mods List of modifications to make. This is null-terminated + * array of struct ldapmod's, specifying the modifications + * to perform. + * + * Example: + * LDAPMod *mods[] = { + * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_DELETE, "ou", 0 }, + * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } } + * 0 + * } + * msgid = ldap_modify( ld, dn, mods ); + */ +int +ldap_modify( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) +{ + int rc, msgid; + + Debug0( LDAP_DEBUG_TRACE, "ldap_modify\n" ); + + rc = ldap_modify_ext( ld, dn, mods, NULL, NULL, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return -1; + + return msgid; +} + +int +ldap_modify_ext_s( LDAP *ld, LDAP_CONST char *dn, + LDAPMod **mods, LDAPControl **sctrl, LDAPControl **cctrl ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_modify_ext( ld, dn, mods, sctrl, cctrl, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_modify_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) +{ + return ldap_modify_ext_s( ld, dn, mods, NULL, NULL ); +} + diff --git a/contrib/libs/openldap/libraries/libldap/modrdn.c b/contrib/libs/openldap/libraries/libldap/modrdn.c new file mode 100644 index 0000000000..756762e228 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/modrdn.c @@ -0,0 +1,273 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* Copyright 1999, Juan C. Gomez, All rights reserved. + * This software is not subject to any license of Silicon Graphics + * Inc. or Purdue University. + * + * Redistribution and use in source and binary forms are permitted + * without restriction or fee of any kind as long as this notice + * is preserved. + */ + +/* ACKNOWLEDGEMENTS: + * Juan C. Gomez + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * A modify rdn request looks like this: + * ModifyRDNRequest ::= SEQUENCE { + * entry DistinguishedName, + * newrdn RelativeDistinguishedName, + * deleteoldrdn BOOLEAN + * newSuperior [0] DistinguishedName [v3 only] + * } + */ + +BerElement * +ldap_build_moddn_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if( newSuperior != NULL ) { + /* must be version 3 (or greater) */ + if ( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + ber_free( ber, 1 ); + return( NULL ); + } + rc = ber_printf( ber, "{it{ssbtsN}", /* '}' */ + *msgidp, LDAP_REQ_MODDN, + dn, newrdn, (ber_int_t) deleteoldrdn, + LDAP_TAG_NEWSUPERIOR, newSuperior ); + + } else { + rc = ber_printf( ber, "{it{ssbN}", /* '}' */ + *msgidp, LDAP_REQ_MODDN, + dn, newrdn, (ber_int_t) deleteoldrdn ); + } + + if ( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + rc = ber_printf( ber, /*{*/ "N}" ); + if ( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_rename - initiate an ldap extended modifyDN operation. + * + * Parameters: + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * newSuperior DN of the new parent if applicable + * + * Returns the LDAP error code. + */ + +int +ldap_rename( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_rename\n" ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_moddn_req( ld, dn, newrdn, newSuperior, + deleteoldrdn, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODRDN, dn, ber, id ); + + if( *msgidp < 0 ) { + return( ld->ld_errno ); + } + + return LDAP_SUCCESS; +} + + +/* + * ldap_rename2 - initiate an ldap (and X.500) modifyDN operation. Parameters: + * (LDAP V3 MODIFYDN REQUEST) + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * newSuperior DN of the new parent if applicable + * + * ldap_rename2 uses a U-Mich Style API. It returns the msgid. + */ + +int +ldap_rename2( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn ) +{ + int msgid; + int rc; + + Debug0( LDAP_DEBUG_TRACE, "ldap_rename2\n" ); + + rc = ldap_rename( ld, dn, newrdn, newSuperior, + deleteoldrdn, NULL, NULL, &msgid ); + + return rc == LDAP_SUCCESS ? msgid : -1; +} + + +/* + * ldap_modrdn2 - initiate an ldap modifyRDN operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * + * Example: + * msgid = ldap_modrdn( ld, dn, newrdn ); + */ +int +ldap_modrdn2( LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + int deleteoldrdn ) +{ + return ldap_rename2( ld, dn, newrdn, NULL, deleteoldrdn ); +} + +int +ldap_modrdn( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn ) +{ + return( ldap_rename2( ld, dn, newrdn, NULL, 1 ) ); +} + + +int +ldap_rename_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_rename( ld, dn, newrdn, newSuperior, + deleteoldrdn, sctrls, cctrls, &msgid ); + + if( rc != LDAP_SUCCESS ) { + return rc; + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &res ); + + if( rc == -1 || !res ) { + return ld->ld_errno; + } + + return ldap_result2error( ld, res, 1 ); +} + +int +ldap_rename2_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, newSuperior, + deleteoldrdn, NULL, NULL ); +} + +int +ldap_modrdn2_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn, int deleteoldrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, NULL, deleteoldrdn, NULL, NULL ); +} + +int +ldap_modrdn_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, NULL, 1, NULL, NULL ); +} + diff --git a/contrib/libs/openldap/libraries/libldap/msctrl.c b/contrib/libs/openldap/libraries/libldap/msctrl.c new file mode 100644 index 0000000000..0f9264e43a --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/msctrl.c @@ -0,0 +1,280 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 2018 Howard Chu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* MS Active Directory controls - not implemented in slapd(8) */ + +#ifdef LDAP_CONTROL_X_DIRSYNC + +int +ldap_create_dirsync_value( + LDAP *ld, + int flags, + int maxAttrCount, + struct berval *cookie, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + + if ( ld == NULL || cookie == NULL || + value == NULL ) + { + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + ld->ld_errno = LDAP_SUCCESS; + + /* maxAttrCount less than 0x100000 is treated as 0x100000 by server */ + + /* prepare value */ + value->bv_val = NULL; + value->bv_len = 0; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{iiO}", flags, maxAttrCount, cookie ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_dirsync_control( + LDAP *ld, + int flags, + int maxAttrCount, + struct berval *cookie, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_dirsync_value( ld, + flags, maxAttrCount, cookie, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DIRSYNC, + 1, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +int +ldap_parse_dirsync_control( + LDAP *ld, + LDAPControl *ctrl, + int *continueFlag, + struct berval *cookie ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + int unused; + + if ( ld == NULL || + ctrl == NULL || + continueFlag == NULL || + cookie == NULL ) + { + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + /* NOTE: we want the caller to get all or nothing; + * we could allow some of the pointers to be NULL, + * if one does not want part of the data */ + return LDAP_PARAM_ERROR; + } + + *continueFlag = 0; + BER_BVZERO( cookie ); + + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_scanf( ber, "{iio}", continueFlag, &unused, cookie ); + if ( tag == LBER_DEFAULT ) + tag = LBER_ERROR; + + (void)ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + return LDAP_DECODING_ERROR; + } + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_DIRSYNC */ + +#ifdef LDAP_CONTROL_X_SHOW_DELETED + +int +ldap_create_show_deleted_control( LDAP *ld, + LDAPControl **ctrlp ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrlp != NULL ); + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SHOW_DELETED, + 0, NULL, 0, ctrlp ); + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_SHOW_DELETED */ + +#ifdef LDAP_CONTROL_X_EXTENDED_DN + +int +ldap_create_extended_dn_value( + LDAP *ld, + int flag, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + + if ( ld == NULL || + value == NULL ) + { + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + ld->ld_errno = LDAP_SUCCESS; + + /* prepare value */ + value->bv_val = NULL; + value->bv_len = 0; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + tag = ber_printf( ber, "{i}", flag ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_extended_dn_control( + LDAP *ld, + int flag, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_extended_dn_value( ld, flag, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_EXTENDED_DN, + 0, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_EXTENDED_DN */ + +#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION + +int +ldap_create_server_notification_control( LDAP *ld, + LDAPControl **ctrlp ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrlp != NULL ); + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SERVER_NOTIFICATION, + 0, NULL, 0, ctrlp ); + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_SERVER_NOTIFICATION */ diff --git a/contrib/libs/openldap/libraries/libldap/open.c b/contrib/libs/openldap/libraries/libldap/open.c new file mode 100644 index 0000000000..bfdbb2575d --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/open.c @@ -0,0 +1,678 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <ac/stdlib.h> + +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "ldap.h" +#include "ldap_log.h" + +/* Caller must hold the conn_mutex since simultaneous accesses are possible */ +int ldap_open_defconn( LDAP *ld ) +{ + ld->ld_defconn = ldap_new_connection( ld, + &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 ); + + if( ld->ld_defconn == NULL ) { + ld->ld_errno = LDAP_SERVER_DOWN; + return -1; + } + + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + return 0; +} + +/* + * ldap_connect - Connect to an ldap server. + * + * Example: + * LDAP *ld; + * ldap_initialize( &ld, url ); + * ldap_connect( ld ); + */ +int +ldap_connect( LDAP *ld ) +{ + ber_socket_t sd = AC_SOCKET_INVALID; + int rc = LDAP_SUCCESS; + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) { + rc = ldap_open_defconn( ld ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + return rc; +} + +/* + * ldap_open - initialize and connect to an ldap server. A magic cookie to + * be used for future communication is returned on success, NULL on failure. + * "host" may be a space-separated list of hosts or IP addresses + * + * Example: + * LDAP *ld; + * ld = ldap_open( hostname, port ); + */ + +LDAP * +ldap_open( LDAP_CONST char *host, int port ) +{ + int rc; + LDAP *ld; + + Debug2( LDAP_DEBUG_TRACE, "ldap_open(%s, %d)\n", + host, port ); + + ld = ldap_init( host, port ); + if ( ld == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + rc = ldap_open_defconn( ld ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + if( rc < 0 ) { + ldap_ld_free( ld, 0, NULL, NULL ); + ld = NULL; + } + + Debug1( LDAP_DEBUG_TRACE, "ldap_open: %s\n", + ld != NULL ? "succeeded" : "failed" ); + + return ld; +} + + + +int +ldap_create( LDAP **ldp ) +{ + LDAP *ld; + struct ldapoptions *gopts; + + *ldp = NULL; + /* Get pointer to global option structure */ + if ( (gopts = LDAP_INT_GLOBAL_OPT()) == NULL) { + return LDAP_NO_MEMORY; + } + + /* Initialize the global options, if not already done. */ + if( gopts->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(gopts, NULL); + if ( gopts->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + Debug0( LDAP_DEBUG_TRACE, "ldap_create\n" ); + + if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) { + return( LDAP_NO_MEMORY ); + } + + if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1, + sizeof(struct ldap_common) )) == NULL ) { + LDAP_FREE( (char *)ld ); + return( LDAP_NO_MEMORY ); + } + /* copy the global options */ + LDAP_MUTEX_LOCK( &gopts->ldo_mutex ); + AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options)); +#ifdef LDAP_R_COMPILE + /* Properly initialize the structs mutex */ + ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) ); +#endif + +#ifdef HAVE_TLS + if ( ld->ld_options.ldo_tls_pin_hashalg ) { + int len = strlen( gopts->ldo_tls_pin_hashalg ); + + ld->ld_options.ldo_tls_pin_hashalg = + LDAP_MALLOC( len + 1 + gopts->ldo_tls_pin.bv_len ); + if ( !ld->ld_options.ldo_tls_pin_hashalg ) goto nomem; + + ld->ld_options.ldo_tls_pin.bv_val = ld->ld_options.ldo_tls_pin_hashalg + + len + 1; + AC_MEMCPY( ld->ld_options.ldo_tls_pin_hashalg, gopts->ldo_tls_pin_hashalg, + len + 1 + gopts->ldo_tls_pin.bv_len ); + } else if ( !BER_BVISEMPTY(&ld->ld_options.ldo_tls_pin) ) { + ber_dupbv( &ld->ld_options.ldo_tls_pin, &gopts->ldo_tls_pin ); + } +#endif + LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex ); + + ld->ld_valid = LDAP_VALID_SESSION; + + /* but not pointers to malloc'ed items */ + ld->ld_options.ldo_sctrls = NULL; + ld->ld_options.ldo_cctrls = NULL; + ld->ld_options.ldo_defludp = NULL; + ld->ld_options.ldo_conn_cbs = NULL; + + ld->ld_options.ldo_defbase = gopts->ldo_defbase + ? LDAP_STRDUP( gopts->ldo_defbase ) : NULL; + +#ifdef HAVE_CYRUS_SASL + ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech + ? LDAP_STRDUP( gopts->ldo_def_sasl_mech ) : NULL; + ld->ld_options.ldo_def_sasl_realm = gopts->ldo_def_sasl_realm + ? LDAP_STRDUP( gopts->ldo_def_sasl_realm ) : NULL; + ld->ld_options.ldo_def_sasl_authcid = gopts->ldo_def_sasl_authcid + ? LDAP_STRDUP( gopts->ldo_def_sasl_authcid ) : NULL; + ld->ld_options.ldo_def_sasl_authzid = gopts->ldo_def_sasl_authzid + ? LDAP_STRDUP( gopts->ldo_def_sasl_authzid ) : NULL; +#endif + +#ifdef HAVE_TLS + /* We explicitly inherit the SSL_CTX, don't need the names/paths. Leave + * them empty to allow new SSL_CTX's to be created from scratch. + */ + memset( &ld->ld_options.ldo_tls_info, 0, + sizeof( ld->ld_options.ldo_tls_info )); + ld->ld_options.ldo_tls_ctx = NULL; +#endif + + if ( gopts->ldo_defludp ) { + ld->ld_options.ldo_defludp = ldap_url_duplist(gopts->ldo_defludp); + + if ( ld->ld_options.ldo_defludp == NULL ) goto nomem; + } + + if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem; + + ld->ld_options.ldo_local_ip_addrs.local_ip_addrs = NULL; + if( gopts->ldo_local_ip_addrs.local_ip_addrs ) { + ld->ld_options.ldo_local_ip_addrs.local_ip_addrs = + LDAP_STRDUP( gopts->ldo_local_ip_addrs.local_ip_addrs ); + if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs == NULL ) + goto nomem; + } + + ld->ld_lberoptions = LBER_USE_DER; + + ld->ld_sb = ber_sockbuf_alloc( ); + if ( ld->ld_sb == NULL ) goto nomem; + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_req_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_res_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex ); +#endif + ld->ld_ldcrefcnt = 1; + *ldp = ld; + return LDAP_SUCCESS; + +nomem: + ldap_free_select_info( ld->ld_selectinfo ); + ldap_free_urllist( ld->ld_options.ldo_defludp ); +#ifdef HAVE_CYRUS_SASL + LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_realm ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_mech ); +#endif + +#ifdef HAVE_TLS + /* tls_pin_hashalg and tls_pin share the same buffer */ + if ( ld->ld_options.ldo_tls_pin_hashalg ) { + LDAP_FREE( ld->ld_options.ldo_tls_pin_hashalg ); + } else { + LDAP_FREE( ld->ld_options.ldo_tls_pin.bv_val ); + } +#endif + LDAP_FREE( (char *)ld ); + return LDAP_NO_MEMORY; +} + +/* + * ldap_init - initialize the LDAP library. A magic cookie to be used for + * future communication is returned on success, NULL on failure. + * "host" may be a space-separated list of hosts or IP addresses + * + * Example: + * LDAP *ld; + * ld = ldap_init( host, port ); + */ +LDAP * +ldap_init( LDAP_CONST char *defhost, int defport ) +{ + LDAP *ld; + int rc; + + rc = ldap_create(&ld); + if ( rc != LDAP_SUCCESS ) + return NULL; + + if (defport != 0) + ld->ld_options.ldo_defport = defport; + + if (defhost != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, defhost); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return NULL; + } + } + + return( ld ); +} + + +int +ldap_initialize( LDAP **ldp, LDAP_CONST char *url ) +{ + int rc; + LDAP *ld; + + *ldp = NULL; + rc = ldap_create(&ld); + if ( rc != LDAP_SUCCESS ) + return rc; + + if (url != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_URI, url); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return rc; + } +#ifdef LDAP_CONNECTIONLESS + if (ldap_is_ldapc_url(url)) + LDAP_IS_UDP(ld) = 1; +#endif + } + + *ldp = ld; + return LDAP_SUCCESS; +} + +int +ldap_init_fd( + ber_socket_t fd, + int proto, + LDAP_CONST char *url, + LDAP **ldp +) +{ + int rc; + LDAP *ld; + LDAPConn *conn; +#ifdef LDAP_CONNECTIONLESS + ber_socklen_t len; +#endif + + *ldp = NULL; + rc = ldap_create( &ld ); + if( rc != LDAP_SUCCESS ) + return( rc ); + + if (url != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_URI, url); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return rc; + } + } + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + /* Attach the passed socket as the LDAP's connection */ + conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); + if( conn == NULL ) { + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + ldap_unbind_ext( ld, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + if( url ) + conn->lconn_server = ldap_url_dup( ld->ld_options.ldo_defludp ); + ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd ); + ld->ld_defconn = conn; + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + switch( proto ) { + case LDAP_PROTO_TCP: +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; + +#ifdef LDAP_CONNECTIONLESS + case LDAP_PROTO_UDP: + LDAP_IS_UDP(ld) = 1; + if( ld->ld_options.ldo_peer ) + ldap_memfree( ld->ld_options.ldo_peer ); + ld->ld_options.ldo_peer = ldap_memcalloc( 1, sizeof( struct sockaddr_storage ) ); + len = sizeof( struct sockaddr_storage ); + if( getpeername ( fd, ld->ld_options.ldo_peer, &len ) < 0) { + ldap_unbind_ext( ld, NULL, NULL ); + return( AC_SOCKET_ERROR ); + } +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; +#endif /* LDAP_CONNECTIONLESS */ + + case LDAP_PROTO_IPC: +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; + + case LDAP_PROTO_EXT: + /* caller must supply sockbuf handlers */ + break; + + default: + ldap_unbind_ext( ld, NULL, NULL ); + return LDAP_PARAM_ERROR; + } + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + INT_MAX, (void *)"ldap_" ); +#endif + + /* Add the connection to the *LDAP's select pool */ + ldap_mark_select_read( ld, conn->lconn_sb ); + + *ldp = ld; + return LDAP_SUCCESS; +} + +/* Protected by ld_conn_mutex */ +int +ldap_int_open_connection( + LDAP *ld, + LDAPConn *conn, + LDAPURLDesc *srv, + int async ) +{ + int rc = -1; + int proto; + + Debug0( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n" ); + + switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) { + case LDAP_PROTO_TCP: + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, srv, async ); + + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; + +#ifdef LDAP_CONNECTIONLESS + case LDAP_PROTO_UDP: + LDAP_IS_UDP(ld) = 1; + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, srv, async ); + + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; +#endif + case LDAP_PROTO_IPC: +#ifdef LDAP_PF_LOCAL + /* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */ + rc = ldap_connect_to_path( ld, conn->lconn_sb, + srv, async ); + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; +#endif /* LDAP_PF_LOCAL */ + default: + return -1; + break; + } + + conn->lconn_created = time( NULL ); + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + INT_MAX, (void *)"ldap_" ); +#endif + +#ifdef LDAP_CONNECTIONLESS + if( proto == LDAP_PROTO_UDP ) return 0; +#endif + + if ( async && rc == -2) { + /* Need to let the connect complete asynchronously before we continue */ + return -2; + } + +#ifdef HAVE_TLS + if ((rc == 0 || rc == -2) && ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD || + strcmp( srv->lud_scheme, "ldaps" ) == 0 )) + { + ++conn->lconn_refcnt; /* avoid premature free */ + + rc = ldap_int_tls_start( ld, conn, srv ); + + --conn->lconn_refcnt; + + if (rc != LDAP_SUCCESS) { + /* process connection callbacks */ + { + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + + lo = &ld->ld_options; + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, conn->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + lo = LDAP_INT_GLOBAL_OPT(); + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, conn->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + } + ber_int_sb_close( conn->lconn_sb ); + return -1; + } + } +#endif + + return( 0 ); +} + +/* + * ldap_open_internal_connection - open connection and set file descriptor + * + * note: ldap_init_fd() may be preferable + */ + +int +ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) +{ + int rc; + LDAPConn *c; + LDAPRequest *lr; + LDAP *ld; + + rc = ldap_create( &ld ); + if( rc != LDAP_SUCCESS ) { + *ldp = NULL; + return( rc ); + } + + /* Make it appear that a search request, msgid 0, was sent */ + lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest )); + if( lr == NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + *ldp = NULL; + return( LDAP_NO_MEMORY ); + } + memset(lr, 0, sizeof( LDAPRequest )); + lr->lr_msgid = 0; + lr->lr_status = LDAP_REQST_INPROGRESS; + lr->lr_res_errno = LDAP_SUCCESS; + /* no mutex lock needed, we just created this ld here */ + rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error ); + assert( rc == LDAP_SUCCESS ); + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + /* Attach the passed socket as the *LDAP's connection */ + c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); + if( c == NULL ) { + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + ldap_unbind_ext( ld, NULL, NULL ); + *ldp = NULL; + return( LDAP_NO_MEMORY ); + } + ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp ); +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"int_" ); +#endif + ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + ld->ld_defconn = c; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + /* Add the connection to the *LDAP's select pool */ + ldap_mark_select_read( ld, c->lconn_sb ); + + /* Make this connection an LDAP V3 protocol connection */ + rc = LDAP_VERSION3; + ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc ); + *ldp = ld; + + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + + return( LDAP_SUCCESS ); +} + +LDAP * +ldap_dup( LDAP *old ) +{ + LDAP *ld; + + if ( old == NULL ) { + return( NULL ); + } + + Debug0( LDAP_DEBUG_TRACE, "ldap_dup\n" ); + + if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( &old->ld_ldcmutex ); + ld->ldc = old->ldc; + old->ld_ldcrefcnt++; + LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex ); + return ( ld ); +} + +int +ldap_int_check_async_open( LDAP *ld, ber_socket_t sd ) +{ + struct timeval tv = { 0 }; + int rc; + + rc = ldap_int_poll( ld, sd, &tv, 1 ); + switch ( rc ) { + case 0: + /* now ready to start tls */ + ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED; + break; + + default: + ld->ld_errno = LDAP_CONNECT_ERROR; + return -1; + + case -2: + /* connect not completed yet */ + ld->ld_errno = LDAP_X_CONNECTING; + return rc; + } + +#ifdef HAVE_TLS + if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD || + !strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) { + + ++ld->ld_defconn->lconn_refcnt; /* avoid premature free */ + + rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server ); + + --ld->ld_defconn->lconn_refcnt; + } +#endif + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/options.c b/contrib/libs/openldap/libraries/libldap/options.c new file mode 100644 index 0000000000..7a096add2b --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/options.c @@ -0,0 +1,1012 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_OPT_REBIND_PROC 0x4e814d +#define LDAP_OPT_REBIND_PARAMS 0x4e814e + +#define LDAP_OPT_NEXTREF_PROC 0x4e815d +#define LDAP_OPT_NEXTREF_PARAMS 0x4e815e + +#define LDAP_OPT_URLLIST_PROC 0x4e816d +#define LDAP_OPT_URLLIST_PARAMS 0x4e816e + +static const LDAPAPIFeatureInfo features[] = { +#ifdef LDAP_API_FEATURE_X_OPENLDAP + { /* OpenLDAP Extensions API Feature */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP", + LDAP_API_FEATURE_X_OPENLDAP + }, +#endif + +#ifdef LDAP_API_FEATURE_THREAD_SAFE + { /* Basic Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "THREAD_SAFE", + LDAP_API_FEATURE_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_SESSION_THREAD_SAFE + { /* Session Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "SESSION_THREAD_SAFE", + LDAP_API_FEATURE_SESSION_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_OPERATION_THREAD_SAFE + { /* Operation Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "OPERATION_THREAD_SAFE", + LDAP_API_FEATURE_OPERATION_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT + { /* OpenLDAP Reentrant */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_REENTRANT", + LDAP_API_FEATURE_X_OPENLDAP_REENTRANT + }, +#endif +#ifdef LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE + { /* OpenLDAP Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_THREAD_SAFE", + LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS + { /* V2 Referrals */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_V2_REFERRALS", + LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS + }, +#endif + {0, NULL, 0} +}; + +int +ldap_get_option( + LDAP *ld, + int option, + void *outvalue) +{ + struct ldapoptions *lo; + int rc = LDAP_OPT_ERROR; + + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if (NULL == lo) { + return LDAP_NO_MEMORY; + } + + if( lo->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(lo, NULL); + if ( lo->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + if(ld != NULL) { + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + } + + if(outvalue == NULL) { + /* no place to get to */ + return LDAP_OPT_ERROR; + } + + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + + switch(option) { + case LDAP_OPT_API_INFO: { + struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue; + + if(info == NULL) { + /* outvalue must point to an apiinfo structure */ + break; /* LDAP_OPT_ERROR */ + } + + if(info->ldapai_info_version != LDAP_API_INFO_VERSION) { + /* api info version mismatch */ + info->ldapai_info_version = LDAP_API_INFO_VERSION; + break; /* LDAP_OPT_ERROR */ + } + + info->ldapai_api_version = LDAP_API_VERSION; + info->ldapai_protocol_version = LDAP_VERSION_MAX; + + if(features[0].ldapaif_name == NULL) { + info->ldapai_extensions = NULL; + } else { + int i; + info->ldapai_extensions = LDAP_MALLOC(sizeof(char *) * + sizeof(features)/sizeof(LDAPAPIFeatureInfo)); + if ( info->ldapai_extensions == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for(i=0; features[i].ldapaif_name != NULL; i++) { + info->ldapai_extensions[i] = + LDAP_STRDUP(features[i].ldapaif_name); + if ( info->ldapai_extensions[i] == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + } + if ( features[i].ldapaif_name != NULL ) { + break; /* LDAP_NO_MEMORY */ + } + + info->ldapai_extensions[i] = NULL; + } + + info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME); + info->ldapai_vendor_version = LDAP_VENDOR_VERSION; + + rc = LDAP_OPT_SUCCESS; + break; + } break; + + case LDAP_OPT_DESC: + if( ld == NULL || ld->ld_sb == NULL ) { + /* bad param */ + break; + } + + ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SOCKBUF: + if( ld == NULL ) break; + *(Sockbuf **)outvalue = ld->ld_sb; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMEOUT: + /* the caller has to free outvalue ! */ + if ( lo->ldo_tm_api.tv_sec < 0 ) { + *(void **)outvalue = NULL; + } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) { + break; /* LDAP_OPT_ERROR */ + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_NETWORK_TIMEOUT: + /* the caller has to free outvalue ! */ + if ( lo->ldo_tm_net.tv_sec < 0 ) { + *(void **)outvalue = NULL; + } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) { + break; /* LDAP_OPT_ERROR */ + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEREF: + * (int *) outvalue = lo->ldo_deref; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SIZELIMIT: + * (int *) outvalue = lo->ldo_sizelimit; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMELIMIT: + * (int *) outvalue = lo->ldo_timelimit; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRALS: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESTART: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_PROTOCOL_VERSION: + * (int *) outvalue = lo->ldo_version; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SERVER_CONTROLS: + * (LDAPControl ***) outvalue = + ldap_controls_dup( lo->ldo_sctrls ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CLIENT_CONTROLS: + * (LDAPControl ***) outvalue = + ldap_controls_dup( lo->ldo_cctrls ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_HOST_NAME: + * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SOCKET_BIND_ADDRESSES: + if ( lo->ldo_local_ip_addrs.local_ip_addrs == NULL ) { + * (void **) outvalue = NULL; + } + else { + * (char **) outvalue = + LDAP_STRDUP( lo->ldo_local_ip_addrs.local_ip_addrs ); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_URI: + * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEFBASE: + if( lo->ldo_defbase == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_ASYNC: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_CB: + { + /* Getting deletes the specified callback */ + ldaplist **ll = &lo->ldo_conn_cbs; + for (;*ll;ll = &(*ll)->ll_next) { + if ((*ll)->ll_data == outvalue) { + ldaplist *lc = *ll; + *ll = lc->ll_next; + LDAP_FREE(lc); + break; + } + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESULT_CODE: + if(ld == NULL) { + /* bad param */ + break; + } + * (int *) outvalue = ld->ld_errno; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DIAGNOSTIC_MESSAGE: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_error == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP(ld->ld_error); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_MATCHED_DN: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_matched == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP( ld->ld_matched ); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRAL_URLS: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_referrals == NULL ) { + * (char ***) outvalue = NULL; + } else { + * (char ***) outvalue = ldap_value_dup(ld->ld_referrals); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_API_FEATURE_INFO: { + LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue; + int i; + + if(info == NULL) + break; /* LDAP_OPT_ERROR */ + + if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) { + /* api info version mismatch */ + info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; + break; /* LDAP_OPT_ERROR */ + } + + if(info->ldapaif_name == NULL) + break; /* LDAP_OPT_ERROR */ + + for(i=0; features[i].ldapaif_name != NULL; i++) { + if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) { + info->ldapaif_version = + features[i].ldapaif_version; + rc = LDAP_OPT_SUCCESS; + break; + } + } + } + break; + + case LDAP_OPT_DEBUG_LEVEL: + * (int *) outvalue = lo->ldo_debug; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SESSION_REFCNT: + if(ld == NULL) { + /* bad param */ + break; + } + LDAP_MUTEX_LOCK( &ld->ld_ldcmutex ); + * (int *) outvalue = ld->ld_ldcrefcnt; + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_KEEPCONN: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_KEEPCONN); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_IDLE: + * (int *) outvalue = lo->ldo_keepalive_idle; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_PROBES: + * (int *) outvalue = lo->ldo_keepalive_probes; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_INTERVAL: + * (int *) outvalue = lo->ldo_keepalive_interval; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TCP_USER_TIMEOUT: + * (unsigned int *) outvalue = lo->ldo_tcp_user_timeout; + rc = LDAP_OPT_SUCCESS; + break; + + default: +#ifdef HAVE_TLS + if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) { + rc = LDAP_OPT_SUCCESS; + break; + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) { + rc = LDAP_OPT_SUCCESS; + break; + } +#endif + /* bad param */ + break; + } + + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); +} + +int +ldap_set_option( + LDAP *ld, + int option, + LDAP_CONST void *invalue) +{ + struct ldapoptions *lo; + int *dbglvl = NULL; + int rc = LDAP_OPT_ERROR; + + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if (lo == NULL) { + return LDAP_NO_MEMORY; + } + + /* + * The architecture to turn on debugging has a chicken and egg + * problem. Thus, we introduce a fix here. + */ + + if (option == LDAP_OPT_DEBUG_LEVEL) { + dbglvl = (int *) invalue; + } + + if( lo->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(lo, dbglvl); + if ( lo->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + if(ld != NULL) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + } + + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + + switch ( option ) { + + /* options with boolean values */ + case LDAP_OPT_REFERRALS: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESTART: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_RESTART); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_ASYNC: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_KEEPCONN: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_KEEPCONN); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_KEEPCONN); + } + rc = LDAP_OPT_SUCCESS; + break; + /* options which can withstand invalue == NULL */ + case LDAP_OPT_SERVER_CONTROLS: { + LDAPControl *const *controls = + (LDAPControl *const *) invalue; + + if( lo->ldo_sctrls ) + ldap_controls_free( lo->ldo_sctrls ); + + if( controls == NULL || *controls == NULL ) { + lo->ldo_sctrls = NULL; + rc = LDAP_OPT_SUCCESS; + break; + } + + lo->ldo_sctrls = ldap_controls_dup( controls ); + + if(lo->ldo_sctrls == NULL) { + /* memory allocation error ? */ + break; /* LDAP_OPT_ERROR */ + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CLIENT_CONTROLS: { + LDAPControl *const *controls = + (LDAPControl *const *) invalue; + + if( lo->ldo_cctrls ) + ldap_controls_free( lo->ldo_cctrls ); + + if( controls == NULL || *controls == NULL ) { + lo->ldo_cctrls = NULL; + rc = LDAP_OPT_SUCCESS; + break; + } + + lo->ldo_cctrls = ldap_controls_dup( controls ); + + if(lo->ldo_cctrls == NULL) { + /* memory allocation error ? */ + break; /* LDAP_OPT_ERROR */ + } + } + rc = LDAP_OPT_SUCCESS; + break; + + + case LDAP_OPT_HOST_NAME: { + const char *host = (const char *) invalue; + LDAPURLDesc *ludlist = NULL; + rc = LDAP_OPT_SUCCESS; + + if(host != NULL) { + rc = ldap_url_parsehosts( &ludlist, host, + lo->ldo_defport ? lo->ldo_defport : LDAP_PORT ); + + } else if(ld == NULL) { + /* + * must want global default returned + * to initial condition. + */ + rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + + } else { + /* + * must want the session default + * updated to the current global default + */ + ludlist = ldap_url_duplist( + ldap_int_global_options.ldo_defludp); + if (ludlist == NULL) + rc = LDAP_NO_MEMORY; + } + + if (rc == LDAP_OPT_SUCCESS) { + if (lo->ldo_defludp != NULL) + ldap_free_urllist(lo->ldo_defludp); + lo->ldo_defludp = ludlist; + } + break; + } + + case LDAP_OPT_SOCKET_BIND_ADDRESSES: { + const char *source_ip = (const char *) invalue; + char **source_ip_lst = NULL; + + ldapsourceip temp_source_ip; + memset( &temp_source_ip, 0, sizeof( ldapsourceip ) ); + rc = LDAP_OPT_SUCCESS; + if( source_ip == NULL ) { + if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) { + LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ); + memset( &ld->ld_options.ldo_local_ip_addrs, 0, + sizeof( ldapsourceip ) ); + } + } + else { + source_ip_lst = ldap_str2charray( source_ip, " " ); + + if ( source_ip_lst == NULL ) + rc = LDAP_NO_MEMORY; + + if( rc == LDAP_OPT_SUCCESS ) { + rc = ldap_validate_and_fill_sourceip ( source_ip_lst, + &temp_source_ip ); + ldap_charray_free( source_ip_lst ); + } + if ( rc == LDAP_OPT_SUCCESS ) { + if ( lo->ldo_local_ip_addrs.local_ip_addrs != NULL ) { + LDAP_FREE( lo->ldo_local_ip_addrs.local_ip_addrs ); + lo->ldo_local_ip_addrs.local_ip_addrs = NULL; + } + lo->ldo_local_ip_addrs = temp_source_ip; + lo->ldo_local_ip_addrs.local_ip_addrs = LDAP_STRDUP( source_ip ); + } + } + break; + } + + case LDAP_OPT_URI: { + const char *urls = (const char *) invalue; + LDAPURLDesc *ludlist = NULL; + rc = LDAP_OPT_SUCCESS; + + if(urls != NULL) { + rc = ldap_url_parselist_ext(&ludlist, urls, NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + } else if(ld == NULL) { + /* + * must want global default returned + * to initial condition. + */ + rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + + } else { + /* + * must want the session default + * updated to the current global default + */ + ludlist = ldap_url_duplist( + ldap_int_global_options.ldo_defludp); + if (ludlist == NULL) + rc = LDAP_URL_ERR_MEM; + } + + switch (rc) { + case LDAP_URL_SUCCESS: /* Success */ + rc = LDAP_SUCCESS; + break; + + case LDAP_URL_ERR_MEM: /* can't allocate memory space */ + rc = LDAP_NO_MEMORY; + break; + + case LDAP_URL_ERR_PARAM: /* parameter is bad */ + case LDAP_URL_ERR_BADSCHEME: /* URL doesn't begin with "ldap[si]://" */ + case LDAP_URL_ERR_BADENCLOSURE: /* URL is missing trailing ">" */ + case LDAP_URL_ERR_BADURL: /* URL is bad */ + case LDAP_URL_ERR_BADHOST: /* host port is bad */ + case LDAP_URL_ERR_BADATTRS: /* bad (or missing) attributes */ + case LDAP_URL_ERR_BADSCOPE: /* scope string is invalid (or missing) */ + case LDAP_URL_ERR_BADFILTER: /* bad or missing filter */ + case LDAP_URL_ERR_BADEXTS: /* bad or missing extensions */ + rc = LDAP_PARAM_ERROR; + break; + } + + if (rc == LDAP_SUCCESS) { + if (lo->ldo_defludp != NULL) + ldap_free_urllist(lo->ldo_defludp); + lo->ldo_defludp = ludlist; + } + break; + } + + case LDAP_OPT_DEFBASE: { + const char *newbase = (const char *) invalue; + char *defbase = NULL; + + if ( newbase != NULL ) { + defbase = LDAP_STRDUP( newbase ); + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + } else if ( ld != NULL ) { + defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase ); + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + } + + if ( lo->ldo_defbase != NULL ) + LDAP_FREE( lo->ldo_defbase ); + lo->ldo_defbase = defbase; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DIAGNOSTIC_MESSAGE: { + const char *err = (const char *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_error ) { + LDAP_FREE(ld->ld_error); + ld->ld_error = NULL; + } + + if ( err ) { + ld->ld_error = LDAP_STRDUP(err); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_MATCHED_DN: { + const char *matched = (const char *) invalue; + + if (ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_matched ) { + LDAP_FREE(ld->ld_matched); + ld->ld_matched = NULL; + } + + if ( matched ) { + ld->ld_matched = LDAP_STRDUP( matched ); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRAL_URLS: { + char *const *referrals = (char *const *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_referrals ) { + LDAP_VFREE(ld->ld_referrals); + } + + if ( referrals ) { + ld->ld_referrals = ldap_value_dup(referrals); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_rebind_proc() */ + case LDAP_OPT_REBIND_PROC: { + lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_REBIND_PARAMS: { + lo->ldo_rebind_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_nextref_proc() */ + case LDAP_OPT_NEXTREF_PROC: { + lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_NEXTREF_PARAMS: { + lo->ldo_nextref_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_urllist_proc() */ + case LDAP_OPT_URLLIST_PROC: { + lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_URLLIST_PARAMS: { + lo->ldo_urllist_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* read-only options */ + case LDAP_OPT_API_INFO: + case LDAP_OPT_DESC: + case LDAP_OPT_SOCKBUF: + case LDAP_OPT_API_FEATURE_INFO: + break; /* LDAP_OPT_ERROR */ + + /* options which cannot withstand invalue == NULL */ + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_RESULT_CODE: + case LDAP_OPT_DEBUG_LEVEL: + case LDAP_OPT_TIMEOUT: + case LDAP_OPT_NETWORK_TIMEOUT: + case LDAP_OPT_CONNECT_CB: + case LDAP_OPT_X_KEEPALIVE_IDLE: + case LDAP_OPT_X_KEEPALIVE_PROBES : + case LDAP_OPT_X_KEEPALIVE_INTERVAL : + case LDAP_OPT_TCP_USER_TIMEOUT: + if(invalue == NULL) { + /* no place to set from */ + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_ERROR ); + } + break; + + default: +#ifdef HAVE_TLS + if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) { + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) { + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); + } +#endif + /* bad param */ + break; /* LDAP_OPT_ERROR */ + } + + /* options which cannot withstand invalue == NULL */ + + switch(option) { + case LDAP_OPT_DEREF: + /* FIXME: check value for protocol compliance? */ + lo->ldo_deref = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SIZELIMIT: + /* FIXME: check value for protocol compliance? */ + lo->ldo_sizelimit = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMELIMIT: + /* FIXME: check value for protocol compliance? */ + lo->ldo_timelimit = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMEOUT: { + const struct timeval *tv = + (const struct timeval *) invalue; + + lo->ldo_tm_api = *tv; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_NETWORK_TIMEOUT: { + const struct timeval *tv = + (const struct timeval *) invalue; + + lo->ldo_tm_net = *tv; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_PROTOCOL_VERSION: { + int vers = * (const int *) invalue; + if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) { + /* not supported */ + break; + } + lo->ldo_version = vers; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESULT_CODE: { + int err = * (const int *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; + } + + ld->ld_errno = err; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEBUG_LEVEL: + lo->ldo_debug = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_CB: + { + /* setting pushes the callback */ + ldaplist *ll; + ll = LDAP_MALLOC( sizeof( *ll )); + if ( ll == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + ll->ll_data = (void *)invalue; + ll->ll_next = lo->ldo_conn_cbs; + lo->ldo_conn_cbs = ll; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_IDLE: + lo->ldo_keepalive_idle = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_PROBES : + lo->ldo_keepalive_probes = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_INTERVAL : + lo->ldo_keepalive_interval = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_TCP_USER_TIMEOUT: + lo->ldo_tcp_user_timeout = * (const unsigned int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); +} + +int +ldap_set_rebind_proc( LDAP *ld, LDAP_REBIND_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_REBIND_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_REBIND_PARAMS, (void *)params ); + return rc; +} + +int +ldap_set_nextref_proc( LDAP *ld, LDAP_NEXTREF_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PARAMS, (void *)params ); + return rc; +} + +int +ldap_set_urllist_proc( LDAP *ld, LDAP_URLLIST_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PARAMS, (void *)params ); + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/os-ip.c b/contrib/libs/openldap/libraries/libldap/os-ip.c new file mode 100644 index 0000000000..a84735e7af --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/os-ip.c @@ -0,0 +1,1265 @@ +/* os-ip.c -- platform-specific TCP & UDP related code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 1999 Lars Uffmann. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* Significant additional contributors include: + * Lars Uffman + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "ldap-int.h" + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) +# ifdef LDAP_PF_INET6 +int ldap_int_inet4or6 = AF_UNSPEC; +# else +int ldap_int_inet4or6 = AF_INET; +# endif +#endif + +static void +ldap_pvt_set_errno(int err) +{ + sock_errset(err); +} + +int +ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src ) +{ + struct timeval *new; + + assert( dest != NULL ); + + if (src == NULL) { + *dest = NULL; + return 0; + } + + new = (struct timeval *) LDAP_MALLOC(sizeof(struct timeval)); + + if( new == NULL ) { + *dest = NULL; + return 1; + } + + AC_MEMCPY( (char *) new, (const char *) src, sizeof(struct timeval)); + + *dest = new; + return 0; +} + +static int +ldap_pvt_ndelay_on(LDAP *ld, int fd) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_on: %d\n",fd ); + return ber_pvt_socket_set_nonblock( fd, 1 ); +} + +static int +ldap_pvt_ndelay_off(LDAP *ld, int fd) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_off: %d\n",fd ); + return ber_pvt_socket_set_nonblock( fd, 0 ); +} + +static ber_socket_t +ldap_int_socket(LDAP *ld, int family, int type ) +{ + ber_socket_t s = socket(family, type, 0); + Debug1(LDAP_DEBUG_TRACE, "ldap_new_socket: %d\n",s ); +#ifdef FD_CLOEXEC + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + return ( s ); +} + +static int +ldap_pvt_close_socket(LDAP *ld, int s) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_close_socket: %d\n",s ); + return tcp_close(s); +} + +static int +ldap_int_prepare_socket(LDAP *ld, int s, int proto ) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: %d\n", s ); + +#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY ) || defined( TCP_USER_TIMEOUT ) + if ( proto == LDAP_PROTO_TCP ) { + int dummy = 1; +#ifdef SO_KEEPALIVE + if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, + (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n", + s ); + } + if ( ld->ld_options.ldo_keepalive_idle > 0 ) + { +#ifdef TCP_KEEPIDLE + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE, + (void*) &ld->ld_options.ldo_keepalive_idle, + sizeof(ld->ld_options.ldo_keepalive_idle) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, + "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n", + s ); + } +#else + Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "sockopt TCP_KEEPIDLE not supported on this system.\n" ); +#endif /* TCP_KEEPIDLE */ + } + if ( ld->ld_options.ldo_keepalive_probes > 0 ) + { +#ifdef TCP_KEEPCNT + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT, + (void*) &ld->ld_options.ldo_keepalive_probes, + sizeof(ld->ld_options.ldo_keepalive_probes) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, + "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n", + s ); + } +#else + Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "sockopt TCP_KEEPCNT not supported on this system.\n" ); +#endif /* TCP_KEEPCNT */ + } + if ( ld->ld_options.ldo_keepalive_interval > 0 ) + { +#ifdef TCP_KEEPINTVL + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL, + (void*) &ld->ld_options.ldo_keepalive_interval, + sizeof(ld->ld_options.ldo_keepalive_interval) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, + "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n", + s ); + } +#else + Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "sockopt TCP_KEEPINTVL not supported on this system.\n" ); +#endif /* TCP_KEEPINTVL */ + } +#endif /* SO_KEEPALIVE */ +#ifdef TCP_NODELAY + if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, + (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "setsockopt(%d, TCP_NODELAY) failed (ignored).\n", + s ); + } +#endif /* TCP_NODELAY */ + if ( ld->ld_options.ldo_tcp_user_timeout > 0 ) + { +#ifdef TCP_USER_TIMEOUT + if ( setsockopt( s, IPPROTO_TCP, TCP_USER_TIMEOUT, + (void*) &ld->ld_options.ldo_tcp_user_timeout, + sizeof(ld->ld_options.ldo_tcp_user_timeout) ) == AC_SOCKET_ERROR ) + { + Debug1(LDAP_DEBUG_TRACE, + "ldap_prepare_socket: " + "setsockopt(%d, TCP_USER_TIMEOUT) failed (ignored).\n", + s ); + } +#else + Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: " + "sockopt TCP_USER_TIMEOUT not supported on this system.\n" ); +#endif /* TCP_USER_TIMEOUT */ + } + } +#endif /* SO_KEEPALIVE || TCP_NODELAY || TCP_USER_TIMEOUT */ + + return 0; +} + +#ifndef HAVE_WINSOCK + +#undef TRACE +#define TRACE do { \ + char ebuf[128]; \ + int saved_errno = errno; \ + Debug3(LDAP_DEBUG_TRACE, "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \ + s, \ + saved_errno, \ + sock_errstr(saved_errno, ebuf, sizeof(ebuf)) ); \ +} while( 0 ) + +/* + * check the socket for errors after select returned. + */ +static int +ldap_pvt_is_socket_ready(LDAP *ld, int s) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_is_sock_ready: %d\n",s ); + +#if defined( notyet ) /* && defined( SO_ERROR ) */ +{ + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy ) + == AC_SOCKET_ERROR ) + { + return -1; + } + if ( so_errno ) { + ldap_pvt_set_errno(so_errno); + TRACE; + return -1; + } + return 0; +} +#else +{ + /* error slippery */ +#ifdef LDAP_PF_INET6 + struct sockaddr_storage sin; +#else + struct sockaddr_in sin; +#endif + char ch; + ber_socklen_t dummy = sizeof(sin); + if ( getpeername( s, (struct sockaddr *) &sin, &dummy ) + == AC_SOCKET_ERROR ) + { + /* XXX: needs to be replace with ber_stream_read() */ + (void)!read(s, &ch, 1); + TRACE; + return -1; + } + return 0; +} +#endif + return -1; +} +#undef TRACE + +#endif /* HAVE_WINSOCK */ + +/* NOTE: this is identical to analogous code in os-local.c */ +int +ldap_int_poll( + LDAP *ld, + ber_socket_t s, + struct timeval *tvp, + int wr ) +{ + int rc; + + + Debug2(LDAP_DEBUG_TRACE, "ldap_int_poll: fd: %d tm: %ld\n", + s, tvp ? tvp->tv_sec : -1L ); + +#ifdef HAVE_POLL + { + struct pollfd fd; + int timeout = INFTIM; + short event = wr ? POLL_WRITE : POLL_READ; + + fd.fd = s; + fd.events = event; + + if ( tvp != NULL ) { + timeout = TV2MILLISEC( tvp ); + } + do { + fd.revents = 0; + rc = poll( &fd, 1, timeout ); + + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( timeout == 0 && rc == 0 ) { + return -2; + } + + if ( fd.revents & event ) { + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } + + if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) { + return -1; + } + return 0; + } + } +#else + { + fd_set wfds, *z = NULL; +#ifdef HAVE_WINSOCK + fd_set efds; +#endif + struct timeval tv = { 0 }; + +#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK ) + if ( s >= FD_SETSIZE ) { + rc = AC_SOCKET_ERROR; + tcp_close( s ); + ldap_pvt_set_errno( EMFILE ); + return rc; + } +#endif + + if ( tvp != NULL ) { + tv = *tvp; + } + + do { + FD_ZERO(&wfds); + FD_SET(s, &wfds ); + +#ifdef HAVE_WINSOCK + FD_ZERO(&efds); + FD_SET(s, &efds ); +#endif + + rc = select( ldap_int_tblsize, z, &wfds, +#ifdef HAVE_WINSOCK + &efds, +#else + z, +#endif + tvp ? &tv : NULL ); + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( rc == 0 && tvp && tvp->tv_sec == 0 && tvp->tv_usec == 0 ) { + return -2; + } + +#ifdef HAVE_WINSOCK + /* This means the connection failed */ + if ( FD_ISSET(s, &efds) ) { + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, + (char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno ) + { + /* impossible */ + so_errno = WSAGetLastError(); + } + ldap_pvt_set_errno( so_errno ); + Debug3(LDAP_DEBUG_TRACE, + "ldap_int_poll: error on socket %d: " + "errno: %d (%s)\n", s, so_errno, sock_errstr( so_errno, dummy, dummy )); + return -1; + } +#endif + if ( FD_ISSET(s, &wfds) ) { +#ifndef HAVE_WINSOCK + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } +#endif + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) { + return -1; + } + return 0; + } + } +#endif + + Debug0(LDAP_DEBUG_TRACE, "ldap_int_poll: timed out\n" ); + ldap_pvt_set_errno( ETIMEDOUT ); + return -1; +} + +static int +ldap_pvt_connect(LDAP *ld, ber_socket_t s, + struct sockaddr *sin, ber_socklen_t addrlen, + int async) +{ + int rc, err; + struct timeval tv, *opt_tv = NULL; + +#ifdef LDAP_CONNECTIONLESS + /* We could do a connect() but that would interfere with + * attempts to poll a broadcast address + */ + if (LDAP_IS_UDP(ld)) { + if (ld->ld_options.ldo_peer) + ldap_memfree(ld->ld_options.ldo_peer); + ld->ld_options.ldo_peer=ldap_memcalloc(1, sizeof(struct sockaddr_storage)); + AC_MEMCPY(ld->ld_options.ldo_peer,sin,addrlen); + return ( 0 ); + } +#endif + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_net; + opt_tv = &tv; + } + + Debug3(LDAP_DEBUG_TRACE, + "ldap_pvt_connect: fd: %d tm: %ld async: %d\n", + s, opt_tv ? tv.tv_sec : -1L, async); + + if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 ) + return ( -1 ); + + do{ + Debug0(LDAP_DEBUG_TRACE, "attempting to connect: \n" ); + if ( connect(s, sin, addrlen) != AC_SOCKET_ERROR ) { + Debug0(LDAP_DEBUG_TRACE, "connect success\n" ); + + if ( !async && opt_tv && ldap_pvt_ndelay_off(ld, s) == -1 ) + return ( -1 ); + return ( 0 ); + } + err = sock_errno(); + Debug1(LDAP_DEBUG_TRACE, "connect errno: %d\n", err ); + + } while(err == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART )); + + if ( err != EINPROGRESS && err != EWOULDBLOCK ) { + return ( -1 ); + } + + if ( async ) { + /* caller will call ldap_int_poll() as appropriate? */ + return ( -2 ); + } + + rc = ldap_int_poll( ld, s, opt_tv, 1 ); + + Debug1(LDAP_DEBUG_TRACE, "ldap_pvt_connect: %d\n", rc ); + + return rc; +} + +#ifndef HAVE_INET_ATON +int +ldap_pvt_inet_aton( const char *host, struct in_addr *in) +{ + unsigned long u = inet_addr( host ); + +#ifdef INADDR_NONE + if ( u == INADDR_NONE ) return 0; +#endif + if ( u == 0xffffffffUL || u == (unsigned long) -1L ) return 0; + + in->s_addr = u; + return 1; +} +#endif + +int +ldap_validate_and_fill_sourceip (char** source_ip_lst, ldapsourceip* temp_source_ip ) +{ + int i = 0; + int rc = LDAP_PARAM_ERROR; + + for ( i = 0; source_ip_lst[i] != NULL; i++ ) { + Debug1( LDAP_DEBUG_TRACE, + "ldap_validate_and_fill_sourceip(%s)\n", + source_ip_lst[i] ); + + if ( !temp_source_ip->has_ipv4 ) { + if ( inet_aton( source_ip_lst[i], &temp_source_ip->ip4_addr ) ) { + temp_source_ip->has_ipv4 = 1; + rc = LDAP_OPT_SUCCESS; + continue; + } + } +#ifdef LDAP_PF_INET6 + if ( !temp_source_ip->has_ipv6 ) { + if ( inet_pton( AF_INET6, source_ip_lst[i], + & temp_source_ip->ip6_addr ) ) { + temp_source_ip->has_ipv6 = 1; + rc = LDAP_OPT_SUCCESS; + continue; + } + } +#endif + memset( temp_source_ip, 0, sizeof( * (temp_source_ip ) ) ); + Debug1( LDAP_DEBUG_TRACE, + "ldap_validate_and_fill_sourceip: validation failed for (%s)\n", + source_ip_lst[i] ); + break; + } + return rc; +} + +int +ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr) +{ + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + int rc; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s ); + + /* Invoke all handle-specific callbacks first */ + lo = &ld->ld_options; + for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) { + cb = ll->ll_data; + rc = cb->lc_add( ld, sb, srv, addr, cb ); + /* on any failure, call the teardown functions for anything + * that previously succeeded + */ + if ( rc ) { + ldaplist *l2; + for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + /* a failure might have implicitly closed the fd */ + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s ); + return rc; + } + } + lo = LDAP_INT_GLOBAL_OPT(); + for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) { + cb = ll->ll_data; + rc = cb->lc_add( ld, sb, srv, addr, cb ); + if ( rc ) { + ldaplist *l2; + for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + lo = &ld->ld_options; + for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s ); + return rc; + } + } + return 0; +} + +int +ldap_connect_to_host(LDAP *ld, Sockbuf *sb, + int proto, LDAPURLDesc *srv, + int async ) +{ + int rc; + int socktype, port; + ber_socket_t s = AC_SOCKET_INVALID; + char *host; + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + char serv[7]; + int err; + struct addrinfo hints, *res, *sai; +#else + int i; + int use_hp = 0; + struct hostent *hp = NULL; + struct hostent he_buf; + struct in_addr in; + char *ha_buf=NULL; +#endif + + if ( srv->lud_host == NULL || *srv->lud_host == 0 ) { + host = "localhost"; + } else { + host = srv->lud_host; + } + + port = srv->lud_port; + + if( !port ) { + if( strcmp(srv->lud_scheme, "ldaps") == 0 ) { + port = LDAPS_PORT; + } else { + port = LDAP_PORT; + } + } + + switch(proto) { + case LDAP_PROTO_TCP: socktype = SOCK_STREAM; + Debug2(LDAP_DEBUG_TRACE, "ldap_connect_to_host: TCP %s:%d\n", + host, port ); + break; + case LDAP_PROTO_UDP: socktype = SOCK_DGRAM; + Debug2(LDAP_DEBUG_TRACE, "ldap_connect_to_host: UDP %s:%d\n", + host, port ); + break; + default: + Debug1(LDAP_DEBUG_TRACE, + "ldap_connect_to_host: unknown proto: %d\n", + proto ); + return -1; + } + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + memset( &hints, '\0', sizeof(hints) ); +#ifdef USE_AI_ADDRCONFIG /* FIXME: configure test needed */ + /* Use AI_ADDRCONFIG only on systems where its known to be needed. */ + hints.ai_flags = AI_ADDRCONFIG; +#endif + hints.ai_family = ldap_int_inet4or6; + hints.ai_socktype = socktype; + snprintf(serv, sizeof serv, "%d", port ); + + /* most getaddrinfo(3) use non-threadsafe resolver libraries */ + LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); + + err = getaddrinfo( host, serv, &hints, &res ); + + LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); + + if ( err != 0 ) { + Debug1(LDAP_DEBUG_TRACE, + "ldap_connect_to_host: getaddrinfo failed: %s\n", + AC_GAI_STRERROR(err) ); + return -1; + } + rc = -1; + + for( sai=res; sai != NULL; sai=sai->ai_next) { + unsigned short bind_success = 1; + if( sai->ai_addr == NULL ) { + Debug0(LDAP_DEBUG_TRACE, + "ldap_connect_to_host: getaddrinfo " + "ai_addr is NULL?\n" ); + continue; + } + +#ifndef LDAP_PF_INET6 + if ( sai->ai_family == AF_INET6 ) continue; +#endif + /* we assume AF_x and PF_x are equal for all x */ + s = ldap_int_socket( ld, sai->ai_family, socktype ); + if ( s == AC_SOCKET_INVALID ) { + continue; + } + + if ( ldap_int_prepare_socket(ld, s, proto ) == -1 ) { + ldap_pvt_close_socket(ld, s); + break; + } + + switch (sai->ai_family) { +#ifdef LDAP_PF_INET6 + case AF_INET6: { + char addr[INET6_ADDRSTRLEN]; + inet_ntop( AF_INET6, + &((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr, + addr, sizeof addr); + Debug2(LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Trying %s %s\n", + addr, serv ); + if( ld->ld_options.ldo_local_ip_addrs.has_ipv6 ) { + struct sockaddr_in6 ip6addr; + char bind_addr[INET6_ADDRSTRLEN]; + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = 0; + ip6addr.sin6_addr = ld->ld_options.ldo_local_ip_addrs.ip6_addr; + inet_ntop( AF_INET6, + &(ip6addr.sin6_addr), + bind_addr, sizeof bind_addr ); + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: From source address %s\n", + bind_addr ); + if ( bind( s, ( struct sockaddr* ) &ip6addr, sizeof ip6addr ) != 0 ) { + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Failed to bind source address %s\n", + bind_addr ); + bind_success = 0; + } + } + } break; +#endif + case AF_INET: { + char addr[INET_ADDRSTRLEN]; + inet_ntop( AF_INET, + &((struct sockaddr_in *)sai->ai_addr)->sin_addr, + addr, sizeof addr); + Debug2(LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Trying %s:%s\n", + addr, serv ); + if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) { + struct sockaddr_in ip4addr; + char bind_addr[INET_ADDRSTRLEN]; + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = 0; + ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr; + inet_ntop( AF_INET, + &(ip4addr.sin_addr), + bind_addr, sizeof bind_addr ); + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: From source address %s\n", + bind_addr ); + if ( bind(s, ( struct sockaddr* )&ip4addr, sizeof ip4addr ) != 0 ) { + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Failed to bind source address %s\n", + bind_addr ); + bind_success = 0; + } + } + } break; + } + if ( bind_success ) { + rc = ldap_pvt_connect( ld, s, + sai->ai_addr, sai->ai_addrlen, async ); + if ( rc == 0 || rc == -2 ) { + err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr ); + if ( err ) + rc = err; + else + break; + } + } + ldap_pvt_close_socket(ld, s); + } + freeaddrinfo(res); + +#else + if (! inet_aton( host, &in ) ) { + int local_h_errno; + rc = ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf, + &hp, &local_h_errno ); + + if ( (rc < 0) || (hp == NULL) ) { +#ifdef HAVE_WINSOCK + ldap_pvt_set_errno( WSAGetLastError() ); +#else + /* not exactly right, but... */ + ldap_pvt_set_errno( EHOSTUNREACH ); +#endif + if (ha_buf) LDAP_FREE(ha_buf); + return -1; + } + + use_hp = 1; + } + + rc = s = -1; + for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) { + struct sockaddr_in sin; + unsigned short bind_success = 1; +#ifdef HAVE_INET_NTOA_B + char address[INET_ADDR_LEN]; + char bind_addr[INET_ADDR_LEN]; +#else + char *address; + char *bind_addr; +#endif + s = ldap_int_socket( ld, PF_INET, socktype ); + if ( s == AC_SOCKET_INVALID ) { + /* use_hp ? continue : break; */ + break; + } + + if ( ldap_int_prepare_socket( ld, s, proto ) == -1 ) { + ldap_pvt_close_socket(ld, s); + break; + } + + (void)memset((char *)&sin, '\0', sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons((unsigned short) port); + + if( use_hp ) { + AC_MEMCPY( &sin.sin_addr, hp->h_addr_list[i], + sizeof(sin.sin_addr) ); + } else { + AC_MEMCPY( &sin.sin_addr, &in.s_addr, + sizeof(sin.sin_addr) ); + } + +#ifdef HAVE_INET_NTOA_B + /* for VxWorks */ + inet_ntoa_b( sin.sin_address, address ); +#else + address = inet_ntoa( sin.sin_addr ); +#endif + Debug2( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Trying %s:%d\n", + address, port ); + if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) { + struct sockaddr_in ip4addr; + ip4addr.sin_family = AF_INET; + ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr; +#ifdef HAVE_INET_NTOA_B + inet_ntoa_b( ip4addr.sin_address, bind_addr ); +#else + bind_addr = inet_ntoa( ip4addr.sin_addr ); +#endif + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: From source address %s\n", + bind_addr ); + if ( bind( s, (struct sockaddr*)&ip4addr, sizeof ip4addr ) != 0 ) { + Debug1( LDAP_DEBUG_TRACE, + "ldap_connect_to_host: Failed to bind source address %s\n", + bind_addr ); + bind_success = 0; + } + } + if ( bind_success ) { + rc = ldap_pvt_connect(ld, s, + (struct sockaddr *)&sin, sizeof(sin), + async); + + if ( (rc == 0) || (rc == -2) ) { + int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin ); + if ( err ) + rc = err; + else + break; + } + } + + ldap_pvt_close_socket(ld, s); + + if (!use_hp) break; + } + if (ha_buf) LDAP_FREE(ha_buf); +#endif + + return rc; +} + +#if defined( HAVE_CYRUS_SASL ) +char * +ldap_host_connected_to( Sockbuf *sb, const char *host ) +{ + ber_socklen_t len; +#ifdef LDAP_PF_INET6 + struct sockaddr_storage sabuf; +#else + struct sockaddr sabuf; +#endif + struct sockaddr *sa = (struct sockaddr *) &sabuf; + ber_socket_t sd; + + (void)memset( (char *)sa, '\0', sizeof sabuf ); + len = sizeof sabuf; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + if ( getpeername( sd, sa, &len ) == -1 ) { + return( NULL ); + } + + /* + * do a reverse lookup on the addr to get the official hostname. + * this is necessary for kerberos to work right, since the official + * hostname is used as the kerberos instance. + */ + + switch (sa->sa_family) { +#ifdef LDAP_PF_LOCAL + case AF_LOCAL: + return LDAP_STRDUP( ldap_int_hostname ); +#endif +#ifdef LDAP_PF_INET6 + case AF_INET6: + { + struct in6_addr localhost = IN6ADDR_LOOPBACK_INIT; + if( memcmp ( &((struct sockaddr_in6 *)sa)->sin6_addr, + &localhost, sizeof(localhost)) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } + } + break; +#endif + case AF_INET: + { + struct in_addr localhost; + localhost.s_addr = htonl( INADDR_ANY ); + + if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr, + &localhost, sizeof(localhost) ) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } + +#ifdef INADDR_LOOPBACK + localhost.s_addr = htonl( INADDR_LOOPBACK ); + + if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr, + &localhost, sizeof(localhost) ) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } +#endif + } + break; + + default: + return( NULL ); + break; + } + + { + char *herr; +#ifdef NI_MAXHOST + char hbuf[NI_MAXHOST]; +#elif defined( MAXHOSTNAMELEN ) + char hbuf[MAXHOSTNAMELEN]; +#else + char hbuf[256]; +#endif + hbuf[0] = 0; + + if (ldap_pvt_get_hname( sa, len, hbuf, sizeof(hbuf), &herr ) == 0 + && hbuf[0] ) + { + return LDAP_STRDUP( hbuf ); + } + } + + return host ? LDAP_STRDUP( host ) : NULL; +} +#endif + + +struct selectinfo { +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + int si_maxfd; + struct pollfd si_fds[FD_SETSIZE]; +#else + /* for UNIX select(2) */ + fd_set si_readfds; + fd_set si_writefds; + fd_set si_use_readfds; + fd_set si_use_writefds; +#endif +}; + +void +ldap_mark_select_write( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int empty=-1; + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events |= POLL_WRITE; + return; + } + if( empty==-1 && sip->si_fds[i].fd == -1 ) { + empty=i; + } + } + + if( empty == -1 ) { + if( sip->si_maxfd >= FD_SETSIZE ) { + /* FIXME */ + return; + } + empty = sip->si_maxfd++; + } + + sip->si_fds[empty].fd = sd; + sip->si_fds[empty].events = POLL_WRITE; + } +#else + /* for UNIX select(2) */ + if ( !FD_ISSET( sd, &sip->si_writefds )) { + FD_SET( sd, &sip->si_writefds ); + } +#endif +} + + +void +ldap_mark_select_read( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int empty=-1; + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events |= POLL_READ; + return; + } + if( empty==-1 && sip->si_fds[i].fd == -1 ) { + empty=i; + } + } + + if( empty == -1 ) { + if( sip->si_maxfd >= FD_SETSIZE ) { + /* FIXME */ + return; + } + empty = sip->si_maxfd++; + } + + sip->si_fds[empty].fd = sd; + sip->si_fds[empty].events = POLL_READ; + } +#else + /* for UNIX select(2) */ + if ( !FD_ISSET( sd, &sip->si_readfds )) { + FD_SET( sd, &sip->si_readfds ); + } +#endif +} + + +void +ldap_mark_select_clear( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].fd = -1; + } + } + } +#else + /* for UNIX select(2) */ + FD_CLR( sd, &sip->si_writefds ); + FD_CLR( sd, &sip->si_readfds ); +#endif +} + +void +ldap_clear_select_write( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events &= ~POLL_WRITE; + } + } + } +#else + /* for UNIX select(2) */ + FD_CLR( sd, &sip->si_writefds ); +#endif +} + + +int +ldap_is_write_ready( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + return sip->si_fds[i].revents & POLL_WRITE; + } + } + + return 0; + } +#else + /* for UNIX select(2) */ + return( FD_ISSET( sd, &sip->si_use_writefds )); +#endif +} + + +int +ldap_is_read_ready( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + if (ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL )) + return 1; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + return sip->si_fds[i].revents & POLL_READ; + } + } + + return 0; + } +#else + /* for UNIX select(2) */ + return( FD_ISSET( sd, &sip->si_use_readfds )); +#endif +} + + +void * +ldap_new_select_info( void ) +{ + struct selectinfo *sip; + + sip = (struct selectinfo *)LDAP_CALLOC( 1, sizeof( struct selectinfo )); + + if ( sip == NULL ) return NULL; + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + /* sip->si_maxfd=0 */ +#else + /* for UNIX select(2) */ + FD_ZERO( &sip->si_readfds ); + FD_ZERO( &sip->si_writefds ); +#endif + + return( (void *)sip ); +} + + +void +ldap_free_select_info( void *sip ) +{ + LDAP_FREE( sip ); +} + + +#ifndef HAVE_POLL +int ldap_int_tblsize = 0; + +void +ldap_int_ip_init( void ) +{ +#if defined( HAVE_SYSCONF ) + long tblsize = sysconf( _SC_OPEN_MAX ); + if( tblsize > INT_MAX ) tblsize = INT_MAX; + +#elif defined( HAVE_GETDTABLESIZE ) + int tblsize = getdtablesize(); +#else + int tblsize = FD_SETSIZE; +#endif /* !USE_SYSCONF */ + +#ifdef FD_SETSIZE + if( tblsize > FD_SETSIZE ) tblsize = FD_SETSIZE; +#endif /* FD_SETSIZE */ + + ldap_int_tblsize = tblsize; +} +#endif + + +int +ldap_int_select( LDAP *ld, struct timeval *timeout ) +{ + int rc; + struct selectinfo *sip; + + Debug0( LDAP_DEBUG_TRACE, "ldap_int_select\n" ); + +#ifndef HAVE_POLL + if ( ldap_int_tblsize == 0 ) ldap_int_ip_init(); +#endif + + sip = (struct selectinfo *)ld->ld_selectinfo; + assert( sip != NULL ); + +#ifdef HAVE_POLL + { + int to = timeout ? TV2MILLISEC( timeout ) : INFTIM; + rc = poll( sip->si_fds, sip->si_maxfd, to ); + } +#else + sip->si_use_readfds = sip->si_readfds; + sip->si_use_writefds = sip->si_writefds; + + rc = select( ldap_int_tblsize, + &sip->si_use_readfds, &sip->si_use_writefds, + NULL, timeout ); +#endif + + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/os-local.c b/contrib/libs/openldap/libraries/libldap/os-local.c new file mode 100644 index 0000000000..8b31030957 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/os-local.c @@ -0,0 +1,351 @@ +/* os-local.c -- platform-specific domain socket code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* Portions (C) Copyright PADL Software Pty Ltd. 1999 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is preserved + * and that due credit is given to PADL Software Pty Ltd. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include "portable.h" + +#ifdef LDAP_PF_LOCAL + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "ldap-int.h" +#include "ldap_defaults.h" + +static void +ldap_pvt_set_errno(int err) +{ + errno = err; +} + +static int +ldap_pvt_ndelay_on(LDAP *ld, int fd) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_on: %d\n",fd ); + return ber_pvt_socket_set_nonblock( fd, 1 ); +} + +static int +ldap_pvt_ndelay_off(LDAP *ld, int fd) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_off: %d\n",fd ); + return ber_pvt_socket_set_nonblock( fd, 0 ); +} + +static ber_socket_t +ldap_pvt_socket(LDAP *ld) +{ + ber_socket_t s = socket(PF_LOCAL, SOCK_STREAM, 0); + Debug1(LDAP_DEBUG_TRACE, "ldap_new_socket: %d\n",s ); +#ifdef FD_CLOEXEC + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + return ( s ); +} + +static int +ldap_pvt_close_socket(LDAP *ld, int s) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_close_socket: %d\n",s ); + return tcp_close(s); +} + +#undef TRACE +#define TRACE do { \ + char ebuf[128]; \ + int saved_errno = errno; \ + Debug3(LDAP_DEBUG_TRACE, "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \ + s, \ + saved_errno, \ + AC_STRERROR_R(saved_errno, ebuf, sizeof ebuf)); \ +} while( 0 ) + +/* + * check the socket for errors after select returned. + */ +static int +ldap_pvt_is_socket_ready(LDAP *ld, int s) +{ + Debug1(LDAP_DEBUG_TRACE, "ldap_is_sock_ready: %d\n",s ); + +#if defined( notyet ) /* && defined( SO_ERROR ) */ +{ + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy ) + == AC_SOCKET_ERROR ) + { + return -1; + } + if ( so_errno ) { + ldap_pvt_set_errno(so_errno); + TRACE; + return -1; + } + return 0; +} +#else +{ + /* error slippery */ + struct sockaddr_un sa; + char ch; + ber_socklen_t dummy = sizeof(sa); + if ( getpeername( s, (struct sockaddr *) &sa, &dummy ) + == AC_SOCKET_ERROR ) + { + /* XXX: needs to be replace with ber_stream_read() */ + (void)read(s, &ch, 1); + TRACE; + return -1; + } + return 0; +} +#endif + return -1; +} +#undef TRACE + +#ifdef LDAP_PF_LOCAL_SENDMSG +static const char abandonPDU[] = {LDAP_TAG_MESSAGE, 6, + LDAP_TAG_MSGID, 1, 0, LDAP_REQ_ABANDON, 1, 0}; +#endif + +static int +ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_un *sa, int async) +{ + int rc; + struct timeval tv, *opt_tv = NULL; + + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_net; + opt_tv = &tv; + } + + Debug3(LDAP_DEBUG_TRACE, + "ldap_connect_timeout: fd: %d tm: %ld async: %d\n", + s, opt_tv ? tv.tv_sec : -1L, async); + + if ( ldap_pvt_ndelay_on(ld, s) == -1 ) return -1; + + if ( connect(s, (struct sockaddr *) sa, sizeof(struct sockaddr_un)) + != AC_SOCKET_ERROR ) + { + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; + +#ifdef LDAP_PF_LOCAL_SENDMSG + /* Send a dummy message with access rights. Remote side will + * obtain our uid/gid by fstat'ing this descriptor. The + * descriptor permissions must match exactly, and we also + * send the socket name, which must also match. + */ +sendcred: + { + int fds[2]; + ber_socklen_t salen = sizeof(*sa); + if (pipe(fds) == 0) { + /* Abandon, noop, has no reply */ + struct iovec iov; + struct msghdr msg = {0}; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + _CMSG_ALIGN(len) ) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + (len) ) +# endif + union { + struct cmsghdr cm; + unsigned char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmsg; +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + msg.msg_name = NULL; + msg.msg_namelen = 0; + iov.iov_base = (char *) abandonPDU; + iov.iov_len = sizeof abandonPDU; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof( control_un.control ); + msg.msg_flags = 0; + + cmsg = CMSG_FIRSTHDR( &msg ); + cmsg->cmsg_len = CMSG_LEN( sizeof(int) ); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + *((int *)CMSG_DATA(cmsg)) = fds[0]; +# else + msg.msg_accrights = (char *)fds; + msg.msg_accrightslen = sizeof(int); +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + getpeername( s, (struct sockaddr *) sa, &salen ); + fchmod( fds[0], S_ISUID|S_IRWXU ); + write( fds[1], sa, salen ); + sendmsg( s, &msg, 0 ); + close(fds[0]); + close(fds[1]); + } + } +#endif + return 0; + } + + if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) return -1; + +#ifdef notyet + if ( async ) return -2; +#endif + +#ifdef HAVE_POLL + { + struct pollfd fd; + int timeout = INFTIM; + + if( opt_tv != NULL ) timeout = TV2MILLISEC( &tv ); + + fd.fd = s; + fd.events = POLL_WRITE; + + do { + fd.revents = 0; + rc = poll( &fd, 1, timeout ); + } while( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); + + if( rc == AC_SOCKET_ERROR ) return rc; + + if( fd.revents & POLL_WRITE ) { + if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; +#ifdef LDAP_PF_LOCAL_SENDMSG + goto sendcred; +#else + return ( 0 ); +#endif + } + } +#else + { + fd_set wfds, *z=NULL; + +#ifdef FD_SETSIZE + if ( s >= FD_SETSIZE ) { + rc = AC_SOCKET_ERROR; + tcp_close( s ); + ldap_pvt_set_errno( EMFILE ); + return rc; + } +#endif + do { + FD_ZERO(&wfds); + FD_SET(s, &wfds ); + rc = select( ldap_int_tblsize, z, &wfds, z, opt_tv ? &tv : NULL ); + } while( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); + + if( rc == AC_SOCKET_ERROR ) return rc; + + if ( FD_ISSET(s, &wfds) ) { + if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; +#ifdef LDAP_PF_LOCAL_SENDMSG + goto sendcred; +#else + return ( 0 ); +#endif + } + } +#endif + + Debug0(LDAP_DEBUG_TRACE, "ldap_connect_timeout: timed out\n" ); + ldap_pvt_set_errno( ETIMEDOUT ); + return ( -1 ); +} + +int +ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async) +{ + struct sockaddr_un server; + ber_socket_t s; + int rc; + const char *path = srv->lud_host; + + Debug0(LDAP_DEBUG_TRACE, "ldap_connect_to_path\n" ); + + if ( path == NULL || path[0] == '\0' ) { + path = LDAPI_SOCK; + } else { + if ( strlen(path) > (sizeof( server.sun_path ) - 1) ) { + ldap_pvt_set_errno( ENAMETOOLONG ); + return -1; + } + } + + s = ldap_pvt_socket( ld ); + if ( s == AC_SOCKET_INVALID ) { + return -1; + } + + Debug1(LDAP_DEBUG_TRACE, "ldap_connect_to_path: Trying %s\n", path ); + + memset( &server, '\0', sizeof(server) ); + server.sun_family = AF_LOCAL; + strcpy( server.sun_path, path ); + + rc = ldap_pvt_connect(ld, s, &server, async); + + if (rc == 0) { + rc = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server ); + } + if ( rc ) { + ldap_pvt_close_socket(ld, s); + } + return rc; +} +#else +static int dummy; /* generate also a warning: 'dummy' defined but not used (at least here) */ +#endif /* LDAP_PF_LOCAL */ diff --git a/contrib/libs/openldap/libraries/libldap/pagectrl.c b/contrib/libs/openldap/libraries/libldap/pagectrl.c new file mode 100644 index 0000000000..c7a1499b41 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/pagectrl.c @@ -0,0 +1,271 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Copyright 2006 Hans Leidekker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* --------------------------------------------------------------------------- + ldap_create_page_control_value + + Create and encode the value of the paged results control (RFC 2696). + + ld (IN) An LDAP session handle + pagesize (IN) Page size requested + cookie (IN) Opaque structure used by the server to track its + location in the search results. NULL on the + first call. + value (OUT) Control value, SHOULD be freed by calling + ldap_memfree() when done. + + pagedResultsControl ::= SEQUENCE { + controlType 1.2.840.113556.1.4.319, + criticality BOOLEAN DEFAULT FALSE, + controlValue searchControlValue } + + searchControlValue ::= SEQUENCE { + size INTEGER (0..maxInt), + -- requested page size from client + -- result set size estimate from server + cookie OCTET STRING } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_page_control_value( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + struct berval null_cookie = { 0, NULL }; + + if ( ld == NULL || value == NULL || + pagesize < 1 || pagesize > LDAP_MAXINT ) + { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + if ( cookie == NULL ) { + cookie = &null_cookie; + } + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{iO}", pagesize, cookie ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_page_control + + Create and encode a page control. + + ld (IN) An LDAP session handle + pagesize (IN) Page size requested + cookie (IN) Opaque structure used by the server to track its + location in the search results. NULL on the + first call. + value (OUT) Control value, SHOULD be freed by calling + ldap_memfree() when done. + iscritical (IN) Criticality + ctrlp (OUT) LDAP control, SHOULD be freed by calling + ldap_control_free() when done. + + pagedResultsControl ::= SEQUENCE { + controlType 1.2.840.113556.1.4.319, + criticality BOOLEAN DEFAULT FALSE, + controlValue searchControlValue } + + searchControlValue ::= SEQUENCE { + size INTEGER (0..maxInt), + -- requested page size from client + -- result set size estimate from server + cookie OCTET STRING } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_page_control( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_page_control_value( ld, + pagesize, cookie, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_pageresponse_control + + Decode a page control. + + ld (IN) An LDAP session handle + ctrl (IN) The page response control + count (OUT) The number of entries in the page. + cookie (OUT) Opaque cookie. Use ldap_memfree() to + free the bv_val member of this structure. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_pageresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *countp, + struct berval *cookie ) +{ + BerElement *ber; + ber_tag_t tag; + ber_int_t count; + + if ( ld == NULL || ctrl == NULL || cookie == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + /* Extract the count and cookie from the control. */ + tag = ber_scanf( ber, "{io}", &count, cookie ); + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + } else { + ld->ld_errno = LDAP_SUCCESS; + + if ( countp != NULL ) { + *countp = (unsigned long)count; + } + } + + return ld->ld_errno; +} + +/* --------------------------------------------------------------------------- + ldap_parse_page_control + + Decode a page control. + + ld (IN) An LDAP session handle + ctrls (IN) Response controls + count (OUT) The number of entries in the page. + cookie (OUT) Opaque cookie. Use ldap_memfree() to + free the bv_val member of this structure. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_page_control( + LDAP *ld, + LDAPControl **ctrls, + ber_int_t *countp, + struct berval **cookiep ) +{ + LDAPControl *c; + struct berval cookie; + + if ( cookiep == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if ( ctrls == NULL ) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL ); + if ( c == NULL ) { + /* No page control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + ld->ld_errno = ldap_parse_pageresponse_control( ld, c, countp, &cookie ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + *cookiep = LDAP_MALLOC( sizeof( struct berval ) ); + if ( *cookiep == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + } else { + **cookiep = cookie; + } + } + + return ld->ld_errno; +} + diff --git a/contrib/libs/openldap/libraries/libldap/passwd.c b/contrib/libs/openldap/libraries/libldap/passwd.c new file mode 100644 index 0000000000..f529de5f32 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/passwd.c @@ -0,0 +1,170 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * LDAP Password Modify (Extended) Operation (RFC 3062) + */ + +int ldap_parse_passwd( + LDAP *ld, + LDAPMessage *res, + struct berval *newpasswd ) +{ + int rc; + struct berval *retdata = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( newpasswd != NULL ); + + newpasswd->bv_val = NULL; + newpasswd->bv_len = 0; + + rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( retdata != NULL ) { + ber_tag_t tag; + BerElement *ber = ber_init( retdata ); + + if ( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + /* we should check the tag */ + tag = ber_scanf( ber, "{o}", newpasswd ); + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + } + +done:; + ber_bvfree( retdata ); + + return rc; +} + +int +ldap_passwd( LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + struct berval bv = BER_BVNULL; + BerElement *ber = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + if( user != NULL || oldpw != NULL || newpw != NULL ) { + /* build change password control */ + ber = ber_alloc_t( LBER_USE_DER ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ber_printf( ber, "{" /*}*/ ); + + if( user != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user ); + } + + if( oldpw != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldpw ); + } + + if( newpw != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newpw ); + } + + ber_printf( ber, /*{*/ "N}" ); + + rc = ber_flatten2( ber, &bv, 0 ); + + if( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_MODIFY_PASSWD, + bv.bv_val ? &bv : NULL, sctrls, cctrls, msgidp ); + + ber_free( ber, 1 ); + + return rc; +} + +int +ldap_passwd_s( + LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + struct berval *newpasswd, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_passwd( ld, user, oldpw, newpw, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return ld->ld_errno; + } + + rc = ldap_parse_passwd( ld, res, newpasswd ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/contrib/libs/openldap/libraries/libldap/ppolicy.c b/contrib/libs/openldap/libraries/libldap/ppolicy.c new file mode 100644 index 0000000000..dc93209645 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/ppolicy.c @@ -0,0 +1,257 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2004-2022 The OpenLDAP Foundation. + * Portions Copyright 2004 Hewlett-Packard Company. + * Portions Copyright 2004 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software, based on prior work by Neil Dunbar (HP). + * This work was sponsored by the Hewlett-Packard Company. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + +/* IMPLICIT TAGS, all context-specific */ +#define PPOLICY_WARNING 0xa0L /* constructed + 0 */ +#define PPOLICY_ERROR 0x81L /* primitive + 1 */ + +#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */ +#define PPOLICY_GRACE 0x81L /* primitive + 1 */ + +/*--- + ldap_create_passwordpolicy_control + + Create and encode the Password Policy Request + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrlp (OUT) A result parameter that will be assigned the address + of an LDAPControl structure that contains the + passwordPolicyRequest control created by this function. + The memory occupied by the LDAPControl structure + SHOULD be freed when it is no longer in use by + calling ldap_control_free(). + + + There is no control value for a password policy request + ---*/ + +int +ldap_create_passwordpolicy_control( LDAP *ld, + LDAPControl **ctrlp ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrlp != NULL ); + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST, + 0, NULL, 0, ctrlp ); + + return ld->ld_errno; +} + + +/*--- + ldap_parse_passwordpolicy_control + + Decode the passwordPolicyResponse control and return information. + + ld (IN) An LDAP session handle. + + ctrl (IN) The address of an + LDAPControl structure, either obtained + by running through the list of response controls or + by a call to ldap_control_find(). + + exptimep (OUT) This result parameter is filled in with the number of seconds before + the password will expire, if expiration is imminent + (imminency defined by the password policy). If expiration + is not imminent, the value is set to -1. + + gracep (OUT) This result parameter is filled in with the number of grace logins after + the password has expired, before no further login attempts + will be allowed. + + errorcodep (OUT) This result parameter is filled in with the error code of the password operation + If no error was detected, this error is set to PP_noError. + + Ber encoding + + PasswordPolicyResponseValue ::= SEQUENCE { + warning [0] CHOICE { + timeBeforeExpiration [0] INTEGER (0 .. maxInt), + graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL + error [1] ENUMERATED { + passwordExpired (0), + accountLocked (1), + changeAfterReset (2), + passwordModNotAllowed (3), + mustSupplyOldPassword (4), + invalidPasswordSyntax (5), + passwordTooShort (6), + passwordTooYoung (7), + passwordInHistory (8) } OPTIONAL } + +---*/ + +int +ldap_parse_passwordpolicy_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *expirep, + ber_int_t *gracep, + LDAPPasswordPolicyError *errorp ) +{ + BerElement *ber; + int exp = -1, grace = -1; + ber_tag_t tag; + ber_len_t berLen; + char *last; + int err = PP_noError; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if ( !ctrl->ldctl_value.bv_val ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + tag = ber_peek_tag( ber, &berLen ); + if (tag != LBER_SEQUENCE) goto exit; + + for( tag = ber_first_element( ber, &berLen, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &berLen, last ) ) + { + switch (tag) { + case PPOLICY_WARNING: + ber_skip_tag(ber, &berLen ); + tag = ber_peek_tag( ber, &berLen ); + switch( tag ) { + case PPOLICY_EXPIRE: + if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit; + break; + case PPOLICY_GRACE: + if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit; + break; + default: + goto exit; + } + break; + case PPOLICY_ERROR: + if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit; + break; + default: + goto exit; + } + } + + ber_free(ber, 1); + + /* Return data to the caller for items that were requested. */ + if (expirep) *expirep = exp; + if (gracep) *gracep = grace; + if (errorp) *errorp = err; + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); + + exit: + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); +} + +const char * +ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err ) +{ + switch(err) { + case PP_passwordExpired: return "Password expired"; + case PP_accountLocked: return "Account locked"; + case PP_changeAfterReset: return "Password must be changed"; + case PP_passwordModNotAllowed: return "Policy prevents password modification"; + case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password"; + case PP_insufficientPasswordQuality: return "Password fails quality checks"; + case PP_passwordTooShort: return "Password is too short for policy"; + case PP_passwordTooYoung: return "Password has been changed too recently"; + case PP_passwordInHistory: return "New password is in list of old passwords"; + case PP_passwordTooLong: return "Password is too long for policy"; + case PP_noError: return "No error"; + default: return "Unknown error code"; + } +} + +#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ + +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING + +int +ldap_parse_password_expiring_control( + LDAP *ld, + LDAPControl *ctrl, + long *secondsp ) +{ + long seconds = 0; + char buf[sizeof("-2147483648")]; + char *next; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if ( BER_BVISEMPTY( &ctrl->ldctl_value ) || + ctrl->ldctl_value.bv_len >= sizeof(buf) ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len ); + buf[ctrl->ldctl_value.bv_len] = '\0'; + + seconds = strtol( buf, &next, 10 ); + if ( next == buf || next[0] != '\0' ) goto exit; + + if ( secondsp != NULL ) { + *secondsp = seconds; + } + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); + + exit: + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); +} + +#endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */ diff --git a/contrib/libs/openldap/libraries/libldap/print.c b/contrib/libs/openldap/libraries/libldap/print.c new file mode 100644 index 0000000000..6f8681a43c --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/print.c @@ -0,0 +1,62 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * ldap log + */ + +static int ldap_log_check( LDAP *ld, int loglvl ) +{ + int errlvl; + + if(ld == NULL) { + errlvl = ldap_debug; + } else { + errlvl = ld->ld_debug; + } + + return errlvl & loglvl ? 1 : 0; +} + +int ldap_log_printf( LDAP *ld, int loglvl, const char *fmt, ... ) +{ + char buf[ 1024 ]; + va_list ap; + + if ( !ldap_log_check( ld, loglvl )) { + return 0; + } + + va_start( ap, fmt ); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + (*ber_pvt_log_print)( buf ); + return 1; +} diff --git a/contrib/libs/openldap/libraries/libldap/psearchctrl.c b/contrib/libs/openldap/libraries/libldap/psearchctrl.c new file mode 100644 index 0000000000..b465873809 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/psearchctrl.c @@ -0,0 +1,348 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* Based on draft-ietf-ldapext-c-api-psearch-00 */ + +/* --------------------------------------------------------------------------- + ldap_create_persistentsearch_control_value + + Create and encode the value of the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + changetypes (IN) A bit-sensitive field that indicates which kinds of + changes the client wants to be informed about. Its + value should be LDAP_CHANGETYPE_ANY, or any logical-OR + combination of LDAP_CHANGETYPE_ADD, + LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and + LDAP_CHANGETYPE_MODDN. This field corresponds to the + changeType element of the BER-encoded PersistentSearch + control value itself. + + changesonly (IN) A Boolean field that indicates whether the client + wishes to only receive searchResultEntry messages for + entries that have been changed. If non-zero, only + entries that result from changes are returned; other- + wise, all of the static entries that match the search + criteria are returned before the server begins change + notification. This field corresponds to the changes- + Only element of the BER-encoded PersistentSearch con- + trol value itself. + + return_echg_ctls (IN) A Boolean field that indicates whether the server + should send back an Entry Change Notification control + with each searchResultEntry that is returned due to a + change to an entry. If non-zero, Entry Change + Notification controls are requested; if zero, they are + not. This field corresponds to the returnECs element + of the BER-encoded PersistentSearch control value + itself. + + value (OUT) Contains the control value; the bv_val member of the berval structure + SHOULD be freed by calling ldap_memfree() when done. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_persistentsearch_control_value( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + struct berval *value ) +{ + int i; + BerElement *ber = NULL; + ber_tag_t tag; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) return LDAP_PARAM_ERROR; + if ( value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + if (( changetypes & 0x0f ) != changetypes ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_persistentsearch_control + + Create and encode the persistent search control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + changetypes (IN) A bit-sensitive field that indicates which kinds of + changes the client wants to be informed about. Its + value should be LDAP_CHANGETYPE_ANY, or any logical-OR + combination of LDAP_CHANGETYPE_ADD, + LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and + LDAP_CHANGETYPE_MODDN. This field corresponds to the + changeType element of the BER-encoded PersistentSearch + control value itself. + + changesonly (IN) A Boolean field that indicates whether the client + wishes to only receive searchResultEntry messages for + entries that have been changed. If non-zero, only + entries that result from changes are returned; other- + wise, all of the static entries that match the search + criteria are returned before the server begins change + notification. This field corresponds to the changes- + Only element of the BER-encoded PersistentSearch con- + trol value itself. + + return_echg_ctls (IN) A Boolean field that indicates whether the server + should send back an Entry Change Notification control + with each searchResultEntry that is returned due to a + change to an entry. If non-zero, Entry Change + Notification controls are requested; if zero, they are + not. This field corresponds to the returnECs element + of the BER-encoded PersistentSearch control value + itself. + + isCritical (IN) 0 - Indicates the control is not critical to the operation. + non-zero - The control is critical to the operation. + + ctrlp (OUT) Returns a pointer to the LDAPControl created. This control + SHOULD be freed by calling ldap_control_free() when done. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_persistentsearch_control( + LDAP *ld, + int changetypes, + int changesonly, + int return_echg_ctls, + int isCritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) { + return LDAP_PARAM_ERROR; + } + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST, + isCritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_entrychange_control + + Decode the entry change notification control return information. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrl (IN) The address of the LDAP Control Structure. + + chgtypep (OUT) This result parameter is filled in with one of the + following values to indicate the type of change that was + made that caused the entry to be returned: + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1), + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2), + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or + LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8). + If this parameter is NULL, the change type information + is not returned. + + prevdnp (OUT) This result parameter points to the DN the + entry had before it was renamed and/or moved by a + modifyDN operation. It is set to NULL for other types + of changes. If this parameter is NULL, the previous DN + information is not returned. The returned value is a + pointer to the contents of the control; it is not a + copy of the data. + + chgnumpresentp (OUT) This result parameter is filled in with a non-zero + value if a change number was returned in the control + (the change number is optional and servers MAY choose + not to return it). If this parameter is NULL, no indication + of whether the change number was present is returned. + + chgnump (OUT) This result parameter is filled in with the change number + if one was returned in the control. If this parameter + is NULL, the change number is not returned. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_entrychange_control( + LDAP *ld, + LDAPControl *ctrl, + int *chgtypep, + struct berval *prevdnp, + int *chgnumpresentp, + long *chgnump ) +{ + BerElement *ber; + ber_tag_t tag, berTag; + ber_len_t berLen; + ber_int_t chgtype; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if (ld == NULL) { + return LDAP_PARAM_ERROR; + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if ( !ctrl->ldctl_value.bv_val ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + if ( prevdnp != NULL ) { + BER_BVZERO( prevdnp ); + } + if ( chgnumpresentp != NULL ) + *chgnumpresentp = 0; + if ( chgnump != NULL ) + *chgnump = 0; + + /* Extract the change type from the control. */ + tag = ber_scanf(ber, "{e" /*}*/, &chgtype); + + if( tag != LBER_ENUMERATED ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + if ( chgtypep != NULL ) + *chgtypep = chgtype; + + tag = ber_peek_tag( ber, &berLen ); + if ( berLen ) { + if (tag == LBER_OCTETSTRING) { + if (prevdnp != NULL) { + tag = ber_get_stringbv( ber, prevdnp, 0 ); + } else { + struct berval bv; + tag = ber_skip_element( ber, &bv ); + } + if ( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + tag = ber_peek_tag( ber, &berLen ); + } + + if ( chgnumpresentp != NULL || chgnump != NULL ) { + ber_int_t chgnum = 0; + int present = 0; + if (tag == LBER_INTEGER) { + present = 1; + tag = ber_get_int( ber, &chgnum ); + if ( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + if ( chgnumpresentp != NULL ) + *chgnumpresentp = present; + if ( chgnump != NULL ) + *chgnump = chgnum; + } + } + } + + ber_free(ber,1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/contrib/libs/openldap/libraries/libldap/rdwr.c b/contrib/libs/openldap/libraries/libldap/rdwr.c new file mode 100644 index 0000000000..dde9403d83 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/rdwr.c @@ -0,0 +1,463 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. Additional significant contributors include: + * Stuart Lynne + */ + +/* + * This is an improved implementation of Reader/Writer locks does + * not protect writers from starvation. That is, if a writer is + * currently waiting on a reader, any new reader will get + * the lock before the writer. + * + * Does not support cancellation nor does any status checking. + */ +/* Adapted from publicly available examples for: + * "Programming with Posix Threads" + * by David R Butenhof, Addison-Wesley + * http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2 + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_R_COMPILE + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +/* + * implementations that provide their own compatible + * reader/writer locks define LDAP_THREAD_HAVE_RDWR + * in ldap_pvt_thread.h + */ +#ifndef LDAP_THREAD_HAVE_RDWR + +struct ldap_int_thread_rdwr_s { + ldap_pvt_thread_mutex_t ltrw_mutex; + ldap_pvt_thread_cond_t ltrw_read; /* wait for read */ + ldap_pvt_thread_cond_t ltrw_write; /* wait for write */ + int ltrw_valid; +#define LDAP_PVT_THREAD_RDWR_VALID 0x0bad + int ltrw_r_active; + int ltrw_w_active; + int ltrw_r_wait; + int ltrw_w_wait; +#ifdef LDAP_RDWR_DEBUG + /* keep track of who has these locks */ +#define MAX_READERS 32 + int ltrw_more_readers; /* Set if ltrw_readers[] is incomplete */ + ldap_pvt_thread_t ltrw_readers[MAX_READERS]; + ldap_pvt_thread_t ltrw_writer; +#endif +}; + +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + + rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1, + sizeof( struct ldap_int_thread_rdwr_s ) ); + if ( !rw ) + return LDAP_NO_MEMORY; + + /* we should check return results */ + ldap_pvt_thread_mutex_init( &rw->ltrw_mutex ); + ldap_pvt_thread_cond_init( &rw->ltrw_read ); + ldap_pvt_thread_cond_init( &rw->ltrw_write ); + + rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID; + + *rwlock = rw; + return 0; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + /* active threads? */ + if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + + /* waiting threads? */ + if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + + rw->ltrw_valid = 0; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex ); + ldap_pvt_thread_cond_destroy( &rw->ltrw_read ); + ldap_pvt_thread_cond_destroy( &rw->ltrw_write ); + + LDAP_FREE(rw); + *rwlock = NULL; + return 0; +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if( rw->ltrw_w_active > 0 ) { + /* writer is active */ + + rw->ltrw_r_wait++; + + do { + ldap_pvt_thread_cond_wait( + &rw->ltrw_read, &rw->ltrw_mutex ); + } while( rw->ltrw_w_active > 0 ); + + rw->ltrw_r_wait--; + assert( rw->ltrw_r_wait >= 0 ); + } + +#ifdef LDAP_RDWR_DEBUG + if( rw->ltrw_r_active < MAX_READERS ) + rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self(); + else + rw->ltrw_more_readers = 1; +#endif + rw->ltrw_r_active++; + + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if( rw->ltrw_w_active > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + +#ifdef LDAP_RDWR_DEBUG + if( rw->ltrw_r_active < MAX_READERS ) + rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self(); + else + rw->ltrw_more_readers = 1; +#endif + rw->ltrw_r_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + rw->ltrw_r_active--; +#ifdef LDAP_RDWR_DEBUG + /* Remove us from the list of readers */ + { + ldap_pvt_thread_t self = ldap_pvt_thread_self(); + int i, j; + for( i = j = rw->ltrw_r_active; i >= 0; i--) { + if (rw->ltrw_readers[i] == self) { + rw->ltrw_readers[i] = rw->ltrw_readers[j]; + rw->ltrw_readers[j] = 0; + break; + } + } + if( !rw->ltrw_more_readers ) + assert( i >= 0 ); + else if( j == 0 ) + rw->ltrw_more_readers = 0; + } +#endif + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) { + ldap_pvt_thread_cond_signal( &rw->ltrw_write ); + } + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) { + rw->ltrw_w_wait++; + + do { + ldap_pvt_thread_cond_wait( + &rw->ltrw_write, &rw->ltrw_mutex ); + } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ); + + rw->ltrw_w_wait--; + assert( rw->ltrw_w_wait >= 0 ); + } + +#ifdef LDAP_RDWR_DEBUG + rw->ltrw_writer = ldap_pvt_thread_self(); +#endif + rw->ltrw_w_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + +#ifdef LDAP_RDWR_DEBUG + rw->ltrw_writer = ldap_pvt_thread_self(); +#endif + rw->ltrw_w_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + rw->ltrw_w_active--; + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if (rw->ltrw_r_wait > 0) { + ldap_pvt_thread_cond_broadcast( &rw->ltrw_read ); + + } else if (rw->ltrw_w_wait > 0) { + ldap_pvt_thread_cond_signal( &rw->ltrw_write ); + } + +#ifdef LDAP_RDWR_DEBUG + assert( rw->ltrw_writer == ldap_pvt_thread_self() ); + rw->ltrw_writer = 0; +#endif + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +#ifdef LDAP_RDWR_DEBUG + +/* just for testing, + * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr)) + * + * Currently they don't check if the calling thread is the one + * that has the lock, just that there is a reader or writer. + * + * Basically sufficient for testing that places that should have + * a lock are caught. + */ + +int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return( rw->ltrw_r_active ); +} + +int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return( rw->ltrw_w_active ); +} + +int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return(ldap_pvt_thread_rdwr_readers(rwlock) + + ldap_pvt_thread_rdwr_writers(rwlock)); +} + +#endif /* LDAP_RDWR_DEBUG */ + +#endif /* LDAP_THREAD_HAVE_RDWR */ + +#endif /* LDAP_R_COMPILE */ diff --git a/contrib/libs/openldap/libraries/libldap/references.c b/contrib/libs/openldap/libraries/libldap/references.c new file mode 100644 index 0000000000..ee04e6371b --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/references.c @@ -0,0 +1,147 @@ +/* references.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_first_reference( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE + ? chain + : ldap_next_reference( ld, chain ); +} + +LDAPMessage * +ldap_next_reference( LDAP *ld, LDAPMessage *ref ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ref != NULL ); + + for ( + ref = ref->lm_chain; + ref != NULL; + ref = ref->lm_chain ) + { + if( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + return( ref ); + } + } + + return( NULL ); +} + +int +ldap_count_references( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + if( chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + i++; + } + } + + return( i ); +} + +int +ldap_parse_reference( + LDAP *ld, + LDAPMessage *ref, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit) +{ + BerElement be; + char **refs = NULL; + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ref != NULL ); + + if( ref->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) { + return LDAP_PARAM_ERROR; + } + + /* make a private copy of BerElement */ + AC_MEMCPY(&be, ref->lm_ber, sizeof(be)); + + if ( ber_scanf( &be, "{v" /*}*/, &refs ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + if ( serverctrls == NULL ) { + rc = LDAP_SUCCESS; + goto free_and_return; + } + + if ( ber_scanf( &be, /*{*/ "}" ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + rc = ldap_pvt_get_controls( &be, serverctrls ); + +free_and_return: + + if( referralsp != NULL ) { + /* provide references regardless of return code */ + *referralsp = refs; + + } else { + LDAP_VFREE( refs ); + } + + if( freeit ) { + ldap_msgfree( ref ); + } + + if( rc != LDAP_SUCCESS ) { + ld->ld_errno = rc; + + if( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + } + + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/request.c b/contrib/libs/openldap/libraries/libldap/request.c new file mode 100644 index 0000000000..850e31fc8e --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/request.c @@ -0,0 +1,1714 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Modification to OpenLDAP source by Novell, Inc. + * April 2000 sfs Added code to chase V3 referrals + * request.c - sending of ldap requests; handling of referrals + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "lber.h" + +/* used by ldap_send_server_request and ldap_new_connection */ +#ifdef LDAP_R_COMPILE +#define LDAP_CONN_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); } +#define LDAP_CONN_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } +#define LDAP_REQ_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); } +#define LDAP_REQ_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } +#define LDAP_RES_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); } +#define LDAP_RES_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); } +#else +#define LDAP_CONN_LOCK_IF(nolock) +#define LDAP_CONN_UNLOCK_IF(nolock) +#define LDAP_REQ_LOCK_IF(nolock) +#define LDAP_REQ_UNLOCK_IF(nolock) +#define LDAP_RES_LOCK_IF(nolock) +#define LDAP_RES_UNLOCK_IF(nolock) +#endif + +static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any )); +static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc )); +static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr )); + +static BerElement * +re_encode_request( LDAP *ld, + BerElement *origber, + ber_int_t msgid, + int sref, + LDAPURLDesc *srv, + int *type ); + +BerElement * +ldap_alloc_ber_with_options( LDAP *ld ) +{ + BerElement *ber; + + ber = ber_alloc_t( ld->ld_lberoptions ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + return( ber ); +} + + +void +ldap_set_ber_options( LDAP *ld, BerElement *ber ) +{ + /* ld_lberoptions is constant, hence no lock */ + ber->ber_options = ld->ld_lberoptions; +} + + +/* sets needed mutexes - no mutexes set to this point */ +ber_int_t +ldap_send_initial_request( + LDAP *ld, + ber_tag_t msgtype, + const char *dn, + BerElement *ber, + ber_int_t msgid) +{ + int rc = 1; + ber_socket_t sd = AC_SOCKET_INVALID; + + Debug0( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n" ); + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) { + /* not connected yet */ + rc = ldap_open_defconn( ld ); + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); + } + } + if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) + rc = ldap_int_check_async_open( ld, sd ); + if( rc < 0 ) { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return( -1 ); + } else if ( rc == 0 ) { + Debug0( LDAP_DEBUG_TRACE, + "ldap_open_defconn: successful\n" ); + } + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + if (msgtype == LDAP_REQ_BIND) { + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + if (ld->ld_options.ldo_cldapdn) + ldap_memfree(ld->ld_options.ldo_cldapdn); + ld->ld_options.ldo_cldapdn = ldap_strdup(dn); + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return 0; + } + if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) + { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return LDAP_PARAM_ERROR; + } + } +#endif + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + rc = ldap_send_server_request( ld, ber, msgid, NULL, + NULL, NULL, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return(rc); +} + + +/* protected by conn_mutex */ +int +ldap_int_flush_request( + LDAP *ld, + LDAPRequest *lr ) +{ + LDAPConn *lc = lr->lr_conn; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) { + if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) { + /* ENOTCONN is returned in Solaris 10 */ + /* need to continue write later */ + lr->lr_status = LDAP_REQST_WRITING; + ldap_mark_select_write( ld, lc->lconn_sb ); + ld->ld_errno = LDAP_BUSY; + return -2; + } else { + ld->ld_errno = LDAP_SERVER_DOWN; + ldap_free_request( ld, lr ); + ldap_free_connection( ld, lc, 0, 0 ); + return( -1 ); + } + } else { + if ( lr->lr_parent == NULL ) { + lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; + lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; + } + lr->lr_status = LDAP_REQST_INPROGRESS; + + /* sent -- waiting for a response */ + ldap_mark_select_read( ld, lc->lconn_sb ); + ldap_clear_select_write( ld, lc->lconn_sb ); + } + return 0; +} + +/* + * protected by req_mutex + * if m_noconn then protect using conn_lock + * else already protected with conn_lock + * if m_res then also protected by res_mutex + */ + +int +ldap_send_server_request( + LDAP *ld, + BerElement *ber, + ber_int_t msgid, + LDAPRequest *parentreq, + LDAPURLDesc **srvlist, + LDAPConn *lc, + LDAPreqinfo *bind, + int m_noconn, + int m_res ) +{ + LDAPRequest *lr; + int incparent, rc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug0( LDAP_DEBUG_TRACE, "ldap_send_server_request\n" ); + + incparent = 0; + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + + LDAP_CONN_LOCK_IF(m_noconn); + if ( lc == NULL ) { + if ( srvlist == NULL ) { + lc = ld->ld_defconn; + } else { + lc = find_connection( ld, *srvlist, 1 ); + if ( lc == NULL ) { + if ( (bind != NULL) && (parentreq != NULL) ) { + /* Remember the bind in the parent */ + incparent = 1; + ++parentreq->lr_outrefcnt; + } + lc = ldap_new_connection( ld, srvlist, 0, + 1, bind, 1, m_res ); + } + } + } + + /* async connect... */ + if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { + ber_socket_t sd = AC_SOCKET_ERROR; + struct timeval tv = { 0 }; + + ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); + + /* poll ... */ + switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) { + case 0: + /* go on! */ + lc->lconn_status = LDAP_CONNST_CONNECTED; + break; + + case -2: + /* async only occurs if a network timeout is set */ + + /* honor network timeout */ + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) + { + /* caller will have to call again */ + ld->ld_errno = LDAP_X_CONNECTING; + } + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + /* fallthru */ + + default: + /* error */ + break; + } + } + + if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = LDAP_SERVER_DOWN; + } + + ber_free( ber, 1 ); + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_CONN_UNLOCK_IF(m_noconn); + return( -1 ); + } + + use_connection( ld, lc ); + +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP( ld )) { + BerElement tmpber = *ber; + ber_rewind( &tmpber ); + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + rc = ber_write( &tmpber, ld->ld_options.ldo_peer, + sizeof( struct sockaddr_storage ), 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + LDAP_CONN_UNLOCK_IF(m_noconn); + return rc; + } + } +#endif + + /* If we still have an incomplete write, try to finish it before + * dealing with the new request. If we don't finish here, return + * LDAP_BUSY and let the caller retry later. We only allow a single + * request to be in WRITING state. + */ + rc = 0; + if ( ld->ld_requests != NULL ) { + TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT ); + LDAPRequest *lr; + + assert( node != NULL ); + lr = node->avl_data; + if ( lr->lr_status == LDAP_REQST_WRITING && + ldap_int_flush_request( ld, lr ) < 0 ) { + rc = -1; + } + } + if ( rc ) { + ber_free( ber, 1 ); + LDAP_CONN_UNLOCK_IF(m_noconn); + return rc; + } + + lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); + if ( lr == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + ldap_free_connection( ld, lc, 0, 0 ); + ber_free( ber, 1 ); + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_CONN_UNLOCK_IF(m_noconn); + return( -1 ); + } + lr->lr_msgid = msgid; + lr->lr_status = LDAP_REQST_INPROGRESS; + lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ + lr->lr_ber = ber; + lr->lr_conn = lc; + if ( parentreq != NULL ) { /* sub-request */ + if ( !incparent ) { + /* Increment if we didn't do it before the bind */ + ++parentreq->lr_outrefcnt; + } + lr->lr_origid = parentreq->lr_origid; + lr->lr_parentcnt = ++parentreq->lr_parentcnt; + lr->lr_parent = parentreq; + lr->lr_refnext = parentreq->lr_child; + parentreq->lr_child = lr; + } else { /* original request */ + lr->lr_origid = lr->lr_msgid; + } + + /* Extract requestDN for future reference */ +#ifdef LDAP_CONNECTIONLESS + if ( !LDAP_IS_UDP(ld) ) +#endif + { + BerElement tmpber = *ber; + ber_int_t bint; + ber_tag_t tag, rtag; + + ber_reset( &tmpber, 1 ); + rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); + switch ( tag ) { + case LDAP_REQ_BIND: + rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); + break; + case LDAP_REQ_DELETE: + break; + default: + rtag = ber_scanf( &tmpber, "{" /*}*/ ); + case LDAP_REQ_ABANDON: + break; + } + if ( tag != LDAP_REQ_ABANDON ) { + ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); + lr->lr_dn.bv_val = tmpber.ber_ptr; + } + } + + rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error ); + assert( rc == LDAP_SUCCESS ); + + ld->ld_errno = LDAP_SUCCESS; + if ( ldap_int_flush_request( ld, lr ) == -1 ) { + msgid = -1; + } + + LDAP_CONN_UNLOCK_IF(m_noconn); + return( msgid ); +} + +/* return 0 if no StartTLS ext, 1 if present, 2 if critical */ +static int +find_tls_ext( LDAPURLDesc *srv ) +{ + int i, crit; + char *ext; + + if ( !srv->lud_exts ) + return 0; + + for (i=0; srv->lud_exts[i]; i++) { + crit = 0; + ext = srv->lud_exts[i]; + if ( ext[0] == '!') { + ext++; + crit = 1; + } + if ( !strcasecmp( ext, "StartTLS" ) || + !strcasecmp( ext, "X-StartTLS" ) || + !strcmp( ext, LDAP_EXOP_START_TLS )) { + return crit + 1; + } + } + return 0; +} + +/* + * always protected by conn_mutex + * optionally protected by req_mutex and res_mutex + */ +LDAPConn * +ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, + int connect, LDAPreqinfo *bind, int m_req, int m_res ) +{ + LDAPConn *lc; + int async = 0; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + Debug3( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", + use_ldsb, connect, (bind != NULL) ); + /* + * make a new LDAP server connection + * XXX open connection synchronously for now + */ + lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) ); + if ( lc == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + + if ( use_ldsb ) { + assert( ld->ld_sb != NULL ); + lc->lconn_sb = ld->ld_sb; + + } else { + lc->lconn_sb = ber_sockbuf_alloc(); + if ( lc->lconn_sb == NULL ) { + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + } + + if ( connect ) { + LDAPURLDesc **srvp, *srv = NULL; + + async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); + + for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) { + int rc; + + rc = ldap_int_open_connection( ld, lc, *srvp, async ); + if ( rc != -1 ) { + srv = *srvp; + + /* If we fully connected, async is moot */ + if ( rc == 0 ) + async = 0; + + if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) { + ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params ); + } + + break; + } + } + + if ( srv == NULL ) { + if ( !use_ldsb ) { + ber_sockbuf_free( lc->lconn_sb ); + } + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_SERVER_DOWN; + return( NULL ); + } + + lc->lconn_server = ldap_url_dup( srv ); + if ( !lc->lconn_server ) { + if ( !use_ldsb ) + ber_sockbuf_free( lc->lconn_sb ); + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + } + + lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; + lc->lconn_next = ld->ld_conns; + ld->ld_conns = lc; + + if ( connect ) { +#ifdef HAVE_TLS + if ( lc->lconn_server->lud_exts ) { + int rc, ext = find_tls_ext( lc->lconn_server ); + if ( ext ) { + LDAPConn *savedefconn; + + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + rc = ldap_start_tls_s( ld, NULL, NULL ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( rc != LDAP_SUCCESS && ext == 2 ) { + ldap_free_connection( ld, lc, 1, 0 ); + return NULL; + } + } + } +#endif + } + + if ( bind != NULL ) { + int err = 0; + LDAPConn *savedefconn; + + /* Set flag to prevent additional referrals + * from being processed on this + * connection until the bind has completed + */ + lc->lconn_rebind_inprogress = 1; + /* V3 rebind function */ + if ( ld->ld_rebind_proc != NULL) { + LDAPURLDesc *srvfunc; + + srvfunc = ldap_url_dup( *srvlist ); + if ( srvfunc == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + err = -1; + } else { + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + Debug0( LDAP_DEBUG_TRACE, "Call application rebind_proc\n" ); + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + err = (*ld->ld_rebind_proc)( ld, + bind->ri_url, bind->ri_request, bind->ri_msgid, + ld->ld_rebind_params ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( err != 0 ) { + err = -1; + ldap_free_connection( ld, lc, 1, 0 ); + lc = NULL; + } + ldap_free_urldesc( srvfunc ); + } + + } else { + int msgid, rc; + struct berval passwd = BER_BVNULL; + + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + Debug0( LDAP_DEBUG_TRACE, + "anonymous rebind via ldap_sasl_bind(\"\")\n" ); + + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd, + NULL, NULL, &msgid ); + if ( rc != LDAP_SUCCESS ) { + err = -1; + + } else { + for ( err = 1; err > 0; ) { + struct timeval tv = { 0, 100000 }; + LDAPMessage *res = NULL; + + switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { + case -1: + err = -1; + break; + + case 0: +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_yield(); +#endif + break; + + case LDAP_RES_BIND: + rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 ); + if ( rc != LDAP_SUCCESS ) { + err = -1; + + } else if ( err != LDAP_SUCCESS ) { + err = -1; + } + /* else err == LDAP_SUCCESS == 0 */ + break; + + default: + Debug3( LDAP_DEBUG_TRACE, + "ldap_new_connection %p: " + "unexpected response %d " + "from BIND request id=%d\n", + (void *) ld, ldap_msgtype( res ), msgid ); + err = -1; + break; + } + } + } + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( err != 0 ) { + ldap_free_connection( ld, lc, 1, 0 ); + lc = NULL; + } + } + if ( lc != NULL ) + lc->lconn_rebind_inprogress = 0; + } + return( lc ); +} + + +/* protected by ld_conn_mutex */ +static LDAPConn * +find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) +/* + * return an existing connection (if any) to the server srv + * if "any" is non-zero, check for any server in the "srv" chain + */ +{ + LDAPConn *lc; + LDAPURLDesc *lcu, *lsu; + int lcu_port, lsu_port; + int found = 0; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + lcu = lc->lconn_server; + lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme, + lcu->lud_port ); + + for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) { + lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme, + lsu->lud_port ); + + if ( lsu_port == lcu_port + && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0 + && lcu->lud_host != NULL && lsu->lud_host != NULL + && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 ) + { + found = 1; + break; + } + + if ( !any ) break; + } + if ( found ) + break; + } + return lc; +} + + + +/* protected by ld_conn_mutex */ +static void +use_connection( LDAP *ld, LDAPConn *lc ) +{ + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + ++lc->lconn_refcnt; + lc->lconn_lastused = time( NULL ); +} + + +/* protected by ld_conn_mutex */ +void +ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) +{ + LDAPConn *tmplc, *prevlc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + Debug2( LDAP_DEBUG_TRACE, + "ldap_free_connection %d %d\n", + force, unbind ); + + if ( force || --lc->lconn_refcnt <= 0 ) { + /* remove from connections list first */ + + for ( prevlc = NULL, tmplc = ld->ld_conns; + tmplc != NULL; + tmplc = tmplc->lconn_next ) + { + if ( tmplc == lc ) { + if ( prevlc == NULL ) { + ld->ld_conns = tmplc->lconn_next; + } else { + prevlc->lconn_next = tmplc->lconn_next; + } + if ( ld->ld_defconn == lc ) { + ld->ld_defconn = NULL; + } + break; + } + prevlc = tmplc; + } + + /* process connection callbacks */ + { + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + + lo = &ld->ld_options; + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, lc->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + lo = LDAP_INT_GLOBAL_OPT(); + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, lc->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + } + + if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { + ldap_mark_select_clear( ld, lc->lconn_sb ); + if ( unbind ) { + ldap_send_unbind( ld, lc->lconn_sb, + NULL, NULL ); + } + } + + if ( lc->lconn_ber != NULL ) { + ber_free( lc->lconn_ber, 1 ); + } + + ldap_int_sasl_close( ld, lc ); + + ldap_free_urllist( lc->lconn_server ); + + /* FIXME: is this at all possible? + * ldap_ld_free() in unbind.c calls ldap_free_connection() + * with force == 1 __after__ explicitly calling + * ldap_tavl_free on ld->ld_requests */ + if ( force ) { + ldap_tavl_free( ld->ld_requests, ldap_do_free_request ); + ld->ld_requests = NULL; + } + + if ( lc->lconn_sb != ld->ld_sb ) { + ber_sockbuf_free( lc->lconn_sb ); + } else { + ber_int_sb_close( lc->lconn_sb ); + } + + if ( lc->lconn_rebind_queue != NULL) { + int i; + for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { + LDAP_VFREE( lc->lconn_rebind_queue[i] ); + } + LDAP_FREE( lc->lconn_rebind_queue ); + } + + LDAP_FREE( lc ); + + Debug0( LDAP_DEBUG_TRACE, + "ldap_free_connection: actually freed\n" ); + + } else { + lc->lconn_lastused = time( NULL ); + Debug1( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n", + lc->lconn_refcnt ); + } +} + + +/* Protects self with ld_conn_mutex */ +#ifdef LDAP_DEBUG +void +ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) +{ + LDAPConn *lc; + char timebuf[32]; + + Debug2( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "" ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { + if ( lc->lconn_server != NULL ) { + Debug3( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n", + ( lc->lconn_server->lud_host == NULL ) ? "(null)" + : lc->lconn_server->lud_host, + lc->lconn_server->lud_port, ( lc->lconn_sb == + ld->ld_sb ) ? " (default)" : "" ); + } + if ( lc->lconn_sb != NULL ) { + char from[LDAP_IPADDRLEN]; + struct berval frombv = BER_BVC(from); + ber_socket_t sb; + if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sb ) == 1 ) { + Sockaddr sin; + socklen_t len = sizeof( sin ); + if ( getsockname( sb, (struct sockaddr *)&sin, &len ) == 0 ) { + ldap_pvt_sockaddrstr( &sin, &frombv ); + Debug1( LDAP_DEBUG_TRACE, "* from: %s\n", + ( from == NULL ) ? "(null)" : from ); + } + } + } + Debug2( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt, + ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) + ? "NeedSocket" : + ( lc->lconn_status == LDAP_CONNST_CONNECTING ) + ? "Connecting" : "Connected" ); + Debug2( LDAP_DEBUG_TRACE, " last used: %s%s\n", + ldap_pvt_ctime( &lc->lconn_lastused, timebuf ), + lc->lconn_rebind_inprogress ? " rebind in progress" : "" ); + if ( lc->lconn_rebind_inprogress ) { + if ( lc->lconn_rebind_queue != NULL) { + int i; + + for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { + int j; + for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) { + Debug3( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n", + i, j, lc->lconn_rebind_queue[i][j] ); + } + } + } else { + Debug0( LDAP_DEBUG_TRACE, " queue is empty\n" ); + } + } + Debug0( LDAP_DEBUG_TRACE, "\n" ); + if ( !all ) { + break; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); +} + + +/* protected by req_mutex and res_mutex */ +void +ldap_dump_requests_and_responses( LDAP *ld ) +{ + LDAPMessage *lm, *l; + TAvlnode *node; + int i; + + Debug1( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n", + (void *)ld ); + node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_LEFT ); + if ( node == NULL ) { + Debug0( LDAP_DEBUG_TRACE, " Empty\n" ); + } + for ( i = 0 ; node != NULL; i++, node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) { + LDAPRequest *lr = node->avl_data; + + Debug3( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n", + lr->lr_msgid, lr->lr_origid, + ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" : + ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" : + ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" : + ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" : + ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted" + : "InvalidStatus" ); + Debug2( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n", + lr->lr_outrefcnt, lr->lr_parentcnt ); + } + Debug3( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n", + (void *)ld, i, ld->ld_nabandoned ); + Debug1( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld ); + if ( ( lm = ld->ld_responses ) == NULL ) { + Debug0( LDAP_DEBUG_TRACE, " Empty\n" ); + } + for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) { + Debug2( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n", + lm->lm_msgid, (unsigned long)lm->lm_msgtype ); + if ( lm->lm_chain != NULL ) { + Debug0( LDAP_DEBUG_TRACE, " chained responses:\n" ); + for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) { + Debug2( LDAP_DEBUG_TRACE, + " * msgid %d, type %lu\n", + l->lm_msgid, + (unsigned long)l->lm_msgtype ); + } + } + } + Debug2( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i ); +} +#endif /* LDAP_DEBUG */ + +/* protected by req_mutex */ +void +ldap_do_free_request( void *arg ) +{ + LDAPRequest *lr = arg; + + Debug3( LDAP_DEBUG_TRACE, "ldap_do_free_request: " + "asked to free lr %p msgid %d refcnt %d\n", + (void *) lr, lr->lr_msgid, lr->lr_refcnt ); + /* if lr_refcnt > 0, the request has been looked up + * by ldap_find_request_by_msgid(); if in the meanwhile + * the request is free()'d by someone else, just decrease + * the reference count; later on, it will be freed. */ + if ( lr->lr_refcnt > 0 ) { + assert( lr->lr_refcnt == 1 ); + lr->lr_refcnt = -lr->lr_refcnt; + return; + } + + if ( lr->lr_ber != NULL ) { + ber_free( lr->lr_ber, 1 ); + lr->lr_ber = NULL; + } + + if ( lr->lr_res_error != NULL ) { + LDAP_FREE( lr->lr_res_error ); + lr->lr_res_error = NULL; + } + + if ( lr->lr_res_matched != NULL ) { + LDAP_FREE( lr->lr_res_matched ); + lr->lr_res_matched = NULL; + } + + LDAP_FREE( lr ); +} + +int +ldap_req_cmp( const void *l, const void *r ) +{ + const LDAPRequest *left = l, *right = r; + return left->lr_msgid - right->lr_msgid; +} + +/* protected by req_mutex */ +static void +ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) +{ + LDAPRequest *removed; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + removed = ldap_tavl_delete( &ld->ld_requests, lr, ldap_req_cmp ); + assert( !removed || removed == lr ); + Debug3( LDAP_DEBUG_TRACE, "ldap_free_request_int: " + "lr %p msgid %d%s removed\n", + (void *) lr, lr->lr_msgid, removed ? "" : " not" ); + + ldap_do_free_request( lr ); +} + +/* protected by req_mutex */ +void +ldap_free_request( LDAP *ld, LDAPRequest *lr ) +{ + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug2( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n", + lr->lr_origid, lr->lr_msgid ); + + /* free all referrals (child requests) */ + while ( lr->lr_child ) { + ldap_free_request( ld, lr->lr_child ); + } + + if ( lr->lr_parent != NULL ) { + LDAPRequest **lrp; + + --lr->lr_parent->lr_outrefcnt; + for ( lrp = &lr->lr_parent->lr_child; + *lrp && *lrp != lr; + lrp = &(*lrp)->lr_refnext ); + + if ( *lrp == lr ) { + *lrp = lr->lr_refnext; + } + } + ldap_free_request_int( ld, lr ); +} + +/* + * call first time with *cntp = -1 + * when returns *cntp == -1, no referrals are left + * + * NOTE: may replace *refsp, or shuffle the contents + * of the original array. + */ +static int ldap_int_nextref( + LDAP *ld, + char ***refsp, + int *cntp, + void *params ) +{ + assert( refsp != NULL ); + assert( *refsp != NULL ); + assert( cntp != NULL ); + + if ( *cntp < -1 ) { + *cntp = -1; + return -1; + } + + (*cntp)++; + + if ( (*refsp)[ *cntp ] == NULL ) { + *cntp = -1; + } + + return 0; +} + +/* + * Chase v3 referrals + * + * Parameters: + * (IN) ld = LDAP connection handle + * (IN) lr = LDAP Request structure + * (IN) refs = array of pointers to referral strings that we will chase + * The array will be free'd by this function when no longer needed + * (IN) sref != 0 if following search reference + * (OUT) errstrp = Place to return a string of referrals which could not be followed + * (OUT) hadrefp = 1 if successfully followed referral + * + * Return value - number of referrals followed + * + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) + */ +int +ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp ) +{ + char *unfollowed; + int unfollowedcnt = 0; + LDAPRequest *origreq; + LDAPURLDesc *srv = NULL; + BerElement *ber; + char **refarray = NULL; + LDAPConn *lc; + int rc, count, i, j, id; + LDAPreqinfo rinfo; + LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug0( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n" ); + + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + *hadrefp = 0; + + unfollowed = NULL; + rc = count = 0; + + /* If no referrals in array, return */ + if ( (refs == NULL) || ( (refs)[0] == NULL) ) { + rc = 0; + goto done; + } + + /* Check for hop limit exceeded */ + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + Debug1( LDAP_DEBUG_ANY, + "more than %d referral hops (dropping)\n", ld->ld_refhoplimit ); + ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED; + rc = -1; + goto done; + } + + /* find original request */ + for ( origreq = lr; + origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) + { + /* empty */ ; + } + + refarray = refs; + refs = NULL; + + /* parse out & follow referrals */ + /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */ + i = -1; + for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ); + i != -1; + nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) ) + { + + /* Parse the referral URL */ + rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); + if ( rc != LDAP_URL_SUCCESS ) { + /* ldap_url_parse_ext() returns LDAP_URL_* errors + * which do not map on API errors */ + ld->ld_errno = LDAP_PARAM_ERROR; + rc = -1; + goto done; + } + + if( srv->lud_crit_exts ) { + int ok = 0; +#ifdef HAVE_TLS + /* If StartTLS is the only critical ext, OK. */ + if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 ) + ok = 1; +#endif + if ( !ok ) { + /* we do not support any other extensions */ + ld->ld_errno = LDAP_NOT_SUPPORTED; + rc = -1; + goto done; + } + } + + /* check connection for re-bind in progress */ + if (( lc = find_connection( ld, srv, 1 )) != NULL ) { + /* See if we've already requested this DN with this conn */ + LDAPRequest *lp; + int looped = 0; + ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; + for ( lp = origreq; lp; ) { + if ( lp->lr_conn == lc + && len == lp->lr_dn.bv_len + && len + && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 ) + { + looped = 1; + break; + } + if ( lp == origreq ) { + lp = lp->lr_child; + } else { + lp = lp->lr_refnext; + } + } + if ( looped ) { + ldap_free_urllist( srv ); + srv = NULL; + ld->ld_errno = LDAP_CLIENT_LOOP; + rc = -1; + continue; + } + + if ( lc->lconn_rebind_inprogress ) { + /* We are already chasing a referral or search reference and a + * bind on that connection is in progress. We must queue + * referrals on that connection, so we don't get a request + * going out before the bind operation completes. This happens + * if two search references come in one behind the other + * for the same server with different contexts. + */ + Debug1( LDAP_DEBUG_TRACE, + "ldap_chase_v3referrals: queue referral \"%s\"\n", + refarray[i] ); + if( lc->lconn_rebind_queue == NULL ) { + /* Create a referral list */ + lc->lconn_rebind_queue = + (char ***) LDAP_MALLOC( sizeof(void *) * 2); + + if( lc->lconn_rebind_queue == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + rc = -1; + goto done; + } + + lc->lconn_rebind_queue[0] = refarray; + lc->lconn_rebind_queue[1] = NULL; + refarray = NULL; + + } else { + /* Count how many referral arrays we already have */ + for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) { + /* empty */; + } + + /* Add the new referral to the list */ + lc->lconn_rebind_queue = (char ***) LDAP_REALLOC( + lc->lconn_rebind_queue, sizeof(void *) * (j + 2)); + + if( lc->lconn_rebind_queue == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + rc = -1; + goto done; + } + lc->lconn_rebind_queue[j] = refarray; + lc->lconn_rebind_queue[j+1] = NULL; + refarray = NULL; + } + + /* We have queued the referral/reference, now just return */ + rc = 0; + *hadrefp = 1; + count = 1; /* Pretend we already followed referral */ + goto done; + } + } + /* Re-encode the request with the new starting point of the search. + * Note: In the future we also need to replace the filter if one + * was provided with the search reference + */ + + /* For references we don't want old dn if new dn empty */ + if ( sref && srv->lud_dn == NULL ) { + srv->lud_dn = LDAP_STRDUP( "" ); + } + + LDAP_NEXT_MSGID( ld, id ); + ber = re_encode_request( ld, origreq->lr_ber, id, + sref, srv, &rinfo.ri_request ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + rc = -1; + goto done; + } + + Debug2( LDAP_DEBUG_TRACE, + "ldap_chase_v3referral: msgid %d, url \"%s\"\n", + lr->lr_msgid, refarray[i] ); + + /* Send the new request to the server - may require a bind */ + rinfo.ri_msgid = origreq->lr_origid; + rinfo.ri_url = refarray[i]; + rc = ldap_send_server_request( ld, ber, id, + origreq, &srv, NULL, &rinfo, 0, 1 ); + if ( rc < 0 ) { + /* Failure, try next referral in the list */ + Debug3( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", + refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) ); + unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] ); + ldap_free_urllist( srv ); + srv = NULL; + ld->ld_errno = LDAP_REFERRAL; + } else { + /* Success, no need to try this referral list further */ + rc = 0; + ++count; + *hadrefp = 1; + + /* check if there is a queue of referrals that came in during bind */ + if ( lc == NULL) { + lc = find_connection( ld, srv, 1 ); + if ( lc == NULL ) { + ld->ld_errno = LDAP_OPERATIONS_ERROR; + rc = -1; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + goto done; + } + } + + if ( lc->lconn_rebind_queue != NULL ) { + /* Release resources of previous list */ + LDAP_VFREE( refarray ); + refarray = NULL; + ldap_free_urllist( srv ); + srv = NULL; + + /* Pull entries off end of queue so list always null terminated */ + for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ ) + ; + refarray = lc->lconn_rebind_queue[j - 1]; + lc->lconn_rebind_queue[j-1] = NULL; + /* we pulled off last entry from queue, free queue */ + if ( j == 1 ) { + LDAP_FREE( lc->lconn_rebind_queue ); + lc->lconn_rebind_queue = NULL; + } + /* restart the loop the with new referral list */ + i = -1; + continue; + } + break; /* referral followed, break out of for loop */ + } + } /* end for loop */ +done: + LDAP_VFREE( refarray ); + ldap_free_urllist( srv ); + LDAP_FREE( *errstrp ); + + if( rc == 0 ) { + *errstrp = NULL; + LDAP_FREE( unfollowed ); + return count; + } else { + *errstrp = unfollowed; + return rc; + } +} + +/* + * XXX merging of errors in this routine needs to be improved + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) + */ +int +ldap_chase_referrals( LDAP *ld, + LDAPRequest *lr, + char **errstrp, + int sref, + int *hadrefp ) +{ + int rc, count, id; + unsigned len; + char *p, *ref, *unfollowed; + LDAPRequest *origreq; + LDAPURLDesc *srv; + BerElement *ber; + LDAPreqinfo rinfo; + LDAPConn *lc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug0( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n" ); + + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + *hadrefp = 0; + + if ( *errstrp == NULL ) { + return( 0 ); + } + + len = strlen( *errstrp ); + for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) { + if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { + *p = '\0'; + p += LDAP_REF_STR_LEN; + break; + } + } + + if ( len < LDAP_REF_STR_LEN ) { + return( 0 ); + } + + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + Debug1( LDAP_DEBUG_ANY, + "more than %d referral hops (dropping)\n", + ld->ld_refhoplimit ); + /* XXX report as error in ld->ld_errno? */ + return( 0 ); + } + + /* find original request */ + for ( origreq = lr; origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) { + /* empty */; + } + + unfollowed = NULL; + rc = count = 0; + + /* parse out & follow referrals */ + for ( ref = p; rc == 0 && ref != NULL; ref = p ) { + p = strchr( ref, '\n' ); + if ( p != NULL ) { + *p++ = '\0'; + } + + rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); + if ( rc != LDAP_URL_SUCCESS ) { + Debug2( LDAP_DEBUG_TRACE, + "ignoring %s referral <%s>\n", + ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect" ); + rc = ldap_append_referral( ld, &unfollowed, ref ); + *hadrefp = 1; + continue; + } + + Debug1( LDAP_DEBUG_TRACE, + "chasing LDAP referral: <%s>\n", ref ); + + *hadrefp = 1; + + /* See if we've already been here */ + if (( lc = find_connection( ld, srv, 1 )) != NULL ) { + LDAPRequest *lp; + int looped = 0; + ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; + for ( lp = lr; lp; lp = lp->lr_parent ) { + if ( lp->lr_conn == lc + && len == lp->lr_dn.bv_len ) + { + if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) ) + continue; + looped = 1; + break; + } + } + if ( looped ) { + ldap_free_urllist( srv ); + ld->ld_errno = LDAP_CLIENT_LOOP; + rc = -1; + continue; + } + } + + LDAP_NEXT_MSGID( ld, id ); + ber = re_encode_request( ld, origreq->lr_ber, + id, sref, srv, &rinfo.ri_request ); + + if ( ber == NULL ) { + ldap_free_urllist( srv ); + return -1 ; + } + + /* copy the complete referral for rebind process */ + rinfo.ri_url = LDAP_STRDUP( ref ); + + rinfo.ri_msgid = origreq->lr_origid; + + rc = ldap_send_server_request( ld, ber, id, + lr, &srv, NULL, &rinfo, 0, 1 ); + LDAP_FREE( rinfo.ri_url ); + + if( rc >= 0 ) { + ++count; + } else { + Debug3( LDAP_DEBUG_ANY, + "Unable to chase referral \"%s\" (%d: %s)\n", + ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) ); + rc = ldap_append_referral( ld, &unfollowed, ref ); + } + + ldap_free_urllist(srv); + } + + LDAP_FREE( *errstrp ); + *errstrp = unfollowed; + + return(( rc == 0 ) ? count : rc ); +} + + +int +ldap_append_referral( LDAP *ld, char **referralsp, char *s ) +{ + int first; + + if ( *referralsp == NULL ) { + first = 1; + *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN + + 1 ); + } else { + first = 0; + *referralsp = (char *)LDAP_REALLOC( *referralsp, + strlen( *referralsp ) + strlen( s ) + 2 ); + } + + if ( *referralsp == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + + if ( first ) { + strcpy( *referralsp, LDAP_REF_STR ); + } else { + strcat( *referralsp, "\n" ); + } + strcat( *referralsp, s ); + + return( 0 ); +} + + + +static BerElement * +re_encode_request( LDAP *ld, + BerElement *origber, + ber_int_t msgid, + int sref, + LDAPURLDesc *srv, + int *type ) +{ + /* + * XXX this routine knows way too much about how the lber library works! + */ + ber_int_t along; + ber_tag_t tag; + ber_tag_t rtag; + ber_int_t ver; + ber_int_t scope; + int rc; + BerElement tmpber, *ber; + struct berval dn; + + Debug2( LDAP_DEBUG_TRACE, + "re_encode_request: new msgid %ld, new dn <%s>\n", + (long) msgid, + ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn ); + + tmpber = *origber; + + /* + * all LDAP requests are sequences that start with a message id. + * For all except delete, this is followed by a sequence that is + * tagged with the operation code. For delete, the provided DN + * is not wrapped by a sequence. + */ + rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag ); + + if ( rtag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + assert( tag != 0); + if ( tag == LDAP_REQ_BIND ) { + /* bind requests have a version number before the DN & other stuff */ + rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn ); + + } else if ( tag == LDAP_REQ_DELETE ) { + /* delete requests don't have a DN wrapping sequence */ + rtag = ber_scanf( &tmpber, "m", &dn ); + + } else if ( tag == LDAP_REQ_SEARCH ) { + /* search requests need to be re-scope-ed */ + rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope ); + + if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { + /* use the scope provided in reference */ + scope = srv->lud_scope; + + } else if ( sref ) { + /* use scope implied by previous operation + * base -> base + * one -> base + * subtree -> subtree + * subordinate -> subtree + */ + switch( scope ) { + default: + case LDAP_SCOPE_BASE: + case LDAP_SCOPE_ONELEVEL: + scope = LDAP_SCOPE_BASE; + break; + case LDAP_SCOPE_SUBTREE: + case LDAP_SCOPE_SUBORDINATE: + scope = LDAP_SCOPE_SUBTREE; + break; + } + } + + } else { + rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn ); + } + + if( rtag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return NULL; + } + + /* restore character zero'd out by ber_scanf*/ + dn.bv_val[dn.bv_len] = tmpber.ber_tag; + + if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return NULL; + } + + if ( srv->lud_dn ) { + ber_str2bv( srv->lud_dn, 0, 0, &dn ); + } + + if ( tag == LDAP_REQ_BIND ) { + rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn ); + } else if ( tag == LDAP_REQ_DELETE ) { + rc = ber_printf( ber, "{itON}", msgid, tag, &dn ); + } else if ( tag == LDAP_REQ_SEARCH ) { + rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope ); + } else { + rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn ); + } + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return NULL; + } + + if ( tag != LDAP_REQ_DELETE && ( + ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0) + != ( tmpber.ber_end - tmpber.ber_ptr ) || + ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return NULL; + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + Debug0( LDAP_DEBUG_ANY, "re_encode_request new request is:\n" ); + ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 ); + } +#endif /* LDAP_DEBUG */ + + *type = tag; /* return request type */ + return ber; +} + + +/* protected by req_mutex */ +LDAPRequest * +ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) +{ + LDAPRequest *lr, needle = {0}; + needle.lr_msgid = msgid; + + lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp ); + if ( lr != NULL && lr->lr_status != LDAP_REQST_COMPLETED ) { + /* lr_refcnt is only negative when we removed it from ld_requests + * already, it is positive if we have sub-requests (referrals) */ + assert( lr->lr_refcnt >= 0 ); + lr->lr_refcnt++; + Debug3( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: " + "msgid %d, lr %p lr->lr_refcnt = %d\n", + msgid, (void *) lr, lr->lr_refcnt ); + return lr; + } + + Debug2( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: " + "msgid %d, lr %p\n", msgid, (void *) lr ); + return NULL; +} + +/* protected by req_mutex */ +void +ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) +{ + LDAPRequest *lr; + + lr = ldap_tavl_find( ld->ld_requests, lrx, ldap_req_cmp ); + Debug2( LDAP_DEBUG_TRACE, "ldap_return_request: " + "lrx %p, lr %p\n", (void *) lrx, (void *) lr ); + if ( lr ) { + assert( lr == lrx ); + if ( lr->lr_refcnt > 0 ) { + lr->lr_refcnt--; + } else if ( lr->lr_refcnt < 0 ) { + lr->lr_refcnt++; + if ( lr->lr_refcnt == 0 ) { + lr = NULL; + } + } + } + Debug3( LDAP_DEBUG_TRACE, "ldap_return_request: " + "lrx->lr_msgid %d, lrx->lr_refcnt is now %d, lr is %s present\n", + lrx->lr_msgid, lrx->lr_refcnt, lr ? "still" : "not" ); + /* The request is not tracked anymore */ + if ( lr == NULL ) { + ldap_free_request_int( ld, lrx ); + } else if ( freeit ) { + ldap_free_request( ld, lrx ); + } +} diff --git a/contrib/libs/openldap/libraries/libldap/result.c b/contrib/libs/openldap/libraries/libldap/result.c new file mode 100644 index 0000000000..40ff1c1722 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/result.c @@ -0,0 +1,1401 @@ +/* result.c - wait for an ldap result */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Modification to OpenLDAP source by Novell, Inc. + * April 2000 sfs Add code to process V3 referrals and search results + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +/* + * LDAPv3 (RFC 4511) + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED { ... }, + * matchedDN LDAPDN, + * diagnosticMessage LDAPString, + * referral [3] Referral OPTIONAL + * } + * Referral ::= SEQUENCE OF LDAPURL (one or more) + * LDAPURL ::= LDAPString (limited to URL chars) + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "ldap_log.h" +#include "lutil.h" + +static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); +static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); +static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, + LDAPMessage **result )); +static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid, + int all, LDAPConn *lc, LDAPMessage **result )); +static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr )); +static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )); +static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all)); + +#define LDAP_MSG_X_KEEP_LOOKING (-2) + + +/* + * ldap_result - wait for an ldap result response to a message from the + * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be + * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited + * message is accepted. Otherwise ldap_result will wait for a response + * with msgid. If all is LDAP_MSG_ONE (0) the first message with id + * msgid will be accepted, otherwise, ldap_result will wait for all + * responses with id msgid and then return a pointer to the entire list + * of messages. In general, this is only useful for search responses, + * which can be of three message types (zero or more entries, zero or + * search references, followed by an ldap result). An extension to + * LDAPv3 allows partial extended responses to be returned in response + * to any request. The type of the first message received is returned. + * When waiting, any messages that have been abandoned/discarded are + * discarded. + * + * Example: + * ldap_result( s, msgid, all, timeout, result ) + */ +int +ldap_result( + LDAP *ld, + int msgid, + int all, + struct timeval *timeout, + LDAPMessage **result ) +{ + int rc; + + assert( ld != NULL ); + assert( result != NULL ); + + Debug2( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid ); + + if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN) + return -1; + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + rc = wait4msg( ld, msgid, all, timeout, result ); + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + + return rc; +} + +/* protected by res_mutex */ +static LDAPMessage * +chkResponseList( + LDAP *ld, + int msgid, + int all) +{ + LDAPMessage *lm, **lastlm, *nextlm; + int cnt = 0; + + /* + * Look through the list of responses we have received on + * this association and see if the response we're interested in + * is there. If it is, return it. If not, call wait4msg() to + * wait until it arrives or timeout occurs. + */ + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + + Debug3( LDAP_DEBUG_TRACE, + "ldap_chkResponseList ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + + lastlm = &ld->ld_responses; + for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { + nextlm = lm->lm_next; + ++cnt; + + if ( ldap_abandoned( ld, lm->lm_msgid ) ) { + Debug2( LDAP_DEBUG_ANY, + "response list msg abandoned, " + "msgid %d message type %s\n", + lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ) ); + + switch ( lm->lm_msgtype ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + break; + + default: + /* there's no need to keep the id + * in the abandoned list any longer */ + ldap_mark_abandoned( ld, lm->lm_msgid ); + break; + } + + /* Remove this entry from list */ + *lastlm = nextlm; + + ldap_msgfree( lm ); + + continue; + } + + if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) { + LDAPMessage *tmp; + + if ( all == LDAP_MSG_ONE || + all == LDAP_MSG_RECEIVED || + msgid == LDAP_RES_UNSOLICITED ) + { + break; + } + + tmp = lm->lm_chain_tail; + if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY || + tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE || + tmp->lm_msgtype == LDAP_RES_INTERMEDIATE ) + { + tmp = NULL; + } + + if ( tmp == NULL ) { + lm = NULL; + } + + break; + } + lastlm = &lm->lm_next; + } + + if ( lm != NULL ) { + /* Found an entry, remove it from the list */ + if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) { + *lastlm = lm->lm_chain; + lm->lm_chain->lm_next = lm->lm_next; + lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain; + lm->lm_chain = NULL; + lm->lm_chain_tail = NULL; + } else { + *lastlm = lm->lm_next; + } + lm->lm_next = NULL; + } + +#ifdef LDAP_DEBUG + if ( lm == NULL) { + Debug1( LDAP_DEBUG_TRACE, + "ldap_chkResponseList returns ld %p NULL\n", (void *)ld ); + } else { + Debug3( LDAP_DEBUG_TRACE, + "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n", + (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype ); + } +#endif + + return lm; +} + +/* protected by res_mutex */ +static int +wait4msg( + LDAP *ld, + ber_int_t msgid, + int all, + struct timeval *timeout, + LDAPMessage **result ) +{ + int rc; + struct timeval tv = { 0 }, + tv0 = { 0 }, + start_time_tv = { 0 }, + *tvp = NULL; + LDAPConn *lc; + + assert( ld != NULL ); + assert( result != NULL ); + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + + if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_api; + timeout = &tv; + } + +#ifdef LDAP_DEBUG + if ( timeout == NULL ) { + Debug2( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n", + (void *)ld, msgid ); + } else { + Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n", + (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec ); + } +#endif /* LDAP_DEBUG */ + + if ( timeout != NULL && timeout->tv_sec != -1 ) { + tv0 = *timeout; + tv = *timeout; + tvp = &tv; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &start_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + start_time_tv.tv_sec = time( NULL ); + start_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + } + + rc = LDAP_MSG_X_KEEP_LOOKING; + while ( rc == LDAP_MSG_X_KEEP_LOOKING ) { +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + Debug3( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + ldap_dump_connection( ld, ld->ld_conns, 1 ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + ldap_dump_requests_and_responses( ld ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + } +#endif /* LDAP_DEBUG */ + + if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) { + rc = (*result)->lm_msgtype; + + } else { + int lc_ready = 0; + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + if ( ber_sockbuf_ctrl( lc->lconn_sb, + LBER_SB_OPT_DATA_READY, NULL ) ) + { + lc_ready = 2; /* ready at ber level, not socket level */ + break; + } + } + + if ( !lc_ready ) { + int err; + rc = ldap_int_select( ld, tvp ); + if ( rc == -1 ) { + err = sock_errno(); +#ifdef LDAP_DEBUG + Debug1( LDAP_DEBUG_TRACE, + "ldap_int_select returned -1: errno %d\n", + err ); +#endif + } + + if ( rc == 0 || ( rc == -1 && ( + !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART) + || err != EINTR ) ) ) + { + ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : + LDAP_TIMEOUT); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return( rc ); + } + + if ( rc == -1 ) { + rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */ + + } else { + lc_ready = 1; + } + } + if ( lc_ready ) { + LDAPConn *lnext; + int serviced = 0; + rc = LDAP_MSG_X_KEEP_LOOKING; + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + if ( ld->ld_requests != NULL ) { + TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT ); + LDAPRequest *lr; + + assert( node != NULL ); + lr = node->avl_data; + if ( lr->lr_status == LDAP_REQST_WRITING && + ldap_is_write_ready( ld, lr->lr_conn->lconn_sb ) ) { + serviced = 1; + ldap_int_flush_request( ld, lr ); + } + } + for ( lc = ld->ld_conns; + rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; + lc = lnext ) + { + if ( lc->lconn_status == LDAP_CONNST_CONNECTED && + ldap_is_read_ready( ld, lc->lconn_sb ) ) + { + serviced = 1; + /* Don't let it get freed out from under us */ + ++lc->lconn_refcnt; + rc = try_read1msg( ld, msgid, all, lc, result ); + lnext = lc->lconn_next; + + /* Only take locks if we're really freeing */ + if ( lc->lconn_refcnt <= 1 ) { + ldap_free_connection( ld, lc, 0, 1 ); + } else { + --lc->lconn_refcnt; + } + } else { + lnext = lc->lconn_next; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + /* Quit looping if no one handled any socket events */ + if (!serviced && lc_ready == 1) + rc = -1; + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + } + + if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { + struct timeval curr_time_tv = { 0 }, + delta_time_tv = { 0 }; + +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &curr_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + curr_time_tv.tv_sec = time( NULL ); + curr_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + + /* delta_time = tmp_time - start_time */ + delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; + delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; + if ( delta_time_tv.tv_usec < 0 ) { + delta_time_tv.tv_sec--; + delta_time_tv.tv_usec += 1000000; + } + + /* tv0 < delta_time ? */ + if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) || + ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) ) + { + rc = 0; /* timed out */ + ld->ld_errno = LDAP_TIMEOUT; + break; + } + + /* tv0 -= delta_time */ + tv0.tv_sec -= delta_time_tv.tv_sec; + tv0.tv_usec -= delta_time_tv.tv_usec; + if ( tv0.tv_usec < 0 ) { + tv0.tv_sec--; + tv0.tv_usec += 1000000; + } + + tv.tv_sec = tv0.tv_sec; + tv.tv_usec = tv0.tv_usec; + + Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n", + (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); + + start_time_tv.tv_sec = curr_time_tv.tv_sec; + start_time_tv.tv_usec = curr_time_tv.tv_usec; + } + } + + return( rc ); +} + + +/* protected by res_mutex, conn_mutex and req_mutex */ +static ber_tag_t +try_read1msg( + LDAP *ld, + ber_int_t msgid, + int all, + LDAPConn *lc, + LDAPMessage **result ) +{ + BerElement *ber; + LDAPMessage *newmsg, *l, *prev; + ber_int_t id; + ber_tag_t tag; + ber_len_t len; + int foundit = 0; + LDAPRequest *lr, *tmplr, dummy_lr = { 0 }; + BerElement tmpber; + int rc, refer_cnt, hadref, simple_request, err; + ber_int_t lderr = -1; + +#ifdef LDAP_CONNECTIONLESS + LDAPMessage *tmp = NULL, *chain_head = NULL; + int moremsgs = 0, isv2 = 0; +#endif + + assert( ld != NULL ); + assert( lc != NULL ); + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + + Debug3( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + +retry: + if ( lc->lconn_ber == NULL ) { + lc->lconn_ber = ldap_alloc_ber_with_options( ld ); + + if ( lc->lconn_ber == NULL ) { + return -1; + } + } + + ber = lc->lconn_ber; + assert( LBER_VALID (ber) ); + + /* get the next message */ + sock_errset(0); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage from; + if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 ) + goto fail; + if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1; + } +nextresp3: +#endif + tag = ber_get_next( lc->lconn_sb, &len, ber ); + switch ( tag ) { + case LDAP_TAG_MESSAGE: + /* + * We read a complete message. + * The connection should no longer need this ber. + */ + lc->lconn_ber = NULL; + break; + + default: + /* + * We read a BerElement that isn't LDAP or the stream has desync'd. + * In either case, anything we read from now on is probably garbage, + * just drop the connection. + */ + ber_free( ber, 1 ); + lc->lconn_ber = NULL; + /* FALLTHRU */ + + case LBER_DEFAULT: +fail: + err = sock_errno(); +#ifdef LDAP_DEBUG + Debug1( LDAP_DEBUG_CONNS, + "ber_get_next failed, errno=%d.\n", err ); +#endif + if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING; + if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING; + ld->ld_errno = LDAP_SERVER_DOWN; + if ( !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) { + --lc->lconn_refcnt; + } + lc->lconn_status = 0; + return -1; + } + + /* message id */ + if ( ber_get_int( ber, &id ) == LBER_ERROR ) { + ber_free( ber, 1 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return( -1 ); + } + + /* id == 0 iff unsolicited notification message (RFC 4511) */ + + /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */ + if ( id < 0 ) { + goto retry_ber; + } + + /* if it's been abandoned, toss it */ + if ( id > 0 ) { + if ( ldap_abandoned( ld, id ) ) { + /* the message type */ + tag = ber_peek_tag( ber, &len ); + switch ( tag ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + case LBER_ERROR: + break; + + default: + /* there's no need to keep the id + * in the abandoned list any longer */ + ldap_mark_abandoned( ld, id ); + break; + } + + Debug3( LDAP_DEBUG_ANY, + "abandoned/discarded ld %p msgid %d message type %s\n", + (void *)ld, id, ldap_int_msgtype2str( tag ) ); + +retry_ber: + ber_free( ber, 1 ); + if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { + goto retry; + } + return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ + } + + lr = ldap_find_request_by_msgid( ld, id ); + if ( lr == NULL ) { + const char *msg = "unknown"; + + /* the message type */ + tag = ber_peek_tag( ber, &len ); + switch ( tag ) { + case LBER_ERROR: + break; + + default: + msg = ldap_int_msgtype2str( tag ); + break; + } + + Debug3( LDAP_DEBUG_ANY, + "no request for response on ld %p msgid %d message type %s (tossing)\n", + (void *)ld, id, msg ); + + goto retry_ber; + } + +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) && isv2 ) { + ber_scanf(ber, "x{"); + } +nextresp2: + ; +#endif + } + + /* the message type */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( -1 ); + } + + Debug3( LDAP_DEBUG_TRACE, + "read1msg: ld %p msgid %d message type %s\n", + (void *)ld, id, ldap_int_msgtype2str( tag ) ); + + if ( id == 0 ) { + /* unsolicited notification message (RFC 4511) */ + if ( tag != LDAP_RES_EXTENDED ) { + /* toss it */ + goto retry_ber; + + /* strictly speaking, it's an error; from RFC 4511: + +4.4. Unsolicited Notification + + An unsolicited notification is an LDAPMessage sent from the server to + the client that is not in response to any LDAPMessage received by the + server. It is used to signal an extraordinary condition in the + server or in the LDAP session between the client and the server. The + notification is of an advisory nature, and the server will not expect + any response to be returned from the client. + + The unsolicited notification is structured as an LDAPMessage in which + the messageID is zero and protocolOp is set to the extendedResp + choice using the ExtendedResponse type (See Section 4.12). The + responseName field of the ExtendedResponse always contains an LDAPOID + that is unique for this notification. + + * however, since unsolicited responses + * are of advisory nature, better + * toss it, right now + */ + +#if 0 + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( -1 ); +#endif + } + + lr = &dummy_lr; + } + + id = lr->lr_origid; + refer_cnt = 0; + hadref = simple_request = 0; + rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */ + lr->lr_res_msgtype = tag; + + /* + * Check for V3 search reference + */ + if ( tag == LDAP_RES_SEARCH_REFERENCE ) { + if ( ld->ld_version > LDAP_VERSION2 ) { + /* This is a V3 search reference */ + if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || + lr->lr_parent != NULL ) + { + char **refs = NULL; + tmpber = *ber; + + /* Get the referral list */ + if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + + } else { + /* Note: refs array is freed by ldap_chase_v3referrals */ + refer_cnt = ldap_chase_v3referrals( ld, lr, refs, + 1, &lr->lr_res_error, &hadref ); + if ( refer_cnt > 0 ) { + /* successfully chased reference */ + /* If haven't got end search, set chasing referrals */ + if ( lr->lr_status != LDAP_REQST_COMPLETED ) { + lr->lr_status = LDAP_REQST_CHASINGREFS; + Debug1( LDAP_DEBUG_TRACE, + "read1msg: search ref chased, " + "mark request chasing refs, " + "id = %d\n", + lr->lr_msgid ); + } + } + } + } + } + + } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) { + /* All results that just return a status, i.e. don't return data + * go through the following code. This code also chases V2 referrals + * and checks if all referrals have been chased. + */ + char *lr_res_error = NULL; + + tmpber = *ber; /* struct copy */ + if ( ber_scanf( &tmpber, "{eAA", &lderr, + &lr->lr_res_matched, &lr_res_error ) + != LBER_ERROR ) + { + if ( lr_res_error != NULL ) { + if ( lr->lr_res_error != NULL ) { + (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error ); + LDAP_FREE( (char *)lr_res_error ); + + } else { + lr->lr_res_error = lr_res_error; + } + lr_res_error = NULL; + } + + /* Do we need to check for referrals? */ + if ( tag != LDAP_RES_BIND && + ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || + lr->lr_parent != NULL )) + { + char **refs = NULL; + ber_len_t len; + + /* Check if V3 referral */ + if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) { + if ( ld->ld_version > LDAP_VERSION2 ) { + /* Get the referral list */ + if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) { + rc = LDAP_DECODING_ERROR; + lr->lr_status = LDAP_REQST_COMPLETED; + Debug2( LDAP_DEBUG_TRACE, + "read1msg: referral decode error, " + "mark request completed, ld %p msgid %d\n", + (void *)ld, lr->lr_msgid ); + + } else { + /* Chase the referral + * refs array is freed by ldap_chase_v3referrals + */ + refer_cnt = ldap_chase_v3referrals( ld, lr, refs, + 0, &lr->lr_res_error, &hadref ); + lr->lr_status = LDAP_REQST_COMPLETED; + Debug3( LDAP_DEBUG_TRACE, + "read1msg: referral %s chased, " + "mark request completed, ld %p msgid %d\n", + refer_cnt > 0 ? "" : "not", + (void *)ld, lr->lr_msgid); + if ( refer_cnt < 0 ) { + refer_cnt = 0; + } + } + } + } else { + switch ( lderr ) { + case LDAP_SUCCESS: + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + break; + + default: + if ( lr->lr_res_error == NULL ) { + break; + } + + /* pedantic, should never happen */ + if ( lr->lr_res_error[ 0 ] == '\0' ) { + LDAP_FREE( lr->lr_res_error ); + lr->lr_res_error = NULL; + break; + } + + /* V2 referrals are in error string */ + refer_cnt = ldap_chase_referrals( ld, lr, + &lr->lr_res_error, -1, &hadref ); + lr->lr_status = LDAP_REQST_COMPLETED; + Debug1( LDAP_DEBUG_TRACE, + "read1msg: V2 referral chased, " + "mark request completed, id = %d\n", + lr->lr_msgid ); + break; + } + } + } + + /* save errno, message, and matched string */ + if ( !hadref || lr->lr_res_error == NULL ) { + lr->lr_res_errno = + lderr == LDAP_PARTIAL_RESULTS + ? LDAP_SUCCESS : lderr; + + } else if ( ld->ld_errno != LDAP_SUCCESS ) { + lr->lr_res_errno = ld->ld_errno; + + } else { + lr->lr_res_errno = LDAP_PARTIAL_RESULTS; + } + } + + /* in any case, don't leave any lr_res_error 'round */ + if ( lr_res_error ) { + LDAP_FREE( lr_res_error ); + } + + Debug2( LDAP_DEBUG_TRACE, + "read1msg: ld %p %d new referrals\n", + (void *)ld, refer_cnt ); + + if ( refer_cnt != 0 ) { /* chasing referrals */ + ber_free( ber, 1 ); + ber = NULL; + if ( refer_cnt < 0 ) { + ldap_return_request( ld, lr, 0 ); + return( -1 ); /* fatal error */ + } + lr->lr_res_errno = LDAP_SUCCESS; /* successfully chased referral */ + if ( lr->lr_res_matched ) { + LDAP_FREE( lr->lr_res_matched ); + lr->lr_res_matched = NULL; + } + + } else { + if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) { + /* request without any referrals */ + simple_request = ( hadref ? 0 : 1 ); + + } else { + /* request with referrals or child request */ + ber_free( ber, 1 ); + ber = NULL; + } + + lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */ + Debug2( LDAP_DEBUG_TRACE, + "read1msg: mark request completed, ld %p msgid %d\n", + (void *)ld, lr->lr_msgid ); + tmplr = lr; + while ( lr->lr_parent != NULL ) { + merge_error_info( ld, lr->lr_parent, lr ); + + lr = lr->lr_parent; + if ( --lr->lr_outrefcnt > 0 ) { + break; /* not completely done yet */ + } + } + /* ITS#6744: Original lr was refcounted when we retrieved it, + * must release it now that we're working with the parent + */ + if ( tmplr->lr_parent ) { + ldap_return_request( ld, tmplr, 0 ); + } + + /* Check if all requests are finished, lr is now parent */ + tmplr = lr; + if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) { + for ( tmplr = lr->lr_child; + tmplr != NULL; + tmplr = tmplr->lr_refnext ) + { + if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break; + } + } + + /* This is the parent request if the request has referrals */ + if ( lr->lr_outrefcnt <= 0 && + lr->lr_parent == NULL && + tmplr == NULL ) + { + id = lr->lr_msgid; + tag = lr->lr_res_msgtype; + Debug2( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n", + (void *)ld, id ); + Debug3( LDAP_DEBUG_TRACE, + "res_errno: %d, res_error: <%s>, " + "res_matched: <%s>\n", + lr->lr_res_errno, + lr->lr_res_error ? lr->lr_res_error : "", + lr->lr_res_matched ? lr->lr_res_matched : "" ); + if ( !simple_request ) { + ber_free( ber, 1 ); + ber = NULL; + if ( build_result_ber( ld, &ber, lr ) + == LBER_ERROR ) + { + rc = -1; /* fatal error */ + } + } + + if ( lr != &dummy_lr ) { + ldap_return_request( ld, lr, 1 ); + } + lr = NULL; + } + + /* + * RFC 4511 unsolicited (id == 0) responses + * shouldn't necessarily end the connection + */ + if ( lc != NULL && id != 0 && + !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) { + --lc->lconn_refcnt; + lc = NULL; + } + } + } + + if ( lr != NULL ) { + if ( lr != &dummy_lr ) { + ldap_return_request( ld, lr, 0 ); + } + lr = NULL; + } + + if ( ber == NULL ) { + return( rc ); + } + + /* try to handle unsolicited responses as appropriate */ + if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) { + int is_nod = 0; + + tag = ber_peek_tag( &tmpber, &len ); + + /* we have a res oid */ + if ( tag == LDAP_TAG_EXOP_RES_OID ) { + static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION ); + struct berval resoid = BER_BVNULL; + + if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return -1; + } + + assert( !BER_BVISEMPTY( &resoid ) ); + + is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0; + + tag = ber_peek_tag( &tmpber, &len ); + } + +#if 0 /* don't need right now */ + /* we have res data */ + if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { + struct berval resdata; + + if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + /* use it... */ + } +#endif + + /* handle RFC 4511 "Notice of Disconnection" locally */ + + if ( is_nod ) { + if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return -1; + } + + /* get rid of the connection... */ + if ( lc != NULL && + !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) { + --lc->lconn_refcnt; + } + + /* need to return -1, because otherwise + * a valid result is expected */ + ld->ld_errno = lderr; + return -1; + } + } + + /* make a new ldap message */ + newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ); + if ( newmsg == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + newmsg->lm_msgid = (int)id; + newmsg->lm_msgtype = tag; + newmsg->lm_ber = ber; + newmsg->lm_chain_tail = newmsg; + +#ifdef LDAP_CONNECTIONLESS + /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798 + * the responses are all a sequence wrapped in one message. In + * LDAPv3 each response is in its own message. The datagram must + * end with a SearchResult. We can't just parse each response in + * separate calls to try_read1msg because the header info is only + * present at the beginning of the datagram, not at the beginning + * of each response. So parse all the responses at once and queue + * them up, then pull off the first response to return to the + * caller when all parsing is complete. + */ + if ( LDAP_IS_UDP(ld) ) { + /* If not a result, look for more */ + if ( tag != LDAP_RES_SEARCH_RESULT ) { + int ok = 0; + moremsgs = 1; + if (isv2) { + /* LDAPv2: dup the current ber, skip past the current + * response, and see if there are any more after it. + */ + ber = ber_dup( ber ); + ber_scanf( ber, "x" ); + if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) { + /* There's more - dup the ber buffer so they can all be + * individually freed by ldap_msgfree. + */ + struct berval bv; + ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len ); + bv.bv_val = LDAP_MALLOC( len ); + if ( bv.bv_val ) { + ok = 1; + ber_read( ber, bv.bv_val, len ); + bv.bv_len = len; + ber_init2( ber, &bv, ld->ld_lberoptions ); + } + } + } else { + /* LDAPv3: Just allocate a new ber. Since this is a buffered + * datagram, if the sockbuf is readable we still have data + * to parse. + */ + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return -1; + } + + if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1; + } + /* set up response chain */ + if ( tmp == NULL ) { + newmsg->lm_next = ld->ld_responses; + ld->ld_responses = newmsg; + chain_head = newmsg; + } else { + tmp->lm_chain = newmsg; + } + chain_head->lm_chain_tail = newmsg; + tmp = newmsg; + /* "ok" means there's more to parse */ + if ( ok ) { + if ( isv2 ) { + goto nextresp2; + + } else { + goto nextresp3; + } + } else { + /* got to end of datagram without a SearchResult. Free + * our dup'd ber, but leave any buffer alone. For v2 case, + * the previous response is still using this buffer. For v3, + * the new ber has no buffer to free yet. + */ + ber_free( ber, 0 ); + return -1; + } + } else if ( moremsgs ) { + /* got search result, and we had multiple responses in 1 datagram. + * stick the result onto the end of the chain, and then pull the + * first response off the head of the chain. + */ + tmp->lm_chain = newmsg; + chain_head->lm_chain_tail = newmsg; + *result = chkResponseList( ld, msgid, all ); + ld->ld_errno = LDAP_SUCCESS; + return( (*result)->lm_msgtype ); + } + } +#endif /* LDAP_CONNECTIONLESS */ + + /* is this the one we're looking for? */ + if ( msgid == LDAP_RES_ANY || id == msgid ) { + if ( all == LDAP_MSG_ONE + || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT + && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY + && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE + && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) + { + *result = newmsg; + ld->ld_errno = LDAP_SUCCESS; + return( tag ); + + } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) { + foundit = 1; /* return the chain later */ + } + } + + /* + * if not, we must add it to the list of responses. if + * the msgid is already there, it must be part of an existing + * search response. + */ + + prev = NULL; + for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) { + if ( l->lm_msgid == newmsg->lm_msgid ) { + break; + } + prev = l; + } + + /* not part of an existing search response */ + if ( l == NULL ) { + if ( foundit ) { + *result = newmsg; + goto exit; + } + + newmsg->lm_next = ld->ld_responses; + ld->ld_responses = newmsg; + goto exit; + } + + Debug3( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n", + (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype ); + + /* part of a search response - add to end of list of entries */ + l->lm_chain_tail->lm_chain = newmsg; + l->lm_chain_tail = newmsg; + + /* return the whole chain if that's what we were looking for */ + if ( foundit ) { + if ( prev == NULL ) { + ld->ld_responses = l->lm_next; + } else { + prev->lm_next = l->lm_next; + } + *result = l; + } + +exit: + if ( foundit ) { + ld->ld_errno = LDAP_SUCCESS; + return( tag ); + } + if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { + goto retry; + } + return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ +} + + +static ber_tag_t +build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr ) +{ + ber_len_t len; + ber_tag_t tag; + ber_int_t along; + BerElement *ber; + + *bp = NULL; + ber = ldap_alloc_ber_with_options( ld ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return LBER_ERROR; + } + + if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid, + lr->lr_res_msgtype, lr->lr_res_errno, + lr->lr_res_matched ? lr->lr_res_matched : "", + lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + ber_reset( ber, 1 ); + + if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + if ( ber_get_enum( ber, &along ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + tag = ber_peek_tag( ber, &len ); + + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + *bp = ber; + return tag; +} + + +/* + * Merge error information in "lr" with "parentr" error code and string. + */ +static void +merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ) +{ + if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) { + parentr->lr_res_errno = lr->lr_res_errno; + if ( lr->lr_res_error != NULL ) { + (void)ldap_append_referral( ld, &parentr->lr_res_error, + lr->lr_res_error ); + } + + } else if ( lr->lr_res_errno != LDAP_SUCCESS && + parentr->lr_res_errno == LDAP_SUCCESS ) + { + parentr->lr_res_errno = lr->lr_res_errno; + if ( parentr->lr_res_error != NULL ) { + LDAP_FREE( parentr->lr_res_error ); + } + parentr->lr_res_error = lr->lr_res_error; + lr->lr_res_error = NULL; + if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) { + if ( parentr->lr_res_matched != NULL ) { + LDAP_FREE( parentr->lr_res_matched ); + } + parentr->lr_res_matched = lr->lr_res_matched; + lr->lr_res_matched = NULL; + } + } + + Debug1( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ", + parentr->lr_msgid ); + Debug3( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n", + parentr->lr_res_errno, + parentr->lr_res_error ? parentr->lr_res_error : "", + parentr->lr_res_matched ? parentr->lr_res_matched : "" ); +} + + + +int +ldap_msgtype( LDAPMessage *lm ) +{ + assert( lm != NULL ); + return ( lm != NULL ) ? (int)lm->lm_msgtype : -1; +} + + +int +ldap_msgid( LDAPMessage *lm ) +{ + assert( lm != NULL ); + + return ( lm != NULL ) ? lm->lm_msgid : -1; +} + + +const char * +ldap_int_msgtype2str( ber_tag_t tag ) +{ + switch( tag ) { + case LDAP_RES_ADD: return "add"; + case LDAP_RES_BIND: return "bind"; + case LDAP_RES_COMPARE: return "compare"; + case LDAP_RES_DELETE: return "delete"; + case LDAP_RES_EXTENDED: return "extended-result"; + case LDAP_RES_INTERMEDIATE: return "intermediate"; + case LDAP_RES_MODIFY: return "modify"; + case LDAP_RES_RENAME: return "rename"; + case LDAP_RES_SEARCH_ENTRY: return "search-entry"; + case LDAP_RES_SEARCH_REFERENCE: return "search-reference"; + case LDAP_RES_SEARCH_RESULT: return "search-result"; + } + return "unknown"; +} + +int +ldap_msgfree( LDAPMessage *lm ) +{ + LDAPMessage *next; + int type = 0; + + Debug0( LDAP_DEBUG_TRACE, "ldap_msgfree\n" ); + + for ( ; lm != NULL; lm = next ) { + next = lm->lm_chain; + type = lm->lm_msgtype; + ber_free( lm->lm_ber, 1 ); + LDAP_FREE( (char *) lm ); + } + + return type; +} + +/* + * ldap_msgdelete - delete a message. It returns: + * 0 if the entire message was deleted + * -1 if the message was not found, or only part of it was found + */ +int +ldap_msgdelete( LDAP *ld, int msgid ) +{ + LDAPMessage *lm, *prev; + int rc = 0; + + assert( ld != NULL ); + + Debug2( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n", + (void *)ld, msgid ); + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + prev = NULL; + for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) { + if ( lm->lm_msgid == msgid ) { + break; + } + prev = lm; + } + + if ( lm == NULL ) { + rc = -1; + + } else { + if ( prev == NULL ) { + ld->ld_responses = lm->lm_next; + } else { + prev->lm_next = lm->lm_next; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + if ( lm ) { + switch ( ldap_msgfree( lm ) ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + rc = -1; + break; + + default: + break; + } + } + + return rc; +} + + +/* + * ldap_abandoned + * + * return the location of the message id in the array of abandoned + * message ids, or -1 + */ +static int +ldap_abandoned( LDAP *ld, ber_int_t msgid ) +{ + int ret, idx; + assert( msgid >= 0 ); + + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; +} + +/* + * ldap_mark_abandoned + */ +static int +ldap_mark_abandoned( LDAP *ld, ber_int_t msgid ) +{ + int ret, idx; + + assert( msgid >= 0 ); + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + if (ret <= 0) { /* error or already deleted by another thread */ + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; + } + /* still in abandoned array, so delete */ + ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned, + msgid, idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; +} diff --git a/contrib/libs/openldap/libraries/libldap/rq.c b/contrib/libs/openldap/libraries/libldap/rq.c new file mode 100644 index 0000000000..6b16409ded --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/rq.c @@ -0,0 +1,225 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2003-2022 The OpenLDAP Foundation. + * Portions Copyright 2003 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Jong Hyuk Choi for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_R_COMPILE + +#include "ldap_pvt_thread.h" +#include "ldap_queue.h" +#include "ldap_rq.h" + +struct re_s * +ldap_pvt_runqueue_insert( + struct runqueue_s* rq, + time_t interval, + ldap_pvt_thread_start_t *routine, + void *arg, + char *tname, + char *tspec +) +{ + struct re_s* entry; + + entry = (struct re_s *) LDAP_CALLOC( 1, sizeof( struct re_s )); + if ( entry ) { + entry->interval.tv_sec = interval; + entry->interval.tv_usec = 0; + entry->next_sched.tv_sec = time( NULL ); + entry->next_sched.tv_usec = 0; + entry->routine = routine; + entry->arg = arg; + entry->tname = tname; + entry->tspec = tspec; + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } + return entry; +} + +struct re_s * +ldap_pvt_runqueue_find( + struct runqueue_s *rq, + ldap_pvt_thread_start_t *routine, + void *arg +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->routine == routine && e->arg == arg ) + return e; + } + return NULL; +} + +void +ldap_pvt_runqueue_remove( + struct runqueue_s* rq, + struct re_s* entry +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e == entry) + break; + } + + assert( e == entry ); + + LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext ); + + LDAP_FREE( entry ); +} + +struct re_s* +ldap_pvt_runqueue_next_sched( + struct runqueue_s* rq, + struct timeval* next_run +) +{ + struct re_s* entry; + + entry = LDAP_STAILQ_FIRST( &rq->task_list ); + if ( entry == NULL || entry->next_sched.tv_sec == 0 ) { + return NULL; + } else { + *next_run = entry->next_sched; + return entry; + } +} + +void +ldap_pvt_runqueue_runtask( + struct runqueue_s* rq, + struct re_s* entry +) +{ + LDAP_STAILQ_INSERT_TAIL( &rq->run_list, entry, rnext ); +} + +void +ldap_pvt_runqueue_stoptask( + struct runqueue_s* rq, + struct re_s* entry +) +{ + LDAP_STAILQ_REMOVE( &rq->run_list, entry, re_s, rnext ); +} + +int +ldap_pvt_runqueue_isrunning( + struct runqueue_s* rq, + struct re_s* entry +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->run_list, rnext ) { + if ( e == entry ) { + return 1; + } + } + return 0; +} + +void +ldap_pvt_runqueue_resched( + struct runqueue_s* rq, + struct re_s* entry, + int defer +) +{ + struct re_s* prev; + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e == entry ) + break; + } + + assert ( e == entry ); + + LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext ); + + if ( !defer ) { + entry->next_sched.tv_sec = time( NULL ) + entry->interval.tv_sec; + } else { + entry->next_sched.tv_sec = 0; + } + + if ( LDAP_STAILQ_EMPTY( &rq->task_list )) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else if ( entry->next_sched.tv_sec == 0 ) { + LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext ); + } else { + prev = NULL; + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->next_sched.tv_sec == 0 ) { + if ( prev == NULL ) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else { + LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext ); + } + return; + } else if ( e->next_sched.tv_sec > entry->next_sched.tv_sec ) { + if ( prev == NULL ) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else { + LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext ); + } + return; + } + prev = e; + } + LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext ); + } +} + +int +ldap_pvt_runqueue_persistent_backload( + struct runqueue_s* rq +) +{ + struct re_s* e; + int count = 0; + + ldap_pvt_thread_mutex_lock( &rq->rq_mutex ); + if ( !LDAP_STAILQ_EMPTY( &rq->task_list )) { + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->next_sched.tv_sec == 0 ) + count++; + } + } + ldap_pvt_thread_mutex_unlock( &rq->rq_mutex ); + return count; +} + +#endif /* LDAP_R_COMPILE */ diff --git a/contrib/libs/openldap/libraries/libldap/sasl.c b/contrib/libs/openldap/libraries/libldap/sasl.c new file mode 100644 index 0000000000..fa5fc13282 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/sasl.c @@ -0,0 +1,867 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> + +#include "ldap-int.h" + +BerElement * +ldap_build_bind_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + if( mechanism == LDAP_SASL_SIMPLE ) { + if( dn == NULL && cred != NULL && cred->bv_len ) { + /* use default binddn */ + dn = ld->ld_defbinddn; + } + + } else if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return( NULL ); + } + + if ( dn == NULL ) { + dn = ""; + } + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if( mechanism == LDAP_SASL_SIMPLE ) { + /* simple bind */ + rc = ber_printf( ber, "{it{istON}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SIMPLE, + cred ); + + } else if ( cred == NULL || cred->bv_val == NULL ) { + /* SASL bind w/o credentials */ + rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SASL, + mechanism ); + + } else { + /* SASL bind w/ credentials */ + rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SASL, + mechanism, cred ); + } + + if( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_sasl_bind - bind to the ldap server (and X.500). + * The dn (usually NULL), mechanism, and credentials are provided. + * The message id of the request initiated is provided upon successful + * (LDAP_SUCCESS) return. + * + * Example: + * ldap_sasl_bind( ld, NULL, "mechanism", + * cred, NULL, NULL, &msgid ) + */ + +int +ldap_sasl_bind( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_bind_req( ld, dn, mechanism, cred, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + + +int +ldap_sasl_bind_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct berval **servercredp ) +{ + int rc, msgid; + LDAPMessage *result; + struct berval *scredp = NULL; + + Debug0( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n" ); + + /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ + if( servercredp != NULL ) { + if (ld->ld_version < LDAP_VERSION3) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + *servercredp = NULL; + } + + rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + return( rc ); + } +#endif + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + return( ld->ld_errno ); /* ldap_result sets ld_errno */ + } + + /* parse the results */ + scredp = NULL; + if( servercredp != NULL ) { + rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 ); + } + + if ( rc != LDAP_SUCCESS ) { + ldap_msgfree( result ); + return( rc ); + } + + rc = ldap_result2error( ld, result, 1 ); + + if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) { + if( servercredp != NULL ) { + *servercredp = scredp; + scredp = NULL; + } + } + + if ( scredp != NULL ) { + ber_bvfree(scredp); + } + + return rc; +} + + +/* +* Parse BindResponse: +* +* BindResponse ::= [APPLICATION 1] SEQUENCE { +* COMPONENTS OF LDAPResult, +* serverSaslCreds [7] OCTET STRING OPTIONAL } +* +* LDAPResult ::= SEQUENCE { +* resultCode ENUMERATED, +* matchedDN LDAPDN, +* errorMessage LDAPString, +* referral [3] Referral OPTIONAL } +*/ + +int +ldap_parse_sasl_bind_result( + LDAP *ld, + LDAPMessage *res, + struct berval **servercredp, + int freeit ) +{ + ber_int_t errcode; + struct berval* scred; + + ber_tag_t tag; + BerElement *ber; + + Debug0( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + if( servercredp != NULL ) { + if( ld->ld_version < LDAP_VERSION2 ) { + return LDAP_NOT_SUPPORTED; + } + *servercredp = NULL; + } + + if( res->lm_msgtype != LDAP_RES_BIND ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + scred = NULL; + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + /* parse results */ + + ber = ber_dup( res->lm_ber ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + if ( ld->ld_version < LDAP_VERSION2 ) { + tag = ber_scanf( ber, "{iA}", + &errcode, &ld->ld_error ); + + if( tag == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + } else { + ber_len_t len; + + tag = ber_scanf( ber, "{eAA" /*}*/, + &errcode, &ld->ld_matched, &ld->ld_error ); + + if( tag == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + tag = ber_peek_tag(ber, &len); + + if( tag == LDAP_TAG_REFERRAL ) { + /* skip 'em */ + if( ber_scanf( ber, "x" ) == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + tag = ber_peek_tag(ber, &len); + } + + if( tag == LDAP_TAG_SASL_RES_CREDS ) { + if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + } + } + + ber_free( ber, 0 ); + + if ( servercredp != NULL ) { + *servercredp = scred; + + } else if ( scred != NULL ) { + ber_bvfree( scred ); + } + + ld->ld_errno = errcode; + + if ( freeit ) { + ldap_msgfree( res ); + } + + return( LDAP_SUCCESS ); +} + +int +ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist ) +{ + /* we need to query the server for supported mechs anyway */ + LDAPMessage *res, *e; + char *attrs[] = { "supportedSASLMechanisms", NULL }; + char **values, *mechlist; + int rc; + + Debug0( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n" ); + + rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE, + NULL, attrs, 0, &res ); + + if ( rc != LDAP_SUCCESS ) { + return ld->ld_errno; + } + + e = ldap_first_entry( ld, res ); + if ( e == NULL ) { + ldap_msgfree( res ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = LDAP_NO_SUCH_OBJECT; + } + return ld->ld_errno; + } + + values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); + if ( values == NULL ) { + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE; + return ld->ld_errno; + } + + mechlist = ldap_charray2str( values, " " ); + if ( mechlist == NULL ) { + LDAP_VFREE( values ); + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + LDAP_VFREE( values ); + ldap_msgfree( res ); + + *pmechlist = mechlist; + + return LDAP_SUCCESS; +} + +/* + * ldap_sasl_interactive_bind - interactive SASL authentication + * + * This routine uses interactive callbacks. + * + * LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further + * calls are needed. + */ +int +ldap_sasl_interactive_bind( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mechs, + LDAPControl **serverControls, + LDAPControl **clientControls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ + char *smechs = NULL; + int rc; + +#ifdef LDAP_CONNECTIONLESS + if( LDAP_IS_UDP(ld) ) { + /* Just force it to simple bind, silly to make the user + * ask all the time. No, we don't ever actually bind, but I'll + * let the final bind handler take care of saving the cdn. + */ + rc = ldap_simple_bind( ld, dn, NULL ); + rc = rc < 0 ? rc : 0; + goto done; + } else +#endif + + /* First time */ + if ( !result ) { + +#ifdef HAVE_CYRUS_SASL + if( mechs == NULL || *mechs == '\0' ) { + mechs = ld->ld_options.ldo_def_sasl_mech; + } +#endif + + if( mechs == NULL || *mechs == '\0' ) { + /* FIXME: this needs to be asynchronous too; + * perhaps NULL should be disallowed for async usage? + */ + rc = ldap_pvt_sasl_getmechs( ld, &smechs ); + if( rc != LDAP_SUCCESS ) { + goto done; + } + + Debug1( LDAP_DEBUG_TRACE, + "ldap_sasl_interactive_bind: server supports: %s\n", + smechs ); + + mechs = smechs; + + } else { + Debug1( LDAP_DEBUG_TRACE, + "ldap_sasl_interactive_bind: user selected: %s\n", + mechs ); + } + } + rc = ldap_int_sasl_bind( ld, dn, mechs, + serverControls, clientControls, + flags, interact, defaults, result, rmech, msgid ); + +done: + if ( smechs ) LDAP_FREE( smechs ); + + return rc; +} + +/* + * ldap_sasl_interactive_bind_s - interactive SASL authentication + * + * This routine uses interactive callbacks. + * + * LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + */ +int +ldap_sasl_interactive_bind_s( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mechs, + LDAPControl **serverControls, + LDAPControl **clientControls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults ) +{ + const char *rmech = NULL; + LDAPMessage *result = NULL; + int rc, msgid; + + do { + rc = ldap_sasl_interactive_bind( ld, dn, mechs, + serverControls, clientControls, + flags, interact, defaults, result, &rmech, &msgid ); + + ldap_msgfree( result ); + + if ( rc != LDAP_SASL_BIND_IN_PROGRESS ) + break; + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + break; + } +#endif + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + return( ld->ld_errno ); /* ldap_result sets ld_errno */ + } + } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); + + return rc; +} + +#ifdef HAVE_CYRUS_SASL + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#endif /* HAVE_CYRUS_SASL */ + +static int +sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod ); + +static int +sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct sb_sasl_generic_data *p; + struct sb_sasl_generic_install *i; + + assert( sbiod != NULL ); + + i = (struct sb_sasl_generic_install *)arg; + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) + return -1; + p->ops = i->ops; + p->ops_private = i->ops_private; + p->sbiod = sbiod; + p->flags = 0; + ber_pvt_sb_buf_init( &p->sec_buf_in ); + ber_pvt_sb_buf_init( &p->buf_in ); + ber_pvt_sb_buf_init( &p->buf_out ); + + sbiod->sbiod_pvt = p; + + p->ops->init( p, &p->min_send, &p->max_send, &p->max_recv ); + + if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, p->min_send ) < 0 ) { + sb_sasl_generic_remove( sbiod ); + sock_errset(ENOMEM); + return -1; + } + + return 0; +} + +static int +sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct sb_sasl_generic_data *p; + + assert( sbiod != NULL ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + p->ops->fini(p); + + ber_pvt_sb_buf_destroy( &p->sec_buf_in ); + ber_pvt_sb_buf_destroy( &p->buf_in ); + ber_pvt_sb_buf_destroy( &p->buf_out ); + LBER_FREE( p ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static ber_len_t +sb_sasl_generic_pkt_length( + struct sb_sasl_generic_data *p, + const unsigned char *buf, + int debuglevel ) +{ + ber_len_t size; + + assert( buf != NULL ); + + size = buf[0] << 24 + | buf[1] << 16 + | buf[2] << 8 + | buf[3]; + + if ( size > p->max_recv ) { + /* somebody is trying to mess me up. */ + ber_log_printf( LDAP_DEBUG_ANY, debuglevel, + "sb_sasl_generic_pkt_length: " + "received illegal packet length of %lu bytes\n", + (unsigned long)size ); + size = 16; /* this should lead to an error. */ + } + + return size + 4; /* include the size !!! */ +} + +/* Drop a processed packet from the input buffer */ +static void +sb_sasl_generic_drop_packet ( + struct sb_sasl_generic_data *p, + int debuglevel ) +{ + ber_slen_t len; + + len = p->sec_buf_in.buf_ptr - p->sec_buf_in.buf_end; + if ( len > 0 ) + AC_MEMCPY( p->sec_buf_in.buf_base, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_end, len ); + + if ( len >= 4 ) { + p->sec_buf_in.buf_end = sb_sasl_generic_pkt_length(p, + (unsigned char *) p->sec_buf_in.buf_base, debuglevel); + } + else { + p->sec_buf_in.buf_end = 0; + } + p->sec_buf_in.buf_ptr = len; +} + +static ber_slen_t +sb_sasl_generic_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct sb_sasl_generic_data *p; + ber_slen_t ret, bufptr; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + /* Are there anything left in the buffer? */ + ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len ); + bufptr = ret; + len -= ret; + + if ( len == 0 ) + return bufptr; + + p->ops->reset_buf( p, &p->buf_in ); + + /* Read the length of the packet */ + while ( p->sec_buf_in.buf_ptr < 4 ) { + ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_ptr, + 4 - p->sec_buf_in.buf_ptr ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return bufptr ? bufptr : ret; + + p->sec_buf_in.buf_ptr += ret; + } + + /* The new packet always starts at p->sec_buf_in.buf_base */ + ret = sb_sasl_generic_pkt_length(p, (unsigned char *) p->sec_buf_in.buf_base, + sbiod->sbiod_sb->sb_debug ); + + /* Grow the packet buffer if necessary */ + if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) && + ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 ) + { + sock_errset(ENOMEM); + return -1; + } + p->sec_buf_in.buf_end = ret; + + /* Did we read the whole encrypted packet? */ + while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) { + /* No, we have got only a part of it */ + ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr; + + ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_ptr, ret ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return bufptr ? bufptr : ret; + + p->sec_buf_in.buf_ptr += ret; + } + + /* Decode the packet */ + ret = p->ops->decode( p, &p->sec_buf_in, &p->buf_in ); + + /* Drop the packet from the input buffer */ + sb_sasl_generic_drop_packet( p, sbiod->sbiod_sb->sb_debug ); + + if ( ret != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, + "sb_sasl_generic_read: failed to decode packet\n" ); + sock_errset(EIO); + return -1; + } + + bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len ); + + return bufptr; +} + +static ber_slen_t +sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct sb_sasl_generic_data *p; + int ret; + ber_len_t len2; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + /* Is there anything left in the buffer? */ + if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); + if ( ret < 0 ) return ret; + + /* Still have something left?? */ + if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + sock_errset(EAGAIN); + return -1; + } + } + + len2 = p->max_send - 100; /* For safety margin */ + len2 = len > len2 ? len2 : len; + + /* If we're just retrying a partial write, tell the + * caller it's done. Let them call again if there's + * still more left to write. + */ + if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) { + p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE; + return len2; + } + + /* now encode the next packet. */ + p->ops->reset_buf( p, &p->buf_out ); + + ret = p->ops->encode( p, buf, len2, &p->buf_out ); + + if ( ret != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, + "sb_sasl_generic_write: failed to encode packet\n" ); + sock_errset(EIO); + return -1; + } + + ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); + + if ( ret < 0 ) { + /* error? */ + int err = sock_errno(); + /* caller can retry this */ + if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR ) + p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE; + return ret; + } else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + /* partial write? pretend nothing got written */ + p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE; + sock_errset(EAGAIN); + len2 = -1; + } + + /* return number of bytes encoded, not written, to ensure + * no byte is encoded twice (even if only sent once). + */ + return len2; +} + +static int +sb_sasl_generic_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct sb_sasl_generic_data *p; + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) return 1; + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +Sockbuf_IO ldap_pvt_sockbuf_io_sasl_generic = { + sb_sasl_generic_setup, /* sbi_setup */ + sb_sasl_generic_remove, /* sbi_remove */ + sb_sasl_generic_ctrl, /* sbi_ctrl */ + sb_sasl_generic_read, /* sbi_read */ + sb_sasl_generic_write, /* sbi_write */ + NULL /* sbi_close */ +}; + +int ldap_pvt_sasl_generic_install( + Sockbuf *sb, + struct sb_sasl_generic_install *install_arg ) +{ + Debug0( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_generic_install\n" ); + + /* don't install the stuff unless security has been negotiated */ + + if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, + &ldap_pvt_sockbuf_io_sasl_generic ) ) + { +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_generic_" ); +#endif + ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl_generic, + LBER_SBIOD_LEVEL_APPLICATION, install_arg ); + } + + return LDAP_SUCCESS; +} + +void ldap_pvt_sasl_generic_remove( Sockbuf *sb ) +{ + ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl_generic, + LBER_SBIOD_LEVEL_APPLICATION ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_APPLICATION ); +#endif +} diff --git a/contrib/libs/openldap/libraries/libldap/sbind.c b/contrib/libs/openldap/libraries/libldap/sbind.c new file mode 100644 index 0000000000..420ac6a3d8 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/sbind.c @@ -0,0 +1,115 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * ldap_simple_bind - bind to the ldap server (and X.500). The dn and + * password of the entry to which to bind are supplied. The message id + * of the request initiated is returned. + * + * Example: + * ldap_simple_bind( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ + +int +ldap_simple_bind( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *passwd ) +{ + int rc; + int msgid; + struct berval cred; + + Debug0( LDAP_DEBUG_TRACE, "ldap_simple_bind\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( passwd != NULL ) { + cred.bv_val = (char *) passwd; + cred.bv_len = strlen( passwd ); + } else { + cred.bv_val = ""; + cred.bv_len = 0; + } + + rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, &msgid ); + + return rc == LDAP_SUCCESS ? msgid : -1; +} + +/* + * ldap_simple_bind - bind to the ldap server (and X.500) using simple + * authentication. The dn and password of the entry to which to bind are + * supplied. LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + * + * Example: + * ldap_simple_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ + +int +ldap_simple_bind_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd ) +{ + struct berval cred; + + Debug0( LDAP_DEBUG_TRACE, "ldap_simple_bind_s\n" ); + + if ( passwd != NULL ) { + cred.bv_val = (char *) passwd; + cred.bv_len = strlen( passwd ); + } else { + cred.bv_val = ""; + cred.bv_len = 0; + } + + return ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, NULL ); +} diff --git a/contrib/libs/openldap/libraries/libldap/schema.c b/contrib/libs/openldap/libraries/libldap/schema.c new file mode 100644 index 0000000000..a4b89fa9fe --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/schema.c @@ -0,0 +1,3400 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * schema.c: parsing routines used by servers and clients to process + * schema definitions + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#include <ldap_schema.h> + +static const char EndOfInput[] = "end of input"; + +static const char * +choose_name( char *names[], const char *fallback ) +{ + return (names != NULL && names[0] != NULL) ? names[0] : fallback; +} + +LDAP_CONST char * +ldap_syntax2name( LDAPSyntax * syn ) +{ + if (!syn) return NULL; + return( syn->syn_oid ); +} + +LDAP_CONST char * +ldap_matchingrule2name( LDAPMatchingRule * mr ) +{ + if (!mr) return NULL; + return( choose_name( mr->mr_names, mr->mr_oid ) ); +} + +LDAP_CONST char * +ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru ) +{ + if (!mru) return NULL; + return( choose_name( mru->mru_names, mru->mru_oid ) ); +} + +LDAP_CONST char * +ldap_attributetype2name( LDAPAttributeType * at ) +{ + if (!at) return NULL; + return( choose_name( at->at_names, at->at_oid ) ); +} + +LDAP_CONST char * +ldap_objectclass2name( LDAPObjectClass * oc ) +{ + if (!oc) return NULL; + return( choose_name( oc->oc_names, oc->oc_oid ) ); +} + +LDAP_CONST char * +ldap_contentrule2name( LDAPContentRule * cr ) +{ + if (!cr) return NULL; + return( choose_name( cr->cr_names, cr->cr_oid ) ); +} + +LDAP_CONST char * +ldap_nameform2name( LDAPNameForm * nf ) +{ + if (!nf) return NULL; + return( choose_name( nf->nf_names, nf->nf_oid ) ); +} + +LDAP_CONST char * +ldap_structurerule2name( LDAPStructureRule * sr ) +{ + if (!sr) return NULL; + return( choose_name( sr->sr_names, NULL ) ); +} + +/* + * When pretty printing the entities we will be appending to a buffer. + * Since checking for overflow, realloc'ing and checking if no error + * is extremely boring, we will use a protection layer that will let + * us blissfully ignore the error until the end. This layer is + * implemented with the help of the next type. + */ + +typedef struct safe_string { + char * val; + ber_len_t size; + ber_len_t pos; + int at_whsp; +} safe_string; + +static safe_string * +new_safe_string(int size) +{ + safe_string * ss; + + ss = LDAP_MALLOC(sizeof(safe_string)); + if ( !ss ) + return(NULL); + + ss->val = LDAP_MALLOC(size); + if ( !ss->val ) { + LDAP_FREE(ss); + return(NULL); + } + + ss->size = size; + ss->pos = 0; + ss->at_whsp = 0; + + return ss; +} + +static void +safe_string_free(safe_string * ss) +{ + if ( !ss ) + return; + LDAP_FREE(ss->val); + LDAP_FREE(ss); +} + +#if 0 /* unused */ +static char * +safe_string_val(safe_string * ss) +{ + ss->val[ss->pos] = '\0'; + return(ss->val); +} +#endif + +static char * +safe_strdup(safe_string * ss) +{ + char *ret = LDAP_MALLOC(ss->pos+1); + if (!ret) + return NULL; + AC_MEMCPY(ret, ss->val, ss->pos); + ret[ss->pos] = '\0'; + return ret; +} + +static int +append_to_safe_string(safe_string * ss, char * s) +{ + int l = strlen(s); + char * temp; + + /* + * Some runaway process is trying to append to a string that + * overflowed and we could not extend. + */ + if ( !ss->val ) + return -1; + + /* We always make sure there is at least one position available */ + if ( ss->pos + l >= ss->size-1 ) { + ss->size *= 2; + if ( ss->pos + l >= ss->size-1 ) { + ss->size = ss->pos + l + 1; + } + + temp = LDAP_REALLOC(ss->val, ss->size); + if ( !temp ) { + /* Trouble, out of memory */ + LDAP_FREE(ss->val); + return -1; + } + ss->val = temp; + } + strncpy(&ss->val[ss->pos], s, l); + ss->pos += l; + if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) ) + ss->at_whsp = 1; + else + ss->at_whsp = 0; + + return 0; +} + +static int +print_literal(safe_string *ss, char *s) +{ + return(append_to_safe_string(ss,s)); +} + +static int +print_whsp(safe_string *ss) +{ + if ( ss->at_whsp ) + return(append_to_safe_string(ss,"")); + else + return(append_to_safe_string(ss," ")); +} + +static int +print_numericoid(safe_string *ss, char *s) +{ + if ( s ) + return(append_to_safe_string(ss,s)); + else + return(append_to_safe_string(ss,"")); +} + +/* This one is identical to print_qdescr */ +static int +print_qdstring(safe_string *ss, char *s) +{ + print_whsp(ss); + print_literal(ss,"'"); + append_to_safe_string(ss,s); + print_literal(ss,"'"); + return(print_whsp(ss)); +} + +static int +print_qdescr(safe_string *ss, char *s) +{ + print_whsp(ss); + print_literal(ss,"'"); + append_to_safe_string(ss,s); + print_literal(ss,"'"); + return(print_whsp(ss)); +} + +static int +print_qdescrlist(safe_string *ss, char **sa) +{ + char **sp; + int ret = 0; + + for (sp=sa; *sp; sp++) { + ret = print_qdescr(ss,*sp); + } + /* If the list was empty, we return zero that is potentially + * incorrect, but since we will be still appending things, the + * overflow will be detected later. Maybe FIX. + */ + return(ret); +} + +static int +print_qdescrs(safe_string *ss, char **sa) +{ + /* The only way to represent an empty list is as a qdescrlist + * so, if the list is empty we treat it as a long list. + * Really, this is what the syntax mandates. We should not + * be here if the list was empty, but if it happens, a label + * has already been output and we cannot undo it. + */ + if ( !sa[0] || ( sa[0] && sa[1] ) ) { + print_whsp(ss); + print_literal(ss,"("/*)*/); + print_qdescrlist(ss,sa); + print_literal(ss,/*(*/")"); + return(print_whsp(ss)); + } else { + return(print_qdescr(ss,*sa)); + } +} + +static int +print_woid(safe_string *ss, char *s) +{ + print_whsp(ss); + append_to_safe_string(ss,s); + return print_whsp(ss); +} + +static int +print_oidlist(safe_string *ss, char **sa) +{ + char **sp; + + for (sp=sa; *(sp+1); sp++) { + print_woid(ss,*sp); + print_literal(ss,"$"); + } + return(print_woid(ss,*sp)); +} + +static int +print_oids(safe_string *ss, char **sa) +{ + if ( sa[0] && sa[1] ) { + print_literal(ss,"("/*)*/); + print_oidlist(ss,sa); + print_whsp(ss); + return(print_literal(ss,/*(*/")")); + } else { + return(print_woid(ss,*sa)); + } +} + +static int +print_noidlen(safe_string *ss, char *s, int l) +{ + char buf[64]; + int ret; + + ret = print_numericoid(ss,s); + if ( l ) { + snprintf(buf, sizeof buf, "{%d}",l); + ret = print_literal(ss,buf); + } + return(ret); +} + +static int +print_ruleid(safe_string *ss, int rid) +{ + char buf[64]; + snprintf(buf, sizeof buf, "%d", rid); + return print_literal(ss,buf); +} + +static int +print_ruleids(safe_string *ss, int n, int *rids) +{ + int i; + + if( n == 1 ) { + print_ruleid(ss,rids[0]); + return print_whsp(ss); + } else { + print_literal(ss,"("/*)*/); + for( i=0; i<n; i++ ) { + print_whsp(ss); + print_ruleid(ss,rids[i]); + } + print_whsp(ss); + return print_literal(ss,/*(*/")"); + } +} + + +static int +print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + print_whsp(ss); + for ( ext = extensions; *ext != NULL; ext++ ) { + print_literal(ss, (*ext)->lsei_name); + print_whsp(ss); + /* Should be print_qdstrings */ + print_qdescrs(ss, (*ext)->lsei_values); + print_whsp(ss); + } + } + + return 0; +} + +char * +ldap_syntax2str( LDAPSyntax * syn ) +{ + struct berval bv; + if (ldap_syntax2bv( syn, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv ) +{ + safe_string * ss; + + if ( !syn || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, syn->syn_oid); + print_whsp(ss); + + if ( syn->syn_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,syn->syn_desc); + } + + print_whsp(ss); + + print_extensions(ss, syn->syn_extensions); + + print_literal(ss,/*(*/ ")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_matchingrule2str( LDAPMatchingRule * mr ) +{ + struct berval bv; + if (ldap_matchingrule2bv( mr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv ) +{ + safe_string * ss; + + if ( !mr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mr->mr_oid); + print_whsp(ss); + + if ( mr->mr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mr->mr_names); + } + + if ( mr->mr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mr->mr_desc); + } + + if ( mr->mr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mr->mr_syntax_oid ) { + print_literal(ss,"SYNTAX"); + print_whsp(ss); + print_literal(ss, mr->mr_syntax_oid); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mr->mr_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru ) +{ + struct berval bv; + if (ldap_matchingruleuse2bv( mru, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv ) +{ + safe_string * ss; + + if ( !mru || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mru->mru_oid); + print_whsp(ss); + + if ( mru->mru_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mru->mru_names); + } + + if ( mru->mru_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mru->mru_desc); + } + + if ( mru->mru_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mru->mru_applies_oids ) { + print_literal(ss,"APPLIES"); + print_whsp(ss); + print_oids(ss, mru->mru_applies_oids); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mru->mru_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_objectclass2str( LDAPObjectClass * oc ) +{ + struct berval bv; + if (ldap_objectclass2bv( oc, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv ) +{ + safe_string * ss; + + if ( !oc || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, oc->oc_oid); + print_whsp(ss); + + if ( oc->oc_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,oc->oc_names); + } + + if ( oc->oc_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,oc->oc_desc); + } + + if ( oc->oc_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( oc->oc_sup_oids ) { + print_literal(ss,"SUP"); + print_whsp(ss); + print_oids(ss,oc->oc_sup_oids); + print_whsp(ss); + } + + switch (oc->oc_kind) { + case LDAP_SCHEMA_ABSTRACT: + print_literal(ss,"ABSTRACT"); + break; + case LDAP_SCHEMA_STRUCTURAL: + print_literal(ss,"STRUCTURAL"); + break; + case LDAP_SCHEMA_AUXILIARY: + print_literal(ss,"AUXILIARY"); + break; + default: + print_literal(ss,"KIND-UNKNOWN"); + break; + } + print_whsp(ss); + + if ( oc->oc_at_oids_must ) { + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,oc->oc_at_oids_must); + print_whsp(ss); + } + + if ( oc->oc_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,oc->oc_at_oids_may); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, oc->oc_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_contentrule2str( LDAPContentRule * cr ) +{ + struct berval bv; + if (ldap_contentrule2bv( cr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv ) +{ + safe_string * ss; + + if ( !cr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, cr->cr_oid); + print_whsp(ss); + + if ( cr->cr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,cr->cr_names); + } + + if ( cr->cr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,cr->cr_desc); + } + + if ( cr->cr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( cr->cr_oc_oids_aux ) { + print_literal(ss,"AUX"); + print_whsp(ss); + print_oids(ss,cr->cr_oc_oids_aux); + print_whsp(ss); + } + + if ( cr->cr_at_oids_must ) { + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_must); + print_whsp(ss); + } + + if ( cr->cr_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_may); + print_whsp(ss); + } + + if ( cr->cr_at_oids_not ) { + print_literal(ss,"NOT"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_not); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, cr->cr_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_structurerule2str( LDAPStructureRule * sr ) +{ + struct berval bv; + if (ldap_structurerule2bv( sr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv ) +{ + safe_string * ss; + + if ( !sr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_ruleid(ss, sr->sr_ruleid); + print_whsp(ss); + + if ( sr->sr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,sr->sr_names); + } + + if ( sr->sr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,sr->sr_desc); + } + + if ( sr->sr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + print_literal(ss,"FORM"); + print_whsp(ss); + print_woid(ss,sr->sr_nameform); + print_whsp(ss); + + if ( sr->sr_nsup_ruleids ) { + print_literal(ss,"SUP"); + print_whsp(ss); + print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, sr->sr_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + + +char * +ldap_nameform2str( LDAPNameForm * nf ) +{ + struct berval bv; + if (ldap_nameform2bv( nf, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv ) +{ + safe_string * ss; + + if ( !nf || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, nf->nf_oid); + print_whsp(ss); + + if ( nf->nf_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,nf->nf_names); + } + + if ( nf->nf_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,nf->nf_desc); + } + + if ( nf->nf_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + print_literal(ss,"OC"); + print_whsp(ss); + print_woid(ss,nf->nf_objectclass); + print_whsp(ss); + + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,nf->nf_at_oids_must); + print_whsp(ss); + + + if ( nf->nf_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,nf->nf_at_oids_may); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, nf->nf_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_attributetype2str( LDAPAttributeType * at ) +{ + struct berval bv; + if (ldap_attributetype2bv( at, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_attributetype2bv( LDAPAttributeType * at, struct berval *bv ) +{ + safe_string * ss; + + if ( !at || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, at->at_oid); + print_whsp(ss); + + if ( at->at_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,at->at_names); + } + + if ( at->at_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,at->at_desc); + } + + if ( at->at_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( at->at_sup_oid ) { + print_literal(ss,"SUP"); + print_woid(ss,at->at_sup_oid); + } + + if ( at->at_equality_oid ) { + print_literal(ss,"EQUALITY"); + print_woid(ss,at->at_equality_oid); + } + + if ( at->at_ordering_oid ) { + print_literal(ss,"ORDERING"); + print_woid(ss,at->at_ordering_oid); + } + + if ( at->at_substr_oid ) { + print_literal(ss,"SUBSTR"); + print_woid(ss,at->at_substr_oid); + } + + if ( at->at_syntax_oid ) { + print_literal(ss,"SYNTAX"); + print_whsp(ss); + print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len); + print_whsp(ss); + } + + if ( at->at_single_value == LDAP_SCHEMA_YES ) { + print_literal(ss,"SINGLE-VALUE"); + print_whsp(ss); + } + + if ( at->at_collective == LDAP_SCHEMA_YES ) { + print_literal(ss,"COLLECTIVE"); + print_whsp(ss); + } + + if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) { + print_literal(ss,"NO-USER-MODIFICATION"); + print_whsp(ss); + } + + if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) { + print_literal(ss,"USAGE"); + print_whsp(ss); + switch (at->at_usage) { + case LDAP_SCHEMA_DIRECTORY_OPERATION: + print_literal(ss,"directoryOperation"); + break; + case LDAP_SCHEMA_DISTRIBUTED_OPERATION: + print_literal(ss,"distributedOperation"); + break; + case LDAP_SCHEMA_DSA_OPERATION: + print_literal(ss,"dSAOperation"); + break; + default: + print_literal(ss,"UNKNOWN"); + break; + } + } + + print_whsp(ss); + + print_extensions(ss, at->at_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +/* + * Now come the parsers. There is one parser for each entity type: + * objectclasses, attributetypes, etc. + * + * Each of them is written as a recursive-descent parser, except that + * none of them is really recursive. But the idea is kept: there + * is one routine per non-terminal that either gobbles lexical tokens + * or calls lower-level routines, etc. + * + * The scanner is implemented in the routine get_token. Actually, + * get_token is more than a scanner and will return tokens that are + * in fact non-terminals in the grammar. So you can see the whole + * approach as the combination of a low-level bottom-up recognizer + * combined with a scanner and a number of top-down parsers. Or just + * consider that the real grammars recognized by the parsers are not + * those of the standards. As a matter of fact, our parsers are more + * liberal than the spec when there is no ambiguity. + * + * The difference is pretty academic (modulo bugs or incorrect + * interpretation of the specs). + */ + +typedef enum tk_t { + TK_NOENDQUOTE = -2, + TK_OUTOFMEM = -1, + TK_EOS = 0, + TK_UNEXPCHAR = 1, + TK_BAREWORD = 2, + TK_QDSTRING = 3, + TK_LEFTPAREN = 4, + TK_RIGHTPAREN = 5, + TK_DOLLAR = 6, + TK_QDESCR = TK_QDSTRING +} tk_t; + +static tk_t +get_token( const char ** sp, char ** token_val ) +{ + tk_t kind; + const char * p; + const char * q; + char * res; + + *token_val = NULL; + switch (**sp) { + case '\0': + kind = TK_EOS; + (*sp)++; + break; + case '(': + kind = TK_LEFTPAREN; + (*sp)++; + break; + case ')': + kind = TK_RIGHTPAREN; + (*sp)++; + break; + case '$': + kind = TK_DOLLAR; + (*sp)++; + break; + case '\'': + kind = TK_QDSTRING; + (*sp)++; + p = *sp; + while ( **sp != '\'' && **sp != '\0' ) + (*sp)++; + if ( **sp == '\'' ) { + q = *sp; + res = LDAP_MALLOC(q-p+1); + if ( !res ) { + kind = TK_OUTOFMEM; + } else { + strncpy(res,p,q-p); + res[q-p] = '\0'; + *token_val = res; + } + (*sp)++; + } else { + kind = TK_NOENDQUOTE; + } + break; + default: + kind = TK_BAREWORD; + p = *sp; + while ( !LDAP_SPACE(**sp) && + **sp != '(' && + **sp != ')' && + **sp != '$' && + **sp != '\'' && + /* for suggested minimum upper bound on the number + * of characters (RFC 4517) */ + **sp != '{' && + **sp != '\0' ) + (*sp)++; + q = *sp; + res = LDAP_MALLOC(q-p+1); + if ( !res ) { + kind = TK_OUTOFMEM; + } else { + strncpy(res,p,q-p); + res[q-p] = '\0'; + *token_val = res; + } + break; +/* kind = TK_UNEXPCHAR; */ +/* break; */ + } + + return kind; +} + +/* Gobble optional whitespace */ +static void +parse_whsp(const char **sp) +{ + while (LDAP_SPACE(**sp)) + (*sp)++; +} + +/* TBC:!! + * General note for all parsers: to guarantee the algorithm halts they + * must always advance the pointer even when an error is found. For + * this one is not that important since an error here is fatal at the + * upper layers, but it is a simple strategy that will not get in + * endless loops. + */ + +/* Parse a sequence of dot-separated decimal strings */ +char * +ldap_int_parse_numericoid(const char **sp, int *code, const int flags) +{ + char * res = NULL; + const char * start = *sp; + int len; + int quoted = 0; + + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) { + quoted = 1; + (*sp)++; + start++; + } + /* Each iteration of this loop gets one decimal string */ + while (**sp) { + if ( !LDAP_DIGIT(**sp) ) { + /* + * Initial char is not a digit or char after dot is + * not a digit + */ + *code = LDAP_SCHERR_NODIGIT; + return NULL; + } + (*sp)++; + while ( LDAP_DIGIT(**sp) ) + (*sp)++; + if ( **sp != '.' ) + break; + /* Otherwise, gobble the dot and loop again */ + (*sp)++; + } + /* Now *sp points at the char past the numericoid. Perfect. */ + len = *sp - start; + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + return NULL; + } + } + if (flags & LDAP_SCHEMA_SKIP) { + res = (char *)start; + } else { + res = LDAP_MALLOC(len+1); + if (!res) { + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + strncpy(res,start,len); + res[len] = '\0'; + } + return(res); +} + +/* Parse a sequence of dot-separated decimal strings */ +int +ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid) +{ + *ruleid=0; + + if ( !LDAP_DIGIT(**sp) ) { + *code = LDAP_SCHERR_NODIGIT; + return -1; + } + *ruleid = (**sp) - '0'; + (*sp)++; + + while ( LDAP_DIGIT(**sp) ) { + *ruleid *= 10; + *ruleid += (**sp) - '0'; + (*sp)++; + } + + return 0; +} + +/* Parse a qdescr or a list of them enclosed in () */ +static char ** +parse_qdescrs(const char **sp, int *code) +{ + char ** res; + char ** res1; + tk_t kind; + char * sval; + int size; + int pos; + + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_LEFTPAREN ) { + /* Let's presume there will be at least 2 entries */ + size = 3; + res = LDAP_CALLOC(3,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + pos = 0; + while (1) { + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_RIGHTPAREN ) + break; + if ( kind == TK_QDESCR ) { + if ( pos == size-2 ) { + size++; + res1 = LDAP_REALLOC(res,size*sizeof(char *)); + if ( !res1 ) { + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + res = res1; + } + res[pos++] = sval; + res[pos] = NULL; + parse_whsp(sp); + } else { + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; + return(NULL); + } + } + parse_whsp(sp); + return(res); + } else if ( kind == TK_QDESCR ) { + res = LDAP_CALLOC(2,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + res[0] = sval; + res[1] = NULL; + parse_whsp(sp); + return res; + } else { + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; + return NULL; + } +} + +/* Parse a woid */ +static char * +parse_woid(const char **sp, int *code) +{ + char * sval; + tk_t kind; + + parse_whsp(sp); + kind = get_token(sp, &sval); + if ( kind != TK_BAREWORD ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; + return NULL; + } + parse_whsp(sp); + return sval; +} + +/* Parse a noidlen */ +static char * +parse_noidlen(const char **sp, int *code, int *len, int flags) +{ + char * sval; + const char *savepos; + int quoted = 0; + int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED ); + int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ); + + *len = 0; + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( allow_quoted && **sp == '\'' ) { + quoted = 1; + (*sp)++; + } + savepos = *sp; + sval = ldap_int_parse_numericoid(sp, code, 0); + if ( !sval ) { + if ( allow_oidmacro + && *sp == savepos + && *code == LDAP_SCHERR_NODIGIT ) + { + if ( get_token(sp, &sval) != TK_BAREWORD ) { + if ( sval != NULL ) { + LDAP_FREE(sval); + } + return NULL; + } + } else { + return NULL; + } + } + if ( **sp == '{' /*}*/ ) { + (*sp)++; + *len = atoi(*sp); + while ( LDAP_DIGIT(**sp) ) + (*sp)++; + if ( **sp != /*{*/ '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + (*sp)++; + } + if ( allow_quoted && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + } + return sval; +} + +/* + * Next routine will accept a qdstring in place of an oid if + * allow_quoted is set. This is necessary to interoperate with + * Netscape Directory server that will improperly quote each oid (at + * least those of the descr kind) in the SUP clause. + */ + +/* Parse a woid or a $-separated list of them enclosed in () */ +static char ** +parse_oids(const char **sp, int *code, const int allow_quoted) +{ + char ** res; + char ** res1; + tk_t kind; + char * sval; + int size; + int pos; + + /* + * Strictly speaking, doing this here accepts whsp before the + * ( at the beginning of an oidlist, but this is harmless. Also, + * we are very liberal in what we accept as an OID. Maybe + * refine later. + */ + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_LEFTPAREN ) { + /* Let's presume there will be at least 2 entries */ + size = 3; + res = LDAP_CALLOC(3,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + pos = 0; + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { + res[pos++] = sval; + res[pos] = NULL; + } else if ( kind == TK_RIGHTPAREN ) { + /* FIXME: be liberal in what we accept... */ + parse_whsp(sp); + LDAP_FREE(res); + return NULL; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + parse_whsp(sp); + while (1) { + kind = get_token(sp,&sval); + if ( kind == TK_RIGHTPAREN ) + break; + if ( kind == TK_DOLLAR ) { + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_BAREWORD || + ( allow_quoted && + kind == TK_QDSTRING ) ) { + if ( pos == size-2 ) { + size++; + res1 = LDAP_REALLOC(res,size*sizeof(char *)); + if ( !res1 ) { + LDAP_FREE(sval); + LDAP_VFREE(res); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + res = res1; + } + res[pos++] = sval; + res[pos] = NULL; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + parse_whsp(sp); + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + } + parse_whsp(sp); + return(res); + } else if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { + res = LDAP_CALLOC(2,sizeof(char *)); + if ( !res ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + res[0] = sval; + res[1] = NULL; + parse_whsp(sp); + return res; + } else { + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; + return NULL; + } +} + +static int +add_extension(LDAPSchemaExtensionItem ***extensions, + char * name, char ** values) +{ + int n; + LDAPSchemaExtensionItem **tmp, *ext; + + ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem)); + if ( !ext ) + return 1; + ext->lsei_name = name; + ext->lsei_values = values; + + if ( !*extensions ) { + *extensions = + LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *)); + if ( !*extensions ) { + LDAP_FREE( ext ); + return 1; + } + n = 0; + } else { + for ( n=0; (*extensions)[n] != NULL; n++ ) + ; + tmp = LDAP_REALLOC(*extensions, + (n+2)*sizeof(LDAPSchemaExtensionItem *)); + if ( !tmp ) { + LDAP_FREE( ext ); + return 1; + } + *extensions = tmp; + } + (*extensions)[n] = ext; + (*extensions)[n+1] = NULL; + return 0; +} + +static void +free_extensions(LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + for ( ext = extensions; *ext != NULL; ext++ ) { + LDAP_FREE((*ext)->lsei_name); + LDAP_VFREE((*ext)->lsei_values); + LDAP_FREE(*ext); + } + LDAP_FREE(extensions); + } +} + +void +ldap_syntax_free( LDAPSyntax * syn ) +{ + if ( !syn ) return; + LDAP_FREE(syn->syn_oid); + if (syn->syn_names) LDAP_VFREE(syn->syn_names); + if (syn->syn_desc) LDAP_FREE(syn->syn_desc); + free_extensions(syn->syn_extensions); + LDAP_FREE(syn); +} + +LDAPSyntax * +ldap_str2syntax( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + LDAPSyntax * syn; + char ** ext_vals; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + syn = LDAP_CALLOC(1,sizeof(LDAPSyntax)); + + if ( !syn ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_NOLEFTPAREN; + ldap_syntax_free(syn); + return NULL; + } + + parse_whsp(&ss); + syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !syn->syn_oid ) { + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_syntax_free(syn); + return NULL; + case TK_RIGHTPAREN: + return syn; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_syntax_free(syn); + return(NULL); + } + seen_name = 1; + syn->syn_names = parse_qdescrs(&ss,code); + if ( !syn->syn_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_syntax_free(syn); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + syn->syn_desc = sval; + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + if ( add_extension(&syn->syn_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + } +} + +void +ldap_matchingrule_free( LDAPMatchingRule * mr ) +{ + if (!mr) return; + LDAP_FREE(mr->mr_oid); + if (mr->mr_names) LDAP_VFREE(mr->mr_names); + if (mr->mr_desc) LDAP_FREE(mr->mr_desc); + if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid); + free_extensions(mr->mr_extensions); + LDAP_FREE(mr); +} + +LDAPMatchingRule * +ldap_str2matchingrule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_syntax = 0; + LDAPMatchingRule * mr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule)); + + if ( !mr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags); + if ( !mr->mr_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SYNTAX") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_matchingrule_free(mr); + return NULL; + case TK_RIGHTPAREN: + if( !seen_syntax ) { + *code = LDAP_SCHERR_MISSING; + ldap_matchingrule_free(mr); + return NULL; + } + return mr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_name = 1; + mr->mr_names = parse_qdescrs(&ss,code); + if ( !mr->mr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + mr->mr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_obsolete = 1; + mr->mr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + mr->mr_syntax_oid = + ldap_int_parse_numericoid(&ss,code,flags); + if ( !mr->mr_syntax_oid ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + if ( add_extension(&mr->mr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } +} + +void +ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru ) +{ + if (!mru) return; + LDAP_FREE(mru->mru_oid); + if (mru->mru_names) LDAP_VFREE(mru->mru_names); + if (mru->mru_desc) LDAP_FREE(mru->mru_desc); + if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids); + free_extensions(mru->mru_extensions); + LDAP_FREE(mru); +} + +LDAPMatchingRuleUse * +ldap_str2matchingruleuse( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_applies = 0; + LDAPMatchingRuleUse * mru; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse)); + + if ( !mru ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags); + if ( !mru->mru_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "APPLIES") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_matchingruleuse_free(mru); + return NULL; + case TK_RIGHTPAREN: + if( !seen_applies ) { + *code = LDAP_SCHERR_MISSING; + ldap_matchingruleuse_free(mru); + return NULL; + } + return mru; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_name = 1; + mru->mru_names = parse_qdescrs(&ss,code); + if ( !mru->mru_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + mru->mru_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_obsolete = 1; + mru->mru_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"APPLIES") ) { + LDAP_FREE(sval); + if ( seen_applies ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_applies = 1; + mru->mru_applies_oids = parse_oids(&ss, + code, + flags); + if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + if ( add_extension(&mru->mru_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } +} + +void +ldap_attributetype_free(LDAPAttributeType * at) +{ + if (!at) return; + LDAP_FREE(at->at_oid); + if (at->at_names) LDAP_VFREE(at->at_names); + if (at->at_desc) LDAP_FREE(at->at_desc); + if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid); + if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid); + if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid); + if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid); + if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid); + free_extensions(at->at_extensions); + LDAP_FREE(at); +} + +LDAPAttributeType * +ldap_str2attributetype( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_equality = 0; + int seen_ordering = 0; + int seen_substr = 0; + int seen_syntax = 0; + int seen_usage = 0; + LDAPAttributeType * at; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + at = LDAP_CALLOC(1,sizeof(LDAPAttributeType)); + + if ( !at ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + at->at_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !at->at_oid ) { + if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID + | LDAP_SCHEMA_ALLOW_OID_MACRO ) ) + && (ss == savepos) ) + { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SUP") || + !strcasecmp(sval, "EQUALITY") || + !strcasecmp(sval, "ORDERING") || + !strcasecmp(sval, "SUBSTR") || + !strcasecmp(sval, "SYNTAX") || + !strcasecmp(sval, "SINGLE-VALUE") || + !strcasecmp(sval, "COLLECTIVE") || + !strcasecmp(sval, "NO-USER-MODIFICATION") || + !strcasecmp(sval, "USAGE") || + !strncasecmp(sval, "X-", 2) ) + { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags + & LDAP_SCHEMA_ALLOW_OID_MACRO) + { + /* Non-numerical OID ... */ + int len = ss-savepos; + at->at_oid = LDAP_MALLOC(len+1); + if ( !at->at_oid ) { + ldap_attributetype_free(at); + return NULL; + } + + strncpy(at->at_oid, savepos, len); + at->at_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_attributetype_free(at); + return NULL; + case TK_RIGHTPAREN: + return at; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_name = 1; + at->at_names = parse_qdescrs(&ss,code); + if ( !at->at_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + at->at_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_obsolete = 1; + at->at_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_sup = 1; + at->at_sup_oid = parse_woid(&ss,code); + if ( !at->at_sup_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"EQUALITY") ) { + LDAP_FREE(sval); + if ( seen_equality ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_equality = 1; + at->at_equality_oid = parse_woid(&ss,code); + if ( !at->at_equality_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"ORDERING") ) { + LDAP_FREE(sval); + if ( seen_ordering ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_ordering = 1; + at->at_ordering_oid = parse_woid(&ss,code); + if ( !at->at_ordering_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"SUBSTR") ) { + LDAP_FREE(sval); + if ( seen_substr ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_substr = 1; + at->at_substr_oid = parse_woid(&ss,code); + if ( !at->at_substr_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + savepos = ss; + at->at_syntax_oid = + parse_noidlen(&ss, + code, + &at->at_syntax_len, + flags); + if ( !at->at_syntax_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) { + kind = get_token(&ss,&sval); + if (kind == TK_BAREWORD) + { + char *sp = strchr(sval, '{'); + at->at_syntax_oid = sval; + if (sp) + { + *sp++ = 0; + at->at_syntax_len = atoi(sp); + while ( LDAP_DIGIT(*sp) ) + sp++; + if ( *sp != '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + } + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) { + LDAP_FREE(sval); + if ( at->at_single_value ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_single_value = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"COLLECTIVE") ) { + LDAP_FREE(sval); + if ( at->at_collective ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_collective = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) { + LDAP_FREE(sval); + if ( at->at_no_user_mod ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_no_user_mod = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"USAGE") ) { + LDAP_FREE(sval); + if ( seen_usage ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_usage = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_BAREWORD ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + if ( !strcasecmp(sval,"userApplications") ) + at->at_usage = + LDAP_SCHEMA_USER_APPLICATIONS; + else if ( !strcasecmp(sval,"directoryOperation") ) + at->at_usage = + LDAP_SCHEMA_DIRECTORY_OPERATION; + else if ( !strcasecmp(sval,"distributedOperation") ) + at->at_usage = + LDAP_SCHEMA_DISTRIBUTED_OPERATION; + else if ( !strcasecmp(sval,"dSAOperation") ) + at->at_usage = + LDAP_SCHEMA_DSA_OPERATION; + else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + LDAP_FREE(sval); + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + if ( add_extension(&at->at_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } +} + +void +ldap_objectclass_free(LDAPObjectClass * oc) +{ + if (!oc) return; + LDAP_FREE(oc->oc_oid); + if (oc->oc_names) LDAP_VFREE(oc->oc_names); + if (oc->oc_desc) LDAP_FREE(oc->oc_desc); + if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids); + if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must); + if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may); + free_extensions(oc->oc_extensions); + LDAP_FREE(oc); +} + +LDAPObjectClass * +ldap_str2objectclass( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_kind = 0; + int seen_must = 0; + int seen_may = 0; + LDAPObjectClass * oc; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass)); + + if ( !oc ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !oc->oc_oid ) { + if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SUP") || + !strcasecmp(sval, "ABSTRACT") || + !strcasecmp(sval, "STRUCTURAL") || + !strcasecmp(sval, "AUXILIARY") || + !strcasecmp(sval, "MUST") || + !strcasecmp(sval, "MAY") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags & + LDAP_SCHEMA_ALLOW_OID_MACRO ) { + /* Non-numerical OID, ignore */ + int len = ss-savepos; + oc->oc_oid = LDAP_MALLOC(len+1); + if ( !oc->oc_oid ) { + ldap_objectclass_free(oc); + return NULL; + } + + strncpy(oc->oc_oid, savepos, len); + oc->oc_oid[len] = 0; + } + } + LDAP_FREE(sval); + *code = 0; + } else { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_objectclass_free(oc); + return NULL; + case TK_RIGHTPAREN: + return oc; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_name = 1; + oc->oc_names = parse_qdescrs(&ss,code); + if ( !oc->oc_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + oc->oc_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_obsolete = 1; + oc->oc_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_sup = 1; + oc->oc_sup_oids = parse_oids(&ss, + code, + flags); + if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + } else if ( !strcasecmp(sval,"ABSTRACT") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_ABSTRACT; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"STRUCTURAL") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"AUXILIARY") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_AUXILIARY; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_must = 1; + oc->oc_at_oids_must = parse_oids(&ss,code,0); + if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_may = 1; + oc->oc_at_oids_may = parse_oids(&ss,code,0); + if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + *code = 0; + if ( !ext_vals ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + if ( add_extension(&oc->oc_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + } +} + +void +ldap_contentrule_free(LDAPContentRule * cr) +{ + if (!cr) return; + LDAP_FREE(cr->cr_oid); + if (cr->cr_names) LDAP_VFREE(cr->cr_names); + if (cr->cr_desc) LDAP_FREE(cr->cr_desc); + if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux); + if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must); + if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may); + if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not); + free_extensions(cr->cr_extensions); + LDAP_FREE(cr); +} + +LDAPContentRule * +ldap_str2contentrule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_aux = 0; + int seen_must = 0; + int seen_may = 0; + int seen_not = 0; + LDAPContentRule * cr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + cr = LDAP_CALLOC(1,sizeof(LDAPContentRule)); + + if ( !cr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + */ + parse_whsp(&ss); + savepos = ss; + cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !cr->cr_oid ) { + if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "AUX") || + !strcasecmp(sval, "MUST") || + !strcasecmp(sval, "MAY") || + !strcasecmp(sval, "NOT") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags & + LDAP_SCHEMA_ALLOW_OID_MACRO ) { + /* Non-numerical OID, ignore */ + int len = ss-savepos; + cr->cr_oid = LDAP_MALLOC(len+1); + if ( !cr->cr_oid ) { + ldap_contentrule_free(cr); + return NULL; + } + + strncpy(cr->cr_oid, savepos, len); + cr->cr_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_contentrule_free(cr); + return NULL; + case TK_RIGHTPAREN: + return cr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_name = 1; + cr->cr_names = parse_qdescrs(&ss,code); + if ( !cr->cr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + cr->cr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_obsolete = 1; + cr->cr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"AUX") ) { + LDAP_FREE(sval); + if ( seen_aux ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_aux = 1; + cr->cr_oc_oids_aux = parse_oids(&ss,code,0); + if ( !cr->cr_oc_oids_aux ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_must = 1; + cr->cr_at_oids_must = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_may = 1; + cr->cr_at_oids_may = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"NOT") ) { + LDAP_FREE(sval); + if ( seen_not ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_not = 1; + cr->cr_at_oids_not = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + if ( add_extension(&cr->cr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + } +} + +void +ldap_structurerule_free(LDAPStructureRule * sr) +{ + if (!sr) return; + if (sr->sr_names) LDAP_VFREE(sr->sr_names); + if (sr->sr_desc) LDAP_FREE(sr->sr_desc); + if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform); + if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids); + free_extensions(sr->sr_extensions); + LDAP_FREE(sr); +} + +LDAPStructureRule * +ldap_str2structurerule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + int ret; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_nameform = 0; + LDAPStructureRule * sr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule)); + + if ( !sr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + + /* + * Definitions MUST begin with a ruleid. + */ + parse_whsp(&ss); + savepos = ss; + ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid); + if ( ret ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_structurerule_free(sr); + return NULL; + case TK_RIGHTPAREN: + if( !seen_nameform ) { + *code = LDAP_SCHERR_MISSING; + ldap_structurerule_free(sr); + return NULL; + } + return sr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_name = 1; + sr->sr_names = parse_qdescrs(&ss,code); + if ( !sr->sr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + sr->sr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_obsolete = 1; + sr->sr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"FORM") ) { + LDAP_FREE(sval); + if ( seen_nameform ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_nameform = 1; + sr->sr_nameform = parse_woid(&ss,code); + if ( !sr->sr_nameform ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + if ( add_extension(&sr->sr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + } +} + +void +ldap_nameform_free(LDAPNameForm * nf) +{ + if (!nf) return; + LDAP_FREE(nf->nf_oid); + if (nf->nf_names) LDAP_VFREE(nf->nf_names); + if (nf->nf_desc) LDAP_FREE(nf->nf_desc); + if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass); + if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must); + if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may); + free_extensions(nf->nf_extensions); + LDAP_FREE(nf); +} + +LDAPNameForm * +ldap_str2nameform( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_class = 0; + int seen_must = 0; + int seen_may = 0; + LDAPNameForm * nf; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + nf = LDAP_CALLOC(1,sizeof(LDAPNameForm)); + + if ( !nf ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !nf->nf_oid ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_nameform_free(nf); + return NULL; + case TK_RIGHTPAREN: + if( !seen_class || !seen_must ) { + *code = LDAP_SCHERR_MISSING; + ldap_nameform_free(nf); + return NULL; + } + return nf; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_name = 1; + nf->nf_names = parse_qdescrs(&ss,code); + if ( !nf->nf_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + nf->nf_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_obsolete = 1; + nf->nf_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OC") ) { + LDAP_FREE(sval); + if ( seen_class ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_class = 1; + nf->nf_objectclass = parse_woid(&ss,code); + if ( !nf->nf_objectclass ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_must = 1; + nf->nf_at_oids_must = parse_oids(&ss,code,0); + if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_may = 1; + nf->nf_at_oids_may = parse_oids(&ss,code,0); + if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + if ( add_extension(&nf->nf_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + } +} + +static char *const err2text[] = { + N_("Success"), + N_("Out of memory"), + N_("Unexpected token"), + N_("Missing opening parenthesis"), + N_("Missing closing parenthesis"), + N_("Expecting digit"), + N_("Expecting a name"), + N_("Bad description"), + N_("Bad superiors"), + N_("Duplicate option"), + N_("Unexpected end of data"), + N_("Missing required field"), + N_("Out of order field") +}; + +char * +ldap_scherr2str(int code) +{ + if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) { + return _("Unknown error"); + } else { + return _(err2text[code]); + } +} diff --git a/contrib/libs/openldap/libraries/libldap/search.c b/contrib/libs/openldap/libraries/libldap/search.c new file mode 100644 index 0000000000..23815777ad --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/search.c @@ -0,0 +1,545 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* + * ldap_search_ext - initiate an ldap search operation. + * + * Parameters: + * + * ld LDAP descriptor + * base DN of the base object + * scope the search scope - one of + * LDAP_SCOPE_BASE (baseObject), + * LDAP_SCOPE_ONELEVEL (oneLevel), + * LDAP_SCOPE_SUBTREE (subtree), or + * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension + * filter a string containing the search filter + * (e.g., "(|(cn=bob)(sn=bob))") + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob", + * attrs, attrsonly, sctrls, ctrls, timeout, sizelimit, + * &msgid ); + */ +int +ldap_search_ext( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int *msgidp ) +{ + return ldap_pvt_search( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp ); +} + +int +ldap_pvt_search( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + int *msgidp ) +{ + int rc; + BerElement *ber; + int timelimit; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_search_ext\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + /* + * if timeout is provided, both tv_sec and tv_usec must + * not be zero + */ + if( timeout != NULL ) { + if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) { + return LDAP_PARAM_ERROR; + } + + /* timelimit must be non-zero if timeout is provided */ + timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1; + + } else { + /* no timeout, no timelimit */ + timelimit = -1; + } + + ber = ldap_build_search_req( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id ); + + if ( ber == NULL ) { + return ld->ld_errno; + } + + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ); + + if( *msgidp < 0 ) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_search_ext_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + LDAPMessage **res ) +{ + return ldap_pvt_search_s( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res ); +} + +int +ldap_pvt_search_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + LDAPMessage **res ) +{ + int rc; + int msgid; + + *res = NULL; + + rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly, + sctrls, cctrls, timeout, sizelimit, deref, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ); + + if( rc <= 0 ) { + /* error(-1) or timeout(0) */ + if ( ld->ld_errno == LDAP_TIMEOUT ) { + /* cleanup request */ + (void) ldap_abandon( ld, msgid ); + ld->ld_errno = LDAP_TIMEOUT; + } + return( ld->ld_errno ); + } + + if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) { + return( ld->ld_errno ); + } + + return( ldap_result2error( ld, *res, 0 ) ); +} + +/* + * ldap_search - initiate an ldap search operation. + * + * Parameters: + * + * ld LDAP descriptor + * base DN of the base object + * scope the search scope - one of + * LDAP_SCOPE_BASE (baseObject), + * LDAP_SCOPE_ONELEVEL (oneLevel), + * LDAP_SCOPE_SUBTREE (subtree), or + * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension + * filter a string containing the search filter + * (e.g., "(|(cn=bob)(sn=bob))") + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob", + * attrs, attrsonly ); + */ +int +ldap_search( + LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter, + char **attrs, int attrsonly ) +{ + BerElement *ber; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_search\n" ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + ber = ldap_build_search_req( ld, base, scope, filter, attrs, + attrsonly, NULL, NULL, -1, -1, -1, &id ); + + if ( ber == NULL ) { + return( -1 ); + } + + + /* send the message */ + return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id )); +} + + +BerElement * +ldap_build_search_req( + LDAP *ld, + LDAP_CONST char *base, + ber_int_t scope, + LDAP_CONST char *filter, + char **attrs, + ber_int_t attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t timelimit, + ber_int_t sizelimit, + ber_int_t deref, + ber_int_t *idp) +{ + BerElement *ber; + int err; + + /* + * Create the search request. It looks like this: + * SearchRequest := [APPLICATION 3] SEQUENCE { + * baseObject DistinguishedName, + * scope ENUMERATED { + * baseObject (0), + * singleLevel (1), + * wholeSubtree (2) + * }, + * derefAliases ENUMERATED { + * neverDerefaliases (0), + * derefInSearching (1), + * derefFindingBaseObj (2), + * alwaysDerefAliases (3) + * }, + * sizelimit INTEGER (0 .. 65535), + * timelimit INTEGER (0 .. 65535), + * attrsOnly BOOLEAN, + * filter Filter, + * attributes SEQUENCE OF AttributeType + * } + * wrapped in an ldap message. + */ + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + if ( base == NULL ) { + /* no base provided, use session default base */ + base = ld->ld_options.ldo_defbase; + + if ( base == NULL ) { + /* no session default base, use top */ + base = ""; + } + } + + LDAP_NEXT_MSGID( ld, *idp ); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage sa = {0}; + /* dummy, filled with ldo_peer in request.c */ + err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 ); + } + if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) { + char *dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{ist{seeiib", *idp, dn, + LDAP_REQ_SEARCH, base, (ber_int_t) scope, + (deref < 0) ? ld->ld_deref : deref, + (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, + (timelimit < 0) ? ld->ld_timelimit : timelimit, + attrsonly ); + } else +#endif + { + err = ber_printf( ber, "{it{seeiib", *idp, + LDAP_REQ_SEARCH, base, (ber_int_t) scope, + (deref < 0) ? ld->ld_deref : deref, + (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, + (timelimit < 0) ? ld->ld_timelimit : timelimit, + attrsonly ); + } + + if ( err == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + if( filter == NULL ) { + filter = "(objectclass=*)"; + } + + err = ldap_pvt_put_filter( ber, filter ); + + if ( err == -1 ) { + ld->ld_errno = LDAP_FILTER_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_ARGS ) { + char buf[ BUFSIZ ], *ptr = " *"; + + if ( attrs != NULL ) { + int i, len, rest = sizeof( buf ); + + for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) { + ptr = &buf[ sizeof( buf ) - rest ]; + len = snprintf( ptr, rest, " %s", attrs[ i ] ); + rest -= (len >= 0 ? len : (int) sizeof( buf )); + } + + if ( rest <= 0 ) { + AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ], + "...(truncated)", STRLENOF( "...(truncated)" ) + 1 ); + } + ptr = buf; + } + + Debug1( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr ); + } +#endif /* LDAP_DEBUG */ + + if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +int +ldap_search_st( + LDAP *ld, LDAP_CONST char *base, int scope, + LDAP_CONST char *filter, char **attrs, + int attrsonly, struct timeval *timeout, LDAPMessage **res ) +{ + int msgid; + + *res = NULL; + + if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly )) + == -1 ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res ) + return( ld->ld_errno ); + + if ( ld->ld_errno == LDAP_TIMEOUT ) { + (void) ldap_abandon( ld, msgid ); + ld->ld_errno = LDAP_TIMEOUT; + return( ld->ld_errno ); + } + + return( ldap_result2error( ld, *res, 0 ) ); +} + +int +ldap_search_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPMessage **res ) +{ + int msgid; + + *res = NULL; + + if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly )) + == -1 ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, *res, 0 ) ); +} + +static char escape[128] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 +}; +#define NEEDFLTESCAPE(c) ((c) & 0x80 || escape[ (unsigned)(c) ]) + +/* + * compute the length of the escaped value + */ +ber_len_t +ldap_bv2escaped_filter_value_len( struct berval *in ) +{ + ber_len_t i, l; + + assert( in != NULL ); + + if ( in->bv_len == 0 ) { + return 0; + } + + for( l = 0, i = 0; i < in->bv_len; l++, i++ ) { + char c = in->bv_val[ i ]; + if ( NEEDFLTESCAPE( c ) ) { + l += 2; + } + } + + return l; +} + +int +ldap_bv2escaped_filter_value( struct berval *in, struct berval *out ) +{ + return ldap_bv2escaped_filter_value_x( in, out, 0, NULL ); +} + +int +ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx ) +{ + ber_len_t i, l; + + assert( in != NULL ); + assert( out != NULL ); + + BER_BVZERO( out ); + + if ( in->bv_len == 0 ) { + return 0; + } + + /* assume we'll escape everything */ + l = ldap_bv2escaped_filter_value_len( in ); + if ( l == in->bv_len ) { + if ( inplace ) { + *out = *in; + } else { + ber_dupbv( out, in ); + } + return 0; + } + out->bv_val = LDAP_MALLOCX( l + 1, ctx ); + if ( out->bv_val == NULL ) { + return -1; + } + + for ( i = 0; i < in->bv_len; i++ ) { + char c = in->bv_val[ i ]; + if ( NEEDFLTESCAPE( c ) ) { + assert( out->bv_len < l - 2 ); + out->bv_val[out->bv_len++] = '\\'; + out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)]; + out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c]; + + } else { + assert( out->bv_len < l ); + out->bv_val[out->bv_len++] = c; + } + } + + out->bv_val[out->bv_len] = '\0'; + + return 0; +} + diff --git a/contrib/libs/openldap/libraries/libldap/sort.c b/contrib/libs/openldap/libraries/libldap/sort.c new file mode 100644 index 0000000000..6159be8a60 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/sort.c @@ -0,0 +1,183 @@ +/* sort.c -- LDAP library entry and value sort routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/time.h> + + +#include "ldap-int.h" + +struct entrything { + char **et_vals; + LDAPMessage *et_msg; + int (*et_cmp_fn) LDAP_P((const char *a, const char *b)); +}; + +static int et_cmp LDAP_P(( const void *aa, const void *bb)); + + +int +ldap_sort_strcasecmp( + LDAP_CONST void *a, + LDAP_CONST void *b +) +{ + return( strcasecmp( *(char *const *)a, *(char *const *)b ) ); +} + +static int +et_cmp( + const void *aa, + const void *bb +) +{ + int i, rc; + const struct entrything *a = (const struct entrything *)aa; + const struct entrything *b = (const struct entrything *)bb; + + if ( a->et_vals == NULL && b->et_vals == NULL ) + return( 0 ); + if ( a->et_vals == NULL ) + return( -1 ); + if ( b->et_vals == NULL ) + return( 1 ); + + for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) { + if ( (rc = a->et_cmp_fn( a->et_vals[i], b->et_vals[i] )) != 0 ) { + return( rc ); + } + } + + if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL ) + return( 0 ); + if ( a->et_vals[i] == NULL ) + return( -1 ); + return( 1 ); +} + +int +ldap_sort_entries( + LDAP *ld, + LDAPMessage **chain, + LDAP_CONST char *attr, /* NULL => sort by DN */ + int (*cmp) (LDAP_CONST char *, LDAP_CONST char *) +) +{ + int i, count = 0; + struct entrything *et; + LDAPMessage *e, *ehead = NULL, *etail = NULL; + LDAPMessage *ohead = NULL, *otail = NULL; + LDAPMessage **ep; + + assert( ld != NULL ); + + /* Separate entries from non-entries */ + for ( e = *chain; e; e=e->lm_chain ) { + if ( e->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + count++; + if ( !ehead ) ehead = e; + if ( etail ) etail->lm_chain = e; + etail = e; + } else { + if ( !ohead ) ohead = e; + if ( otail ) otail->lm_chain = e; + otail = e; + } + } + + if ( count < 2 ) { + /* zero or one entries -- already sorted! */ + if ( ehead ) { + etail->lm_chain = ohead; + *chain = ehead; + } else { + *chain = ohead; + } + return 0; + } + + if ( (et = (struct entrything *) LDAP_MALLOC( count * + sizeof(struct entrything) )) == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + + e = ehead; + for ( i = 0; i < count; i++ ) { + et[i].et_cmp_fn = cmp; + et[i].et_msg = e; + if ( attr == NULL ) { + char *dn; + + dn = ldap_get_dn( ld, e ); + et[i].et_vals = ldap_explode_dn( dn, 1 ); + LDAP_FREE( dn ); + } else { + et[i].et_vals = ldap_get_values( ld, e, attr ); + } + + e = e->lm_chain; + } + + qsort( et, count, sizeof(struct entrything), et_cmp ); + + ep = chain; + for ( i = 0; i < count; i++ ) { + *ep = et[i].et_msg; + ep = &(*ep)->lm_chain; + + LDAP_VFREE( et[i].et_vals ); + } + *ep = ohead; + (*chain)->lm_chain_tail = otail ? otail : etail; + + LDAP_FREE( (char *) et ); + + return( 0 ); +} + +int +ldap_sort_values( + LDAP *ld, + char **vals, + int (*cmp) (LDAP_CONST void *, LDAP_CONST void *) +) +{ + int nel; + + for ( nel = 0; vals[nel] != NULL; nel++ ) + ; /* NULL */ + + qsort( vals, nel, sizeof(char *), cmp ); + + return( 0 ); +} diff --git a/contrib/libs/openldap/libraries/libldap/sortctrl.c b/contrib/libs/openldap/libraries/libldap/sortctrl.c new file mode 100644 index 0000000000..537dac5869 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/sortctrl.c @@ -0,0 +1,552 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + */ +/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_MATCHRULE_IDENTIFIER 0x80L +#define LDAP_REVERSEORDER_IDENTIFIER 0x81L +#define LDAP_ATTRTYPES_IDENTIFIER 0x80L + + + +/* --------------------------------------------------------------------------- + countKeys + + Internal function to determine the number of keys in the string. + + keyString (IN) String of items separated by whitespace. + ---------------------------------------------------------------------------*/ + +static int countKeys(char *keyString) +{ + char *p = keyString; + int count = 0; + + for (;;) + { + while (LDAP_SPACE(*p)) /* Skip leading whitespace */ + p++; + + if (*p == '\0') /* End of string? */ + return count; + + count++; /* Found start of a key */ + + while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */ + if (*p++ == '\0') + return count; + } +} + + +/* --------------------------------------------------------------------------- + readNextKey + + Internal function to parse the next sort key in the string. + Allocate an LDAPSortKey structure and initialize it with + attribute name, reverse flag, and matching rule OID. + + Each sort key in the string has the format: + [whitespace][-]attribute[:[OID]] + + pNextKey (IN/OUT) Points to the next key in the sortkey string to parse. + The pointer is updated to point to the next character + after the sortkey being parsed. + + key (OUT) Points to the address of an LDAPSortKey structure + which has been allocated by this routine and + initialized with information from the next sortkey. + ---------------------------------------------------------------------------*/ + +static int readNextKey( char **pNextKey, LDAPSortKey **key) +{ + char *p = *pNextKey; + int rev = 0; + char *attrStart; + int attrLen; + char *oidStart = NULL; + int oidLen = 0; + + /* Skip leading white space. */ + while (LDAP_SPACE(*p)) + p++; + + if (*p == '-') /* Check if the reverse flag is present. */ + { + rev=1; + p++; + } + + /* We're now positioned at the start of the attribute. */ + attrStart = p; + + /* Get the length of the attribute until the next whitespace or ":". */ + attrLen = strcspn(p, " \t:"); + p += attrLen; + + if (attrLen == 0) /* If no attribute name was present, quit. */ + return LDAP_PARAM_ERROR; + + if (*p == ':') + { + oidStart = ++p; /* Start of the OID, after the colon */ + oidLen = strcspn(p, " \t"); /* Get length of OID till next whitespace */ + p += oidLen; + } + + *pNextKey = p; /* Update argument to point to next key */ + + /* Allocate an LDAPSortKey structure */ + *key = LDAP_MALLOC(sizeof(LDAPSortKey)); + if (*key == NULL) return LDAP_NO_MEMORY; + + /* Allocate memory for the attribute and copy to it. */ + (*key)->attributeType = LDAP_MALLOC(attrLen+1); + if ((*key)->attributeType == NULL) { + LDAP_FREE(*key); + return LDAP_NO_MEMORY; + } + + strncpy((*key)->attributeType, attrStart, attrLen); + (*key)->attributeType[attrLen] = 0; + + /* If present, allocate memory for the OID and copy to it. */ + if (oidLen) { + (*key)->orderingRule = LDAP_MALLOC(oidLen+1); + if ((*key)->orderingRule == NULL) { + LDAP_FREE((*key)->attributeType); + LDAP_FREE(*key); + return LDAP_NO_MEMORY; + } + strncpy((*key)->orderingRule, oidStart, oidLen); + (*key)->orderingRule[oidLen] = 0; + + } else { + (*key)->orderingRule = NULL; + } + + (*key)->reverseOrder = rev; + + return LDAP_SUCCESS; +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_keylist + + Create an array of pointers to LDAPSortKey structures, containing the + information specified by the string representation of one or more + sort keys. + + sortKeyList (OUT) Points to a null-terminated array of pointers to + LDAPSortKey structures allocated by this routine. + This memory SHOULD be freed by the calling program + using ldap_free_sort_keylist(). + + keyString (IN) Points to a string of one or more sort keys. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString ) +{ + int numKeys, rc, i; + char *nextKey; + LDAPSortKey **keyList = NULL; + + assert( sortKeyList != NULL ); + assert( keyString != NULL ); + + *sortKeyList = NULL; + + /* Determine the number of sort keys so we can allocate memory. */ + if (( numKeys = countKeys(keyString)) == 0) { + return LDAP_PARAM_ERROR; + } + + /* Allocate the array of pointers. Initialize to NULL. */ + keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*)); + if ( keyList == NULL) return LDAP_NO_MEMORY; + + /* For each sort key in the string, create an LDAPSortKey structure + and add it to the list. + */ + nextKey = keyString; /* Points to the next key in the string */ + for (i=0; i < numKeys; i++) { + rc = readNextKey(&nextKey, &keyList[i]); + + if (rc != LDAP_SUCCESS) { + ldap_free_sort_keylist(keyList); + return rc; + } + } + + *sortKeyList = keyList; + return LDAP_SUCCESS; +} + + +/* --------------------------------------------------------------------------- + ldap_free_sort_keylist + + Frees the sort key structures created by ldap_create_sort_keylist(). + Frees the memory referenced by the LDAPSortKey structures, + the LDAPSortKey structures themselves, and the array of pointers + to the structures. + + keyList (IN) Points to an array of pointers to LDAPSortKey structures. + ---------------------------------------------------------------------------*/ + +void +ldap_free_sort_keylist ( LDAPSortKey **keyList ) +{ + int i; + LDAPSortKey *nextKeyp; + + if (keyList == NULL) return; + + i=0; + while ( 0 != (nextKeyp = keyList[i++]) ) { + if (nextKeyp->attributeType) { + LBER_FREE(nextKeyp->attributeType); + } + + if (nextKeyp->orderingRule != NULL) { + LBER_FREE(nextKeyp->orderingRule); + } + + LBER_FREE(nextKeyp); + } + + LBER_FREE(keyList); +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_control_value + + Create and encode the value of the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + keyList (IN) Points to a null-terminated array of pointers to + LDAPSortKey structures, containing a description of + each of the sort keys to be used. The description + consists of an attribute name, ascending/descending flag, + and an optional matching rule (OID) to use. + + value (OUT) Contains the control value; the bv_val member of the berval structure + SHOULD be freed by calling ldap_memfree() when done. + + + Ber encoding + + SortKeyList ::= SEQUENCE OF SEQUENCE { + attributeType AttributeDescription, + orderingRule [0] MatchingRuleId OPTIONAL, + reverseOrder [1] BOOLEAN DEFAULT FALSE } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_control_value( + LDAP *ld, + LDAPSortKey **keyList, + struct berval *value ) +{ + int i; + BerElement *ber = NULL; + ber_tag_t tag; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) return LDAP_PARAM_ERROR; + if ( keyList == NULL || value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{" /*}*/ ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + for ( i = 0; keyList[i] != NULL; i++ ) { + tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( keyList[i]->orderingRule != NULL ) { + tag = ber_printf( ber, "ts", + LDAP_MATCHRULE_IDENTIFIER, + keyList[i]->orderingRule ); + + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + if ( keyList[i]->reverseOrder ) { + tag = ber_printf( ber, "tb", + LDAP_REVERSEORDER_IDENTIFIER, + keyList[i]->reverseOrder ); + + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_control + + Create and encode the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + keyList (IN) Points to a null-terminated array of pointers to + LDAPSortKey structures, containing a description of + each of the sort keys to be used. The description + consists of an attribute name, ascending/descending flag, + and an optional matching rule (OID) to use. + + isCritical (IN) 0 - Indicates the control is not critical to the operation. + non-zero - The control is critical to the operation. + + ctrlp (OUT) Returns a pointer to the LDAPControl created. This control + SHOULD be freed by calling ldap_control_free() when done. + + + Ber encoding + + SortKeyList ::= SEQUENCE OF SEQUENCE { + attributeType AttributeDescription, + orderingRule [0] MatchingRuleId OPTIONAL, + reverseOrder [1] BOOLEAN DEFAULT FALSE } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_control( + LDAP *ld, + LDAPSortKey **keyList, + int isCritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) { + return LDAP_PARAM_ERROR; + } + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST, + isCritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_sortedresult_control + + Decode the server-side sort control return information. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrl (IN) The address of the LDAP Control Structure. + + returnCode (OUT) This result parameter is filled in with the sort control + result code. This parameter MUST not be NULL. + + attribute (OUT) If an error occurred the server may return a string + indicating the first attribute in the sortkey list + that was in error. If a string is returned, the memory + should be freed with ldap_memfree. If this parameter is + NULL, no string is returned. + + + Ber encoding for sort control + + SortResult ::= SEQUENCE { + sortResult ENUMERATED { + success (0), -- results are sorted + operationsError (1), -- server internal failure + timeLimitExceeded (3), -- timelimit reached before + -- sorting was completed + strongAuthRequired (8), -- refused to return sorted + -- results via insecure + -- protocol + adminLimitExceeded (11), -- too many matching entries + -- for the server to sort + noSuchAttribute (16), -- unrecognized attribute + -- type in sort key + inappropriateMatching (18), -- unrecognized or inappro- + -- priate matching rule in + -- sort key + insufficientAccessRights (50), -- refused to return sorted + -- results to this client + busy (51), -- too busy to process + unwillingToPerform (53), -- unable to sort + other (80) + }, + attributeType [0] AttributeDescription OPTIONAL } + ---------------------------------------------------------------------------*/ + +int +ldap_parse_sortresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *returnCode, + char **attribute ) +{ + BerElement *ber; + ber_tag_t tag, berTag; + ber_len_t berLen; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if (ld == NULL) { + return LDAP_PARAM_ERROR; + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if (attribute) { + *attribute = NULL; + } + + if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) { + /* Not sort result control */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + /* Extract the result code from the control. */ + tag = ber_scanf(ber, "{e" /*}*/, returnCode); + + if( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* If caller wants the attribute name, and if it's present in the control, + extract the attribute name which caused the error. */ + if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen))) + { + tag = ber_scanf(ber, "ta", &berTag, attribute); + + if (tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + + ber_free(ber,1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/contrib/libs/openldap/libraries/libldap/stctrl.c b/contrib/libs/openldap/libraries/libldap/stctrl.c new file mode 100644 index 0000000000..0299f83d6e --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/stctrl.c @@ -0,0 +1,302 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 2007 Pierangelo Masarati. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Pierangelo Masarati for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + +/* + * Client-side of <draft-wahl-ldap-session-03> + */ + +int +ldap_create_session_tracking_value( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + + struct berval ip, name, oid, id; + + if ( ld == NULL || + formatOID == NULL || + value == NULL ) + { +param_error:; + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + ld->ld_errno = LDAP_SUCCESS; + + /* check sizes according to I.D. */ + if ( sessionSourceIp == NULL ) { + BER_BVSTR( &ip, "" ); + + } else { + ber_str2bv( sessionSourceIp, 0, 0, &ip ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( ip.bv_len > 128 ) goto param_error; + } + + if ( sessionSourceName == NULL ) { + BER_BVSTR( &name, "" ); + + } else { + ber_str2bv( sessionSourceName, 0, 0, &name ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( name.bv_len > 65536 ) goto param_error; + } + + ber_str2bv( formatOID, 0, 0, &oid ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( oid.bv_len > 1024 ) goto param_error; + + if ( sessionTrackingIdentifier == NULL || + sessionTrackingIdentifier->bv_val == NULL ) + { + BER_BVSTR( &id, "" ); + + } else { + id = *sessionTrackingIdentifier; + } + + /* prepare value */ + value->bv_val = NULL; + value->bv_len = 0; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{OOOO}", &ip, &name, &oid, &id ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +/* + * NOTE: this API is bad; it could be much more efficient... + */ +int +ldap_create_session_tracking_control( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_session_tracking_value( ld, + sessionSourceIp, sessionSourceName, formatOID, + sessionTrackingIdentifier, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SESSION_TRACKING, + 0, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +int +ldap_parse_session_tracking_control( + LDAP *ld, + LDAPControl *ctrl, + struct berval *ip, + struct berval *name, + struct berval *oid, + struct berval *id ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + + if ( ld == NULL || + ctrl == NULL || + ip == NULL || + name == NULL || + oid == NULL || + id == NULL ) + { + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + /* NOTE: we want the caller to get all or nothing; + * we could allow some of the pointers to be NULL, + * if one does not want part of the data */ + return LDAP_PARAM_ERROR; + } + + BER_BVZERO( ip ); + BER_BVZERO( name ); + BER_BVZERO( oid ); + BER_BVZERO( id ); + + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_skip_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) { + tag = LBER_ERROR; + goto error; + } + + /* sessionSourceIp */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { + if ( len > 128 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", ip ); + } + + /* sessionSourceName */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { + if ( len > 65536 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", name ); + } + + /* formatOID */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + ld->ld_errno = LDAP_DECODING_ERROR; + goto error; + + } else { + if ( len > 1024 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", oid ); + } + + /* FIXME: should check if it is an OID... leave it to the caller */ + + /* sessionTrackingIdentifier */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { +#if 0 + if ( len > 65536 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } +#endif + tag = ber_scanf( ber, "o", id ); + } + + /* closure */ + tag = ber_skip_tag( ber, &len ); + if ( tag == LBER_DEFAULT && len == 0 ) { + tag = 0; + } + +error:; + (void)ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + return LDAP_DECODING_ERROR; + } + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ diff --git a/contrib/libs/openldap/libraries/libldap/string.c b/contrib/libs/openldap/libraries/libldap/string.c new file mode 100644 index 0000000000..92f37d3ffb --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/string.c @@ -0,0 +1,177 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * Locale-specific 1-byte character versions + * See utf-8.c for UTF-8 versions + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/ctype.h> + +#include "ldap-int.h" + + +#if defined ( HAVE_STRSPN ) +#define int_strspn strspn +#else +static int int_strspn( const char *str, const char *delim ) +{ + int pos; + const char *p=delim; + + for( pos=0; (*str) ; pos++,str++) { + if (*str!=*p) { + for( p=delim; (*p) ; p++ ) { + if (*str==*p) { + break; + } + } + } + + if (*p=='\0') { + return pos; + } + } + return pos; +} +#endif + +#if defined( HAVE_STRPBRK ) +#define int_strpbrk strpbrk +#else +static char *(int_strpbrk)( const char *str, const char *accept ) +{ + const char *p; + + for( ; (*str) ; str++ ) { + for( p=accept; (*p) ; p++) { + if (*str==*p) { + return str; + } + } + } + + return NULL; +} +#endif + +char *(ldap_pvt_strtok)( char *str, const char *delim, char **pos ) +{ + char *p; + + if (pos==NULL) { + return NULL; + } + + if (str==NULL) { + if (*pos==NULL) { + return NULL; + } + + str=*pos; + } + + /* skip any initial delimiters */ + str += int_strspn( str, delim ); + if (*str == '\0') { + return NULL; + } + + p = int_strpbrk( str, delim ); + if (p==NULL) { + *pos = NULL; + + } else { + *p ='\0'; + *pos = p+1; + } + + return str; +} + +char * +ldap_pvt_str2upper( char *str ) +{ + char *s; + + /* to upper */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOUPPER( (unsigned char) *s ); + } + } + + return( str ); +} + +struct berval * +ldap_pvt_str2upperbv( char *str, struct berval *bv ) +{ + char *s = NULL; + + assert( bv != NULL ); + + /* to upper */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOUPPER( (unsigned char) *s ); + } + } + + bv->bv_val = str; + bv->bv_len = (ber_len_t)(s - str); + + return( bv ); +} + +char * +ldap_pvt_str2lower( char *str ) +{ + char *s; + + /* to lower */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOLOWER( (unsigned char) *s ); + } + } + + return( str ); +} + +struct berval * +ldap_pvt_str2lowerbv( char *str, struct berval *bv ) +{ + char *s = NULL; + + assert( bv != NULL ); + + /* to lower */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOLOWER( (unsigned char) *s ); + } + } + + bv->bv_val = str; + bv->bv_len = (ber_len_t)(s - str); + + return( bv ); +} diff --git a/contrib/libs/openldap/libraries/libldap/tavl.c b/contrib/libs/openldap/libraries/libldap/tavl.c new file mode 100644 index 0000000000..18a6e3768f --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/tavl.c @@ -0,0 +1,523 @@ +/* avl.c - routines to implement an avl tree */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2022 The OpenLDAP Foundation. + * Portions Copyright (c) 2005 by Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP software. + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memrealloc realloc +#define ber_memfree free +#else +#include "lber.h" +#endif + +#define AVL_INTERNAL +#include "ldap_avl.h" + +/* Maximum tree depth this host's address space could support */ +#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT) + +static const int avl_bfs[] = {LH, RH}; + +/* + * Threaded AVL trees - for fast in-order traversal of nodes. + */ +/* + * ldap_tavl_insert -- insert a node containing data data into the avl tree + * with root root. fcmp is a function to call to compare the data portion + * of two nodes. it should take two arguments and return <, >, or == 0, + * depending on whether its first argument is <, >, or == its second + * argument (like strcmp, e.g.). fdup is a function to call when a duplicate + * node is inserted. it should return 0, or -1 and its return value + * will be the return value from ldap_avl_insert in the case of a duplicate node. + * the function will be called with the original node's data as its first + * argument and with the incoming duplicate node's data as its second + * argument. this could be used, for example, to keep a count with each + * node. + * + * NOTE: this routine may malloc memory + */ +int +ldap_tavl_insert( TAvlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup ) +{ + TAvlnode *t, *p, *s, *q, *r; + int a, cmp, ncmp; + + if ( *root == NULL ) { + if (( r = (TAvlnode *) ber_memalloc( sizeof( TAvlnode ))) == NULL ) { + return( -1 ); + } + r->avl_link[0] = r->avl_link[1] = NULL; + r->avl_data = data; + r->avl_bf = EH; + r->avl_bits[0] = r->avl_bits[1] = AVL_THREAD; + *root = r; + + return( 0 ); + } + + t = NULL; + s = p = *root; + + /* find insertion point */ + while (1) { + cmp = fcmp( data, p->avl_data ); + if ( cmp == 0 ) + return (*fdup)( p->avl_data, data ); + + cmp = (cmp > 0); + q = ldap_avl_child( p, cmp ); + if (q == NULL) { + /* insert */ + if (( q = (TAvlnode *) ber_memalloc( sizeof( TAvlnode ))) == NULL ) { + return( -1 ); + } + q->avl_link[cmp] = p->avl_link[cmp]; + q->avl_link[!cmp] = p; + q->avl_data = data; + q->avl_bf = EH; + q->avl_bits[0] = q->avl_bits[1] = AVL_THREAD; + + p->avl_link[cmp] = q; + p->avl_bits[cmp] = AVL_CHILD; + break; + } else if ( q->avl_bf ) { + t = p; + s = q; + } + p = q; + } + + /* adjust balance factors */ + cmp = fcmp( data, s->avl_data ) > 0; + r = p = s->avl_link[cmp]; + a = avl_bfs[cmp]; + + while ( p != q ) { + cmp = fcmp( data, p->avl_data ) > 0; + p->avl_bf = avl_bfs[cmp]; + p = p->avl_link[cmp]; + } + + /* checks and balances */ + + if ( s->avl_bf == EH ) { + s->avl_bf = a; + return 0; + } else if ( s->avl_bf == -a ) { + s->avl_bf = EH; + return 0; + } else if ( s->avl_bf == a ) { + cmp = (a > 0); + ncmp = !cmp; + if ( r->avl_bf == a ) { + /* single rotation */ + p = r; + if ( r->avl_bits[ncmp] == AVL_THREAD ) { + r->avl_bits[ncmp] = AVL_CHILD; + s->avl_bits[cmp] = AVL_THREAD; + } else { + s->avl_link[cmp] = r->avl_link[ncmp]; + r->avl_link[ncmp] = s; + } + s->avl_bf = 0; + r->avl_bf = 0; + } else if ( r->avl_bf == -a ) { + /* double rotation */ + p = r->avl_link[ncmp]; + if ( p->avl_bits[cmp] == AVL_THREAD ) { + p->avl_bits[cmp] = AVL_CHILD; + r->avl_bits[ncmp] = AVL_THREAD; + } else { + r->avl_link[ncmp] = p->avl_link[cmp]; + p->avl_link[cmp] = r; + } + if ( p->avl_bits[ncmp] == AVL_THREAD ) { + p->avl_bits[ncmp] = AVL_CHILD; + s->avl_link[cmp] = p; + s->avl_bits[cmp] = AVL_THREAD; + } else { + s->avl_link[cmp] = p->avl_link[ncmp]; + p->avl_link[ncmp] = s; + } + if ( p->avl_bf == a ) { + s->avl_bf = -a; + r->avl_bf = 0; + } else if ( p->avl_bf == -a ) { + s->avl_bf = 0; + r->avl_bf = a; + } else { + s->avl_bf = 0; + r->avl_bf = 0; + } + p->avl_bf = 0; + } + /* Update parent */ + if ( t == NULL ) + *root = p; + else if ( s == t->avl_right ) + t->avl_right = p; + else + t->avl_left = p; + } + + return 0; +} + +void* +ldap_tavl_delete( TAvlnode **root, void* data, AVL_CMP fcmp ) +{ + TAvlnode *p, *q, *r, *top; + int side, side_bf, shorter, nside = -1; + + /* parent stack */ + TAvlnode *pptr[MAX_TREE_DEPTH]; + unsigned char pdir[MAX_TREE_DEPTH]; + int depth = 0; + + if ( *root == NULL ) + return NULL; + + p = *root; + + while (1) { + side = fcmp( data, p->avl_data ); + if ( !side ) + break; + side = ( side > 0 ); + pdir[depth] = side; + pptr[depth++] = p; + + if ( p->avl_bits[side] == AVL_THREAD ) + return NULL; + p = p->avl_link[side]; + } + data = p->avl_data; + + /* If this node has two children, swap so we are deleting a node with + * at most one child. + */ + if ( p->avl_bits[0] == AVL_CHILD && p->avl_bits[1] == AVL_CHILD && + p->avl_link[0] && p->avl_link[1] ) { + + /* find the immediate predecessor <q> */ + q = p->avl_link[0]; + side = depth; + pdir[depth++] = 0; + while (q->avl_bits[1] == AVL_CHILD && q->avl_link[1]) { + pdir[depth] = 1; + pptr[depth++] = q; + q = q->avl_link[1]; + } + /* swap links */ + r = p->avl_link[0]; + p->avl_link[0] = q->avl_link[0]; + q->avl_link[0] = r; + + q->avl_link[1] = p->avl_link[1]; + p->avl_link[1] = q; + + p->avl_bits[0] = q->avl_bits[0]; + p->avl_bits[1] = q->avl_bits[1]; + q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD; + + q->avl_bf = p->avl_bf; + + /* fix stack positions: old parent of p points to q */ + pptr[side] = q; + if ( side ) { + r = pptr[side-1]; + r->avl_link[pdir[side-1]] = q; + } else { + *root = q; + } + /* new parent of p points to p */ + if ( depth-side > 1 ) { + r = pptr[depth-1]; + r->avl_link[1] = p; + } else { + q->avl_link[0] = p; + } + + /* fix right subtree: successor of p points to q */ + r = q->avl_link[1]; + while ( r->avl_bits[0] == AVL_CHILD && r->avl_link[0] ) + r = r->avl_link[0]; + r->avl_link[0] = q; + } + + /* now <p> has at most one child, get it */ + if ( p->avl_link[0] && p->avl_bits[0] == AVL_CHILD ) { + q = p->avl_link[0]; + /* Preserve thread continuity */ + r = p->avl_link[1]; + nside = 1; + } else if ( p->avl_link[1] && p->avl_bits[1] == AVL_CHILD ) { + q = p->avl_link[1]; + r = p->avl_link[0]; + nside = 0; + } else { + q = NULL; + if ( depth > 0 ) + r = p->avl_link[pdir[depth-1]]; + else + r = NULL; + } + + ber_memfree( p ); + + /* Update child thread */ + if ( q ) { + for ( ; q->avl_bits[nside] == AVL_CHILD && q->avl_link[nside]; + q = q->avl_link[nside] ) ; + q->avl_link[nside] = r; + } + + if ( !depth ) { + *root = q; + return data; + } + + /* set the child into p's parent */ + depth--; + p = pptr[depth]; + side = pdir[depth]; + p->avl_link[side] = q; + + if ( !q ) { + p->avl_bits[side] = AVL_THREAD; + p->avl_link[side] = r; + } + + top = NULL; + shorter = 1; + + while ( shorter ) { + p = pptr[depth]; + side = pdir[depth]; + nside = !side; + side_bf = avl_bfs[side]; + + /* case 1: height unchanged */ + if ( p->avl_bf == EH ) { + /* Tree is now heavier on opposite side */ + p->avl_bf = avl_bfs[nside]; + shorter = 0; + + } else if ( p->avl_bf == side_bf ) { + /* case 2: taller subtree shortened, height reduced */ + p->avl_bf = EH; + } else { + /* case 3: shorter subtree shortened */ + if ( depth ) + top = pptr[depth-1]; /* p->parent; */ + else + top = NULL; + /* set <q> to the taller of the two subtrees of <p> */ + q = p->avl_link[nside]; + if ( q->avl_bf == EH ) { + /* case 3a: height unchanged, single rotate */ + if ( q->avl_bits[side] == AVL_THREAD ) { + q->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + } else { + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + } + shorter = 0; + q->avl_bf = side_bf; + p->avl_bf = (- side_bf); + + } else if ( q->avl_bf == p->avl_bf ) { + /* case 3b: height reduced, single rotate */ + if ( q->avl_bits[side] == AVL_THREAD ) { + q->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + } else { + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + } + shorter = 1; + q->avl_bf = EH; + p->avl_bf = EH; + + } else { + /* case 3c: height reduced, balance factors opposite */ + r = q->avl_link[side]; + if ( r->avl_bits[nside] == AVL_THREAD ) { + r->avl_bits[nside] = AVL_CHILD; + q->avl_bits[side] = AVL_THREAD; + } else { + q->avl_link[side] = r->avl_link[nside]; + r->avl_link[nside] = q; + } + + if ( r->avl_bits[side] == AVL_THREAD ) { + r->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + p->avl_link[nside] = r; + } else { + p->avl_link[nside] = r->avl_link[side]; + r->avl_link[side] = p; + } + + if ( r->avl_bf == side_bf ) { + q->avl_bf = (- side_bf); + p->avl_bf = EH; + } else if ( r->avl_bf == (- side_bf)) { + q->avl_bf = EH; + p->avl_bf = side_bf; + } else { + q->avl_bf = EH; + p->avl_bf = EH; + } + r->avl_bf = EH; + q = r; + } + /* a rotation has caused <q> (or <r> in case 3c) to become + * the root. let <p>'s former parent know this. + */ + if ( top == NULL ) { + *root = q; + } else if (top->avl_link[0] == p) { + top->avl_link[0] = q; + } else { + top->avl_link[1] = q; + } + /* end case 3 */ + p = q; + } + if ( !depth ) + break; + depth--; + } /* end while(shorter) */ + + return data; +} + +/* + * ldap_tavl_free -- traverse avltree root, freeing the memory it is using. + * the dfree() is called to free the data portion of each node. The + * number of items actually freed is returned. + */ + +int +ldap_tavl_free( TAvlnode *root, AVL_FREE dfree ) +{ + int nleft, nright; + + if ( root == 0 ) + return( 0 ); + + nleft = ldap_tavl_free( ldap_avl_lchild( root ), dfree ); + + nright = ldap_tavl_free( ldap_avl_rchild( root ), dfree ); + + if ( dfree ) + (*dfree)( root->avl_data ); + ber_memfree( root ); + + return( nleft + nright + 1 ); +} + +/* + * ldap_tavl_find -- search avltree root for a node with data data. the function + * cmp is used to compare things. it is called with data as its first arg + * and the current node data as its second. it should return 0 if they match, + * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. + */ + +/* + * ldap_tavl_find2 - returns TAvlnode instead of data pointer. + * ldap_tavl_find3 - as above, but returns TAvlnode even if no match is found. + * also set *ret = last comparison result, or -1 if root == NULL. + */ +TAvlnode * +ldap_tavl_find3( TAvlnode *root, const void *data, AVL_CMP fcmp, int *ret ) +{ + int cmp = -1, dir; + TAvlnode *prev = root; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + prev = root; + dir = cmp > 0; + root = ldap_avl_child( root, dir ); + } + *ret = cmp; + return root ? root : prev; +} + +TAvlnode * +ldap_tavl_find2( TAvlnode *root, const void *data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = ldap_avl_child( root, cmp ); + } + return root; +} + +void* +ldap_tavl_find( TAvlnode *root, const void* data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = ldap_avl_child( root, cmp ); + } + + return( root ? root->avl_data : 0 ); +} + +/* Return the leftmost or rightmost node in the tree */ +TAvlnode * +ldap_tavl_end( TAvlnode *root, int dir ) +{ + if ( root ) { + while ( root->avl_bits[dir] == AVL_CHILD ) + root = root->avl_link[dir]; + } + return root; +} + +/* Return the next node in the given direction */ +TAvlnode * +ldap_tavl_next( TAvlnode *root, int dir ) +{ + if ( root ) { + int c = root->avl_bits[dir]; + + root = root->avl_link[dir]; + if ( c == AVL_CHILD ) { + dir ^= 1; + while ( root->avl_bits[dir] == AVL_CHILD ) + root = root->avl_link[dir]; + } + } + return root; +} diff --git a/contrib/libs/openldap/libraries/libldap/thr_debug.c b/contrib/libs/openldap/libraries/libldap/thr_debug.c new file mode 100644 index 0000000000..9e028b3273 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/thr_debug.c @@ -0,0 +1,1338 @@ +/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * This package provides several types of thread operation debugging: + * + * - Check the results of operations on threads, mutexes, condition + * variables and read/write locks. Also check some thread pool + * operations, but not those for which failure can happen in normal + * slapd operation. + * + * - Wrap those types except threads and pools in structs with state + * information, and check that on all operations: + * + * + Check that the resources are initialized and are only used at + * their original address (i.e. not realloced or copied). + * + * + Check the owner (thread ID) on mutex operations. + * + * + Optionally allocate a reference to a byte of dummy memory. + * This lets malloc debuggers see some incorrect use as memory + * leaks, access to freed memory, etc. + * + * - Print an error message and by default abort() upon errors. + * + * - Print a count of leaked thread resources after cleanup. + * + * Compile-time (./configure) setup: Macros defined in CPPFLAGS. + * + * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2 + * Enables debugging, but value & 2 turns off type wrapping. + * + * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned. + * Used by dummy memory option "scramble". Default = unsigned long. + * + * LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID. + * + * In addition, you may need to set up an implementation-specific way + * to enable whatever error checking your thread library provides. + * Currently only implemented for Posix threads (pthreads), where + * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default + * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for + * Linux threads. See pthread_mutexattr_settype(3). + * + * Run-time configuration: + * + * Memory debugging tools: + * Tools that report uninitialized memory accesses should disable + * such warnings about the function debug_already_initialized(). + * Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG. + * + * Environment variable $LDAP_THREAD_DEBUG: + * The variable may contain a comma- or space-separated option list. + * Options: + * off - Disable this package. (It still slows things down). + * tracethreads - Report create/join/exit/kill of threads. + * noabort - Do not abort() on errors. + * noerror - Do not report errors. Implies noabort. + * nocount - Do not report counts of unreleased resources. + * nosync - Disable tests that use synchronization and thus + * clearly affect thread scheduling: + * Implies nocount, and cancels threadID if that is set. + * Note that if you turn on tracethreads or malloc + * debugging, these also use library calls which may + * affect thread scheduling (fprintf and malloc). + * The following options do not apply if type wrapping is disabled: + * nomem - Do not check memory operations. + * Implies noreinit,noalloc. + * noreinit - Do not catch reinitialization of existing resources. + * (That test accesses uninitialized memory). + * threadID - Trace thread IDs. Currently mostly useless. + * Malloc debugging -- allocate dummy memory for initialized + * resources, so malloc debuggers will report them as memory leaks: + * noalloc - Default. Do not allocate dummy memory. + * alloc - Store a pointer to dummy memory. However, leak + * detectors might not catch unreleased resources in + * global variables. + * scramble - Store bitwise complement of dummy memory pointer. + * That never escapes memory leak detectors - + * but detection while the program is running will + * report active resources as leaks. Do not + * use this if a garbage collector is in use:-) + * adjptr - Point to end of dummy memory. + * Purify reports these as "potential leaks" (PLK). + * I have not checked other malloc debuggers. + */ + +#include "portable.h" + +#if defined( LDAP_THREAD_DEBUG ) + +#include <stdio.h> +#include <ac/errno.h> +#include <ac/stdlib.h> +#include <ac/string.h> + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_DEBUG_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* Get the underlying implementation */ + +#ifndef LDAP_THREAD_DEBUG_WRAP +#undef LDAP_THREAD_DEBUG_THREAD_ID +#elif !defined LDAP_THREAD_DEBUG_THREAD_ID +#define LDAP_THREAD_DEBUG_THREAD_ID 1 +#endif + +/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */ +#undef malloc +#undef calloc +#undef realloc +#undef free + + +/* Options from environment variable $LDAP_THREAD_DEBUG */ +enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more }; +static int count = Count_yes; +#ifdef LDAP_THREAD_DEBUG_WRAP +enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr }; +static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset; +static int nomem, noreinit; +#endif +#if LDAP_THREAD_DEBUG_THREAD_ID +0 +static int threadID; +#else +enum { threadID = 0 }; +#endif +static int nodebug, noabort, noerror, nosync, tracethreads; +static int wrap_threads; +static int options_done; + + +/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */ +static int threading_enabled; + + +/* Resource counts */ +enum { + Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex, + Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max +}; +static int resource_counts[Idx_max]; +static const char *const resource_names[] = { + "unexited threads", "unjoined threads", "locked mutexes", + "mutexes", "conds", "rdwrs", "thread pools" +}; +static ldap_int_thread_mutex_t resource_mutexes[Idx_max]; + + +/* Hide pointers from malloc debuggers. */ +#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr)) +#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num)) +#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num)) + + +#define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var))) +#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));} + +#define ERROR(var, msg) { \ + if (!noerror) { \ + errmsg(__FILE__, __LINE__, (msg), #var, (var)); \ + if( !noabort ) abort(); \ + } \ +} + +#define ERROR_IF(rc, msg) { \ + if (!noerror) { \ + int rc_ = (rc); \ + if (rc_) { \ + errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \ + if( !noabort ) abort(); \ + } \ + } \ +} + +#ifdef LDAP_THREAD_DEBUG_WRAP +#define MEMERROR_IF(rc, msg, mem_act) { \ + if (!noerror) { \ + int rc_ = (rc); \ + if (rc_) { \ + errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \ + if( wraptype != Wrap_noalloc ) { mem_act; } \ + if( !noabort ) abort(); \ + } \ + } \ +} +#endif /* LDAP_THREAD_DEBUG_WRAP */ + +#if 0 +static void +warn( const char *file, int line, const char *msg, const char *var, int val ) +{ + fprintf( stderr, + (strpbrk( var, "!=" ) + ? "%s:%d: %s warning: %s\n" + : "%s:%d: %s warning: %s is %d\n"), + file, line, msg, var, val ); +} +#endif + +static void +errmsg( const char *file, int line, const char *msg, const char *var, int val ) +{ + fprintf( stderr, + (strpbrk( var, "!=" ) + ? "%s:%d: %s error: %s\n" + : "%s:%d: %s error: %s is %d\n"), + file, line, msg, var, val ); +} + +static void +count_resource_leaks( void ) +{ + int i, j; + char errbuf[200]; + if( count == Count_yes ) { + count = Count_reported; +#if 0 /* Could break if there are still threads after atexit */ + for( i = j = 0; i < Idx_max; i++ ) + j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] ); + WARN_IF( j, "ldap_debug_thread_destroy:mutexes" ); +#endif + for( i = j = 0; i < Idx_max; i++ ) + if( resource_counts[i] ) + j += sprintf( errbuf + j, ", %d %s", + resource_counts[i], resource_names[i] ); + if( j ) + fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 ); + } +} + +static void +get_options( void ) +{ + static const struct option_info_s { + const char *name; + int *var, val; + } option_info[] = { + { "off", &nodebug, 1 }, + { "noabort", &noabort, 1 }, + { "noerror", &noerror, 1 }, + { "nocount", &count, Count_no }, + { "nosync", &nosync, 1 }, +#if LDAP_THREAD_DEBUG_THREAD_ID +0 + { "threadID", &threadID, 1 }, +#endif +#ifdef LDAP_THREAD_DEBUG_WRAP + { "nomem", &nomem, 1 }, + { "noreinit", &noreinit, 1 }, + { "noalloc", &wraptype, Wrap_noalloc }, + { "alloc", &wraptype, Wrap_alloc }, + { "adjptr", &wraptype, Wrap_adjptr }, + { "scramble", &wraptype, Wrap_scramble }, +#endif + { "tracethreads", &tracethreads, 1 }, + { NULL, NULL, 0 } + }; + const char *s = getenv( "LDAP_THREAD_DEBUG" ); + if( s != NULL ) { + while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) { + size_t optlen = strcspn( s, ", \t\r\n" ); + const struct option_info_s *oi = option_info; + while( oi->name && + (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) ) + oi++; + if( oi->name ) + *oi->var = oi->val; + else + fprintf( stderr, + "== thr_debug: Unknown $%s option '%.*s' ==\n", + "LDAP_THREAD_DEBUG", (int) optlen, s ); + s += optlen; + } + } + if( nodebug ) { + tracethreads = 0; + nosync = noerror = 1; + } + if( nosync ) + count = Count_no; + if( noerror ) + noabort = 1; +#if LDAP_THREAD_DEBUG_THREAD_ID +0 + if( nosync ) + threadID = 0; +#endif +#ifdef LDAP_THREAD_DEBUG_WRAP + if( noerror ) + nomem = 1; + if( !nomem ) { + static const ldap_debug_usage_info_t usage; + if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *) + || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *) + || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage + || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) ) + { + fputs( "== thr_debug: Memory checks unsupported, " + "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr ); + nomem = 1; + } + } + if( nomem ) { + noreinit = 1; + wraptype = Wrap_noalloc; + } + unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr)); +#endif + wrap_threads = (tracethreads || threadID || count); + options_done = 1; +} + + +#ifndef LDAP_THREAD_DEBUG_WRAP + +#define WRAPPED(ptr) (ptr) +#define GET_OWNER(ptr) 0 +#define SET_OWNER(ptr, thread) ((void) 0) +#define RESET_OWNER(ptr) ((void) 0) +#define ASSERT_OWNER(ptr, msg) ((void) 0) +#define ASSERT_NO_OWNER(ptr, msg) ((void) 0) + +#define init_usage(ptr, msg) ((void) 0) +#define check_usage(ptr, msg) ((void) 0) +#define destroy_usage(ptr) ((void) 0) + +#else /* LDAP_THREAD_DEBUG_WRAP */ + +/* Specialize this if the initializer is not appropriate. */ +/* The ASSERT_NO_OWNER() definition may also need an override. */ +#ifndef LDAP_DEBUG_THREAD_NONE +#define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */ +#endif + +static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE; + +#define THREAD_MUTEX_OWNER(mutex) \ + ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() ) + +void +ldap_debug_thread_assert_mutex_owner( + const char *file, + int line, + const char *msg, + ldap_pvt_thread_mutex_t *mutex ) +{ + if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) { + errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 ); + if( !noabort ) abort(); + } +} + +#define WRAPPED(ptr) (&(ptr)->wrapped) +#define GET_OWNER(ptr) ((ptr)->owner) +#define SET_OWNER(ptr, thread) ((ptr)->owner = (thread)) +#define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none) +#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg ) +#ifndef ASSERT_NO_OWNER +#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \ + !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg ) +#endif + +/* Try to provoke memory access error (for malloc debuggers) */ +#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();} + +static void debug_noop( void ); +static int debug_already_initialized( const ldap_debug_usage_info_t *usage ); + +/* Name used for clearer error message */ +#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage )) + +#define DUMMY_ADDR(usage) \ + (wraptype == Wrap_scramble \ + ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \ + : (usage)->mem.ptr + unwrap_offset) + +/* Mark resource as initialized */ +static void +init_usage( ldap_debug_usage_info_t *usage, const char *msg ) +{ + if( !options_done ) + get_options(); + if( !nomem ) { + if( !noreinit ) { + MEMERROR_IF( debug_already_initialized( usage ), msg, { + /* Provoke malloc debuggers */ + unsigned char *dummy = DUMMY_ADDR( usage ); + PEEK( dummy ); + free( dummy ); + free( dummy ); + } ); + } + if( wraptype != Wrap_noalloc ) { + unsigned char *dummy = malloc( 1 ); + assert( dummy != NULL ); + if( wraptype == Wrap_scramble ) { + usage->mem.num = SCRAMBLE( dummy ); + /* Verify that ptr<->integer casts work on this host */ + assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy ); + } else { + usage->mem.ptr = dummy + wrap_offset; + } + } + } else { + /* Unused, but set for readability in debugger */ + usage->mem.ptr = NULL; + } + usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */ + usage->magic = ldap_debug_magic; + usage->state = ldap_debug_state_inited; +} + +/* Check that resource is initialized and not copied/realloced */ +static void +check_usage( const ldap_debug_usage_info_t *usage, const char *msg ) +{ + enum { Is_destroyed = 1 }; /* Name used for clearer error message */ + + if( usage->magic != ldap_debug_magic ) { + ERROR( usage->magic, msg ); + return; + } + switch( usage->state ) { + case ldap_debug_state_destroyed: + MEMERROR_IF( Is_destroyed, msg, { + PEEK( DUMMY_ADDR( usage ) ); + } ); + break; + default: + ERROR( usage->state, msg ); + break; + case ldap_debug_state_inited: + if( !nomem ) { + MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, { + PEEK( DUMMY_ADDR( usage ) ); + PEEK( UNSCRAMBLE_usagep( usage->self ) ); + } ); + } + break; + } +} + +/* Mark resource as destroyed. */ +/* Does not check for errors, call check_usage()/init_usage() first. */ +static void +destroy_usage( ldap_debug_usage_info_t *usage ) +{ + if( usage->state == ldap_debug_state_inited ) { + if( wraptype != Wrap_noalloc ) { + free( DUMMY_ADDR( usage ) ); + /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers + * in case the resource is used after it is freed. */ + } + usage->state = ldap_debug_state_destroyed; + } +} + +/* Define these after they are used, so they are hopefully not inlined */ + +static void +debug_noop( void ) +{ +} + +/* + * Valid programs access uninitialized memory here unless "noreinit". + * + * Returns true if the resource is initialized and not copied/realloced. + */ +LDAP_GCCATTR((noinline)) +static int +debug_already_initialized( const ldap_debug_usage_info_t *usage ) +{ + /* + * 'ret' keeps the Valgrind warning "Conditional jump or move + * depends on uninitialised value(s)" _inside_ this function. + */ + volatile int ret = 0; + if( usage->state == ldap_debug_state_inited ) + if( !IS_COPY_OR_MOVED( usage ) ) + if( usage->magic == ldap_debug_magic ) + ret = 1; + return ret; +} + +#endif /* LDAP_THREAD_DEBUG_WRAP */ + + +#if !(LDAP_THREAD_DEBUG_THREAD_ID +0) + +typedef void ldap_debug_thread_t; +#define init_thread_info() {} +#define with_thread_info_lock(statements) { statements; } +#define thread_info_detached(t) 0 +#define add_thread_info(msg, thr, det) ((void) 0) +#define remove_thread_info(tinfo, msg) ((void) 0) +#define get_thread_info(thread, msg) NULL + +#else /* LDAP_THREAD_DEBUG_THREAD_ID */ + +/* + * Thread ID tracking. Currently achieves little. + * Should be either expanded or deleted. + */ + +/* + * Array of threads. Used instead of making ldap_pvt_thread_t a wrapper + * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self(). + */ +typedef struct { + ldap_pvt_thread_t wrapped; + ldap_debug_usage_info_t usage; + int detached; + int idx; +} ldap_debug_thread_t; + +static ldap_debug_thread_t **thread_info; +static unsigned int thread_info_size, thread_info_used; +static ldap_int_thread_mutex_t thread_info_mutex; + +#define init_thread_info() { \ + if( threadID ) { \ + int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \ + assert( mutex_init_rc == 0 ); \ + } \ +} + +#define with_thread_info_lock(statements) { \ + int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \ + assert( rc_wtl_ == 0 ); \ + { statements; } \ + rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \ + assert( rc_wtl_ == 0 ); \ +} + +#define thread_info_detached(t) ((t)->detached) + +static void +add_thread_info( + const char *msg, + const ldap_pvt_thread_t *thread, + int detached ) +{ + ldap_debug_thread_t *t; + + if( thread_info_used >= thread_info_size ) { + unsigned int more = thread_info_size + 8; + unsigned int new_size = thread_info_size + more; + + t = calloc( more, sizeof(ldap_debug_thread_t) ); + assert( t != NULL ); + thread_info = realloc( thread_info, new_size * sizeof(*thread_info) ); + assert( thread_info != NULL ); + do { + t->idx = thread_info_size; + thread_info[thread_info_size++] = t++; + } while( thread_info_size < new_size ); + } + + t = thread_info[thread_info_used]; + init_usage( &t->usage, msg ); + t->wrapped = *thread; + t->detached = detached; + thread_info_used++; +} + +static void +remove_thread_info( ldap_debug_thread_t *t, const char *msg ) +{ + ldap_debug_thread_t *last; + int idx; + check_usage( &t->usage, msg ); + destroy_usage( &t->usage ); + idx = t->idx; + assert( thread_info[idx] == t ); + last = thread_info[--thread_info_used]; + assert( last->idx == thread_info_used ); + (thread_info[idx] = last)->idx = idx; + (thread_info[thread_info_used] = t )->idx = thread_info_used; +} + +static ldap_debug_thread_t * +get_thread_info( ldap_pvt_thread_t thread, const char *msg ) +{ + unsigned int i; + ldap_debug_thread_t *t; + for( i = 0; i < thread_info_used; i++ ) { + if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) ) + break; + } + ERROR_IF( i == thread_info_used, msg ); + t = thread_info[i]; + check_usage( &t->usage, msg ); + return t; +} + +#endif /* LDAP_THREAD_DEBUG_THREAD_ID */ + + +static char * +thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread ) +{ + int i; + --bufsize; + if( bufsize > 2*sizeof(thread) ) + bufsize = 2*sizeof(thread); + for( i = 0; i < bufsize; i += 2 ) + snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] ); + return buf; +} + + +/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */ +static void +adjust_count( int which, int adjust ) +{ + int rc; + switch( count ) { + case Count_no: + break; + case Count_yes: + rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] ); + assert( rc == 0 ); + resource_counts[which] += adjust; + rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] ); + assert( rc == 0 ); + break; + case Count_reported: + fputs( "== thr_debug: More thread activity after exit ==\n", stderr ); + count = Count_reported_more; + /* FALL THROUGH */ + case Count_reported_more: + /* Not used, but result might be inspected with debugger */ + /* (Hopefully threading is disabled by now...) */ + resource_counts[which] += adjust; + break; + } +} + + +/* Wrappers for LDAP_THREAD_IMPLEMENTATION: */ + +/* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */ +int +ldap_debug_thread_initialize( void ) +{ + int i, rc, rc2; + if( !options_done ) + get_options(); + ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" ); + threading_enabled = 1; + rc = ldap_int_thread_initialize(); + if( rc ) { + ERROR( rc, "ldap_debug_thread_initialize:threads" ); + threading_enabled = 0; + } else { + init_thread_info(); + if( count != Count_no ) { + for( i = rc2 = 0; i < Idx_max; i++ ) + rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] ); + assert( rc2 == 0 ); + /* FIXME: Only for static libldap as in init.c? If so, why? */ + atexit( count_resource_leaks ); + } + } + return rc; +} + +/* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */ +int +ldap_debug_thread_destroy( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" ); + /* sleep(1) -- need to wait for thread pool to finish? */ + rc = ldap_int_thread_destroy(); + if( rc ) { + ERROR( rc, "ldap_debug_thread_destroy:threads" ); + } else { + threading_enabled = 0; + } + return rc; +} + +int +ldap_pvt_thread_set_concurrency( int n ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" ); + rc = ldap_int_thread_set_concurrency( n ); + ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" ); + return rc; +} + +int +ldap_pvt_thread_get_concurrency( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" ); + rc = ldap_int_thread_get_concurrency(); + ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" ); + return rc; +} + +unsigned int +ldap_pvt_thread_sleep( unsigned int interval ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" ); + rc = ldap_int_thread_sleep( interval ); + ERROR_IF( rc, "ldap_pvt_thread_sleep" ); + return 0; +} + +static void +thread_exiting( const char *how, const char *msg ) +{ + ldap_pvt_thread_t thread; +#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */ + ERROR_IF( !threading_enabled, msg ); +#endif + thread = ldap_pvt_thread_self(); + if( tracethreads ) { + char buf[40]; + fprintf( stderr, "== thr_debug: %s thread %s ==\n", + how, thread_name( buf, sizeof(buf), thread ) ); + } + if( threadID ) { + with_thread_info_lock({ + ldap_debug_thread_t *t = get_thread_info( thread, msg ); + if( thread_info_detached( t ) ) + remove_thread_info( t, msg ); + }); + } + adjust_count( Idx_unexited_thread, -1 ); +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + thread_exiting( "Exiting", "ldap_pvt_thread_exit" ); + ldap_int_thread_exit( retval ); +} + +typedef struct { + void *(*start_routine)( void * ); + void *arg; +} ldap_debug_thread_call_t; + +static void * +ldap_debug_thread_wrapper( void *arg ) +{ + void *ret; + ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg; + free( arg ); + ret = call.start_routine( call.arg ); + thread_exiting( "Returning from", "ldap_debug_thread_wrapper" ); + return ret; +} + +int +ldap_pvt_thread_create( + ldap_pvt_thread_t *thread, + int detach, + void *(*start_routine)( void * ), + void *arg ) +{ + int rc; + if( !options_done ) + get_options(); + ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" ); + + if( wrap_threads ) { + ldap_debug_thread_call_t *call = malloc( + sizeof( ldap_debug_thread_call_t ) ); + assert( call != NULL ); + call->start_routine = start_routine; + call->arg = arg; + start_routine = ldap_debug_thread_wrapper; + arg = call; + } + if( threadID ) { + with_thread_info_lock({ + rc = ldap_int_thread_create( thread, detach, start_routine, arg ); + if( rc == 0 ) + add_thread_info( "ldap_pvt_thread_create", thread, detach ); + }); + } else { + rc = ldap_int_thread_create( thread, detach, start_routine, arg ); + } + if( rc ) { + ERROR( rc, "ldap_pvt_thread_create" ); + if( wrap_threads ) + free( arg ); + } else { + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, + "== thr_debug: Created thread %s%s from thread %s ==\n", + thread_name( buf, sizeof(buf), *thread ), + detach ? " (detached)" : "", + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + adjust_count( Idx_unexited_thread, +1 ); + if( !detach ) + adjust_count( Idx_unjoined_thread, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + int rc; + ldap_debug_thread_t *t = NULL; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" ); + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n", + thread_name( buf, sizeof(buf), thread ), + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + if( threadID ) + with_thread_info_lock( { + t = get_thread_info( thread, "ldap_pvt_thread_join" ); + ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" ); + } ); + rc = ldap_int_thread_join( thread, thread_return ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_join" ); + } else { + if( threadID ) + with_thread_info_lock( + remove_thread_info( t, "ldap_pvt_thread_join" ) ); + adjust_count( Idx_unjoined_thread, -1 ); + } + + return rc; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" ); + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, + "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n", + thread_name( buf, sizeof(buf), thread ), signo, + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + rc = ldap_int_thread_kill( thread, signo ); + ERROR_IF( rc, "ldap_pvt_thread_kill" ); + return rc; +} + +int +ldap_pvt_thread_yield( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" ); + rc = ldap_int_thread_yield(); + ERROR_IF( rc, "ldap_pvt_thread_yield" ); + return rc; +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" ); +#endif + return ldap_int_thread_self(); +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + init_usage( &cond->usage, "ldap_pvt_thread_cond_init" ); + rc = ldap_int_thread_cond_init( WRAPPED( cond ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_cond_init" ); + destroy_usage( &cond->usage ); + } else { + adjust_count( Idx_cond, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" ); + rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_cond_destroy" ); + } else { + destroy_usage( &cond->usage ); + adjust_count( Idx_cond, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" ); + rc = ldap_int_thread_cond_signal( WRAPPED( cond ) ); + ERROR_IF( rc, "ldap_pvt_thread_cond_signal" ); + return rc; +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" ); + rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) ); + ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" ); + return rc; +} + +int +ldap_pvt_thread_cond_wait( + ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + ldap_int_thread_t owner; + check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" ); + check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" ); + adjust_count( Idx_locked_mutex, -1 ); + owner = GET_OWNER( mutex ); + ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); + RESET_OWNER( mutex ); + rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) ); + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); + SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + ERROR_IF( rc, "ldap_pvt_thread_cond_wait" ); + return rc; +} + +int +ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + init_usage( &mutex->usage, "ldap_pvt_thread_mutex_recursive_init" ); + rc = ldap_int_thread_mutex_recursive_init( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_recursive_init" ); + destroy_usage( &mutex->usage ); + } else { + RESET_OWNER( mutex ); + adjust_count( Idx_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" ); + rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_init" ); + destroy_usage( &mutex->usage ); + } else { + RESET_OWNER( mutex ); + adjust_count( Idx_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" ); + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" ); + rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_destroy" ); + } else { + destroy_usage( &mutex->usage ); + RESET_OWNER( mutex ); + adjust_count( Idx_mutex, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" ); + rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) ); + if( rc ) { + ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" ); + } else { + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" ); + SET_OWNER( mutex, ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" ); + rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) ); + if( rc == 0 ) { + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" ); + SET_OWNER( mutex, ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" ); + ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" ); + RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */ + rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) ); + if( rc ) { + ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" ); + } else { + adjust_count( Idx_locked_mutex, -1 ); + } + return rc; +} + + +/* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */ + +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" ); + rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_rdwr_init" ); + destroy_usage( &rwlock->usage ); + } else { + adjust_count( Idx_rdwr, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" ); + rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_rdwr_destroy" ); + } else { + destroy_usage( &rwlock->usage ); + adjust_count( Idx_rdwr, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" ); + rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" ); + return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" ); + rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" ); + rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" ); + return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" ); + rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" ); + return rc; +} + +#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR) + +int +ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" ); + return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" ); + return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" ); + return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) ); +} + +#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */ + + +/* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */ +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION + +int +ldap_pvt_thread_pool_init( + ldap_pvt_thread_pool_t *tpool, + int max_threads, + int max_pending ) +{ + int rc; + if( !options_done ) + get_options(); + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" ); + rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_pool_init" ); + } else { + adjust_count( Idx_tpool, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_pool_submit( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_submit( tpool, start_routine, arg ); + if( has_pool ) + ERROR_IF( rc, "ldap_pvt_thread_pool_submit" ); + return rc; +} + +int +ldap_pvt_thread_pool_maxthreads( + ldap_pvt_thread_pool_t *tpool, + int max_threads ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" ); + return ldap_int_thread_pool_maxthreads( tpool, max_threads ); +} + +int +ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" ); + return ldap_int_thread_pool_backload( tpool ); +} + +int +ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_destroy( tpool, run_pending ); + if( has_pool ) { + if( rc ) { + ERROR( rc, "ldap_pvt_thread_pool_destroy" ); + } else { + adjust_count( Idx_tpool, -1 ); + } + } + return rc; +} + +int +ldap_pvt_thread_pool_close( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_close" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_close( tpool, run_pending ); + if( has_pool && rc ) { + ERROR( rc, "ldap_pvt_thread_pool_close" ); + } + return rc; +} + +int +ldap_pvt_thread_pool_free( ldap_pvt_thread_pool_t *tpool ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_free" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_free( tpool ); + if( has_pool ) { + if( rc ) { + ERROR( rc, "ldap_pvt_thread_pool_free" ); + } else { + adjust_count( Idx_tpool, -1 ); + } + } + return rc; +} + +int +ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" ); + return ldap_int_thread_pool_pause( tpool ); +} + +int +ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" ); + return ldap_int_thread_pool_resume( tpool ); +} + +int +ldap_pvt_thread_pool_getkey( + void *xctx, + void *key, + void **data, + ldap_pvt_thread_pool_keyfree_t **kfree ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" ); +#endif + return ldap_int_thread_pool_getkey( xctx, key, data, kfree ); +} + +int +ldap_pvt_thread_pool_setkey( + void *xctx, + void *key, + void *data, + ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, + ldap_pvt_thread_pool_keyfree_t **oldkfreep ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" ); + rc = ldap_int_thread_pool_setkey( + xctx, key, data, kfree, olddatap, oldkfreep ); + ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" ); + return rc; +} + +void +ldap_pvt_thread_pool_purgekey( void *key ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" ); + ldap_int_thread_pool_purgekey( key ); +} + +void * +ldap_pvt_thread_pool_context( void ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" ); +#endif + return ldap_int_thread_pool_context(); +} + +void +ldap_pvt_thread_pool_context_reset( void *vctx ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" ); + ldap_int_thread_pool_context_reset( vctx ); +} + +#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ + +#endif /* LDAP_THREAD_DEBUG */ diff --git a/contrib/libs/openldap/libraries/libldap/thr_nt.c b/contrib/libs/openldap/libraries/libldap/thr_nt.c new file mode 100644 index 0000000000..0bb1deaafa --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/thr_nt.c @@ -0,0 +1,252 @@ +/* thr_nt.c - wrapper around NT threads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_NT_THREADS ) + +#define _WIN32_WINNT 0x0400 +#include <windows.h> +#include <process.h> + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +typedef struct ldap_int_thread_s { + long tid; + HANDLE thd; +} ldap_int_thread_s; + +#ifndef NT_MAX_THREADS +#define NT_MAX_THREADS 1024 +#endif + +static ldap_int_thread_s tids[NT_MAX_THREADS]; +static int ntids; + + +/* mingw compiler very sensitive about getting prototypes right */ +typedef unsigned __stdcall thrfunc_t(void *); + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +int +ldap_int_mutex_firstcreate( ldap_int_thread_mutex_t *mutex ) +{ + if ( *mutex == NULL ) { + HANDLE p = CreateMutex( NULL, 0, NULL ); + if ( InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL) + CloseHandle( p ); + } + return 0; +} + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + unsigned tid; + HANDLE thd; + int rc = -1; + + thd = (HANDLE) _beginthreadex(NULL, LDAP_PVT_THREAD_STACK_SIZE, (thrfunc_t *) start_routine, + arg, 0, &tid); + + if ( thd ) { + *thread = (ldap_pvt_thread_t) tid; + tids[ntids].tid = tid; + tids[ntids].thd = thd; + ntids++; + rc = 0; + } + return rc; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + _endthread( ); +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + DWORD status; + int i; + + for (i=0; i<ntids; i++) { + if ( tids[i].tid == thread ) + break; + } + if ( i > ntids ) return -1; + + status = WaitForSingleObject( tids[i].thd, INFINITE ); + for (; i<ntids; i++) { + tids[i] = tids[i+1]; + } + ntids--; + return status == WAIT_FAILED ? -1 : 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + Sleep( 0 ); + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + *cond = CreateEvent( NULL, FALSE, FALSE, NULL ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + CloseHandle( *cv ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + SetEvent( *cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + SignalObjectAndWait( *mutex, *cond, INFINITE, FALSE ); + WaitForSingleObject( *mutex, INFINITE ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + while ( WaitForSingleObject( *cond, 0 ) == WAIT_TIMEOUT ) + SetEvent( *cond ); + return( 0 ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + *mutex = CreateMutex( NULL, 0, NULL ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex ) +{ + /* All NT mutexes are recursive */ + return ldap_pvt_thread_mutex_init( mutex ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + CloseHandle( *mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + DWORD status; + status = WaitForSingleObject( *mutex, INFINITE ); + return status == WAIT_FAILED ? -1 : 0; +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + ReleaseMutex( *mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp ) +{ + DWORD status; + status = WaitForSingleObject( *mp, 0 ); + return status == WAIT_FAILED || status == WAIT_TIMEOUT + ? -1 : 0; +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return GetCurrentThreadId(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *keyp ) +{ + DWORD key = TlsAlloc(); + if ( key != TLS_OUT_OF_INDEXES ) { + *keyp = key; + return 0; + } else { + return -1; + } +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + /* TlsFree returns 0 on failure */ + return( TlsFree( key ) == 0 ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return ( TlsSetValue( key, data ) == 0 ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + void *ptr = TlsGetValue( key ); + *data = ptr; + return( ptr ? GetLastError() : 0 ); +} + +#endif diff --git a/contrib/libs/openldap/libraries/libldap/thr_posix.c b/contrib/libs/openldap/libraries/libldap/thr_posix.c new file mode 100644 index 0000000000..004dc0bb18 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/thr_posix.c @@ -0,0 +1,411 @@ +/* thr_posix.c - wrapper around posix and posixish thread implementations. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + + +#include "portable.h" + +#if defined( HAVE_PTHREADS ) + +#ifdef __GLIBC__ +#undef _FEATURES_H +#define _XOPEN_SOURCE 500 /* For pthread_setconcurrency() on glibc */ +#endif + +#include <ac/errno.h> + +#ifdef REPLACE_BROKEN_YIELD +#ifndef HAVE_NANOSLEEP +#include <ac/socket.h> +#endif +#include <ac/time.h> +#endif + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ +#include <signal.h> /* For pthread_kill() */ + +extern int ldap_int_stackguard; + +#if HAVE_PTHREADS < 6 +# define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default +# define LDAP_INT_THREAD_CONDATTR_DEFAULT pthread_condattr_default +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT pthread_mutexattr_default +#else +# define LDAP_INT_THREAD_ATTR_DEFAULT NULL +# define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL +#endif + +#ifdef LDAP_THREAD_DEBUG +# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */ +# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP + /* LinuxThreads hack */ +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP +# else +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK +# endif +static pthread_mutexattr_t mutex_attr; +# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr +#endif + +static pthread_mutexattr_t mutex_attr_recursive; + +#if HAVE_PTHREADS < 7 +#define ERRVAL(val) ((val) < 0 ? errno : 0) +#else +#define ERRVAL(val) (val) +#endif + +int +ldap_int_thread_initialize( void ) +{ +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_init( &mutex_attr ); + pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR ); +#endif + if (pthread_mutexattr_init(&mutex_attr_recursive)) + return -1; + if (pthread_mutexattr_settype(&mutex_attr_recursive, PTHREAD_MUTEX_RECURSIVE)) + return -1; + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ +#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP + /* LinuxThreads: kill clones */ + pthread_kill_other_threads_np(); +#endif +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_destroy( &mutex_attr ); +#endif + pthread_mutexattr_destroy( &mutex_attr_recursive ); + return 0; +} + +#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency(int n) +{ +#ifdef HAVE_PTHREAD_SETCONCURRENCY + return pthread_setconcurrency( n ); +#elif defined(HAVE_THR_SETCONCURRENCY) + return thr_setconcurrency( n ); +#else + return 0; +#endif +} +#endif + +#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency(void) +{ +#ifdef HAVE_PTHREAD_GETCONCURRENCY + return pthread_getconcurrency(); +#elif defined(HAVE_THR_GETCONCURRENCY) + return thr_getconcurrency(); +#else + return 0; +#endif +} +#endif + +/* detachstate appeared in Draft 6, but without manifest constants. + * in Draft 7 they were called PTHREAD_CREATE_UNDETACHED and ...DETACHED. + * in Draft 8 on, ...UNDETACHED became ...JOINABLE. + */ +#ifndef PTHREAD_CREATE_JOINABLE +#ifdef PTHREAD_CREATE_UNDETACHED +#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED +#else +#define PTHREAD_CREATE_JOINABLE 0 +#endif +#endif + +#ifndef PTHREAD_CREATE_DETACHED +#define PTHREAD_CREATE_DETACHED 1 +#endif + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void * ), + void *arg) +{ + int rtn; + pthread_attr_t attr; + +/* Always create the thread attrs, so we can set stacksize if we need to */ +#if HAVE_PTHREADS > 5 + pthread_attr_init(&attr); +#else + pthread_attr_create(&attr); +#endif + +#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE + /* this should be tunable */ + pthread_attr_setstacksize( &attr, LDAP_PVT_THREAD_STACK_SIZE ); + if ( ldap_int_stackguard ) + pthread_attr_setguardsize( &attr, LDAP_PVT_THREAD_STACK_SIZE ); +#endif + +#if HAVE_PTHREADS > 5 + detach = detach ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE; +#if HAVE_PTHREADS == 6 + pthread_attr_setdetachstate(&attr, &detach); +#else + pthread_attr_setdetachstate(&attr, detach); +#endif +#endif + +#if HAVE_PTHREADS < 5 + rtn = pthread_create( thread, attr, start_routine, arg ); +#else + rtn = pthread_create( thread, &attr, start_routine, arg ); +#endif + +#if HAVE_PTHREADS > 5 + pthread_attr_destroy(&attr); +#else + pthread_attr_delete(&attr); + if( detach ) { + pthread_detach( thread ); + } +#endif + +#if HAVE_PTHREADS < 7 + if ( rtn < 0 ) rtn = errno; +#endif + return rtn; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + pthread_exit( retval ); +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ +#if HAVE_PTHREADS < 7 + void *dummy; + if (thread_return==NULL) + thread_return=&dummy; +#endif + return ERRVAL( pthread_join( thread, thread_return ) ); +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ +#if defined(HAVE_PTHREAD_KILL) && HAVE_PTHREADS > 4 + /* MacOS 10.1 is detected as v10 but has no pthread_kill() */ + return ERRVAL( pthread_kill( thread, signo ) ); +#else + /* pthread package with DCE */ + if (kill( getpid(), signo )<0) + return errno; + return 0; +#endif +} + +int +ldap_pvt_thread_yield( void ) +{ +#ifdef REPLACE_BROKEN_YIELD +#ifdef HAVE_NANOSLEEP + struct timespec t = { 0, 0 }; + nanosleep(&t, NULL); +#else + struct timeval tv = {0,0}; + select( 0, NULL, NULL, NULL, &tv ); +#endif + return 0; + +#elif defined(HAVE_THR_YIELD) + thr_yield(); + return 0; + +#elif HAVE_PTHREADS == 10 + return sched_yield(); + +#elif defined(_POSIX_THREAD_IS_GNU_PTH) + sched_yield(); + return 0; + +#elif HAVE_PTHREADS == 6 + pthread_yield(NULL); + return 0; + +#else + pthread_yield(); + return 0; +#endif +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_init( + cond, LDAP_INT_THREAD_CONDATTR_DEFAULT ) ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_destroy( cond ) ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_signal( cond ) ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_broadcast( cond ) ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_cond_wait( cond, mutex ) ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_init( + mutex, LDAP_INT_THREAD_MUTEXATTR_DEFAULT ) ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_destroy( mutex ) ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_lock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_trylock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_unlock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_init( mutex, &mutex_attr_recursive ) ); +} + +ldap_pvt_thread_t ldap_pvt_thread_self( void ) +{ + return pthread_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return pthread_key_create( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return pthread_key_delete( key ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return pthread_setspecific( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + *data = pthread_getspecific( key ); + return 0; +} + +#ifdef LDAP_THREAD_HAVE_RDWR +#ifdef HAVE_PTHREAD_RWLOCK_DESTROY +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_init( rw, NULL ) ); +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_destroy( rw ) ); +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_rdlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_tryrdlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_unlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_wrlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_trywrlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_unlock( rw ) ); +} + +#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */ +#endif /* LDAP_THREAD_HAVE_RDWR */ +#endif /* HAVE_PTHREADS */ + diff --git a/contrib/libs/openldap/libraries/libldap/thr_pth.c b/contrib/libs/openldap/libraries/libldap/thr_pth.c new file mode 100644 index 0000000000..894082d854 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/thr_pth.c @@ -0,0 +1,238 @@ +/* thr_pth.c - wrappers around GNU Pth */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_GNU_PTH ) + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +#include <errno.h> + +/******************* + * * + * GNU Pth Threads * + * * + *******************/ + +static pth_attr_t detach_attr; +static pth_attr_t joined_attr; + +int +ldap_int_thread_initialize( void ) +{ + if( !pth_init() ) { + return -1; + } + detach_attr = pth_attr_new(); + joined_attr = pth_attr_new(); +#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE + pth_attr_set( joined_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE ); + pth_attr_set( detach_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE ); +#endif + return pth_attr_set( detach_attr, PTH_ATTR_JOINABLE, FALSE ); +} + +int +ldap_int_thread_destroy( void ) +{ + pth_attr_destroy(detach_attr); + pth_kill(); + return 0; +} + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + *thread = pth_spawn( detach ? detach_attr : joined_attr, + start_routine, arg ); + + return *thread == NULL ? errno : 0; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + pth_exit( retval ); +} + +int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + return pth_join( thread, thread_return ) ? 0 : errno; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return pth_raise( thread, signo ) ? 0 : errno; +} + +int +ldap_pvt_thread_yield( void ) +{ + return pth_yield(NULL) ? 0 : errno; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_init( cond ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_notify( cond, 0 ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_notify( cond, 1 ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_cond_await( cond, mutex, NULL ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_init( mutex ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex ) +{ + /* All pth mutexes are recursive */ + return ldap_pvt_thread_mutex_init( mutex ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_acquire( mutex, 0, NULL ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_release( mutex ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_acquire( mutex, 1, NULL ) ? 0 : errno ); +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return pth_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return pth_key_create( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return pth_key_delete( key ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return pth_key_setdata( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + *data = pth_key_getdata( key ); + return 0; +} + +#ifdef LDAP_THREAD_HAVE_RDWR +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_init( rw ) ? 0 : errno; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw ) +{ + return 0; +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 0, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 1, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_release( rw ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 0, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 1, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_release( rw ) ? 0 : errno; +} + +#endif /* LDAP_THREAD_HAVE_RDWR */ +#endif /* HAVE_GNU_PTH */ diff --git a/contrib/libs/openldap/libraries/libldap/thr_thr.c b/contrib/libs/openldap/libraries/libldap/thr_thr.c new file mode 100644 index 0000000000..86656da6f0 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/thr_thr.c @@ -0,0 +1,192 @@ +/* thr_thr.c - wrappers around solaris threads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_THR ) + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +/******************* + * * + * Solaris Threads * + * * + *******************/ + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency(int n) +{ + return thr_setconcurrency( n ); +} +#endif + +#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency(void) +{ + return thr_getconcurrency(); +} +#endif + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + return( thr_create( NULL, LDAP_PVT_THREAD_STACK_SIZE, start_routine, + arg, detach ? THR_DETACHED : 0, thread ) ); +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + thr_exit( NULL ); +} + +int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + thr_join( thread, NULL, thread_return ); + return 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + thr_kill( thread, signo ); + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + thr_yield(); + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return( cond_init( cond, USYNC_THREAD, NULL ) ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return( cond_signal( cond ) ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cv ) +{ + return( cond_broadcast( cv ) ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return( cond_wait( cond, mutex ) ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + return( cond_destroy( cv ) ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_init( mutex, USYNC_THREAD, NULL ) ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_destroy( mutex ) ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_lock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_unlock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp ) +{ + return( mutex_trylock( mp ) ); +} + +int +ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_init( mutex, USYNC_THREAD | LOCK_RECURSIVE, NULL ) ); +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return thr_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return thr_keycreate( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return( 0 ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return thr_setspecific( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + return thr_getspecific( key, data ); +} + +#endif /* HAVE_THR */ diff --git a/contrib/libs/openldap/libraries/libldap/threads.c b/contrib/libs/openldap/libraries/libldap/threads.c new file mode 100644 index 0000000000..eb5b96ea98 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/threads.c @@ -0,0 +1,111 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include "ldap-int.h" + +#ifdef LDAP_R_COMPILE + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */ + + +/* + * Common LDAP thread routines + * see thr_*.c for implementation specific routines + * see rdwr.c for generic reader/writer lock implementation + * see tpool.c for generic thread pool implementation + */ + + +int ldap_pvt_thread_initialize( void ) +{ + int rc; + static int init = 0; + ldap_pvt_thread_t tid; + + /* we only get one shot at this */ + if( init++ ) return -1; + + rc = ldap_int_thread_initialize(); + if( rc ) return rc; + +#ifndef LDAP_THREAD_HAVE_TPOOL + rc = ldap_int_thread_pool_startup(); + if( rc ) return rc; +#endif + + /* kludge to pull symbol definitions in */ + tid = ldap_pvt_thread_self(); + return 0; +} + +int ldap_pvt_thread_destroy( void ) +{ +#ifndef LDAP_THREAD_HAVE_TPOOL + (void) ldap_int_thread_pool_shutdown(); +#endif + return ldap_int_thread_destroy(); +} + + +/* + * Default implementations of some LDAP thread routines + */ + +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + + +#ifndef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency ( void ) +{ + return 1; +} +#endif + +#ifndef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency ( int concurrency ) +{ + return 1; +} +#endif + +#ifndef LDAP_THREAD_HAVE_SLEEP +/* + * Here we assume we have fully preemptive threads and that sleep() + * does the right thing. + */ +unsigned int +ldap_pvt_thread_sleep( + unsigned int interval +) +{ + sleep( interval ); + return 0; +} +#endif + +#endif /* LDAP_R_COMPILE */ diff --git a/contrib/libs/openldap/libraries/libldap/tls2.c b/contrib/libs/openldap/libraries/libldap/tls2.c new file mode 100644 index 0000000000..dff845bc10 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/tls2.c @@ -0,0 +1,1690 @@ +/* tls.c - Handle tls/ssl. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: restructured by Howard Chu. + */ + +#include "portable.h" +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> + +#include "ldap-int.h" + +#ifdef HAVE_TLS + +#include "ldap-tls.h" + +static tls_impl *tls_imp = &ldap_int_tls_impl; +#define HAS_TLS( sb ) ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \ + (void *)tls_imp->ti_sbio ) + +#endif /* HAVE_TLS */ + +/* RFC2459 minimum required set of supported attribute types + * in a certificate DN + */ +typedef struct oid_name { + struct berval oid; + struct berval name; +} oid_name; + +static oid_name oids[] = { + { BER_BVC("2.5.4.3"), BER_BVC("cn") }, + { BER_BVC("2.5.4.4"), BER_BVC("sn") }, + { BER_BVC("2.5.4.6"), BER_BVC("c") }, + { BER_BVC("2.5.4.7"), BER_BVC("l") }, + { BER_BVC("2.5.4.8"), BER_BVC("st") }, + { BER_BVC("2.5.4.10"), BER_BVC("o") }, + { BER_BVC("2.5.4.11"), BER_BVC("ou") }, + { BER_BVC("2.5.4.12"), BER_BVC("title") }, + { BER_BVC("2.5.4.41"), BER_BVC("name") }, + { BER_BVC("2.5.4.42"), BER_BVC("givenName") }, + { BER_BVC("2.5.4.43"), BER_BVC("initials") }, + { BER_BVC("2.5.4.44"), BER_BVC("generationQualifier") }, + { BER_BVC("2.5.4.46"), BER_BVC("dnQualifier") }, + { BER_BVC("1.2.840.113549.1.9.1"), BER_BVC("email") }, + { BER_BVC("0.9.2342.19200300.100.1.25"), BER_BVC("dc") }, + { BER_BVNULL, BER_BVNULL } +}; + +#ifdef HAVE_TLS + +LDAP_F(int) ldap_pvt_tls_check_hostname LDAP_P(( LDAP *ld, void *s, const char *name_in )); +LDAP_F(int) ldap_pvt_tls_get_peercert LDAP_P(( void *s, struct berval *der )); + +void +ldap_pvt_tls_ctx_free ( void *c ) +{ + if ( !c ) return; + tls_imp->ti_ctx_free( c ); +} + +static void +tls_ctx_ref( tls_ctx *ctx ) +{ + if ( !ctx ) return; + + tls_imp->ti_ctx_ref( ctx ); +} + +#ifdef LDAP_R_COMPILE +/* + * an extra mutex for the default ctx. + */ +static ldap_pvt_thread_mutex_t tls_def_ctx_mutex; +#endif + +void +ldap_int_tls_destroy( struct ldapoptions *lo ) +{ + if ( lo->ldo_tls_ctx ) { + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + } + + if ( lo->ldo_tls_certfile ) { + LDAP_FREE( lo->ldo_tls_certfile ); + lo->ldo_tls_certfile = NULL; + } + if ( lo->ldo_tls_keyfile ) { + LDAP_FREE( lo->ldo_tls_keyfile ); + lo->ldo_tls_keyfile = NULL; + } + if ( lo->ldo_tls_dhfile ) { + LDAP_FREE( lo->ldo_tls_dhfile ); + lo->ldo_tls_dhfile = NULL; + } + if ( lo->ldo_tls_ecname ) { + LDAP_FREE( lo->ldo_tls_ecname ); + lo->ldo_tls_ecname = NULL; + } + if ( lo->ldo_tls_cacertfile ) { + LDAP_FREE( lo->ldo_tls_cacertfile ); + lo->ldo_tls_cacertfile = NULL; + } + if ( lo->ldo_tls_cacertdir ) { + LDAP_FREE( lo->ldo_tls_cacertdir ); + lo->ldo_tls_cacertdir = NULL; + } + if ( lo->ldo_tls_ciphersuite ) { + LDAP_FREE( lo->ldo_tls_ciphersuite ); + lo->ldo_tls_ciphersuite = NULL; + } + if ( lo->ldo_tls_crlfile ) { + LDAP_FREE( lo->ldo_tls_crlfile ); + lo->ldo_tls_crlfile = NULL; + } + /* tls_pin_hashalg and tls_pin share the same buffer */ + if ( lo->ldo_tls_pin_hashalg ) { + LDAP_FREE( lo->ldo_tls_pin_hashalg ); + lo->ldo_tls_pin_hashalg = NULL; + } else { + LDAP_FREE( lo->ldo_tls_pin.bv_val ); + } + BER_BVZERO( &lo->ldo_tls_pin ); +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +void +ldap_pvt_tls_destroy( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + + ldap_int_tls_destroy( lo ); + + tls_imp->ti_tls_destroy(); +} + +/* + * Initialize a particular TLS implementation. + * Called once per implementation. + */ +static int +tls_init(tls_impl *impl, int do_threads ) +{ + static int tls_initialized = 0; + + if ( !tls_initialized++ ) { +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex ); +#endif + } + + if ( impl->ti_inited++ ) return 0; + + if ( do_threads ) { +#ifdef LDAP_R_COMPILE + impl->ti_thr_init(); +#endif + } + + return impl->ti_tls_init(); +} + +/* + * Initialize TLS subsystem. Called once per implementation. + */ +int +ldap_pvt_tls_init( int do_threads ) +{ + return tls_init( tls_imp, do_threads ); +} + +/* + * initialize a new TLS context + */ +static int +ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server, char *errmsg ) +{ + int rc = 0; + tls_impl *ti = tls_imp; + struct ldaptls lts = lo->ldo_tls_info; + + if ( lo->ldo_tls_ctx ) + return 0; + + tls_init( ti, 0 ); + + if ( is_server && !lts.lt_certfile && !lts.lt_keyfile && + !lts.lt_cacertfile && !lts.lt_cacertdir && + !lts.lt_cacert.bv_val && !lts.lt_cert.bv_val && + !lts.lt_key.bv_val ) { + /* minimum configuration not provided */ + return LDAP_NOT_SUPPORTED; + } + +#ifdef HAVE_EBCDIC + /* This ASCII/EBCDIC handling is a real pain! */ + if ( lts.lt_ciphersuite ) { + lts.lt_ciphersuite = LDAP_STRDUP( lts.lt_ciphersuite ); + __atoe( lts.lt_ciphersuite ); + } + if ( lts.lt_cacertfile ) { + lts.lt_cacertfile = LDAP_STRDUP( lts.lt_cacertfile ); + __atoe( lts.lt_cacertfile ); + } + if ( lts.lt_certfile ) { + lts.lt_certfile = LDAP_STRDUP( lts.lt_certfile ); + __atoe( lts.lt_certfile ); + } + if ( lts.lt_keyfile ) { + lts.lt_keyfile = LDAP_STRDUP( lts.lt_keyfile ); + __atoe( lts.lt_keyfile ); + } + if ( lts.lt_crlfile ) { + lts.lt_crlfile = LDAP_STRDUP( lts.lt_crlfile ); + __atoe( lts.lt_crlfile ); + } + if ( lts.lt_cacertdir ) { + lts.lt_cacertdir = LDAP_STRDUP( lts.lt_cacertdir ); + __atoe( lts.lt_cacertdir ); + } + if ( lts.lt_dhfile ) { + lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile ); + __atoe( lts.lt_dhfile ); + } + if ( lts.lt_ecname ) { + lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname ); + __atoe( lts.lt_ecname ); + } +#endif + lo->ldo_tls_ctx = ti->ti_ctx_new( lo ); + if ( lo->ldo_tls_ctx == NULL ) { + Debug0( LDAP_DEBUG_ANY, + "TLS: could not allocate default ctx.\n" ); + rc = -1; + goto error_exit; + } + + rc = ti->ti_ctx_init( lo, <s, is_server, errmsg ); + +error_exit: + if ( rc < 0 && lo->ldo_tls_ctx != NULL ) { + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + } +#ifdef HAVE_EBCDIC + LDAP_FREE( lts.lt_ciphersuite ); + LDAP_FREE( lts.lt_cacertfile ); + LDAP_FREE( lts.lt_certfile ); + LDAP_FREE( lts.lt_keyfile ); + LDAP_FREE( lts.lt_crlfile ); + LDAP_FREE( lts.lt_cacertdir ); + LDAP_FREE( lts.lt_dhfile ); + LDAP_FREE( lts.lt_ecname ); +#endif + return rc; +} + +/* + * initialize the default context + */ +int +ldap_pvt_tls_init_def_ctx( int is_server ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + char errmsg[ERRBUFSIZE]; + int rc; + errmsg[0] = 0; + LDAP_MUTEX_LOCK( &tls_def_ctx_mutex ); + rc = ldap_int_tls_init_ctx( lo, is_server, errmsg ); + LDAP_MUTEX_UNLOCK( &tls_def_ctx_mutex ); + if ( rc ) { + Debug1( LDAP_DEBUG_ANY,"TLS: init_def_ctx: %s.\n", errmsg ); + } + return rc; +} + +static tls_session * +alloc_handle( void *ctx_arg, int is_server ) +{ + tls_ctx *ctx; + tls_session *ssl; + + if ( ctx_arg ) { + ctx = ctx_arg; + } else { + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + if ( ldap_pvt_tls_init_def_ctx( is_server ) < 0 ) return NULL; + ctx = lo->ldo_tls_ctx; + } + + ssl = tls_imp->ti_session_new( ctx, is_server ); + if ( ssl == NULL ) { + Debug0( LDAP_DEBUG_ANY,"TLS: can't create ssl handle.\n" ); + return NULL; + } + return ssl; +} + +static int +update_flags( Sockbuf *sb, tls_session * ssl, int rc ) +{ + sb->sb_trans_needs_read = 0; + sb->sb_trans_needs_write = 0; + + return tls_imp->ti_session_upflags( sb, ssl, rc ); +} + +/* + * Call this to do a TLS connect on a sockbuf. ctx_arg can be + * a SSL_CTX * or NULL, in which case the default ctx is used. + * + * Return value: + * + * 0 - Success. Connection is ready for communication. + * <0 - Error. Can't create a TLS stream. + * >0 - Partial success. + * Do a select (using information from lber_pvt_sb_needs_{read,write} + * and call again. + */ + +static int +ldap_int_tls_connect( LDAP *ld, LDAPConn *conn, const char *host ) +{ + Sockbuf *sb = conn->lconn_sb; + int err; + tls_session *ssl = NULL; + const char *sni = host; + + if ( HAS_TLS( sb )) { + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl ); + } else { + struct ldapoptions *lo; + tls_ctx *ctx; + + ctx = ld->ld_options.ldo_tls_ctx; + + ssl = alloc_handle( ctx, 0 ); + + if ( ssl == NULL ) return -1; + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" ); +#endif + ber_sockbuf_add_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl ); + + lo = LDAP_INT_GLOBAL_OPT(); + if( ctx == NULL ) { + ctx = lo->ldo_tls_ctx; + ld->ld_options.ldo_tls_ctx = ctx; + tls_ctx_ref( ctx ); + } + if ( ld->ld_options.ldo_tls_connect_cb ) + ld->ld_options.ldo_tls_connect_cb( ld, ssl, ctx, + ld->ld_options.ldo_tls_connect_arg ); + if ( lo && lo->ldo_tls_connect_cb && lo->ldo_tls_connect_cb != + ld->ld_options.ldo_tls_connect_cb ) + lo->ldo_tls_connect_cb( ld, ssl, ctx, lo->ldo_tls_connect_arg ); + } + + /* pass hostname for SNI, but only if it's an actual name + * and not a numeric address + */ + { + int numeric = 1; + unsigned char *c; + for ( c = (unsigned char *)sni; *c; c++ ) { + if ( *c == ':' ) /* IPv6 address */ + break; + if ( *c == '.' ) + continue; + if ( !isdigit( *c )) { + numeric = 0; + break; + } + } + if ( numeric ) + sni = NULL; + } + err = tls_imp->ti_session_connect( ld, ssl, sni ); + +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + + if ( err == 0 ) { + err = ldap_pvt_tls_check_hostname( ld, ssl, host ); + } + + if ( err < 0 ) + { + char buf[256], *msg; + if ( update_flags( sb, ssl, err )) { + return 1; + } + + msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) ); + if ( msg ) { + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( msg ); +#ifdef HAVE_EBCDIC + if ( ld->ld_error ) __etoa(ld->ld_error); +#endif + } + + Debug1( LDAP_DEBUG_ANY,"TLS: can't connect: %s.\n", + ld->ld_error ? ld->ld_error : "" ); + + ber_sockbuf_remove_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT ); +#endif + return -1; + } + + return 0; +} + +int +ldap_pvt_tls_connect( LDAP *ld, Sockbuf *sb, const char *host ) +{ + LDAPConn conn = { .lconn_sb = sb }; + return ldap_int_tls_connect( ld, &conn, host ); +} + +/* + * Call this to do a TLS accept on a sockbuf. + * Everything else is the same as with tls_connect. + */ +int +ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg ) +{ + int err; + tls_session *ssl = NULL; + + if ( HAS_TLS( sb )) { + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl ); + } else { + ssl = alloc_handle( ctx_arg, 1 ); + if ( ssl == NULL ) return -1; + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" ); +#endif + ber_sockbuf_add_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl ); + } + + err = tls_imp->ti_session_accept( ssl ); + +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + + if ( err < 0 ) + { + if ( update_flags( sb, ssl, err )) return 1; + + if ( DebugTest( LDAP_DEBUG_ANY ) ) { + char buf[256], *msg; + msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) ); + Debug1( LDAP_DEBUG_ANY,"TLS: can't accept: %s.\n", + msg ? msg : "(unknown)" ); + } + + ber_sockbuf_remove_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT ); +#endif + return -1; + } + return 0; +} + +int +ldap_pvt_tls_inplace ( Sockbuf *sb ) +{ + return HAS_TLS( sb ) ? 1 : 0; +} + +int +ldap_tls_inplace( LDAP *ld ) +{ + Sockbuf *sb = NULL; + + if ( ld->ld_defconn && ld->ld_defconn->lconn_sb ) { + sb = ld->ld_defconn->lconn_sb; + + } else if ( ld->ld_sb ) { + sb = ld->ld_sb; + + } else { + return 0; + } + + return ldap_pvt_tls_inplace( sb ); +} + +int +ldap_pvt_tls_get_peer_dn( void *s, struct berval *dn, + LDAPDN_rewrite_dummy *func, unsigned flags ) +{ + tls_session *session = s; + struct berval bvdn; + int rc; + + rc = tls_imp->ti_session_peer_dn( session, &bvdn ); + if ( rc ) return rc; + + rc = ldap_X509dn2bv( &bvdn, dn, + (LDAPDN_rewrite_func *)func, flags); + return rc; +} + +int +ldap_pvt_tls_check_hostname( LDAP *ld, void *s, const char *name_in ) +{ + tls_session *session = s; + + if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER && + ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) { + ld->ld_errno = tls_imp->ti_session_chkhost( ld, session, name_in ); + if (ld->ld_errno != LDAP_SUCCESS) { + return ld->ld_errno; + } + } + + /* + * If instructed to do pinning, do it now + */ + if ( !BER_BVISNULL( &ld->ld_options.ldo_tls_pin ) ) { + ld->ld_errno = tls_imp->ti_session_pinning( ld, s, + ld->ld_options.ldo_tls_pin_hashalg, + &ld->ld_options.ldo_tls_pin ); + if (ld->ld_errno != LDAP_SUCCESS) { + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; +} + +int +ldap_pvt_tls_config( LDAP *ld, int option, const char *arg ) +{ + int i; + + switch( option ) { + case LDAP_OPT_X_TLS_CACERTFILE: + case LDAP_OPT_X_TLS_CACERTDIR: + case LDAP_OPT_X_TLS_CERTFILE: + case LDAP_OPT_X_TLS_KEYFILE: + case LDAP_OPT_X_TLS_RANDOM_FILE: + case LDAP_OPT_X_TLS_CIPHER_SUITE: + case LDAP_OPT_X_TLS_DHFILE: + case LDAP_OPT_X_TLS_PEERKEY_HASH: + case LDAP_OPT_X_TLS_ECNAME: + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + return ldap_pvt_tls_set_option( ld, option, (void *) arg ); + + case LDAP_OPT_X_TLS_REQUIRE_CERT: + case LDAP_OPT_X_TLS_REQUIRE_SAN: + case LDAP_OPT_X_TLS: + i = -1; + if ( strcasecmp( arg, "never" ) == 0 ) { + i = LDAP_OPT_X_TLS_NEVER ; + + } else if ( strcasecmp( arg, "demand" ) == 0 ) { + i = LDAP_OPT_X_TLS_DEMAND ; + + } else if ( strcasecmp( arg, "allow" ) == 0 ) { + i = LDAP_OPT_X_TLS_ALLOW ; + + } else if ( strcasecmp( arg, "try" ) == 0 ) { + i = LDAP_OPT_X_TLS_TRY ; + + } else if ( ( strcasecmp( arg, "hard" ) == 0 ) || + ( strcasecmp( arg, "on" ) == 0 ) || + ( strcasecmp( arg, "yes" ) == 0) || + ( strcasecmp( arg, "true" ) == 0 ) ) + { + i = LDAP_OPT_X_TLS_HARD ; + } + + if (i >= 0) { + return ldap_pvt_tls_set_option( ld, option, &i ); + } + return -1; + case LDAP_OPT_X_TLS_PROTOCOL_MAX: + case LDAP_OPT_X_TLS_PROTOCOL_MIN: { + char *next; + long l; + l = strtol( arg, &next, 10 ); + if ( l < 0 || l > 0xff || next == arg || + ( *next != '\0' && *next != '.' ) ) + return -1; + i = l << 8; + if (*next == '.') { + arg = next + 1; + l = strtol( arg, &next, 10 ); + if ( l < 0 || l > 0xff || next == arg || *next != '\0' ) + return -1; + i += l; + } + return ldap_pvt_tls_set_option( ld, option, &i ); + } +#ifdef HAVE_OPENSSL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + i = -1; + if ( strcasecmp( arg, "none" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_NONE ; + } else if ( strcasecmp( arg, "peer" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_PEER ; + } else if ( strcasecmp( arg, "all" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_ALL ; + } + if (i >= 0) { + return ldap_pvt_tls_set_option( ld, option, &i ); + } + return -1; +#endif + } + return -1; +} + +int +ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg ) +{ + struct ldapoptions *lo; + + if( option == LDAP_OPT_X_TLS_PACKAGE ) { + *(char **)arg = LDAP_STRDUP( tls_imp->ti_name ); + return 0; + } + + if( ld != NULL ) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + + } else { + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if ( lo == NULL ) { + return LDAP_NO_MEMORY; + } + } + + switch( option ) { + case LDAP_OPT_X_TLS: + *(int *)arg = lo->ldo_tls_mode; + break; + case LDAP_OPT_X_TLS_CTX: + *(void **)arg = lo->ldo_tls_ctx; + if ( lo->ldo_tls_ctx ) { + tls_ctx_ref( lo->ldo_tls_ctx ); + } + break; + case LDAP_OPT_X_TLS_CACERTFILE: + *(char **)arg = lo->ldo_tls_cacertfile ? + LDAP_STRDUP( lo->ldo_tls_cacertfile ) : NULL; + break; + case LDAP_OPT_X_TLS_CACERTDIR: + *(char **)arg = lo->ldo_tls_cacertdir ? + LDAP_STRDUP( lo->ldo_tls_cacertdir ) : NULL; + break; + case LDAP_OPT_X_TLS_CERTFILE: + *(char **)arg = lo->ldo_tls_certfile ? + LDAP_STRDUP( lo->ldo_tls_certfile ) : NULL; + break; + case LDAP_OPT_X_TLS_KEYFILE: + *(char **)arg = lo->ldo_tls_keyfile ? + LDAP_STRDUP( lo->ldo_tls_keyfile ) : NULL; + break; + case LDAP_OPT_X_TLS_DHFILE: + *(char **)arg = lo->ldo_tls_dhfile ? + LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL; + break; + case LDAP_OPT_X_TLS_ECNAME: + *(char **)arg = lo->ldo_tls_ecname ? + LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL; + break; + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + *(char **)arg = lo->ldo_tls_crlfile ? + LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL; + break; + case LDAP_OPT_X_TLS_REQUIRE_CERT: + *(int *)arg = lo->ldo_tls_require_cert; + break; + case LDAP_OPT_X_TLS_REQUIRE_SAN: + *(int *)arg = lo->ldo_tls_require_san; + break; +#ifdef HAVE_OPENSSL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + *(int *)arg = lo->ldo_tls_crlcheck; + break; +#endif + case LDAP_OPT_X_TLS_CIPHER_SUITE: + *(char **)arg = lo->ldo_tls_ciphersuite ? + LDAP_STRDUP( lo->ldo_tls_ciphersuite ) : NULL; + break; + case LDAP_OPT_X_TLS_PROTOCOL_MIN: + *(int *)arg = lo->ldo_tls_protocol_min; + break; + case LDAP_OPT_X_TLS_PROTOCOL_MAX: + *(int *)arg = lo->ldo_tls_protocol_max; + break; + case LDAP_OPT_X_TLS_RANDOM_FILE: + *(char **)arg = lo->ldo_tls_randfile ? + LDAP_STRDUP( lo->ldo_tls_randfile ) : NULL; + break; + case LDAP_OPT_X_TLS_SSL_CTX: { + void *retval = 0; + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + retval = ldap_pvt_tls_sb_ctx( sb ); + } + } + *(void **)arg = retval; + break; + } + case LDAP_OPT_X_TLS_CONNECT_CB: + *(LDAP_TLS_CONNECT_CB **)arg = lo->ldo_tls_connect_cb; + break; + case LDAP_OPT_X_TLS_CONNECT_ARG: + *(void **)arg = lo->ldo_tls_connect_arg; + break; + case LDAP_OPT_X_TLS_VERSION: { + void *sess = NULL; + const char *retval = NULL; + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + sess = ldap_pvt_tls_sb_ctx( sb ); + if ( sess != NULL ) + retval = ldap_pvt_tls_get_version( sess ); + } + } + *(char **)arg = retval ? LDAP_STRDUP( retval ) : NULL; + break; + } + case LDAP_OPT_X_TLS_CIPHER: { + void *sess = NULL; + const char *retval = NULL; + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + sess = ldap_pvt_tls_sb_ctx( sb ); + if ( sess != NULL ) + retval = ldap_pvt_tls_get_cipher( sess ); + } + } + *(char **)arg = retval ? LDAP_STRDUP( retval ) : NULL; + break; + } + case LDAP_OPT_X_TLS_PEERCERT: { + void *sess = NULL; + struct berval *bv = arg; + bv->bv_len = 0; + bv->bv_val = NULL; + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + sess = ldap_pvt_tls_sb_ctx( sb ); + if ( sess != NULL ) + return ldap_pvt_tls_get_peercert( sess, bv ); + } + } + break; + } + case LDAP_OPT_X_TLS_CACERT: { + struct berval *bv = arg; + if ( lo->ldo_tls_cacert.bv_val ) { + ber_dupbv( bv, &lo->ldo_tls_cacert ); + } else { + BER_BVZERO( bv ); + } + break; + } + case LDAP_OPT_X_TLS_CERT: { + struct berval *bv = arg; + if ( lo->ldo_tls_cert.bv_val ) { + ber_dupbv( bv, &lo->ldo_tls_cert ); + } else { + BER_BVZERO( bv ); + } + break; + } + case LDAP_OPT_X_TLS_KEY: { + struct berval *bv = arg; + if ( lo->ldo_tls_key.bv_val ) { + ber_dupbv( bv, &lo->ldo_tls_key ); + } else { + BER_BVZERO( bv ); + } + break; + } + + default: + return -1; + } + return 0; +} + +int +ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg ) +{ + struct ldapoptions *lo; + + if( ld != NULL ) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + + } else { + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if ( lo == NULL ) { + return LDAP_NO_MEMORY; + } + } + + switch( option ) { + case LDAP_OPT_X_TLS: + if ( !arg ) return -1; + + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + if (lo != NULL) { + lo->ldo_tls_mode = *(int *)arg; + } + + return 0; + } + return -1; + + case LDAP_OPT_X_TLS_CTX: + if ( lo->ldo_tls_ctx ) + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = arg; + tls_ctx_ref( lo->ldo_tls_ctx ); + return 0; + case LDAP_OPT_X_TLS_CONNECT_CB: + lo->ldo_tls_connect_cb = (LDAP_TLS_CONNECT_CB *)arg; + return 0; + case LDAP_OPT_X_TLS_CONNECT_ARG: + lo->ldo_tls_connect_arg = arg; + return 0; + case LDAP_OPT_X_TLS_CACERTFILE: + if ( lo->ldo_tls_cacertfile ) LDAP_FREE( lo->ldo_tls_cacertfile ); + lo->ldo_tls_cacertfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CACERTDIR: + if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir ); + lo->ldo_tls_cacertdir = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CERTFILE: + if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile ); + lo->ldo_tls_certfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_KEYFILE: + if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile ); + lo->ldo_tls_keyfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_DHFILE: + if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile ); + lo->ldo_tls_dhfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_ECNAME: + if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname ); + lo->ldo_tls_ecname = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile ); + lo->ldo_tls_crlfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_REQUIRE_CERT: + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + lo->ldo_tls_require_cert = * (int *) arg; + return 0; + } + return -1; + case LDAP_OPT_X_TLS_REQUIRE_SAN: + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + lo->ldo_tls_require_san = * (int *) arg; + return 0; + } + return -1; +#ifdef HAVE_OPENSSL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_CRL_NONE: + case LDAP_OPT_X_TLS_CRL_PEER: + case LDAP_OPT_X_TLS_CRL_ALL: + lo->ldo_tls_crlcheck = * (int *) arg; + return 0; + } + return -1; +#endif + case LDAP_OPT_X_TLS_CIPHER_SUITE: + if ( lo->ldo_tls_ciphersuite ) LDAP_FREE( lo->ldo_tls_ciphersuite ); + lo->ldo_tls_ciphersuite = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + + case LDAP_OPT_X_TLS_PROTOCOL_MIN: + if ( !arg ) return -1; + lo->ldo_tls_protocol_min = *(int *)arg; + return 0; + case LDAP_OPT_X_TLS_PROTOCOL_MAX: + if ( !arg ) return -1; + lo->ldo_tls_protocol_max = *(int *)arg; + return 0; + case LDAP_OPT_X_TLS_RANDOM_FILE: + if ( ld != NULL ) + return -1; + if ( lo->ldo_tls_randfile ) LDAP_FREE (lo->ldo_tls_randfile ); + lo->ldo_tls_randfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + break; + case LDAP_OPT_X_TLS_NEWCTX: { + int rc; + char errmsg[ERRBUFSIZE]; + if ( !arg ) return -1; + if ( lo->ldo_tls_ctx ) + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + errmsg[0] = 0; + rc = ldap_int_tls_init_ctx( lo, *(int *)arg, errmsg ); + if ( rc && errmsg[0] && ld ) { + if ( ld->ld_error ) + LDAP_FREE( ld->ld_error ); + ld->ld_error = LDAP_STRDUP( errmsg ); + } + return rc; + } + case LDAP_OPT_X_TLS_CACERT: + if ( lo->ldo_tls_cacert.bv_val ) + LDAP_FREE( lo->ldo_tls_cacert.bv_val ); + if ( arg ) { + lo->ldo_tls_cacert.bv_len = ((struct berval *)arg)->bv_len; + lo->ldo_tls_cacert.bv_val = LDAP_MALLOC( lo->ldo_tls_cacert.bv_len ); + if ( !lo->ldo_tls_cacert.bv_val ) + return -1; + AC_MEMCPY( lo->ldo_tls_cacert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cacert.bv_len ); + } else { + BER_BVZERO( &lo->ldo_tls_cacert ); + } + break; + case LDAP_OPT_X_TLS_CERT: + if ( lo->ldo_tls_cert.bv_val ) + LDAP_FREE( lo->ldo_tls_cert.bv_val ); + if ( arg ) { + lo->ldo_tls_cert.bv_len = ((struct berval *)arg)->bv_len; + lo->ldo_tls_cert.bv_val = LDAP_MALLOC( lo->ldo_tls_cert.bv_len ); + if ( !lo->ldo_tls_cert.bv_val ) + return -1; + AC_MEMCPY( lo->ldo_tls_cert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cert.bv_len ); + } else { + BER_BVZERO( &lo->ldo_tls_cert ); + } + break; + case LDAP_OPT_X_TLS_KEY: + if ( lo->ldo_tls_key.bv_val ) + LDAP_FREE( lo->ldo_tls_key.bv_val ); + if ( arg ) { + lo->ldo_tls_key.bv_len = ((struct berval *)arg)->bv_len; + lo->ldo_tls_key.bv_val = LDAP_MALLOC( lo->ldo_tls_key.bv_len ); + if ( !lo->ldo_tls_key.bv_val ) + return -1; + AC_MEMCPY( lo->ldo_tls_key.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_key.bv_len ); + } else { + BER_BVZERO( &lo->ldo_tls_key ); + } + break; + case LDAP_OPT_X_TLS_PEERKEY_HASH: { + /* arg = "[hashalg:]pubkey_hash" */ + struct berval bv; + char *p, *pin = arg; + int rc = LDAP_SUCCESS; + + if ( !tls_imp->ti_session_pinning ) return -1; + + if ( !pin || !*pin ) { + if ( lo->ldo_tls_pin_hashalg ) { + LDAP_FREE( lo->ldo_tls_pin_hashalg ); + } else if ( lo->ldo_tls_pin.bv_val ) { + LDAP_FREE( lo->ldo_tls_pin.bv_val ); + } + lo->ldo_tls_pin_hashalg = NULL; + BER_BVZERO( &lo->ldo_tls_pin ); + return rc; + } + + pin = LDAP_STRDUP( pin ); + p = strchr( pin, ':' ); + + /* pubkey (its hash) goes in bv, alg in p */ + if ( p ) { + *p = '\0'; + bv.bv_val = p+1; + p = pin; + } else { + bv.bv_val = pin; + } + + bv.bv_len = strlen(bv.bv_val); + if ( ldap_int_decode_b64_inplace( &bv ) ) { + LDAP_FREE( pin ); + return -1; + } + + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + void *sess = ldap_pvt_tls_sb_ctx( sb ); + if ( sess != NULL ) { + rc = tls_imp->ti_session_pinning( ld, sess, p, &bv ); + } + } + } + + if ( rc == LDAP_SUCCESS ) { + if ( lo->ldo_tls_pin_hashalg ) { + LDAP_FREE( lo->ldo_tls_pin_hashalg ); + } else if ( lo->ldo_tls_pin.bv_val ) { + LDAP_FREE( lo->ldo_tls_pin.bv_val ); + } + lo->ldo_tls_pin_hashalg = p; + lo->ldo_tls_pin = bv; + } else { + LDAP_FREE( pin ); + } + + return rc; + } + default: + return -1; + } + return 0; +} + +int +ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv ) +{ + Sockbuf *sb; + char *host; + void *ssl; + int ret, async; + struct timeval start_time_tv, tv, tv0; + ber_socket_t sd = AC_SOCKET_ERROR; + + if ( !conn ) + return LDAP_PARAM_ERROR; + + sb = conn->lconn_sb; + if( srv ) { + host = srv->lud_host; + } else { + host = conn->lconn_server->lud_host; + } + + /* avoid NULL host */ + if( host == NULL ) { + host = "localhost"; + } + + (void) tls_init( tls_imp, 0 ); + + /* + * Use non-blocking io during SSL Handshake when a timeout is configured + */ + async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + if ( !async ) { + /* if async, this has already been set */ + ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 ); + } + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + tv = ld->ld_options.ldo_tm_net; + tv0 = tv; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &start_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + time( &start_time_tv.tv_sec ); + start_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + } + + ld->ld_errno = LDAP_SUCCESS; + ret = ldap_int_tls_connect( ld, conn, host ); + + /* this mainly only happens for non-blocking io + * but can also happen when the handshake is too + * big for a single network message. + */ + while ( ret > 0 ) { + if ( async ) { + struct timeval curr_time_tv, delta_tv; + int wr=0; + + if ( sb->sb_trans_needs_read ) { + wr=0; + } else if ( sb->sb_trans_needs_write ) { + wr=1; + } + Debug1( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ldap_int_tls_connect needs %s\n", + wr ? "write": "read" ); + + /* This is mostly copied from result.c:wait4msg(), should + * probably be moved into a separate function */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &curr_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + time( &curr_time_tv.tv_sec ); + curr_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + + /* delta = curr - start */ + delta_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; + delta_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; + if ( delta_tv.tv_usec < 0 ) { + delta_tv.tv_sec--; + delta_tv.tv_usec += 1000000; + } + + /* tv0 < delta ? */ + if ( ( tv0.tv_sec < delta_tv.tv_sec ) || + ( ( tv0.tv_sec == delta_tv.tv_sec ) && + ( tv0.tv_usec < delta_tv.tv_usec ) ) ) + { + ret = -1; + ld->ld_errno = LDAP_TIMEOUT; + break; + } + /* timeout -= delta_time */ + tv0.tv_sec -= delta_tv.tv_sec; + tv0.tv_usec -= delta_tv.tv_usec; + if ( tv0.tv_usec < 0 ) { + tv0.tv_sec--; + tv0.tv_usec += 1000000; + } + start_time_tv.tv_sec = curr_time_tv.tv_sec; + start_time_tv.tv_usec = curr_time_tv.tv_usec; + tv = tv0; + Debug3( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ld %p %ld s %ld us to go\n", + (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); + ret = ldap_int_poll( ld, sd, &tv, wr); + if ( ret < 0 ) { + ld->ld_errno = LDAP_TIMEOUT; + break; + } + } + ret = ldap_int_tls_connect( ld, conn, host ); + } + + if ( ret < 0 ) { + if ( ld->ld_errno == LDAP_SUCCESS ) + ld->ld_errno = LDAP_CONNECT_ERROR; + return (ld->ld_errno); + } + + return LDAP_SUCCESS; +} + +void * +ldap_pvt_tls_sb_ctx( Sockbuf *sb ) +{ + void *p = NULL; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p ); + return p; +} + +int +ldap_pvt_tls_get_strength( void *s ) +{ + tls_session *session = s; + + return tls_imp->ti_session_strength( session ); +} + +int +ldap_pvt_tls_get_my_dn( void *s, struct berval *dn, LDAPDN_rewrite_dummy *func, unsigned flags ) +{ + tls_session *session = s; + struct berval der_dn; + int rc; + + rc = tls_imp->ti_session_my_dn( session, &der_dn ); + if ( rc == LDAP_SUCCESS ) + rc = ldap_X509dn2bv(&der_dn, dn, (LDAPDN_rewrite_func *)func, flags ); + return rc; +} + +int +ldap_pvt_tls_get_unique( void *s, struct berval *buf, int is_server ) +{ + tls_session *session = s; + return tls_imp->ti_session_unique( session, buf, is_server ); +} + +int +ldap_pvt_tls_get_endpoint( void *s, struct berval *buf, int is_server ) +{ + tls_session *session = s; + return tls_imp->ti_session_endpoint( session, buf, is_server ); +} + +const char * +ldap_pvt_tls_get_version( void *s ) +{ + tls_session *session = s; + return tls_imp->ti_session_version( session ); +} + +const char * +ldap_pvt_tls_get_cipher( void *s ) +{ + tls_session *session = s; + return tls_imp->ti_session_cipher( session ); +} + +int +ldap_pvt_tls_get_peercert( void *s, struct berval *der ) +{ + tls_session *session = s; + return tls_imp->ti_session_peercert( session, der ); +} +#endif /* HAVE_TLS */ + +int +ldap_start_tls( LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp ) +{ + return ldap_extended_operation( ld, LDAP_EXOP_START_TLS, + NULL, serverctrls, clientctrls, msgidp ); +} + +int +ldap_install_tls( LDAP *ld ) +{ +#ifndef HAVE_TLS + return LDAP_NOT_SUPPORTED; +#else + if ( ldap_tls_inplace( ld ) ) { + return LDAP_LOCAL_ERROR; + } + + return ldap_int_tls_start( ld, ld->ld_defconn, NULL ); +#endif +} + +int +ldap_start_tls_s ( LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ +#ifndef HAVE_TLS + return LDAP_NOT_SUPPORTED; +#else + int rc; + char *rspoid = NULL; + struct berval *rspdata = NULL; + + /* XXYYZ: this initiates operation only on default connection! */ + + if ( ldap_tls_inplace( ld ) ) { + return LDAP_LOCAL_ERROR; + } + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_START_TLS, + NULL, serverctrls, clientctrls, &rspoid, &rspdata ); + + if ( rspoid != NULL ) { + LDAP_FREE(rspoid); + } + + if ( rspdata != NULL ) { + ber_bvfree( rspdata ); + } + + if ( rc == LDAP_SUCCESS ) { + rc = ldap_int_tls_start( ld, ld->ld_defconn, NULL ); + } + + return rc; +#endif +} + +/* These tags probably all belong in lber.h, but they're + * not normally encountered when processing LDAP, so maybe + * they belong somewhere else instead. + */ + +#define LBER_TAG_OID ((ber_tag_t) 0x06UL) + +/* Tags for string types used in a DirectoryString. + * + * Note that IA5string is not one of the defined choices for + * DirectoryString in X.520, but it gets used for email AVAs. + */ +#define LBER_TAG_UTF8 ((ber_tag_t) 0x0cUL) +#define LBER_TAG_PRINTABLE ((ber_tag_t) 0x13UL) +#define LBER_TAG_TELETEX ((ber_tag_t) 0x14UL) +#define LBER_TAG_IA5 ((ber_tag_t) 0x16UL) +#define LBER_TAG_UNIVERSAL ((ber_tag_t) 0x1cUL) +#define LBER_TAG_BMP ((ber_tag_t) 0x1eUL) + +static oid_name * +find_oid( struct berval *oid ) +{ + int i; + + for ( i=0; !BER_BVISNULL( &oids[i].oid ); i++ ) { + if ( oids[i].oid.bv_len != oid->bv_len ) continue; + if ( !strcmp( oids[i].oid.bv_val, oid->bv_val )) + return &oids[i]; + } + return NULL; +} + +/* Converts BER Bitstring value to LDAP BitString value (RFC4517) + * + * berValue : IN + * rfc4517Value: OUT + * + * berValue and ldapValue should not be NULL + */ + +#define BITS_PER_BYTE 8 +#define SQUOTE_LENGTH 1 +#define B_CHAR_LENGTH 1 +#define STR_OVERHEAD (2*SQUOTE_LENGTH + B_CHAR_LENGTH) + +static int +der_to_ldap_BitString (struct berval *berValue, + struct berval *ldapValue) +{ + ber_len_t bitPadding=0; + ber_len_t bits, maxBits; + char *tmpStr; + unsigned char byte; + ber_len_t bitLength; + ber_len_t valLen; + unsigned char* valPtr; + + ldapValue->bv_len=0; + ldapValue->bv_val=NULL; + + /* Gets padding and points to binary data */ + valLen=berValue->bv_len; + valPtr=(unsigned char*)berValue->bv_val; + if (valLen) { + bitPadding=(ber_len_t)(valPtr[0]); + valLen--; + valPtr++; + } + /* If Block is non DER encoding fixes to DER encoding */ + if (bitPadding >= BITS_PER_BYTE) { + if (valLen*BITS_PER_BYTE > bitPadding ) { + valLen-=(bitPadding/BITS_PER_BYTE); + bitPadding%=BITS_PER_BYTE; + } else { + valLen=0; + bitPadding=0; + } + } + /* Just in case bad encoding */ + if (valLen*BITS_PER_BYTE < bitPadding ) { + bitPadding=0; + valLen=0; + } + + /* Gets buffer to hold RFC4517 Bit String format */ + bitLength=valLen*BITS_PER_BYTE-bitPadding; + tmpStr=LDAP_MALLOC(bitLength + STR_OVERHEAD + 1); + + if (!tmpStr) + return LDAP_NO_MEMORY; + + ldapValue->bv_val=tmpStr; + ldapValue->bv_len=bitLength + STR_OVERHEAD; + + /* Formatting in '*binary-digit'B format */ + maxBits=BITS_PER_BYTE; + *tmpStr++ ='\''; + while(valLen) { + byte=*valPtr; + if (valLen==1) + maxBits-=bitPadding; + for (bits=0; bits<maxBits; bits++) { + if (0x80 & byte) + *tmpStr='1'; + else + *tmpStr='0'; + tmpStr++; + byte<<=1; + } + valPtr++; + valLen--; + } + *tmpStr++ ='\''; + *tmpStr++ ='B'; + *tmpStr=0; + + return LDAP_SUCCESS; +} + +/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN. + * x509_name must be raw DER. If func is non-NULL, the + * constructed DN will use numeric OIDs to identify attributeTypes, + * and the func() will be invoked to rewrite the DN with the given + * flags. + * + * Otherwise the DN will use shortNames from a hardcoded table. + */ +int +ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, + unsigned flags ) +{ + LDAPDN newDN; + LDAPRDN newRDN; + LDAPAVA *newAVA, *baseAVA; + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + char oids[8192], *oidptr = oids, *oidbuf = NULL; + void *ptrs[2048]; + char *dn_end, *rdn_end; + int i, navas, nrdns, rc = LDAP_SUCCESS; + size_t dnsize, oidrem = sizeof(oids), oidsize = 0; + int csize; + ber_tag_t tag; + ber_len_t len; + oid_name *oidname; + + struct berval Oid, Val, oid2, *in = x509_name; + + assert( bv != NULL ); + + bv->bv_len = 0; + bv->bv_val = NULL; + + navas = 0; + nrdns = 0; + + /* A DN is a SEQUENCE of RDNs. An RDN is a SET of AVAs. + * An AVA is a SEQUENCE of attr and value. + * Count the number of AVAs and RDNs + */ + ber_init2( ber, in, LBER_USE_DER ); + tag = ber_peek_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) + return LDAP_DECODING_ERROR; + + for ( tag = ber_first_element( ber, &len, &dn_end ); + tag == LBER_SET; + tag = ber_next_element( ber, &len, dn_end )) { + nrdns++; + for ( tag = ber_first_element( ber, &len, &rdn_end ); + tag == LBER_SEQUENCE; + tag = ber_next_element( ber, &len, rdn_end )) { + if ( rdn_end > dn_end ) + return LDAP_DECODING_ERROR; + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + navas++; + } + } + + /* Rewind and prepare to extract */ + ber_rewind( ber ); + tag = ber_first_element( ber, &len, &dn_end ); + if ( tag != LBER_SET ) + return LDAP_DECODING_ERROR; + + /* Allocate the DN/RDN/AVA stuff as a single block */ + dnsize = sizeof(LDAPRDN) * (nrdns+1); + dnsize += sizeof(LDAPAVA *) * (navas+nrdns); + dnsize += sizeof(LDAPAVA) * navas; + if (dnsize > sizeof(ptrs)) { + newDN = (LDAPDN)LDAP_MALLOC( dnsize ); + if ( newDN == NULL ) + return LDAP_NO_MEMORY; + } else { + newDN = (LDAPDN)(char *)ptrs; + } + + newDN[nrdns] = NULL; + newRDN = (LDAPRDN)(newDN + nrdns+1); + newAVA = (LDAPAVA *)(newRDN + navas + nrdns); + baseAVA = newAVA; + + for ( i = nrdns - 1; i >= 0; i-- ) { + newDN[i] = newRDN; + + for ( tag = ber_first_element( ber, &len, &rdn_end ); + tag == LBER_SEQUENCE; + tag = ber_next_element( ber, &len, rdn_end )) { + + *newRDN++ = newAVA; + tag = ber_skip_tag( ber, &len ); + tag = ber_get_stringbv( ber, &Oid, LBER_BV_NOTERM ); + if ( tag != LBER_TAG_OID ) { + rc = LDAP_DECODING_ERROR; + goto nomem; + } + + oid2.bv_val = oidptr; + oid2.bv_len = oidrem; + if ( ber_decode_oid( &Oid, &oid2 ) < 0 ) { + rc = LDAP_DECODING_ERROR; + goto nomem; + } + oidname = find_oid( &oid2 ); + if ( !oidname ) { + newAVA->la_attr = oid2; + oidptr += oid2.bv_len + 1; + oidrem -= oid2.bv_len + 1; + + /* Running out of OID buffer space? */ + if (oidrem < 128) { + if ( oidsize == 0 ) { + oidsize = sizeof(oids) * 2; + oidrem = oidsize; + oidbuf = LDAP_MALLOC( oidsize ); + if ( oidbuf == NULL ) goto nomem; + oidptr = oidbuf; + } else { + char *old = oidbuf; + oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 ); + if ( oidbuf == NULL ) goto nomem; + /* Buffer moved! Fix AVA pointers */ + if ( old != oidbuf ) { + LDAPAVA *a; + long dif = oidbuf - old; + + for (a=baseAVA; a<=newAVA; a++){ + if (a->la_attr.bv_val >= old && + a->la_attr.bv_val <= (old + oidsize)) + a->la_attr.bv_val += dif; + } + } + oidptr = oidbuf + oidsize - oidrem; + oidrem += oidsize; + oidsize *= 2; + } + } + } else { + if ( func ) { + newAVA->la_attr = oidname->oid; + } else { + newAVA->la_attr = oidname->name; + } + } + newAVA->la_private = NULL; + newAVA->la_flags = LDAP_AVA_STRING; + tag = ber_get_stringbv( ber, &Val, LBER_BV_NOTERM ); + switch(tag) { + case LBER_TAG_UNIVERSAL: + /* This uses 32-bit ISO 10646-1 */ + csize = 4; goto to_utf8; + case LBER_TAG_BMP: + /* This uses 16-bit ISO 10646-1 */ + csize = 2; goto to_utf8; + case LBER_TAG_TELETEX: + /* This uses 8-bit, assume ISO 8859-1 */ + csize = 1; +to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value ); + newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; +allocd: + newAVA->la_flags |= LDAP_AVA_FREE_VALUE; + if (rc != LDAP_SUCCESS) goto nomem; + break; + case LBER_TAG_UTF8: + newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; + /* This is already in UTF-8 encoding */ + case LBER_TAG_IA5: + case LBER_TAG_PRINTABLE: + /* These are always 7-bit strings */ + newAVA->la_value = Val; + break; + case LBER_BITSTRING: + /* X.690 bitString value converted to RFC4517 Bit String */ + rc = der_to_ldap_BitString( &Val, &newAVA->la_value ); + goto allocd; + case LBER_DEFAULT: + /* decode error */ + rc = LDAP_DECODING_ERROR; + goto nomem; + default: + /* Not a string type at all */ + newAVA->la_flags = 0; + newAVA->la_value = Val; + break; + } + newAVA++; + } + *newRDN++ = NULL; + tag = ber_next_element( ber, &len, dn_end ); + } + + if ( func ) { + rc = func( newDN, flags, NULL ); + if ( rc != LDAP_SUCCESS ) + goto nomem; + } + + rc = ldap_dn2bv_x( newDN, bv, LDAP_DN_FORMAT_LDAPV3, NULL ); + +nomem: + for (;baseAVA < newAVA; baseAVA++) { + if (baseAVA->la_flags & LDAP_AVA_FREE_ATTR) + LDAP_FREE( baseAVA->la_attr.bv_val ); + if (baseAVA->la_flags & LDAP_AVA_FREE_VALUE) + LDAP_FREE( baseAVA->la_value.bv_val ); + } + + if ( oidsize != 0 ) + LDAP_FREE( oidbuf ); + if ( newDN != (LDAPDN)(char *) ptrs ) + LDAP_FREE( newDN ); + return rc; +} + diff --git a/contrib/libs/openldap/libraries/libldap/tls_g.c b/contrib/libs/openldap/libraries/libldap/tls_g.c new file mode 100644 index 0000000000..68b6226ca1 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/tls_g.c @@ -0,0 +1,1278 @@ +/* tls_g.c - Handle tls/ssl using GNUTLS. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: GNUTLS support written by Howard Chu and + * Emily Backes; sponsored by The Written Word (thewrittenword.com) + * and Stanford University (stanford.edu). + */ + +#include "portable.h" + +#ifdef HAVE_GNUTLS + +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "ldap-int.h" +#include "ldap-tls.h" + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#error #include <gnutls/abstract.h> +#include <gnutls/crypto.h> + +typedef struct tlsg_ctx { + gnutls_certificate_credentials_t cred; + gnutls_dh_params_t dh_params; + unsigned long verify_depth; + int refcount; + int reqcert; + gnutls_priority_t prios; +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ref_mutex; +#endif +} tlsg_ctx; + +typedef struct tlsg_session { + gnutls_session_t session; + tlsg_ctx *ctx; + struct berval peer_der_dn; +} tlsg_session; + +static int tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites ); +static int tlsg_cert_verify( tlsg_session *s ); + +#ifdef LDAP_R_COMPILE + +static void +tlsg_thr_init( void ) +{ + /* do nothing */ +} +#endif /* LDAP_R_COMPILE */ + +/* + * Initialize TLS subsystem. Should be called only once. + */ +static int +tlsg_init( void ) +{ + gnutls_global_init(); + return 0; +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +static void +tlsg_destroy( void ) +{ + gnutls_global_deinit(); +} + +static tls_ctx * +tlsg_ctx_new ( struct ldapoptions *lo ) +{ + tlsg_ctx *ctx; + + ctx = ber_memcalloc ( 1, sizeof (*ctx) ); + if ( ctx ) { + if ( gnutls_certificate_allocate_credentials( &ctx->cred )) { + ber_memfree( ctx ); + return NULL; + } + ctx->refcount = 1; + gnutls_priority_init( &ctx->prios, "NORMAL", NULL ); +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ctx->ref_mutex ); +#endif + } + return (tls_ctx *)ctx; +} + +static void +tlsg_ctx_ref( tls_ctx *ctx ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + LDAP_MUTEX_LOCK( &c->ref_mutex ); + c->refcount++; + LDAP_MUTEX_UNLOCK( &c->ref_mutex ); +} + +static void +tlsg_ctx_free ( tls_ctx *ctx ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + int refcount; + + if ( !c ) return; + + LDAP_MUTEX_LOCK( &c->ref_mutex ); + refcount = --c->refcount; + LDAP_MUTEX_UNLOCK( &c->ref_mutex ); + if ( refcount ) + return; + gnutls_priority_deinit( c->prios ); + gnutls_certificate_free_credentials( c->cred ); + if ( c->dh_params ) + gnutls_dh_params_deinit( c->dh_params ); + ber_memfree ( c ); +} + +static int +tlsg_getfile( const char *path, gnutls_datum_t *buf ) +{ + int rc = -1, fd; + struct stat st; + char ebuf[128]; + + fd = open( path, O_RDONLY ); + if ( fd < 0 ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: opening `%s' failed: %s\n", + path, + AC_STRERROR_R( errno, ebuf, sizeof ebuf )); + return -1; + } + if ( fstat( fd, &st ) == 0 ) { + buf->size = st.st_size; + buf->data = LDAP_MALLOC( st.st_size + 1 ); + if ( buf->data ) { + rc = read( fd, buf->data, st.st_size ); + close( fd ); + if ( rc < st.st_size ) + rc = -1; + else + rc = 0; + } + } + return rc; +} + +/* This is the GnuTLS default */ +#define VERIFY_DEPTH 6 + +/* + * initialize a new TLS context + */ +static int +tlsg_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server, char *errmsg ) +{ + tlsg_ctx *ctx = lo->ldo_tls_ctx; + int rc; + + if ( lo->ldo_tls_ciphersuite && + tlsg_parse_ciphers( ctx, lt->lt_ciphersuite )) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not set cipher list %s.\n", + lo->ldo_tls_ciphersuite ); + return -1; + } + + if (lo->ldo_tls_cacertdir != NULL) { + char **dirs = ldap_str2charray( lt->lt_cacertdir, CERTPATHSEP ); + int i; + for ( i=0; dirs[i]; i++ ) { + rc = gnutls_certificate_set_x509_trust_dir( + ctx->cred, + dirs[i], + GNUTLS_X509_FMT_PEM ); + if ( rc > 0 ) { + Debug2( LDAP_DEBUG_TRACE, + "TLS: loaded %d CA certificates from directory `%s'.\n", + rc, dirs[i] ); + } else { + Debug1( LDAP_DEBUG_ANY, + "TLS: warning: no certificate found in CA certificate directory `%s'.\n", + dirs[i] ); + /* only warn, no return */ + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + } + } + ldap_charray_free( dirs ); + } + + if (lo->ldo_tls_cacertfile != NULL) { + rc = gnutls_certificate_set_x509_trust_file( + ctx->cred, + lt->lt_cacertfile, + GNUTLS_X509_FMT_PEM ); + if ( rc < 0 ) { + Debug3( LDAP_DEBUG_ANY, + "TLS: could not use CA certificate file `%s': %s (%d)\n", + lo->ldo_tls_cacertfile, + gnutls_strerror( rc ), + rc ); + return -1; + } else if ( rc == 0 ) { + Debug1( LDAP_DEBUG_ANY, + "TLS: warning: no certificate loaded from CA certificate file `%s'.\n", + lo->ldo_tls_cacertfile ); + /* only warn, no return */ + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + } + } + + if (lo->ldo_tls_cacert.bv_val != NULL ) { + gnutls_datum_t buf; + buf.data = (unsigned char *)lo->ldo_tls_cacert.bv_val; + buf.size = lo->ldo_tls_cacert.bv_len; + rc = gnutls_certificate_set_x509_trust_mem( + ctx->cred, + &buf, + GNUTLS_X509_FMT_DER ); + if ( rc < 0 ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: could not use CA certificate: %s (%d)\n", + gnutls_strerror( rc ), + rc ); + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return -1; + } + } + + if (( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) || + ( lo->ldo_tls_cert.bv_val && lo->ldo_tls_key.bv_val )) { + gnutls_x509_privkey_t key; + gnutls_datum_t buf; + gnutls_x509_crt_t certs[VERIFY_DEPTH]; + unsigned int max = VERIFY_DEPTH; + + rc = gnutls_x509_privkey_init( &key ); + if ( rc ) { + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return -1; + } + + /* OpenSSL builds the cert chain for us, but GnuTLS + * expects it to be present in the certfile. If it's + * not, we have to build it ourselves. So we have to + * do some special checks here... + */ + if ( lo->ldo_tls_key.bv_val ) { + buf.data = (unsigned char *)lo->ldo_tls_key.bv_val; + buf.size = lo->ldo_tls_key.bv_len; + rc = gnutls_x509_privkey_import( key, &buf, + GNUTLS_X509_FMT_DER ); + } else { + rc = tlsg_getfile( lt->lt_keyfile, &buf ); + if ( rc ) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not use private key file `%s`.\n", + lt->lt_keyfile); + return -1; + } + rc = gnutls_x509_privkey_import( key, &buf, + GNUTLS_X509_FMT_PEM ); + LDAP_FREE( buf.data ); + } + if ( rc < 0 ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: could not use private key: %s (%d)\n", + gnutls_strerror( rc ), + rc ); + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return rc; + } + + if ( lo->ldo_tls_cert.bv_val ) { + buf.data = (unsigned char *)lo->ldo_tls_cert.bv_val; + buf.size = lo->ldo_tls_cert.bv_len; + rc = gnutls_x509_crt_list_import( certs, &max, &buf, + GNUTLS_X509_FMT_DER, 0 ); + } else { + rc = tlsg_getfile( lt->lt_certfile, &buf ); + if ( rc ) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not use certificate file `%s`.\n", + lt->lt_certfile); + return -1; + } + rc = gnutls_x509_crt_list_import( certs, &max, &buf, + GNUTLS_X509_FMT_PEM, 0 ); + LDAP_FREE( buf.data ); + } + if ( rc < 0 ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: could not use certificate: %s (%d)\n", + gnutls_strerror( rc ), + rc ); + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return rc; + } + + /* If there's only one cert and it's not self-signed, + * then we have to build the cert chain. + */ + if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) { + unsigned int i; + for ( i = 1; i<VERIFY_DEPTH; i++ ) { + if ( gnutls_certificate_get_issuer( ctx->cred, certs[i-1], &certs[i], 0 )) + break; + max++; + /* If this CA is self-signed, we're done */ + if ( gnutls_x509_crt_check_issuer( certs[i], certs[i] )) + break; + } + } + rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key ); + if ( rc ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: could not use certificate with key: %s (%d)\n", + gnutls_strerror( rc ), + rc ); + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return -1; + } + } else if (( lo->ldo_tls_certfile || lo->ldo_tls_keyfile )) { + Debug0( LDAP_DEBUG_ANY, + "TLS: only one of certfile and keyfile specified\n" ); + return -1; + } else if (( lo->ldo_tls_cert.bv_val || lo->ldo_tls_key.bv_val )) { + Debug0( LDAP_DEBUG_ANY, + "TLS: only one of cert and key specified\n" ); + return -1; + } + + if ( lo->ldo_tls_crlfile ) { + rc = gnutls_certificate_set_x509_crl_file( + ctx->cred, + lt->lt_crlfile, + GNUTLS_X509_FMT_PEM ); + if ( rc < 0 ) { + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return -1; + } + rc = 0; + } + + /* FIXME: ITS#5992 - this should be configurable, + * and V1 CA certs should be phased out ASAP. + */ + gnutls_certificate_set_verify_flags( ctx->cred, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT ); + + if ( is_server && lo->ldo_tls_dhfile ) { + gnutls_datum_t buf; + rc = tlsg_getfile( lo->ldo_tls_dhfile, &buf ); + if ( rc ) return -1; + rc = gnutls_dh_params_init( &ctx->dh_params ); + if ( rc == 0 ) + rc = gnutls_dh_params_import_pkcs3( ctx->dh_params, &buf, + GNUTLS_X509_FMT_PEM ); + LDAP_FREE( buf.data ); + if ( rc ) { + strncpy( errmsg, gnutls_strerror( rc ), ERRBUFSIZE ); + return -1; + } + gnutls_certificate_set_dh_params( ctx->cred, ctx->dh_params ); + } + + ctx->reqcert = lo->ldo_tls_require_cert; + + return 0; +} + +static tls_session * +tlsg_session_new ( tls_ctx * ctx, int is_server ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + tlsg_session *session; + + session = ber_memcalloc ( 1, sizeof (*session) ); + if ( !session ) + return NULL; + + session->ctx = c; + gnutls_init( &session->session, is_server ? GNUTLS_SERVER : GNUTLS_CLIENT ); + gnutls_priority_set( session->session, c->prios ); + if ( c->cred ) + gnutls_credentials_set( session->session, GNUTLS_CRD_CERTIFICATE, c->cred ); + + if ( is_server ) { + int flag = 0; + if ( c->reqcert ) { + flag = GNUTLS_CERT_REQUEST; + if ( c->reqcert == LDAP_OPT_X_TLS_DEMAND || + c->reqcert == LDAP_OPT_X_TLS_HARD ) + flag = GNUTLS_CERT_REQUIRE; + gnutls_certificate_server_set_request( session->session, flag ); + } + } + return (tls_session *)session; +} + +static int +tlsg_session_accept( tls_session *session ) +{ + tlsg_session *s = (tlsg_session *)session; + int rc; + + rc = gnutls_handshake( s->session ); + if ( rc == 0 && s->ctx->reqcert != LDAP_OPT_X_TLS_NEVER ) { + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list && s->ctx->reqcert == LDAP_OPT_X_TLS_TRY ) + rc = 0; + else { + rc = tlsg_cert_verify( s ); + if ( rc && s->ctx->reqcert == LDAP_OPT_X_TLS_ALLOW ) + rc = 0; + } + } + return rc; +} + +static int +tlsg_session_connect( LDAP *ld, tls_session *session, const char *name_in ) +{ + tlsg_session *s = (tlsg_session *)session; + int rc; + + if ( name_in ) { + rc = gnutls_server_name_set( s->session, GNUTLS_NAME_DNS, name_in, strlen(name_in) ); + if ( rc != GNUTLS_E_SUCCESS ) { + return rc; + } + } + + return tlsg_session_accept( session); +} + +static int +tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc ) +{ + tlsg_session *s = (tlsg_session *)session; + + if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN ) + return 0; + + switch (gnutls_record_get_direction (s->session)) { + case 0: + sb->sb_trans_needs_read = 1; + return 1; + case 1: + sb->sb_trans_needs_write = 1; + return 1; + } + return 0; +} + +static char * +tlsg_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) +{ + return (char *)gnutls_strerror( rc ); +} + +static void +tlsg_x509_cert_dn( struct berval *cert, struct berval *dn, int get_subject ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t i; + + ber_init2( ber, cert, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_peek_tag( ber, &len ); /* Context + Constructed (version) */ + if ( tag == 0xa0 ) { /* Version is optional */ + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &i ); /* Int: Version */ + } + tag = ber_skip_tag( ber, &len ); /* Int: Serial (can be longer than ber_int_t) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Sequence: Signature */ + ber_skip_data( ber, len ); + if ( !get_subject ) { + tag = ber_peek_tag( ber, &len ); /* Sequence: Issuer DN */ + } else { + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Sequence: Validity */ + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); /* Sequence: Subject DN */ + } + len = ber_ptrlen( ber ); + dn->bv_val = cert->bv_val + len; + dn->bv_len = cert->bv_len - len; +} + +static int +tlsg_session_my_dn( tls_session *session, struct berval *der_dn ) +{ + tlsg_session *s = (tlsg_session *)session; + const gnutls_datum_t *x; + struct berval bv; + + x = gnutls_certificate_get_ours( s->session ); + + if (!x) return LDAP_INVALID_CREDENTIALS; + + bv.bv_val = (char *) x->data; + bv.bv_len = x->size; + + tlsg_x509_cert_dn( &bv, der_dn, 1 ); + return 0; +} + +static int +tlsg_session_peer_dn( tls_session *session, struct berval *der_dn ) +{ + tlsg_session *s = (tlsg_session *)session; + if ( !s->peer_der_dn.bv_val ) { + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + struct berval bv; + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS; + + bv.bv_len = peer_cert_list->size; + bv.bv_val = (char *) peer_cert_list->data; + + tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 ); + } + *der_dn = s->peer_der_dn; + return 0; +} + +/* what kind of hostname were we given? */ +#define IS_DNS 0 +#define IS_IP4 1 +#define IS_IP6 2 + +#define CN_OID "2.5.4.3" + +static int +tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in ) +{ + tlsg_session *s = (tlsg_session *)session; + int i, ret; + int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0; + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + char altname[NI_MAXHOST]; + size_t altnamesize; + + gnutls_x509_crt_t cert; + const char *name; + char *ptr; + char *domain = NULL; +#ifdef LDAP_PF_INET6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + int len1 = 0, len2 = 0; + int ntype = IS_DNS; + + if( ldap_int_hostname && + ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) + { + name = ldap_int_hostname; + } else { + name = name_in; + } + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list ) { + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get peer certificate.\n" ); + /* If this was a fatal condition, things would have + * aborted long before now. + */ + return LDAP_SUCCESS; + } + ret = gnutls_x509_crt_init( &cert ); + if ( ret < 0 ) + return LDAP_LOCAL_ERROR; + ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER ); + if ( ret ) { + gnutls_x509_crt_deinit( cert ); + return LDAP_LOCAL_ERROR; + } + +#ifdef LDAP_PF_INET6 + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; + } else +#endif + if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { + if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; + } + + if (ntype == IS_DNS) { + len1 = strlen(name); + domain = strchr(name, '.'); + if (domain) { + len2 = len1 - (domain-name); + } + } + + if (chkSAN) { + for ( i=0, ret=0; ret >= 0; i++ ) { + altnamesize = sizeof(altname); + ret = gnutls_x509_crt_get_subject_alt_name( cert, i, + altname, &altnamesize, NULL ); + if ( ret < 0 ) break; + + gotSAN = 1; + /* ignore empty */ + if ( altnamesize == 0 ) continue; + + if ( ret == GNUTLS_SAN_DNSNAME ) { + if (ntype != IS_DNS) continue; + + /* Is this an exact match? */ + if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) { + break; + } + + /* Is this a wildcard match? */ + if (domain && (altname[0] == '*') && (altname[1] == '.') && + (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) + { + break; + } + } else if ( ret == GNUTLS_SAN_IPADDRESS ) { + if (ntype == IS_DNS) continue; + +#ifdef LDAP_PF_INET6 + if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) { + continue; + } else +#endif + if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) { + continue; + } + if (!memcmp(altname, &addr, altnamesize)) { + break; + } + } + } + if ( ret >= 0 ) { + ret = LDAP_SUCCESS; + } + } + if (ret != LDAP_SUCCESS && chkSAN) { + switch(chkSAN) { + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_HARD: + if (!gotSAN) { + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get subjectAltName from peer certificate.\n" ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get subjectAltName from peer certificate")); + goto done; + } + /* FALLTHRU */ + case LDAP_OPT_X_TLS_TRY: + if (gotSAN) { + Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "subjectAltName in certificate.\n", + name ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match subjectAltName in peer certificate")); + goto done; + } + break; + case LDAP_OPT_X_TLS_ALLOW: + break; + } + } + + if ( ret != LDAP_SUCCESS ){ + /* find the last CN */ + i=0; + do { + altnamesize = 0; + ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID, + i, 1, altname, &altnamesize ); + if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER ) + i++; + else + break; + } while ( 1 ); + + if ( i ) { + altnamesize = sizeof(altname); + ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID, + i-1, 0, altname, &altnamesize ); + } + + if ( ret < 0 ) { + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get common name from peer certificate.\n" ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get CN from peer certificate")); + + } else { + ret = LDAP_LOCAL_ERROR; + if ( !len1 ) len1 = strlen( name ); + if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) { + ret = LDAP_SUCCESS; + + } else if (( altname[0] == '*' ) && ( altname[1] == '.' )) { + /* Is this a wildcard match? */ + if( domain && + (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) { + ret = LDAP_SUCCESS; + } + } + } + + if( ret == LDAP_LOCAL_ERROR ) { + altname[altnamesize] = '\0'; + Debug2( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "common name in certificate (%s).\n", + name, altname ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match name in peer certificate")); + } + } +done: + gnutls_x509_crt_deinit( cert ); + return ret; +} + +static int +tlsg_session_strength( tls_session *session ) +{ + tlsg_session *s = (tlsg_session *)session; + gnutls_cipher_algorithm_t c; + + c = gnutls_cipher_get( s->session ); + return gnutls_cipher_get_key_size( c ) * 8; +} + +static int +tlsg_session_unique( tls_session *sess, struct berval *buf, int is_server) +{ + tlsg_session *s = (tlsg_session *)sess; + gnutls_datum_t cb; + int rc; + + rc = gnutls_session_channel_binding( s->session, GNUTLS_CB_TLS_UNIQUE, &cb ); + if ( rc == 0 ) { + int len = cb.size; + if ( len > buf->bv_len ) + len = buf->bv_len; + buf->bv_len = len; + memcpy( buf->bv_val, cb.data, len ); + return len; + } + return 0; +} + +static int +tlsg_session_endpoint( tls_session *sess, struct berval *buf, int is_server ) +{ + tlsg_session *s = (tlsg_session *)sess; + const gnutls_datum_t *cert_data; + gnutls_x509_crt_t server_cert; + gnutls_digest_algorithm_t md; + int sign_algo, md_len, rc; + + if ( is_server ) + cert_data = gnutls_certificate_get_ours( s->session ); + else + cert_data = gnutls_certificate_get_peers( s->session, NULL ); + + if ( cert_data == NULL ) + return 0; + + rc = gnutls_x509_crt_init( &server_cert ); + if ( rc != GNUTLS_E_SUCCESS ) + return 0; + + rc = gnutls_x509_crt_import( server_cert, cert_data, GNUTLS_X509_FMT_DER ); + if ( rc != GNUTLS_E_SUCCESS ) { + gnutls_x509_crt_deinit( server_cert ); + return 0; + } + + sign_algo = gnutls_x509_crt_get_signature_algorithm( server_cert ); + gnutls_x509_crt_deinit( server_cert ); + if ( sign_algo <= GNUTLS_SIGN_UNKNOWN ) + return 0; + + md = gnutls_sign_get_hash_algorithm( sign_algo ); + if ( md == GNUTLS_DIG_UNKNOWN ) + return 0; + + /* See RFC 5929 */ + switch (md) { + case GNUTLS_DIG_NULL: + case GNUTLS_DIG_MD2: + case GNUTLS_DIG_MD5: + case GNUTLS_DIG_SHA1: + md = GNUTLS_DIG_SHA256; + } + + md_len = gnutls_hash_get_len( md ); + if ( md_len == 0 || md_len > buf->bv_len ) + return 0; + + rc = gnutls_hash_fast( md, cert_data->data, cert_data->size, buf->bv_val ); + if ( rc != GNUTLS_E_SUCCESS ) + return 0; + + buf->bv_len = md_len; + + return md_len; +} + +static const char * +tlsg_session_version( tls_session *sess ) +{ + tlsg_session *s = (tlsg_session *)sess; + return gnutls_protocol_get_name(gnutls_protocol_get_version( s->session )); +} + +static const char * +tlsg_session_cipher( tls_session *sess ) +{ + tlsg_session *s = (tlsg_session *)sess; + return gnutls_cipher_get_name(gnutls_cipher_get( s->session )); +} + +static int +tlsg_session_peercert( tls_session *sess, struct berval *der ) +{ + tlsg_session *s = (tlsg_session *)sess; + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + + peer_cert_list = gnutls_certificate_get_peers( s->session, &list_size ); + if (!peer_cert_list) + return -1; + der->bv_len = peer_cert_list[0].size; + der->bv_val = LDAP_MALLOC( der->bv_len ); + if (!der->bv_val) + return -1; + memcpy(der->bv_val, peer_cert_list[0].data, der->bv_len); + return 0; +} + +static int +tlsg_session_pinning( LDAP *ld, tls_session *sess, char *hashalg, struct berval *hash ) +{ + tlsg_session *s = (tlsg_session *)sess; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + gnutls_x509_crt_t crt; + gnutls_pubkey_t pubkey; + gnutls_datum_t key = {}; + gnutls_digest_algorithm_t alg; + struct berval keyhash; + size_t len; + int rc = -1; + + if ( hashalg ) { + alg = gnutls_digest_get_id( hashalg ); + if ( alg == GNUTLS_DIG_UNKNOWN ) { + Debug1( LDAP_DEBUG_ANY, "tlsg_session_pinning: " + "unknown hashing algorithm for GnuTLS: '%s'\n", + hashalg ); + return rc; + } + } + + cert_list = gnutls_certificate_get_peers( s->session, &cert_list_size ); + if ( cert_list_size == 0 ) { + return rc; + } + + if ( gnutls_x509_crt_init( &crt ) < 0 ) { + return rc; + } + + if ( gnutls_x509_crt_import( crt, &cert_list[0], GNUTLS_X509_FMT_DER ) ) { + goto done; + } + + if ( gnutls_pubkey_init( &pubkey ) ) { + goto done; + } + + if ( gnutls_pubkey_import_x509( pubkey, crt, 0 ) < 0 ) { + goto done; + } + + gnutls_pubkey_export( pubkey, GNUTLS_X509_FMT_DER, key.data, &len ); + if ( len <= 0 ) { + goto done; + } + + key.data = LDAP_MALLOC( len ); + if ( !key.data ) { + goto done; + } + + key.size = len; + + if ( gnutls_pubkey_export( pubkey, GNUTLS_X509_FMT_DER, + key.data, &len ) < 0 ) { + goto done; + } + + if ( hashalg ) { + keyhash.bv_len = gnutls_hash_get_len( alg ); + keyhash.bv_val = LDAP_MALLOC( keyhash.bv_len ); + if ( !keyhash.bv_val || gnutls_fingerprint( alg, &key, + keyhash.bv_val, &keyhash.bv_len ) < 0 ) { + goto done; + } + } else { + keyhash.bv_val = (char *)key.data; + keyhash.bv_len = key.size; + } + + if ( ber_bvcmp( hash, &keyhash ) ) { + rc = LDAP_CONNECT_ERROR; + Debug0( LDAP_DEBUG_ANY, "tlsg_session_pinning: " + "public key hash does not match provided pin.\n" ); + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: public key hash does not match provided pin")); + } else { + rc = LDAP_SUCCESS; + } + +done: + if ( pubkey ) { + gnutls_pubkey_deinit( pubkey ); + } + if ( crt ) { + gnutls_x509_crt_deinit( crt ); + } + if ( keyhash.bv_val != (char *)key.data ) { + LDAP_FREE( keyhash.bv_val ); + } + if ( key.data ) { + LDAP_FREE( key.data ); + } + return rc; +} + +/* suites is a string of colon-separated cipher suite names. */ +static int +tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites ) +{ + const char *err; + int rc = gnutls_priority_init( &ctx->prios, suites, &err ); + if ( rc ) + ctx->prios = NULL; + return rc; +} + +/* + * TLS support for LBER Sockbufs + */ + +struct tls_data { + tlsg_session *session; + Sockbuf_IO_Desc *sbiod; +}; + +static ssize_t +tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len ) +{ + struct tls_data *p; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)ptr; + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len ); +} + +static ssize_t +tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len ) +{ + struct tls_data *p; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)ptr; + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len ); +} + +static int +tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct tls_data *p; + tlsg_session *session = arg; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) { + return -1; + } + + gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr_t)p ); + gnutls_transport_set_pull_function( session->session, tlsg_recv ); + gnutls_transport_set_push_function( session->session, tlsg_send ); + p->session = session; + p->sbiod = sbiod; + sbiod->sbiod_pvt = p; + return 0; +} + +static int +tlsg_sb_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + gnutls_deinit ( p->session->session ); + LBER_FREE( p->session ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +tlsg_sb_close( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + gnutls_bye ( p->session->session, GNUTLS_SHUT_WR ); + return 0; +} + +static int +tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_GET_SSL ) { + *((tlsg_session **)arg) = p->session; + return 1; + + } else if ( opt == LBER_SB_OPT_DATA_READY ) { + if( gnutls_record_check_pending( p->session->session ) > 0 ) { + return 1; + } + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = gnutls_record_recv ( p->session->session, buf, len ); + switch (ret) { + case GNUTLS_E_INTERRUPTED: + case GNUTLS_E_AGAIN: + sbiod->sbiod_sb->sb_trans_needs_read = 1; + sock_errset(EWOULDBLOCK); + ret = 0; + break; + case GNUTLS_E_REHANDSHAKE: + for ( ret = gnutls_handshake ( p->session->session ); + ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN; + ret = gnutls_handshake ( p->session->session ) ); + sbiod->sbiod_sb->sb_trans_needs_read = 1; + ret = 0; + break; + default: + sbiod->sbiod_sb->sb_trans_needs_read = 0; + } + return ret; +} + +static ber_slen_t +tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = gnutls_record_send ( p->session->session, (char *)buf, len ); + + if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) { + sbiod->sbiod_sb->sb_trans_needs_write = 1; + sock_errset(EWOULDBLOCK); + ret = 0; + } else { + sbiod->sbiod_sb->sb_trans_needs_write = 0; + } + return ret; +} + +static Sockbuf_IO tlsg_sbio = +{ + tlsg_sb_setup, /* sbi_setup */ + tlsg_sb_remove, /* sbi_remove */ + tlsg_sb_ctrl, /* sbi_ctrl */ + tlsg_sb_read, /* sbi_read */ + tlsg_sb_write, /* sbi_write */ + tlsg_sb_close /* sbi_close */ +}; + +/* Certs are not automatically verified during the handshake */ +static int +tlsg_cert_verify( tlsg_session *ssl ) +{ + unsigned int status = 0; + int err; + time_t now = time(0); + time_t peertime; + + err = gnutls_certificate_verify_peers2( ssl->session, &status ); + if ( err < 0 ) { + Debug1( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n", + err ); + return -1; + } + if ( status ) { + Debug1( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n", + status ); + return -1; + } + peertime = gnutls_certificate_expiration_time_peers( ssl->session ); + if ( peertime == (time_t) -1 ) { + Debug0( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n" ); + return -1; + } + if ( peertime < now ) { + Debug0( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n" ); + return -1; + } + peertime = gnutls_certificate_activation_time_peers( ssl->session ); + if ( peertime == (time_t) -1 ) { + Debug0( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n" ); + return -1; + } + if ( peertime > now ) { + Debug0( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n" ); + return -1; + } + return 0; +} + +tls_impl ldap_int_tls_impl = { + "GnuTLS", + + tlsg_init, + tlsg_destroy, + + tlsg_ctx_new, + tlsg_ctx_ref, + tlsg_ctx_free, + tlsg_ctx_init, + + tlsg_session_new, + tlsg_session_connect, + tlsg_session_accept, + tlsg_session_upflags, + tlsg_session_errmsg, + tlsg_session_my_dn, + tlsg_session_peer_dn, + tlsg_session_chkhost, + tlsg_session_strength, + tlsg_session_unique, + tlsg_session_endpoint, + tlsg_session_version, + tlsg_session_cipher, + tlsg_session_peercert, + tlsg_session_pinning, + + &tlsg_sbio, + +#ifdef LDAP_R_COMPILE + tlsg_thr_init, +#else + NULL, +#endif + + 0 +}; + +#endif /* HAVE_GNUTLS */ diff --git a/contrib/libs/openldap/libraries/libldap/tls_o.c b/contrib/libs/openldap/libraries/libldap/tls_o.c new file mode 100644 index 0000000000..8f1d84efa1 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/tls_o.c @@ -0,0 +1,1728 @@ +/* tls_o.c - Handle tls/ssl using OpenSSL */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: Rewritten by Howard Chu + */ + +#include "portable.h" + +#ifdef HAVE_OPENSSL + +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> + +#include "ldap-int.h" +#include "ldap-tls.h" + +#ifdef HAVE_OPENSSL_SSL_H +#include <openssl/ssl.h> +#include <openssl/x509v3.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/safestack.h> +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dh.h> +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +#define ASN1_STRING_data(x) ASN1_STRING_get0_data(x) +#endif + +#if OPENSSL_VERSION_MAJOR >= 3 +#define ERR_get_error_line( a, b ) ERR_get_error_all( a, b, NULL, NULL, NULL ) +#define SSL_get_peer_certificate( s ) SSL_get1_peer_certificate( s ) +#endif +typedef SSL_CTX tlso_ctx; +typedef SSL tlso_session; + +static BIO_METHOD * tlso_bio_method = NULL; +static BIO_METHOD * tlso_bio_setup( void ); + +static int tlso_opt_trace = 1; + +static void tlso_report_error( char *errmsg ); + +static void tlso_info_cb( const SSL *ssl, int where, int ret ); +static int tlso_verify_cb( int ok, X509_STORE_CTX *ctx ); +static int tlso_verify_ok( int ok, X509_STORE_CTX *ctx ); +static int tlso_seed_PRNG( const char *randfile ); +#if OPENSSL_VERSION_NUMBER < 0x10100000 +/* + * OpenSSL 1.1 API and later has new locking code +*/ +static RSA * tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length ); + +#ifdef LDAP_R_COMPILE +/* + * provide mutexes for the OpenSSL library. + */ +static ldap_pvt_thread_mutex_t tlso_mutexes[CRYPTO_NUM_LOCKS]; + +static void tlso_locking_cb( int mode, int type, const char *file, int line ) +{ + if ( mode & CRYPTO_LOCK ) { + ldap_pvt_thread_mutex_lock( &tlso_mutexes[type] ); + } else { + ldap_pvt_thread_mutex_unlock( &tlso_mutexes[type] ); + } +} + +#if OPENSSL_VERSION_NUMBER >= 0x0909000 +static void tlso_thread_self( CRYPTO_THREADID *id ) +{ + CRYPTO_THREADID_set_pointer( id, (void *)ldap_pvt_thread_self() ); +} +#define CRYPTO_set_id_callback(foo) CRYPTO_THREADID_set_callback(foo) +#else +static unsigned long tlso_thread_self( void ) +{ + /* FIXME: CRYPTO_set_id_callback only works when ldap_pvt_thread_t + * is an integral type that fits in an unsigned long + */ + + /* force an error if the ldap_pvt_thread_t type is too large */ + enum { ok = sizeof( ldap_pvt_thread_t ) <= sizeof( unsigned long ) }; + typedef struct { int dummy: ok ? 1 : -1; } Check[ok ? 1 : -1]; + + return (unsigned long) ldap_pvt_thread_self(); +} +#endif + +static void tlso_thr_init( void ) +{ + int i; + + for( i=0; i< CRYPTO_NUM_LOCKS ; i++ ) { + ldap_pvt_thread_mutex_init( &tlso_mutexes[i] ); + } + CRYPTO_set_locking_callback( tlso_locking_cb ); + CRYPTO_set_id_callback( tlso_thread_self ); +} +#endif /* LDAP_R_COMPILE */ +#else +#ifdef LDAP_R_COMPILE +static void tlso_thr_init( void ) {} +#endif +#endif /* OpenSSL 1.1 */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +/* + * OpenSSL 1.1 API and later makes the BIO method concrete types internal. + */ + +static BIO_METHOD * +BIO_meth_new( int type, const char *name ) +{ + BIO_METHOD *method = LDAP_MALLOC( sizeof(BIO_METHOD) ); + memset( method, 0, sizeof(BIO_METHOD) ); + + method->type = type; + method->name = name; + + return method; +} + +static void +BIO_meth_free( BIO_METHOD *meth ) +{ + if ( meth == NULL ) { + return; + } + + LDAP_FREE( meth ); +} + +#define BIO_meth_set_write(m, f) (m)->bwrite = (f) +#define BIO_meth_set_read(m, f) (m)->bread = (f) +#define BIO_meth_set_puts(m, f) (m)->bputs = (f) +#define BIO_meth_set_gets(m, f) (m)->bgets = (f) +#define BIO_meth_set_ctrl(m, f) (m)->ctrl = (f) +#define BIO_meth_set_create(m, f) (m)->create = (f) +#define BIO_meth_set_destroy(m, f) (m)->destroy = (f) + +#endif /* OpenSSL 1.1 */ + +static STACK_OF(X509_NAME) * +tlso_ca_list( char * bundle, char * dir, X509 *cert ) +{ + STACK_OF(X509_NAME) *ca_list = NULL; + + if ( bundle ) { + ca_list = SSL_load_client_CA_file( bundle ); + } + if ( dir ) { + char **dirs = ldap_str2charray( dir, CERTPATHSEP ); + int freeit = 0, i, success = 0; + + if ( !ca_list ) { + ca_list = sk_X509_NAME_new_null(); + freeit = 1; + } + for ( i=0; dirs[i]; i++ ) { + success += SSL_add_dir_cert_subjects_to_stack( ca_list, dir ); + } + if ( !success && freeit ) { + sk_X509_NAME_free( ca_list ); + ca_list = NULL; + } + ldap_charray_free( dirs ); + } + if ( cert ) { + X509_NAME *xn = X509_get_subject_name( cert ); + xn = X509_NAME_dup( xn ); + if ( !ca_list ) + ca_list = sk_X509_NAME_new_null(); + if ( xn && ca_list ) + sk_X509_NAME_push( ca_list, xn ); + } + return ca_list; +} + +/* + * Initialize TLS subsystem. Should be called only once. + */ +static int +tlso_init( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); +#ifdef HAVE_EBCDIC + { + char *file = LDAP_STRDUP( lo->ldo_tls_randfile ); + if ( file ) __atoe( file ); + (void) tlso_seed_PRNG( file ); + LDAP_FREE( file ); + } +#else + (void) tlso_seed_PRNG( lo->ldo_tls_randfile ); +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000 + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_digests(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + + /* FIXME: mod_ssl does this */ + X509V3_add_standard_extensions(); + + tlso_bio_method = tlso_bio_setup(); + + return 0; +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +static void +tlso_destroy( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + + BIO_meth_free( tlso_bio_method ); + +#if OPENSSL_VERSION_NUMBER < 0x10100000 + EVP_cleanup(); + ERR_remove_thread_state(NULL); + ERR_free_strings(); +#endif + + if ( lo->ldo_tls_randfile ) { + LDAP_FREE( lo->ldo_tls_randfile ); + lo->ldo_tls_randfile = NULL; + } +} + +static tls_ctx * +tlso_ctx_new( struct ldapoptions *lo ) +{ + return (tls_ctx *) SSL_CTX_new( SSLv23_method() ); +} + +static void +tlso_ctx_ref( tls_ctx *ctx ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000 +#define SSL_CTX_up_ref(ctx) CRYPTO_add( &(ctx->references), 1, CRYPTO_LOCK_SSL_CTX ) +#endif + SSL_CTX_up_ref( c ); +} + +static void +tlso_ctx_free ( tls_ctx *ctx ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; + SSL_CTX_free( c ); +} + +#if OPENSSL_VERSION_NUMBER >= 0x10101000 +static char * +tlso_stecpy( char *dst, const char *src, const char *end ) +{ + while ( dst < end && *src ) + *dst++ = *src++; + if ( dst < end ) + *dst = '\0'; + return dst; +} + +/* OpenSSL 1.1.1 uses a separate API for TLS1.3 ciphersuites. + * Try to find any TLS1.3 ciphers in the given list of suites. + */ +static void +tlso_ctx_cipher13( tlso_ctx *ctx, char *suites ) +{ + char tls13_suites[1024], *ts = tls13_suites, *te = tls13_suites + sizeof(tls13_suites); + char *ptr, *colon, *nptr; + char sname[128]; + STACK_OF(SSL_CIPHER) *cs; + SSL *s = SSL_new( ctx ); + int ret; + + if ( !s ) + return; + + *ts = '\0'; + + /* check individual suites in a separate SSL handle before + * mucking with the provided ctx. Init it to a known + * mostly-empty state. + */ + SSL_set_ciphersuites( s, "" ); + SSL_set_cipher_list( s, SSL3_TXT_RSA_NULL_SHA ); + + for ( ptr = suites;; ) { + colon = strchr( ptr, ':' ); + if ( colon ) { + int len = colon - ptr; + if ( len > 63 ) len = 63; + strncpy( sname, ptr, len ); + sname[len] = '\0'; + nptr = sname; + } else { + nptr = ptr; + } + if ( SSL_set_ciphersuites( s, nptr )) { + cs = SSL_get_ciphers( s ); + if ( cs ) { + const char *ver = SSL_CIPHER_get_version( sk_SSL_CIPHER_value( cs, 0 )); + if ( !strncmp( ver, "TLSv", 4 ) && strncmp( ver+4, "1.3", 3 ) >= 0 ) { + if ( tls13_suites[0] ) + ts = tlso_stecpy( ts, ":", te ); + ts = tlso_stecpy( ts, nptr, te ); + } + } + } + if ( !colon || ts >= te ) + break; + ptr = colon+1; + } + SSL_free( s ); + + /* If no TLS1.3 ciphersuites were specified, leave current settings untouched. */ + if ( tls13_suites[0] ) + SSL_CTX_set_ciphersuites( ctx, tls13_suites ); +} +#endif /* OpenSSL 1.1.1 */ + +/* + * initialize a new TLS context + */ +static int +tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server, char *errmsg ) +{ + tlso_ctx *ctx = (tlso_ctx *)lo->ldo_tls_ctx; + int i; + + if ( is_server ) { + SSL_CTX_set_session_id_context( ctx, + (const unsigned char *) "OpenLDAP", sizeof("OpenLDAP")-1 ); + } + + if ( lo->ldo_tls_protocol_min ) { + int opt = 0; + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) { + opt |= SSL_OP_NO_SSLv2; + SSL_CTX_clear_options( ctx, SSL_OP_NO_SSLv3 ); + } + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) + opt |= SSL_OP_NO_SSLv3; +#ifdef SSL_OP_NO_TLSv1 + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 ) + opt |= SSL_OP_NO_TLSv1; +#endif +#ifdef SSL_OP_NO_TLSv1_1 + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ) + opt |= SSL_OP_NO_TLSv1_1; +#endif +#ifdef SSL_OP_NO_TLSv1_2 + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ) + opt |= SSL_OP_NO_TLSv1_2; +#endif +#ifdef SSL_OP_NO_TLSv1_3 + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 ) + opt |= SSL_OP_NO_TLSv1_3; +#endif + if ( opt ) + SSL_CTX_set_options( ctx, opt ); + } + if ( lo->ldo_tls_protocol_max ) { + int opt = 0; +#ifdef SSL_OP_NO_TLSv1_3 + if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 ) + opt |= SSL_OP_NO_TLSv1_3; +#endif +#ifdef SSL_OP_NO_TLSv1_2 + if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ) + opt |= SSL_OP_NO_TLSv1_2; +#endif +#ifdef SSL_OP_NO_TLSv1_1 + if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ) + opt |= SSL_OP_NO_TLSv1_1; +#endif +#ifdef SSL_OP_NO_TLSv1 + if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 ) + opt |= SSL_OP_NO_TLSv1; +#endif + if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) + opt |= SSL_OP_NO_SSLv3; + if ( opt ) + SSL_CTX_set_options( ctx, opt ); + } + + if ( lo->ldo_tls_ciphersuite ) { +#if OPENSSL_VERSION_NUMBER >= 0x10101000 + tlso_ctx_cipher13( ctx, lt->lt_ciphersuite ); +#endif + if ( !SSL_CTX_set_cipher_list( ctx, lt->lt_ciphersuite ) ) + { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not set cipher list %s.\n", + lo->ldo_tls_ciphersuite ); + tlso_report_error( errmsg ); + return -1; + } + } + + if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL && + lo->ldo_tls_cacert.bv_val == NULL ) { + if ( !SSL_CTX_set_default_verify_paths( ctx ) ) { + Debug0( LDAP_DEBUG_ANY, "TLS: " + "could not use default certificate paths" ); + tlso_report_error( errmsg ); + return -1; + } + } else { + X509 *cert = NULL; + if ( lo->ldo_tls_cacert.bv_val ) { + const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_cacert.bv_val); + cert = d2i_X509( NULL, &pp, lo->ldo_tls_cacert.bv_len ); + X509_STORE *store = SSL_CTX_get_cert_store( ctx ); + if ( !X509_STORE_add_cert( store, cert )) { + Debug0( LDAP_DEBUG_ANY, "TLS: " + "could not use CA certificate" ); + tlso_report_error( errmsg ); + return -1; + } + } + if ( lt->lt_cacertfile || lt->lt_cacertdir ) { + char **dirs, *dummy = NULL; + if ( lt->lt_cacertdir ) { + dirs = ldap_str2charray( lt->lt_cacertdir, CERTPATHSEP ); + } else { + dirs = &dummy; + } + /* Start with the first dir in path */ + if ( !SSL_CTX_load_verify_locations( ctx, + lt->lt_cacertfile, dirs[0] ) ) + { + Debug2( LDAP_DEBUG_ANY, "TLS: " + "could not load verify locations (file:`%s',dir:`%s').\n", + lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "", + dirs[0] ? dirs[0] : "" ); + tlso_report_error( errmsg ); + if ( dirs != &dummy ) + ldap_charray_free( dirs ); + return -1; + } + /* Then additional dirs, if any */ + if ( dirs != &dummy ) { + if ( dirs[1] ) { + int i; + X509_STORE *store = SSL_CTX_get_cert_store( ctx ); + X509_LOOKUP *lookup = X509_STORE_add_lookup( store, X509_LOOKUP_hash_dir() ); + for ( i=1; dirs[i]; i++ ) + X509_LOOKUP_add_dir( lookup, dirs[i], X509_FILETYPE_PEM ); + } + ldap_charray_free( dirs ); + } + } + + if ( is_server ) { + STACK_OF(X509_NAME) *calist; + /* List of CA names to send to a client */ + calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir, cert ); + if ( !calist ) { + Debug2( LDAP_DEBUG_ANY, "TLS: " + "could not load client CA list (file:`%s',dir:`%s').\n", + lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "", + lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "" ); + tlso_report_error( errmsg ); + return -1; + } + + SSL_CTX_set_client_CA_list( ctx, calist ); + } + if ( cert ) + X509_free( cert ); + } + + if ( lo->ldo_tls_cert.bv_val ) + { + const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_cert.bv_val); + X509 *cert = d2i_X509( NULL, &pp, lo->ldo_tls_cert.bv_len ); + if ( !SSL_CTX_use_certificate( ctx, cert )) { + Debug0( LDAP_DEBUG_ANY, + "TLS: could not use certificate.\n" ); + tlso_report_error( errmsg ); + return -1; + } + X509_free( cert ); + } else + if ( lo->ldo_tls_certfile && + !SSL_CTX_use_certificate_chain_file( ctx, lt->lt_certfile) ) + { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not use certificate file `%s'.\n", + lo->ldo_tls_certfile ); + tlso_report_error( errmsg ); + return -1; + } + + /* Key validity is checked automatically if cert has already been set */ + if ( lo->ldo_tls_key.bv_val ) + { + const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_key.bv_val); + EVP_PKEY *pkey = d2i_AutoPrivateKey( NULL, &pp, lo->ldo_tls_key.bv_len ); + if ( !SSL_CTX_use_PrivateKey( ctx, pkey )) + { + Debug0( LDAP_DEBUG_ANY, + "TLS: could not use private key.\n" ); + tlso_report_error( errmsg ); + return -1; + } + EVP_PKEY_free( pkey ); + } else + if ( lo->ldo_tls_keyfile && + !SSL_CTX_use_PrivateKey_file( ctx, + lt->lt_keyfile, SSL_FILETYPE_PEM ) ) + { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not use key file `%s'.\n", + lo->ldo_tls_keyfile ); + tlso_report_error( errmsg ); + return -1; + } + + if ( is_server && lo->ldo_tls_dhfile ) { +#if OPENSSL_VERSION_MAJOR >= 3 + EVP_PKEY *dh; +#define bio_params( bio, dh ) dh = PEM_read_bio_Parameters( bio, &dh ) +#else + DH *dh; +#define bio_params( bio, dh ) dh = PEM_read_bio_DHparams( bio, NULL, NULL, NULL ) +#endif + BIO *bio; + + if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not use DH parameters file `%s'.\n", + lo->ldo_tls_dhfile ); + tlso_report_error( errmsg ); + return -1; + } + if (!( bio_params( bio, dh ))) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not read DH parameters file `%s'.\n", + lo->ldo_tls_dhfile ); + tlso_report_error( errmsg ); + BIO_free( bio ); + return -1; + } + BIO_free( bio ); +#if OPENSSL_VERSION_MAJOR >= 3 + SSL_CTX_set0_tmp_dh_pkey( ctx, dh ); +#else + SSL_CTX_set_tmp_dh( ctx, dh ); + SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE ); + DH_free( dh ); +#endif + } + + if ( lo->ldo_tls_ecname ) { +#ifdef OPENSSL_NO_EC + Debug0( LDAP_DEBUG_ANY, + "TLS: Elliptic Curves not supported.\n" ); + return -1; +#else + if ( !SSL_CTX_set1_curves_list( ctx, lt->lt_ecname )) { + Debug1( LDAP_DEBUG_ANY, + "TLS: could not set EC name `%s'.\n", + lo->ldo_tls_ecname ); + tlso_report_error( errmsg ); + return -1; + } + /* + * This is a NOP in OpenSSL 1.1.0 and later, where curves are always + * auto-negotiated. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000UL + if ( SSL_CTX_set_ecdh_auto( ctx, 1 ) <= 0 ) { + Debug0( LDAP_DEBUG_ANY, + "TLS: could not enable automatic EC negotiation.\n" ); + } +#endif +#endif /* OPENSSL_NO_EC */ + } + + if ( tlso_opt_trace ) { + SSL_CTX_set_info_callback( ctx, tlso_info_cb ); + } + + i = SSL_VERIFY_NONE; + if ( lo->ldo_tls_require_cert ) { + i = SSL_VERIFY_PEER; + if ( lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_DEMAND || + lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_HARD ) { + i |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + } + + SSL_CTX_set_verify( ctx, i, + lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW ? + tlso_verify_ok : tlso_verify_cb ); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + SSL_CTX_set_tmp_rsa_callback( ctx, tlso_tmp_rsa_cb ); +#endif + if ( lo->ldo_tls_crlcheck ) { + X509_STORE *x509_s = SSL_CTX_get_cert_store( ctx ); + if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_PEER ) { + X509_STORE_set_flags( x509_s, X509_V_FLAG_CRL_CHECK ); + } else if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_ALL ) { + X509_STORE_set_flags( x509_s, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); + } + } + /* Explicitly honor the server side cipher suite preference */ + SSL_CTX_set_options( ctx, SSL_OP_CIPHER_SERVER_PREFERENCE ); + return 0; +} + +static tls_session * +tlso_session_new( tls_ctx *ctx, int is_server ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; + return (tls_session *)SSL_new( c ); +} + +static int +tlso_session_connect( LDAP *ld, tls_session *sess, const char *name_in ) +{ + tlso_session *s = (tlso_session *)sess; + int rc; + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if ( name_in ) { + rc = SSL_set_tlsext_host_name( s, name_in ); + if ( !rc ) /* can fail to strdup the name */ + return -1; + } +#endif + /* Caller expects 0 = success, OpenSSL returns 1 = success */ + rc = SSL_connect( s ) - 1; + return rc; +} + +static int +tlso_session_accept( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + + /* Caller expects 0 = success, OpenSSL returns 1 = success */ + return SSL_accept( s ) - 1; +} + +static int +tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc ) +{ + tlso_session *s = (tlso_session *)sess; + + /* 1 was subtracted above, offset it back now */ + rc = SSL_get_error(s, rc+1); + if (rc == SSL_ERROR_WANT_READ) { + sb->sb_trans_needs_read = 1; + return 1; + + } else if (rc == SSL_ERROR_WANT_WRITE) { + sb->sb_trans_needs_write = 1; + return 1; + + } else if (rc == SSL_ERROR_WANT_CONNECT) { + return 1; + } + return 0; +} + +static char * +tlso_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) +{ + char err[256] = ""; + const char *certerr=NULL; + tlso_session *s = (tlso_session *)sess; + + rc = ERR_peek_error(); + if ( rc ) { + ERR_error_string_n( rc, err, sizeof(err) ); + if ( ( ERR_GET_LIB(rc) == ERR_LIB_SSL ) && + ( ERR_GET_REASON(rc) == SSL_R_CERTIFICATE_VERIFY_FAILED ) ) { + int certrc = SSL_get_verify_result(s); + certerr = (char *)X509_verify_cert_error_string(certrc); + } + snprintf(buf, len, "%s%s%s%s", err, certerr ? " (" :"", + certerr ? certerr : "", certerr ? ")" : "" ); + return buf; + } + return NULL; +} + +static int +tlso_session_my_dn( tls_session *sess, struct berval *der_dn ) +{ + tlso_session *s = (tlso_session *)sess; + X509 *x; + X509_NAME *xn; + + x = SSL_get_certificate( s ); + + if (!x) return LDAP_INVALID_CREDENTIALS; + + xn = X509_get_subject_name(x); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + der_dn->bv_len = i2d_X509_NAME( xn, NULL ); + der_dn->bv_val = xn->bytes->data; +#else + { + size_t len = 0; + der_dn->bv_val = NULL; + X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len ); + der_dn->bv_len = len; + } +#endif + /* Don't X509_free, the session is still using it */ + return 0; +} + +static X509 * +tlso_get_cert( SSL *s ) +{ + /* If peer cert was bad, treat as if no cert was given */ + if (SSL_get_verify_result(s)) { + return NULL; + } + return SSL_get_peer_certificate(s); +} + +static int +tlso_session_peer_dn( tls_session *sess, struct berval *der_dn ) +{ + tlso_session *s = (tlso_session *)sess; + X509 *x = tlso_get_cert( s ); + X509_NAME *xn; + + if ( !x ) + return LDAP_INVALID_CREDENTIALS; + + xn = X509_get_subject_name(x); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + der_dn->bv_len = i2d_X509_NAME( xn, NULL ); + der_dn->bv_val = xn->bytes->data; +#else + { + size_t len = 0; + der_dn->bv_val = NULL; + X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len ); + der_dn->bv_len = len; + } +#endif + X509_free(x); + return 0; +} + +/* what kind of hostname were we given? */ +#define IS_DNS 0 +#define IS_IP4 1 +#define IS_IP6 2 + +static int +tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) +{ + tlso_session *s = (tlso_session *)sess; + int i, ret = LDAP_LOCAL_ERROR; + int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0; + X509 *x; + const char *name; + char *ptr; + int ntype = IS_DNS, nlen; +#ifdef LDAP_PF_INET6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + if( ldap_int_hostname && + ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) + { + name = ldap_int_hostname; + } else { + name = name_in; + } + nlen = strlen(name); + + x = tlso_get_cert(s); + if (!x) { + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get peer certificate.\n" ); + /* If this was a fatal condition, things would have + * aborted long before now. + */ + return LDAP_SUCCESS; + } + +#ifdef LDAP_PF_INET6 + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; + } else +#endif + if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { + if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; + } + + if (chkSAN) { + i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1); + if (i >= 0) { + X509_EXTENSION *ex; + STACK_OF(GENERAL_NAME) *alt; + + ex = X509_get_ext(x, i); + alt = X509V3_EXT_d2i(ex); + if (alt) { + int n, len2 = 0; + char *domain = NULL; + GENERAL_NAME *gn; + + gotSAN = 1; + if (ntype == IS_DNS) { + domain = strchr(name, '.'); + if (domain) { + len2 = nlen - (domain-name); + } + } + n = sk_GENERAL_NAME_num(alt); + for (i=0; i<n; i++) { + char *sn; + int sl; + gn = sk_GENERAL_NAME_value(alt, i); + if (gn->type == GEN_DNS) { + if (ntype != IS_DNS) continue; + + sn = (char *) ASN1_STRING_data(gn->d.ia5); + sl = ASN1_STRING_length(gn->d.ia5); + + /* ignore empty */ + if (sl == 0) continue; + + /* Is this an exact match? */ + if ((nlen == sl) && !strncasecmp(name, sn, nlen)) { + break; + } + + /* Is this a wildcard match? */ + if (domain && (sn[0] == '*') && (sn[1] == '.') && + (len2 == sl-1) && !strncasecmp(domain, &sn[1], len2)) + { + break; + } + + } else if (gn->type == GEN_IPADD) { + if (ntype == IS_DNS) continue; + + sn = (char *) ASN1_STRING_data(gn->d.ia5); + sl = ASN1_STRING_length(gn->d.ia5); + +#ifdef LDAP_PF_INET6 + if (ntype == IS_IP6 && sl != sizeof(struct in6_addr)) { + continue; + } else +#endif + if (ntype == IS_IP4 && sl != sizeof(struct in_addr)) { + continue; + } + if (!memcmp(sn, &addr, sl)) { + break; + } + } + } + + GENERAL_NAMES_free(alt); + if (i < n) { /* Found a match */ + ret = LDAP_SUCCESS; + } + } + } + } + if (ret != LDAP_SUCCESS && chkSAN) { + switch(chkSAN) { + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_HARD: + if (!gotSAN) { + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get subjectAltName from peer certificate.\n" ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get subjectAltName from peer certificate")); + goto done; + } + /* FALLTHRU */ + case LDAP_OPT_X_TLS_TRY: + if (gotSAN) { + Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "subjectAltName in certificate.\n", + name ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match subjectAltName in peer certificate")); + goto done; + } + break; + case LDAP_OPT_X_TLS_ALLOW: + break; + } + } + + if (ret != LDAP_SUCCESS) { + X509_NAME *xn; + X509_NAME_ENTRY *ne; + ASN1_OBJECT *obj; + ASN1_STRING *cn = NULL; + int navas; + + /* find the last CN */ + obj = OBJ_nid2obj( NID_commonName ); + if ( !obj ) goto no_cn; /* should never happen */ + + xn = X509_get_subject_name(x); + navas = X509_NAME_entry_count( xn ); + for ( i=navas-1; i>=0; i-- ) { + ne = X509_NAME_get_entry( xn, i ); + if ( !OBJ_cmp( X509_NAME_ENTRY_get_object(ne), obj )) { + cn = X509_NAME_ENTRY_get_data( ne ); + break; + } + } + + if( !cn ) + { +no_cn: + Debug0( LDAP_DEBUG_ANY, + "TLS: unable to get common name from peer certificate.\n" ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get CN from peer certificate")); + + } else if ( cn->length == nlen && + strncasecmp( name, (char *) cn->data, nlen ) == 0 ) { + ret = LDAP_SUCCESS; + + } else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) { + char *domain = strchr(name, '.'); + if( domain ) { + int dlen; + + dlen = nlen - (domain-name); + + /* Is this a wildcard match? */ + if ((dlen == cn->length-1) && + !strncasecmp(domain, (char *) &cn->data[1], dlen)) { + ret = LDAP_SUCCESS; + } + } + } + + if( ret == LDAP_LOCAL_ERROR ) { + Debug3( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "common name in certificate (%.*s).\n", + name, cn->length, cn->data ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match name in peer certificate")); + } + } +done: + X509_free(x); + return ret; +} + +static int +tlso_session_strength( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + + return SSL_CIPHER_get_bits(SSL_get_current_cipher(s), NULL); +} + +static int +tlso_session_unique( tls_session *sess, struct berval *buf, int is_server) +{ + tlso_session *s = (tlso_session *)sess; + + /* Usually the client sends the finished msg. But if the + * session was resumed, the server sent the msg. + */ + if (SSL_session_reused(s) ^ !is_server) + buf->bv_len = SSL_get_finished(s, buf->bv_val, buf->bv_len); + else + buf->bv_len = SSL_get_peer_finished(s, buf->bv_val, buf->bv_len); + return buf->bv_len; +} + +static int +tlso_session_endpoint( tls_session *sess, struct berval *buf, int is_server ) +{ + tlso_session *s = (tlso_session *)sess; + const EVP_MD *md; + unsigned int md_len; + X509 *cert; + + if ( buf->bv_len < EVP_MAX_MD_SIZE ) + return 0; + + if ( is_server ) + cert = SSL_get_certificate( s ); + else + cert = SSL_get_peer_certificate( s ); + + if ( cert == NULL ) + return 0; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + md = EVP_get_digestbynid( X509_get_signature_nid( cert )); +#else + md = EVP_get_digestbynid(OBJ_obj2nid( cert->sig_alg->algorithm )); +#endif + + /* See RFC 5929 */ + if ( md == NULL || + md == EVP_md_null() || +#ifndef OPENSSL_NO_MD2 + md == EVP_md2() || +#endif +#ifndef OPENSSL_NO_MD4 + md == EVP_md4() || +#endif +#ifndef OPENSSL_NO_MD5 + md == EVP_md5() || +#endif + md == EVP_sha1() ) + md = EVP_sha256(); + + if ( !X509_digest( cert, md, (unsigned char *) (buf->bv_val), &md_len )) + md_len = 0; + + buf->bv_len = md_len; + if ( !is_server ) + X509_free( cert ); + + return md_len; +} + +static const char * +tlso_session_version( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + return SSL_get_version(s); +} + +static const char * +tlso_session_cipher( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + return SSL_CIPHER_get_name(SSL_get_current_cipher(s)); +} + +static int +tlso_session_peercert( tls_session *sess, struct berval *der ) +{ + tlso_session *s = (tlso_session *)sess; + int ret = -1; + X509 *x = SSL_get_peer_certificate(s); + if ( x ) { + der->bv_len = i2d_X509(x, NULL); + der->bv_val = LDAP_MALLOC(der->bv_len); + if ( der->bv_val ) { + unsigned char *ptr = (unsigned char *) (der->bv_val); + i2d_X509(x, &ptr); + ret = 0; + } + X509_free( x ); + } + return ret; +} + +static int +tlso_session_pinning( LDAP *ld, tls_session *sess, char *hashalg, struct berval *hash ) +{ + tlso_session *s = (tlso_session *)sess; + unsigned char *tmp, digest[EVP_MAX_MD_SIZE]; + struct berval key, + keyhash = { sizeof(digest), (char *) digest }; + X509 *cert = SSL_get_peer_certificate(s); + int len, rc = LDAP_SUCCESS; + + if ( !cert ) + return -1; + + len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), NULL ); + + tmp = LDAP_MALLOC( len ); + key.bv_val = (char *) tmp; + + if ( !key.bv_val ) { + rc = -1; + goto done; + } + + key.bv_len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), &tmp ); + + if ( hashalg ) { + const EVP_MD *md; + EVP_MD_CTX *mdctx; + unsigned int len = keyhash.bv_len; + + md = EVP_get_digestbyname( hashalg ); + if ( !md ) { + Debug1( LDAP_DEBUG_TRACE, "tlso_session_pinning: " + "hash %s not recognised by OpenSSL\n", hashalg ); + rc = -1; + goto done; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + mdctx = EVP_MD_CTX_new(); +#else + mdctx = EVP_MD_CTX_create(); +#endif + if ( !mdctx ) { + rc = -1; + goto done; + } + + EVP_DigestInit_ex( mdctx, md, NULL ); + EVP_DigestUpdate( mdctx, key.bv_val, key.bv_len ); + EVP_DigestFinal_ex( mdctx, (unsigned char *)keyhash.bv_val, &len ); + keyhash.bv_len = len; +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + EVP_MD_CTX_free( mdctx ); +#else + EVP_MD_CTX_destroy( mdctx ); +#endif + } else { + keyhash = key; + } + + if ( ber_bvcmp( hash, &keyhash ) ) { + rc = LDAP_CONNECT_ERROR; + Debug0( LDAP_DEBUG_ANY, "tlso_session_pinning: " + "public key hash does not match provided pin.\n" ); + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: public key hash does not match provided pin")); + } + +done: + LDAP_FREE( key.bv_val ); + X509_free( cert ); + return rc; +} + +/* + * TLS support for LBER Sockbufs + */ + +struct tls_data { + tlso_session *session; + Sockbuf_IO_Desc *sbiod; +}; + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +#define BIO_set_init(b, x) b->init = x +#define BIO_set_data(b, x) b->ptr = x +#define BIO_clear_flags(b, x) b->flags &= ~(x) +#define BIO_get_data(b) b->ptr +#endif +static int +tlso_bio_create( BIO *b ) { + BIO_set_init( b, 1 ); + BIO_set_data( b, NULL ); + BIO_clear_flags( b, ~0 ); + return 1; +} + +static int +tlso_bio_destroy( BIO *b ) +{ + if ( b == NULL ) return 0; + + BIO_set_data( b, NULL ); /* sb_tls_remove() will free it */ + BIO_set_init( b, 0 ); + BIO_clear_flags( b, ~0 ); + return 1; +} + +static int +tlso_bio_read( BIO *b, char *buf, int len ) +{ + struct tls_data *p; + int ret; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)BIO_get_data(b); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + ret = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len ); + + BIO_clear_retry_flags( b ); + if ( ret < 0 ) { + int err = sock_errno(); + if ( err == EAGAIN || err == EWOULDBLOCK ) { + BIO_set_retry_read( b ); + } + } + + return ret; +} + +static int +tlso_bio_write( BIO *b, const char *buf, int len ) +{ + struct tls_data *p; + int ret; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)BIO_get_data(b); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + ret = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len ); + + BIO_clear_retry_flags( b ); + if ( ret < 0 ) { + int err = sock_errno(); + if ( err == EAGAIN || err == EWOULDBLOCK ) { + BIO_set_retry_write( b ); + } + } + + return ret; +} + +static long +tlso_bio_ctrl( BIO *b, int cmd, long num, void *ptr ) +{ + if ( cmd == BIO_CTRL_FLUSH ) { + /* The OpenSSL library needs this */ + return 1; + } + return 0; +} + +static int +tlso_bio_gets( BIO *b, char *buf, int len ) +{ + return -1; +} + +static int +tlso_bio_puts( BIO *b, const char *str ) +{ + return tlso_bio_write( b, str, strlen( str ) ); +} + +static BIO_METHOD * +tlso_bio_setup( void ) +{ + /* it's a source/sink BIO */ + BIO_METHOD * method = BIO_meth_new( 100 | 0x400, "sockbuf glue" ); + BIO_meth_set_write( method, tlso_bio_write ); + BIO_meth_set_read( method, tlso_bio_read ); + BIO_meth_set_puts( method, tlso_bio_puts ); + BIO_meth_set_gets( method, tlso_bio_gets ); + BIO_meth_set_ctrl( method, tlso_bio_ctrl ); + BIO_meth_set_create( method, tlso_bio_create ); + BIO_meth_set_destroy( method, tlso_bio_destroy ); + + return method; +} + +static int +tlso_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct tls_data *p; + BIO *bio; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) { + return -1; + } + + p->session = arg; + p->sbiod = sbiod; + bio = BIO_new( tlso_bio_method ); + BIO_set_data( bio, p ); + SSL_set_bio( p->session, bio, bio ); + sbiod->sbiod_pvt = p; + return 0; +} + +static int +tlso_sb_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + SSL_free( p->session ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +tlso_sb_close( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + SSL_shutdown( p->session ); + return 0; +} + +static int +tlso_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_GET_SSL ) { + *((tlso_session **)arg) = p->session; + return 1; + + } else if ( opt == LBER_SB_OPT_DATA_READY ) { + if( SSL_pending( p->session ) > 0 ) { + return 1; + } + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +tlso_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = SSL_read( p->session, (char *)buf, len ); +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + err = SSL_get_error( p->session, ret ); + if (err == SSL_ERROR_WANT_READ ) { + sbiod->sbiod_sb->sb_trans_needs_read = 1; + sock_errset(EWOULDBLOCK); + } + else + sbiod->sbiod_sb->sb_trans_needs_read = 0; + return ret; +} + +static ber_slen_t +tlso_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = SSL_write( p->session, (char *)buf, len ); +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + err = SSL_get_error( p->session, ret ); + if (err == SSL_ERROR_WANT_WRITE ) { + sbiod->sbiod_sb->sb_trans_needs_write = 1; + sock_errset(EWOULDBLOCK); + + } else { + sbiod->sbiod_sb->sb_trans_needs_write = 0; + } + return ret; +} + +static Sockbuf_IO tlso_sbio = +{ + tlso_sb_setup, /* sbi_setup */ + tlso_sb_remove, /* sbi_remove */ + tlso_sb_ctrl, /* sbi_ctrl */ + tlso_sb_read, /* sbi_read */ + tlso_sb_write, /* sbi_write */ + tlso_sb_close /* sbi_close */ +}; + +/* Derived from openssl/apps/s_cb.c */ +static void +tlso_info_cb( const SSL *ssl, int where, int ret ) +{ + int w; + char *op; + char *state = (char *) SSL_state_string_long( (SSL *)ssl ); + + w = where & ~SSL_ST_MASK; + if ( w & SSL_ST_CONNECT ) { + op = "SSL_connect"; + } else if ( w & SSL_ST_ACCEPT ) { + op = "SSL_accept"; + } else { + op = "undefined"; + } + +#ifdef HAVE_EBCDIC + if ( state ) { + state = LDAP_STRDUP( state ); + __etoa( state ); + } +#endif + if ( where & SSL_CB_LOOP ) { + Debug2( LDAP_DEBUG_TRACE, + "TLS trace: %s:%s\n", + op, state ); + + } else if ( where & SSL_CB_ALERT ) { + char *atype = (char *) SSL_alert_type_string_long( ret ); + char *adesc = (char *) SSL_alert_desc_string_long( ret ); + op = ( where & SSL_CB_READ ) ? "read" : "write"; +#ifdef HAVE_EBCDIC + if ( atype ) { + atype = LDAP_STRDUP( atype ); + __etoa( atype ); + } + if ( adesc ) { + adesc = LDAP_STRDUP( adesc ); + __etoa( adesc ); + } +#endif + Debug3( LDAP_DEBUG_TRACE, + "TLS trace: SSL3 alert %s:%s:%s\n", + op, atype, adesc ); +#ifdef HAVE_EBCDIC + if ( atype ) LDAP_FREE( atype ); + if ( adesc ) LDAP_FREE( adesc ); +#endif + } else if ( where & SSL_CB_EXIT ) { + if ( ret == 0 ) { + Debug2( LDAP_DEBUG_TRACE, + "TLS trace: %s:failed in %s\n", + op, state ); + } else if ( ret < 0 ) { + Debug2( LDAP_DEBUG_TRACE, + "TLS trace: %s:error in %s\n", + op, state ); + } + } +#ifdef HAVE_EBCDIC + if ( state ) LDAP_FREE( state ); +#endif +} + +static int +tlso_verify_cb( int ok, X509_STORE_CTX *ctx ) +{ + X509 *cert; + int errnum; + int errdepth; + X509_NAME *subject; + X509_NAME *issuer; + char *sname; + char *iname; + char *certerr = NULL; + + cert = X509_STORE_CTX_get_current_cert( ctx ); + errnum = X509_STORE_CTX_get_error( ctx ); + errdepth = X509_STORE_CTX_get_error_depth( ctx ); + + /* + * X509_get_*_name return pointers to the internal copies of + * those things requested. So do not free them. + */ + subject = X509_get_subject_name( cert ); + issuer = X509_get_issuer_name( cert ); + /* X509_NAME_oneline, if passed a NULL buf, allocate memory */ + sname = X509_NAME_oneline( subject, NULL, 0 ); + iname = X509_NAME_oneline( issuer, NULL, 0 ); + if ( !ok ) certerr = (char *)X509_verify_cert_error_string( errnum ); +#ifdef HAVE_EBCDIC + if ( sname ) __etoa( sname ); + if ( iname ) __etoa( iname ); + if ( certerr ) { + certerr = LDAP_STRDUP( certerr ); + __etoa( certerr ); + } +#endif + Debug3( LDAP_DEBUG_TRACE, + "TLS certificate verification: depth: %d, err: %d, subject: %s,", + errdepth, errnum, + sname ? sname : "-unknown-" ); + Debug1( LDAP_DEBUG_TRACE, " issuer: %s\n", iname ? iname : "-unknown-" ); + if ( !ok ) { + Debug1( LDAP_DEBUG_ANY, + "TLS certificate verification: Error, %s\n", + certerr ); + } + if ( sname ) + OPENSSL_free ( sname ); + if ( iname ) + OPENSSL_free ( iname ); +#ifdef HAVE_EBCDIC + if ( certerr ) LDAP_FREE( certerr ); +#endif + return ok; +} + +static int +tlso_verify_ok( int ok, X509_STORE_CTX *ctx ) +{ + (void) tlso_verify_cb( ok, ctx ); + return 1; +} + +/* Inspired by ERR_print_errors in OpenSSL */ +static void +tlso_report_error( char *errmsg ) +{ + unsigned long l; + char buf[ERRBUFSIZE]; + const char *file; + int line; + + while ( ( l = ERR_get_error_line( &file, &line ) ) != 0 ) { + ERR_error_string_n( l, buf, ERRBUFSIZE ); + if ( !*errmsg ) + strcpy(errmsg, buf ); +#ifdef HAVE_EBCDIC + if ( file ) { + file = LDAP_STRDUP( file ); + __etoa( (char *)file ); + } + __etoa( buf ); +#endif + Debug3( LDAP_DEBUG_ANY, "TLS: %s %s:%d\n", + buf, file, line ); +#ifdef HAVE_EBCDIC + if ( file ) LDAP_FREE( (void *)file ); +#endif + } +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +static RSA * +tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length ) +{ + RSA *tmp_rsa; + /* FIXME: Pregenerate the key on startup */ + /* FIXME: Who frees the key? */ + BIGNUM *bn = BN_new(); + tmp_rsa = NULL; + if ( bn ) { + if ( BN_set_word( bn, RSA_F4 )) { + tmp_rsa = RSA_new(); + if ( tmp_rsa && !RSA_generate_key_ex( tmp_rsa, key_length, bn, NULL )) { + RSA_free( tmp_rsa ); + tmp_rsa = NULL; + } + } + BN_free( bn ); + } + + if ( !tmp_rsa ) { + Debug2( LDAP_DEBUG_ANY, + "TLS: Failed to generate temporary %d-bit %s RSA key\n", + key_length, is_export ? "export" : "domestic" ); + } + return tmp_rsa; +} +#endif /* OPENSSL_VERSION_NUMBER < 1.1 */ + +static int +tlso_seed_PRNG( const char *randfile ) +{ +#ifndef URANDOM_DEVICE + /* no /dev/urandom (or equiv) */ + long total=0; + char buffer[MAXPATHLEN]; + + if (randfile == NULL) { + /* The seed file is $RANDFILE if defined, otherwise $HOME/.rnd. + * If $HOME is not set or buffer too small to hold the pathname, + * an error occurs. - From RAND_file_name() man page. + * The fact is that when $HOME is NULL, .rnd is used. + */ + randfile = RAND_file_name( buffer, sizeof( buffer ) ); + } +#ifndef OPENSSL_NO_EGD + else if (RAND_egd(randfile) > 0) { + /* EGD socket */ + return 0; + } +#endif + + if (randfile == NULL) { + Debug0( LDAP_DEBUG_ANY, + "TLS: Use configuration file or $RANDFILE to define seed PRNG\n" ); + return -1; + } + + total = RAND_load_file(randfile, -1); + + if (RAND_status() == 0) { + Debug0( LDAP_DEBUG_ANY, + "TLS: PRNG not been seeded with enough data\n" ); + return -1; + } + + /* assume if there was enough bits to seed that it's okay + * to write derived bits to the file + */ + RAND_write_file(randfile); + +#endif + + return 0; +} + + +tls_impl ldap_int_tls_impl = { + "OpenSSL", + + tlso_init, + tlso_destroy, + + tlso_ctx_new, + tlso_ctx_ref, + tlso_ctx_free, + tlso_ctx_init, + + tlso_session_new, + tlso_session_connect, + tlso_session_accept, + tlso_session_upflags, + tlso_session_errmsg, + tlso_session_my_dn, + tlso_session_peer_dn, + tlso_session_chkhost, + tlso_session_strength, + tlso_session_unique, + tlso_session_endpoint, + tlso_session_version, + tlso_session_cipher, + tlso_session_peercert, + tlso_session_pinning, + + &tlso_sbio, + +#ifdef LDAP_R_COMPILE + tlso_thr_init, +#else + NULL, +#endif + + 0 +}; + +#endif /* HAVE_OPENSSL */ diff --git a/contrib/libs/openldap/libraries/libldap/tpool.c b/contrib/libs/openldap/libraries/libldap/tpool.c new file mode 100644 index 0000000000..f3b0da8ca2 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/tpool.c @@ -0,0 +1,1495 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/signal.h> +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> + +#include "ldap-int.h" + +#ifdef LDAP_R_COMPILE + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#include "ldap_queue.h" +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename symbols defined below */ + +#ifndef LDAP_THREAD_HAVE_TPOOL + +#ifndef CACHELINE +#define CACHELINE 64 +#endif + +/* Thread-specific key with data and optional free function */ +typedef struct ldap_int_tpool_key_s { + void *ltk_key; + void *ltk_data; + ldap_pvt_thread_pool_keyfree_t *ltk_free; +} ldap_int_tpool_key_t; + +/* Max number of thread-specific keys we store per thread. + * We don't expect to use many... + */ +#define MAXKEYS 32 + +/* Max number of threads */ +#define LDAP_MAXTHR 1024 /* must be a power of 2 */ + +/* (Theoretical) max number of pending requests */ +#define MAX_PENDING (INT_MAX/2) /* INT_MAX - (room to avoid overflow) */ + +/* pool->ltp_pause values */ +enum { NOT_PAUSED = 0, WANT_PAUSE = 1, PAUSED = 2 }; + +/* Context: thread ID and thread-specific key/data pairs */ +typedef struct ldap_int_thread_userctx_s { + struct ldap_int_thread_poolq_s *ltu_pq; + ldap_pvt_thread_t ltu_id; + ldap_int_tpool_key_t ltu_key[MAXKEYS]; +} ldap_int_thread_userctx_t; + + +/* Simple {thread ID -> context} hash table; key=ctx->ltu_id. + * Protected by ldap_pvt_thread_pool_mutex. + */ +static struct { + ldap_int_thread_userctx_t *ctx; + /* ctx is valid when not NULL or DELETED_THREAD_CTX */ +# define DELETED_THREAD_CTX (&ldap_int_main_thrctx + 1) /* dummy addr */ +} thread_keys[LDAP_MAXTHR]; + +#define TID_HASH(tid, hash) do { \ + unsigned const char *ptr_ = (unsigned const char *)&(tid); \ + unsigned i_; \ + for (i_ = 0, (hash) = ptr_[0]; ++i_ < sizeof(tid);) \ + (hash) += ((hash) << 5) ^ ptr_[i_]; \ +} while(0) + + +/* Task for a thread to perform */ +typedef struct ldap_int_thread_task_s { + union { + LDAP_STAILQ_ENTRY(ldap_int_thread_task_s) q; + LDAP_SLIST_ENTRY(ldap_int_thread_task_s) l; + } ltt_next; + ldap_pvt_thread_start_t *ltt_start_routine; + void *ltt_arg; + struct ldap_int_thread_poolq_s *ltt_queue; +} ldap_int_thread_task_t; + +typedef LDAP_STAILQ_HEAD(tcq, ldap_int_thread_task_s) ldap_int_tpool_plist_t; + +struct ldap_int_thread_poolq_s { + void *ltp_free; + + struct ldap_int_thread_pool_s *ltp_pool; + + /* protect members below */ + ldap_pvt_thread_mutex_t ltp_mutex; + + /* not paused and something to do for pool_<wrapper/pause/destroy>() + * Used for normal pool operation, to synch between submitter and + * worker threads. Not used for pauses. In normal operation multiple + * queues can rendezvous without acquiring the main pool lock. + */ + ldap_pvt_thread_cond_t ltp_cond; + + /* ltp_pause == 0 ? <p_pending_list : &empty_pending_list, + * maintained to reduce work for pool_wrapper() + */ + ldap_int_tpool_plist_t *ltp_work_list; + + /* pending tasks, and unused task objects */ + ldap_int_tpool_plist_t ltp_pending_list; + LDAP_SLIST_HEAD(tcl, ldap_int_thread_task_s) ltp_free_list; + + /* Max number of threads in this queue */ + int ltp_max_count; + + /* Max pending + paused + idle tasks, negated when ltp_finishing */ + int ltp_max_pending; + + int ltp_pending_count; /* Pending + paused + idle tasks */ + int ltp_active_count; /* Active, not paused/idle tasks */ + int ltp_open_count; /* Number of threads */ + int ltp_starting; /* Currently starting threads */ +}; + +struct ldap_int_thread_pool_s { + LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next; + + struct ldap_int_thread_poolq_s **ltp_wqs; + + /* number of poolqs */ + int ltp_numqs; + + /* protect members below */ + ldap_pvt_thread_mutex_t ltp_mutex; + + /* paused and waiting for resume + * When a pause is in effect all workers switch to waiting on + * this cond instead of their per-queue cond. + */ + ldap_pvt_thread_cond_t ltp_cond; + + /* ltp_active_queues < 1 && ltp_pause */ + ldap_pvt_thread_cond_t ltp_pcond; + + /* number of active queues */ + int ltp_active_queues; + + /* The pool is finishing, waiting for its threads to close. + * They close when ltp_pending_list is done. pool_submit() + * rejects new tasks. ltp_max_pending = -(its old value). + */ + int ltp_finishing; + + /* Some active task needs to be the sole active task. + * Atomic variable so ldap_pvt_thread_pool_pausing() can read it. + */ + volatile sig_atomic_t ltp_pause; + + /* Max number of threads in pool */ + int ltp_max_count; + + /* Configured max number of threads in pool, 0 for default (LDAP_MAXTHR) */ + int ltp_conf_max_count; + + /* Max pending + paused + idle tasks, negated when ltp_finishing */ + int ltp_max_pending; +}; + +static ldap_int_tpool_plist_t empty_pending_list = + LDAP_STAILQ_HEAD_INITIALIZER(empty_pending_list); + +static int ldap_int_has_thread_pool = 0; +static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s) + ldap_int_thread_pool_list = + LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list); + +static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex; + +static void *ldap_int_thread_pool_wrapper( void *pool ); + +static ldap_pvt_thread_key_t ldap_tpool_key; + +/* Context of the main thread */ +static ldap_int_thread_userctx_t ldap_int_main_thrctx; + +int +ldap_int_thread_pool_startup ( void ) +{ + ldap_int_main_thrctx.ltu_id = ldap_pvt_thread_self(); + ldap_pvt_thread_key_create( &ldap_tpool_key ); + return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex); +} + +int +ldap_int_thread_pool_shutdown ( void ) +{ + struct ldap_int_thread_pool_s *pool; + + while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) { + (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */ + } + ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex); + ldap_pvt_thread_key_destroy( ldap_tpool_key ); + return(0); +} + + +/* Create a thread pool */ +int +ldap_pvt_thread_pool_init_q ( + ldap_pvt_thread_pool_t *tpool, + int max_threads, + int max_pending, + int numqs ) +{ + ldap_pvt_thread_pool_t pool; + struct ldap_int_thread_poolq_s *pq; + int i, rc, rem_thr, rem_pend; + + /* multiple pools are currently not supported (ITS#4943) */ + assert(!ldap_int_has_thread_pool); + + if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR)) + max_threads = 0; + if (! (1 <= max_pending && max_pending <= MAX_PENDING)) + max_pending = MAX_PENDING; + + *tpool = NULL; + pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1, + sizeof(struct ldap_int_thread_pool_s)); + + if (pool == NULL) return(-1); + + pool->ltp_wqs = LDAP_MALLOC(numqs * sizeof(struct ldap_int_thread_poolq_s *)); + if (pool->ltp_wqs == NULL) { + LDAP_FREE(pool); + return(-1); + } + + for (i=0; i<numqs; i++) { + char *ptr = LDAP_CALLOC(1, sizeof(struct ldap_int_thread_poolq_s) + CACHELINE-1); + if (ptr == NULL) { + for (--i; i>=0; i--) + LDAP_FREE(pool->ltp_wqs[i]->ltp_free); + LDAP_FREE(pool->ltp_wqs); + LDAP_FREE(pool); + return(-1); + } + pool->ltp_wqs[i] = (struct ldap_int_thread_poolq_s *)(((size_t)ptr + CACHELINE-1) & ~(CACHELINE-1)); + pool->ltp_wqs[i]->ltp_free = ptr; + } + + pool->ltp_numqs = numqs; + pool->ltp_conf_max_count = max_threads; + if ( !max_threads ) + max_threads = LDAP_MAXTHR; + + rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex); + if (rc != 0) { +fail: + for (i=0; i<numqs; i++) + LDAP_FREE(pool->ltp_wqs[i]->ltp_free); + LDAP_FREE(pool->ltp_wqs); + LDAP_FREE(pool); + return(rc); + } + + rc = ldap_pvt_thread_cond_init(&pool->ltp_cond); + if (rc != 0) + goto fail; + + rc = ldap_pvt_thread_cond_init(&pool->ltp_pcond); + if (rc != 0) + goto fail; + + rem_thr = max_threads % numqs; + rem_pend = max_pending % numqs; + for ( i=0; i<numqs; i++ ) { + pq = pool->ltp_wqs[i]; + pq->ltp_pool = pool; + rc = ldap_pvt_thread_mutex_init(&pq->ltp_mutex); + if (rc != 0) + return(rc); + rc = ldap_pvt_thread_cond_init(&pq->ltp_cond); + if (rc != 0) + return(rc); + LDAP_STAILQ_INIT(&pq->ltp_pending_list); + pq->ltp_work_list = &pq->ltp_pending_list; + LDAP_SLIST_INIT(&pq->ltp_free_list); + + pq->ltp_max_count = max_threads / numqs; + if ( rem_thr ) { + pq->ltp_max_count++; + rem_thr--; + } + pq->ltp_max_pending = max_pending / numqs; + if ( rem_pend ) { + pq->ltp_max_pending++; + rem_pend--; + } + } + + ldap_int_has_thread_pool = 1; + + pool->ltp_max_count = max_threads; + pool->ltp_max_pending = max_pending; + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next); + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + /* Start no threads just yet. That can break if the process forks + * later, as slapd does in order to daemonize. On at least POSIX, + * only the forking thread would survive in the child. Yet fork() + * can't unlock/clean up other threads' locks and data structures, + * unless pthread_atfork() handlers have been set up to do so. + */ + + *tpool = pool; + return(0); +} + +int +ldap_pvt_thread_pool_init ( + ldap_pvt_thread_pool_t *tpool, + int max_threads, + int max_pending ) +{ + return ldap_pvt_thread_pool_init_q( tpool, max_threads, max_pending, 1 ); +} + +/* Submit a task to be performed by the thread pool */ +int +ldap_pvt_thread_pool_submit ( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + return ldap_pvt_thread_pool_submit2( tpool, start_routine, arg, NULL ); +} + +/* Submit a task to be performed by the thread pool */ +int +ldap_pvt_thread_pool_submit2 ( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg, + void **cookie ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + ldap_int_thread_task_t *task; + ldap_pvt_thread_t thr; + int i, j; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + if ( pool->ltp_numqs > 1 ) { + int min = pool->ltp_wqs[0]->ltp_max_pending + pool->ltp_wqs[0]->ltp_max_count; + int min_x = 0, cnt; + for ( i = 0; i < pool->ltp_numqs; i++ ) { + /* take first queue that has nothing active */ + if ( !pool->ltp_wqs[i]->ltp_active_count ) { + min_x = i; + break; + } + cnt = pool->ltp_wqs[i]->ltp_active_count + pool->ltp_wqs[i]->ltp_pending_count; + if ( cnt < min ) { + min = cnt; + min_x = i; + } + } + i = min_x; + } else + i = 0; + + j = i; + while(1) { + ldap_pvt_thread_mutex_lock(&pool->ltp_wqs[i]->ltp_mutex); + if (pool->ltp_wqs[i]->ltp_pending_count < pool->ltp_wqs[i]->ltp_max_pending) { + break; + } + ldap_pvt_thread_mutex_unlock(&pool->ltp_wqs[i]->ltp_mutex); + i++; + i %= pool->ltp_numqs; + if ( i == j ) + return -1; + } + + pq = pool->ltp_wqs[i]; + task = LDAP_SLIST_FIRST(&pq->ltp_free_list); + if (task) { + LDAP_SLIST_REMOVE_HEAD(&pq->ltp_free_list, ltt_next.l); + } else { + task = (ldap_int_thread_task_t *) LDAP_MALLOC(sizeof(*task)); + if (task == NULL) + goto failed; + } + + task->ltt_start_routine = start_routine; + task->ltt_arg = arg; + task->ltt_queue = pq; + if ( cookie ) + *cookie = task; + + pq->ltp_pending_count++; + LDAP_STAILQ_INSERT_TAIL(&pq->ltp_pending_list, task, ltt_next.q); + + if (pool->ltp_pause) + goto done; + + /* should we open (create) a thread? */ + if (pq->ltp_open_count < pq->ltp_active_count+pq->ltp_pending_count && + pq->ltp_open_count < pq->ltp_max_count) + { + pq->ltp_starting++; + pq->ltp_open_count++; + + if (0 != ldap_pvt_thread_create( + &thr, 1, ldap_int_thread_pool_wrapper, pq)) + { + /* couldn't create thread. back out of + * ltp_open_count and check for even worse things. + */ + pq->ltp_starting--; + pq->ltp_open_count--; + + if (pq->ltp_open_count == 0) { + /* no open threads at all?!? + */ + ldap_int_thread_task_t *ptr; + + /* let pool_close know there are no more threads */ + ldap_pvt_thread_cond_signal(&pq->ltp_cond); + + LDAP_STAILQ_FOREACH(ptr, &pq->ltp_pending_list, ltt_next.q) + if (ptr == task) break; + if (ptr == task) { + /* no open threads, task not handled, so + * back out of ltp_pending_count, free the task, + * report the error. + */ + pq->ltp_pending_count--; + LDAP_STAILQ_REMOVE(&pq->ltp_pending_list, task, + ldap_int_thread_task_s, ltt_next.q); + LDAP_SLIST_INSERT_HEAD(&pq->ltp_free_list, task, + ltt_next.l); + goto failed; + } + } + /* there is another open thread, so this + * task will be handled eventually. + */ + } + } + ldap_pvt_thread_cond_signal(&pq->ltp_cond); + + done: + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + return(0); + + failed: + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + return(-1); +} + +static void * +no_task( void *ctx, void *arg ) +{ + return NULL; +} + +/* Cancel a pending task that was previously submitted. + * Return 1 if the task was successfully cancelled, 0 if + * not found, -1 for invalid parameters + */ +int +ldap_pvt_thread_pool_retract ( + void *cookie ) +{ + ldap_int_thread_task_t *task, *ttmp; + struct ldap_int_thread_poolq_s *pq; + + if (cookie == NULL) + return(-1); + + ttmp = cookie; + pq = ttmp->ltt_queue; + if (pq == NULL) + return(-1); + + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + LDAP_STAILQ_FOREACH(task, &pq->ltp_pending_list, ltt_next.q) + if (task == ttmp) { + /* Could LDAP_STAILQ_REMOVE the task, but that + * walks ltp_pending_list again to find it. + */ + task->ltt_start_routine = no_task; + task->ltt_arg = NULL; + break; + } + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + return task != NULL; +} + +/* Walk the pool and allow tasks to be retracted, only to be called while the + * pool is paused */ +int +ldap_pvt_thread_pool_walk( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start, + ldap_pvt_thread_walk_t *cb, void *arg ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + ldap_int_thread_task_t *task; + int i; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + assert(pool->ltp_pause == PAUSED); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + + for (i=0; i<pool->ltp_numqs; i++) { + pq = pool->ltp_wqs[i]; + LDAP_STAILQ_FOREACH(task, &pq->ltp_pending_list, ltt_next.q) { + if ( task->ltt_start_routine == start ) { + if ( cb( task->ltt_start_routine, task->ltt_arg, arg ) ) { + /* retract */ + task->ltt_start_routine = no_task; + task->ltt_arg = NULL; + } + } + } + } + return 0; +} + +/* Set number of work queues in this pool. Should not be + * more than the number of CPUs. */ +int +ldap_pvt_thread_pool_queues( + ldap_pvt_thread_pool_t *tpool, + int numqs ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + int i, rc, rem_thr, rem_pend; + + if (numqs < 1 || tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + if (numqs < pool->ltp_numqs) { + for (i=numqs; i<pool->ltp_numqs; i++) + pool->ltp_wqs[i]->ltp_max_count = 0; + } else if (numqs > pool->ltp_numqs) { + struct ldap_int_thread_poolq_s **wqs; + wqs = LDAP_REALLOC(pool->ltp_wqs, numqs * sizeof(struct ldap_int_thread_poolq_s *)); + if (wqs == NULL) + return(-1); + pool->ltp_wqs = wqs; + for (i=pool->ltp_numqs; i<numqs; i++) { + char *ptr = LDAP_CALLOC(1, sizeof(struct ldap_int_thread_poolq_s) + CACHELINE-1); + if (ptr == NULL) { + for (; i<numqs; i++) + pool->ltp_wqs[i] = NULL; + return(-1); + } + pq = (struct ldap_int_thread_poolq_s *)(((size_t)ptr + CACHELINE-1) & ~(CACHELINE-1)); + pq->ltp_free = ptr; + pool->ltp_wqs[i] = pq; + pq->ltp_pool = pool; + rc = ldap_pvt_thread_mutex_init(&pq->ltp_mutex); + if (rc != 0) + return(rc); + rc = ldap_pvt_thread_cond_init(&pq->ltp_cond); + if (rc != 0) + return(rc); + LDAP_STAILQ_INIT(&pq->ltp_pending_list); + pq->ltp_work_list = &pq->ltp_pending_list; + LDAP_SLIST_INIT(&pq->ltp_free_list); + } + } + rem_thr = pool->ltp_max_count % numqs; + rem_pend = pool->ltp_max_pending % numqs; + for ( i=0; i<numqs; i++ ) { + pq = pool->ltp_wqs[i]; + pq->ltp_max_count = pool->ltp_max_count / numqs; + if ( rem_thr ) { + pq->ltp_max_count++; + rem_thr--; + } + pq->ltp_max_pending = pool->ltp_max_pending / numqs; + if ( rem_pend ) { + pq->ltp_max_pending++; + rem_pend--; + } + } + pool->ltp_numqs = numqs; + return 0; +} + +/* Set max #threads. value <= 0 means max supported #threads (LDAP_MAXTHR) */ +int +ldap_pvt_thread_pool_maxthreads( + ldap_pvt_thread_pool_t *tpool, + int max_threads ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + int remthr, i; + + if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR)) + max_threads = 0; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + pool->ltp_conf_max_count = max_threads; + if ( !max_threads ) + max_threads = LDAP_MAXTHR; + pool->ltp_max_count = max_threads; + + remthr = max_threads % pool->ltp_numqs; + max_threads /= pool->ltp_numqs; + + for (i=0; i<pool->ltp_numqs; i++) { + pq = pool->ltp_wqs[i]; + pq->ltp_max_count = max_threads; + if (remthr) { + pq->ltp_max_count++; + remthr--; + } + } + return(0); +} + +/* Inspect the pool */ +int +ldap_pvt_thread_pool_query( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_pool_param_t param, + void *value ) +{ + struct ldap_int_thread_pool_s *pool; + int count = -1; + + if ( tpool == NULL || value == NULL ) { + return -1; + } + + pool = *tpool; + + if ( pool == NULL ) { + return 0; + } + + switch ( param ) { + case LDAP_PVT_THREAD_POOL_PARAM_MAX: + count = pool->ltp_conf_max_count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING: + count = pool->ltp_max_pending; + if (count < 0) + count = -count; + if (count == MAX_PENDING) + count = 0; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PAUSING: + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + count = (pool->ltp_pause != 0); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + break; + + case LDAP_PVT_THREAD_POOL_PARAM_OPEN: + case LDAP_PVT_THREAD_POOL_PARAM_STARTING: + case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE: + case LDAP_PVT_THREAD_POOL_PARAM_PENDING: + case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD: + { + int i; + count = 0; + for (i=0; i<pool->ltp_numqs; i++) { + struct ldap_int_thread_poolq_s *pq = pool->ltp_wqs[i]; + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + switch(param) { + case LDAP_PVT_THREAD_POOL_PARAM_OPEN: + count += pq->ltp_open_count; + break; + case LDAP_PVT_THREAD_POOL_PARAM_STARTING: + count += pq->ltp_starting; + break; + case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE: + count += pq->ltp_active_count; + break; + case LDAP_PVT_THREAD_POOL_PARAM_PENDING: + count += pq->ltp_pending_count; + break; + case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD: + count += pq->ltp_pending_count + pq->ltp_active_count; + break; + default: + break; + } + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + } + if (count < 0) + count = -count; + } + break; + + case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_STATE: + if (pool->ltp_pause) + *((char **)value) = "pausing"; + else if (!pool->ltp_finishing) + *((char **)value) = "running"; + else { + int i; + for (i=0; i<pool->ltp_numqs; i++) + if (pool->ltp_wqs[i]->ltp_pending_count) break; + if (i<pool->ltp_numqs) + *((char **)value) = "finishing"; + else + *((char **)value) = "stopping"; + } + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PAUSED: + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + count = (pool->ltp_pause == PAUSED); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + break; + + case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN: + break; + } + + if ( count > -1 ) { + *((int *)value) = count; + } + + return ( count == -1 ? -1 : 0 ); +} + +/* + * true if pool is pausing; does not lock any mutex to check. + * 0 if not pause, 1 if pause, -1 if error or no pool. + */ +int +ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool ) +{ + int rc = -1; + struct ldap_int_thread_pool_s *pool; + + if ( tpool != NULL && (pool = *tpool) != NULL ) { + rc = (pool->ltp_pause != 0); + } + + return rc; +} + +/* + * wrapper for ldap_pvt_thread_pool_query(), left around + * for backwards compatibility + */ +int +ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool ) +{ + int rc, count; + + rc = ldap_pvt_thread_pool_query( tpool, + LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, (void *)&count ); + + if ( rc == 0 ) { + return count; + } + + return rc; +} + + +/* + * wrapper for ldap_pvt_thread_pool_close+free(), left around + * for backwards compatibility + */ +int +ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + int rc; + + if ( (rc = ldap_pvt_thread_pool_close( tpool, run_pending )) ) { + return rc; + } + + return ldap_pvt_thread_pool_free( tpool ); +} + +/* Shut down the pool making its threads finish */ +int +ldap_pvt_thread_pool_close ( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + struct ldap_int_thread_pool_s *pool, *pptr; + struct ldap_int_thread_poolq_s *pq; + ldap_int_thread_task_t *task; + int i; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) return(-1); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next) + if (pptr == pool) break; + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + if (pool != pptr) return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + pool->ltp_finishing = 1; + if (pool->ltp_max_pending > 0) + pool->ltp_max_pending = -pool->ltp_max_pending; + + ldap_pvt_thread_cond_broadcast(&pool->ltp_cond); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + + for (i=0; i<pool->ltp_numqs; i++) { + pq = pool->ltp_wqs[i]; + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + if (pq->ltp_max_pending > 0) + pq->ltp_max_pending = -pq->ltp_max_pending; + if (!run_pending) { + while ((task = LDAP_STAILQ_FIRST(&pq->ltp_pending_list)) != NULL) { + LDAP_STAILQ_REMOVE_HEAD(&pq->ltp_pending_list, ltt_next.q); + LDAP_FREE(task); + } + pq->ltp_pending_count = 0; + } + + while (pq->ltp_open_count) { + ldap_pvt_thread_cond_broadcast(&pq->ltp_cond); + ldap_pvt_thread_cond_wait(&pq->ltp_cond, &pq->ltp_mutex); + } + + while ((task = LDAP_SLIST_FIRST(&pq->ltp_free_list)) != NULL) + { + LDAP_SLIST_REMOVE_HEAD(&pq->ltp_free_list, ltt_next.l); + LDAP_FREE(task); + } + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + } + + return(0); +} + +/* Destroy the pool, everything must have already shut down */ +int +ldap_pvt_thread_pool_free ( ldap_pvt_thread_pool_t *tpool ) +{ + struct ldap_int_thread_pool_s *pool, *pptr; + struct ldap_int_thread_poolq_s *pq; + int i; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) return(-1); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next) + if (pptr == pool) break; + if (pptr == pool) + LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool, + ldap_int_thread_pool_s, ltp_next); + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + if (pool != pptr) return(-1); + + ldap_pvt_thread_cond_destroy(&pool->ltp_pcond); + ldap_pvt_thread_cond_destroy(&pool->ltp_cond); + ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex); + for (i=0; i<pool->ltp_numqs; i++) { + pq = pool->ltp_wqs[i]; + + assert( !pq->ltp_open_count ); + assert( LDAP_SLIST_EMPTY(&pq->ltp_free_list) ); + ldap_pvt_thread_cond_destroy(&pq->ltp_cond); + ldap_pvt_thread_mutex_destroy(&pq->ltp_mutex); + if (pq->ltp_free) { + LDAP_FREE(pq->ltp_free); + } + } + LDAP_FREE(pool->ltp_wqs); + LDAP_FREE(pool); + *tpool = NULL; + ldap_int_has_thread_pool = 0; + return(0); +} + +/* Thread loop. Accept and handle submitted tasks. */ +static void * +ldap_int_thread_pool_wrapper ( + void *xpool ) +{ + struct ldap_int_thread_poolq_s *pq = xpool; + struct ldap_int_thread_pool_s *pool = pq->ltp_pool; + ldap_int_thread_task_t *task; + ldap_int_tpool_plist_t *work_list; + ldap_int_thread_userctx_t ctx, *kctx; + unsigned i, keyslot, hash; + int pool_lock = 0, freeme = 0; + + assert(pool != NULL); + + for ( i=0; i<MAXKEYS; i++ ) { + ctx.ltu_key[i].ltk_key = NULL; + } + + ctx.ltu_pq = pq; + ctx.ltu_id = ldap_pvt_thread_self(); + TID_HASH(ctx.ltu_id, hash); + + ldap_pvt_thread_key_setdata( ldap_tpool_key, &ctx ); + + if (pool->ltp_pause) { + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + /* thread_keys[] is read-only when paused */ + while (pool->ltp_pause) + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + } + + /* find a key slot to give this thread ID and store a + * pointer to our keys there; start at the thread ID + * itself (mod LDAP_MAXTHR) and look for an empty slot. + */ + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + for (keyslot = hash & (LDAP_MAXTHR-1); + (kctx = thread_keys[keyslot].ctx) && kctx != DELETED_THREAD_CTX; + keyslot = (keyslot+1) & (LDAP_MAXTHR-1)); + thread_keys[keyslot].ctx = &ctx; + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + pq->ltp_starting--; + pq->ltp_active_count++; + + for (;;) { + work_list = pq->ltp_work_list; /* help the compiler a bit */ + task = LDAP_STAILQ_FIRST(work_list); + if (task == NULL) { /* paused or no pending tasks */ + if (--(pq->ltp_active_count) < 1) { + if (pool->ltp_pause) { + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + pool_lock = 1; + if (--(pool->ltp_active_queues) < 1) { + /* Notify pool_pause it is the sole active thread. */ + ldap_pvt_thread_cond_signal(&pool->ltp_pcond); + } + } + } + + do { + if (pool->ltp_finishing || pq->ltp_open_count > pq->ltp_max_count) { + /* Not paused, and either finishing or too many + * threads running (can happen if ltp_max_count + * was reduced). Let this thread die. + */ + goto done; + } + + /* We could check an idle timer here, and let the + * thread die if it has been inactive for a while. + * Only die if there are other open threads (i.e., + * always have at least one thread open). + * The check should be like this: + * if (pool->ltp_open_count>1 && pool->ltp_starting==0) + * check timer, wait if ltp_pause, leave thread; + * + * Just use pthread_cond_timedwait() if we want to + * check idle time. + */ + if (pool_lock) { + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + if (!pool->ltp_pause) { + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + pool_lock = 0; + } + } else + ldap_pvt_thread_cond_wait(&pq->ltp_cond, &pq->ltp_mutex); + + work_list = pq->ltp_work_list; + task = LDAP_STAILQ_FIRST(work_list); + } while (task == NULL); + + if (pool_lock) { + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + pool_lock = 0; + } + pq->ltp_active_count++; + } + + LDAP_STAILQ_REMOVE_HEAD(work_list, ltt_next.q); + pq->ltp_pending_count--; + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + + task->ltt_start_routine(&ctx, task->ltt_arg); + + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + LDAP_SLIST_INSERT_HEAD(&pq->ltp_free_list, task, ltt_next.l); + } + done: + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + + /* The pool_mutex lock protects ctx->ltu_key from pool_purgekey() + * during this call, since it prevents new pauses. */ + ldap_pvt_thread_pool_context_reset(&ctx); + + thread_keys[keyslot].ctx = DELETED_THREAD_CTX; + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + pq->ltp_open_count--; + if (pq->ltp_open_count == 0) { + if (pool->ltp_finishing) + /* let pool_destroy know we're all done */ + ldap_pvt_thread_cond_signal(&pq->ltp_cond); + else + freeme = 1; + } + + if (pool_lock) + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + else + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + + if (freeme) { + ldap_pvt_thread_cond_destroy(&pq->ltp_cond); + ldap_pvt_thread_mutex_destroy(&pq->ltp_mutex); + LDAP_FREE(pq->ltp_free); + pq->ltp_free = NULL; + } + ldap_pvt_thread_exit(NULL); + return(NULL); +} + +/* Arguments > ltp_pause to handle_pause(,PAUSE_ARG()). arg=PAUSE_ARG + * ensures (arg-ltp_pause) sets GO_* at need and keeps DO_PAUSE/GO_*. + */ +#define GO_IDLE 8 +#define GO_UNIDLE 16 +#define CHECK_PAUSE 32 /* if ltp_pause: GO_IDLE; wait; GO_UNIDLE */ +#define DO_PAUSE 64 /* CHECK_PAUSE; pause the pool */ +#define PAUSE_ARG(a) \ + ((a) | ((a) & (GO_IDLE|GO_UNIDLE) ? GO_IDLE-1 : CHECK_PAUSE)) + +static int +handle_pause( ldap_pvt_thread_pool_t *tpool, int pause_type ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + int ret = 0, pause, max_ltp_pause; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(0); + + if (pause_type == CHECK_PAUSE && !pool->ltp_pause) + return(0); + + { + ldap_int_thread_userctx_t *ctx = ldap_pvt_thread_pool_context(); + pq = ctx->ltu_pq; + if ( !pq ) + return(-1); + } + + /* Let pool_unidle() ignore requests for new pauses */ + max_ltp_pause = pause_type==PAUSE_ARG(GO_UNIDLE) ? WANT_PAUSE : NOT_PAUSED; + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + pause = pool->ltp_pause; /* NOT_PAUSED, WANT_PAUSE or PAUSED */ + + /* If ltp_pause and not GO_IDLE|GO_UNIDLE: Set GO_IDLE,GO_UNIDLE */ + pause_type -= pause; + + if (pause_type & GO_IDLE) { + int do_pool = 0; + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + pq->ltp_pending_count++; + pq->ltp_active_count--; + if (pause && pq->ltp_active_count < 1) { + do_pool = 1; + } + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + if (do_pool) { + pool->ltp_active_queues--; + if (pool->ltp_active_queues < 1) + /* Tell the task waiting to DO_PAUSE it can proceed */ + ldap_pvt_thread_cond_signal(&pool->ltp_pcond); + } + } + + if (pause_type & GO_UNIDLE) { + /* Wait out pause if any, then cancel GO_IDLE */ + if (pause > max_ltp_pause) { + ret = 1; + do { + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + } while (pool->ltp_pause > max_ltp_pause); + } + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + pq->ltp_pending_count--; + pq->ltp_active_count++; + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + } + + if (pause_type & DO_PAUSE) { + int i, j; + /* Tell everyone else to pause or finish, then await that */ + ret = 0; + assert(!pool->ltp_pause); + pool->ltp_pause = WANT_PAUSE; + pool->ltp_active_queues = 0; + + for (i=0; i<pool->ltp_numqs; i++) + if (pool->ltp_wqs[i] == pq) break; + + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + /* temporarily remove ourself from active count */ + pq->ltp_active_count--; + + j=i; + do { + pq = pool->ltp_wqs[j]; + if (j != i) + ldap_pvt_thread_mutex_lock(&pq->ltp_mutex); + + /* Hide pending tasks from ldap_pvt_thread_pool_wrapper() */ + pq->ltp_work_list = &empty_pending_list; + + if (pq->ltp_active_count > 0) + pool->ltp_active_queues++; + + ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex); + if (pool->ltp_numqs > 1) { + j++; + j %= pool->ltp_numqs; + } + } while (j != i); + + /* Wait for this task to become the sole active task */ + while (pool->ltp_active_queues > 0) + ldap_pvt_thread_cond_wait(&pool->ltp_pcond, &pool->ltp_mutex); + + /* restore us to active count */ + pool->ltp_wqs[i]->ltp_active_count++; + + assert(pool->ltp_pause == WANT_PAUSE); + pool->ltp_pause = PAUSED; + } + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + + return(ret); +} + +/* Consider this task idle: It will not block pool_pause() in other tasks. */ +void +ldap_pvt_thread_pool_idle( ldap_pvt_thread_pool_t *tpool ) +{ + handle_pause(tpool, PAUSE_ARG(GO_IDLE)); +} + +/* Cancel pool_idle(). If the pool is paused, wait it out first. */ +void +ldap_pvt_thread_pool_unidle( ldap_pvt_thread_pool_t *tpool ) +{ + handle_pause(tpool, PAUSE_ARG(GO_UNIDLE)); +} + +/* + * If a pause was requested, wait for it. If several threads + * are waiting to pause, let through one or more pauses. + * The calling task must be active, not idle. + * Return 1 if we waited, 0 if not, -1 at parameter error. + */ +int +ldap_pvt_thread_pool_pausewait( ldap_pvt_thread_pool_t *tpool ) +{ + return handle_pause(tpool, PAUSE_ARG(CHECK_PAUSE)); +} + +/* Return 1 if a pause has been requested */ +int +ldap_pvt_thread_pool_pausequery( ldap_pvt_thread_pool_t *tpool ) +{ + struct ldap_int_thread_pool_s *pool; + if ( !tpool ) + return -1; + + pool = *tpool; + if ( !pool ) + return 0; + + return pool->ltp_pause != 0; +} + +/* + * Wait for a pause, from a non-pooled thread. + */ +int +ldap_pvt_thread_pool_pausecheck_native( ldap_pvt_thread_pool_t *tpool ) +{ + struct ldap_int_thread_pool_s *pool; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(0); + + if (!pool->ltp_pause) + return(0); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + while (pool->ltp_pause) + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return 1; +} + +/* + * Pause the pool. The calling task must be active, not idle. + * Return when all other tasks are paused or idle. + */ +int +ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool ) +{ + return handle_pause(tpool, PAUSE_ARG(DO_PAUSE)); +} + +/* End a pause */ +int +ldap_pvt_thread_pool_resume ( + ldap_pvt_thread_pool_t *tpool ) +{ + struct ldap_int_thread_pool_s *pool; + struct ldap_int_thread_poolq_s *pq; + int i; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(0); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + assert(pool->ltp_pause == PAUSED); + pool->ltp_pause = 0; + for (i=0; i<pool->ltp_numqs; i++) { + pq = pool->ltp_wqs[i]; + pq->ltp_work_list = &pq->ltp_pending_list; + ldap_pvt_thread_cond_broadcast(&pq->ltp_cond); + } + ldap_pvt_thread_cond_broadcast(&pool->ltp_cond); + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(0); +} + +/* + * Get the key's data and optionally free function in the given context. + */ +int ldap_pvt_thread_pool_getkey( + void *xctx, + void *key, + void **data, + ldap_pvt_thread_pool_keyfree_t **kfree ) +{ + ldap_int_thread_userctx_t *ctx = xctx; + int i; + + if ( !ctx || !key || !data ) return EINVAL; + + for ( i=0; i<MAXKEYS && ctx->ltu_key[i].ltk_key; i++ ) { + if ( ctx->ltu_key[i].ltk_key == key ) { + *data = ctx->ltu_key[i].ltk_data; + if ( kfree ) *kfree = ctx->ltu_key[i].ltk_free; + return 0; + } + } + return ENOENT; +} + +static void +clear_key_idx( ldap_int_thread_userctx_t *ctx, int i ) +{ + for ( ; i < MAXKEYS-1 && ctx->ltu_key[i+1].ltk_key; i++ ) + ctx->ltu_key[i] = ctx->ltu_key[i+1]; + ctx->ltu_key[i].ltk_key = NULL; +} + +/* + * Set or remove data for the key in the given context. + * key can be any unique pointer. + * kfree() is an optional function to free the data (but not the key): + * pool_context_reset() and pool_purgekey() call kfree(key, data), + * but pool_setkey() does not. For pool_setkey() it is the caller's + * responsibility to free any existing data with the same key. + * kfree() must not call functions taking a tpool argument. + */ +int ldap_pvt_thread_pool_setkey( + void *xctx, + void *key, + void *data, + ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, + ldap_pvt_thread_pool_keyfree_t **oldkfreep ) +{ + ldap_int_thread_userctx_t *ctx = xctx; + int i, found; + + if ( !ctx || !key ) return EINVAL; + + for ( i=found=0; i<MAXKEYS; i++ ) { + if ( ctx->ltu_key[i].ltk_key == key ) { + found = 1; + break; + } else if ( !ctx->ltu_key[i].ltk_key ) { + break; + } + } + + if ( olddatap ) { + if ( found ) { + *olddatap = ctx->ltu_key[i].ltk_data; + } else { + *olddatap = NULL; + } + } + + if ( oldkfreep ) { + if ( found ) { + *oldkfreep = ctx->ltu_key[i].ltk_free; + } else { + *oldkfreep = 0; + } + } + + if ( data || kfree ) { + if ( i>=MAXKEYS ) + return ENOMEM; + ctx->ltu_key[i].ltk_key = key; + ctx->ltu_key[i].ltk_data = data; + ctx->ltu_key[i].ltk_free = kfree; + } else if ( found ) { + clear_key_idx( ctx, i ); + } + + return 0; +} + +/* Free all elements with this key, no matter which thread they're in. + * May only be called while the pool is paused. + */ +void ldap_pvt_thread_pool_purgekey( void *key ) +{ + int i, j; + ldap_int_thread_userctx_t *ctx; + + assert ( key != NULL ); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + for ( i=0; i<LDAP_MAXTHR; i++ ) { + ctx = thread_keys[i].ctx; + if ( ctx && ctx != DELETED_THREAD_CTX ) { + for ( j=0; j<MAXKEYS && ctx->ltu_key[j].ltk_key; j++ ) { + if ( ctx->ltu_key[j].ltk_key == key ) { + if (ctx->ltu_key[j].ltk_free) + ctx->ltu_key[j].ltk_free( ctx->ltu_key[j].ltk_key, + ctx->ltu_key[j].ltk_data ); + clear_key_idx( ctx, j ); + break; + } + } + } + } + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); +} + +/* + * Find the context of the current thread. + * This is necessary if the caller does not have access to the + * thread context handle (for example, a slapd plugin calling + * slapi_search_internal()). No doubt it is more efficient + * for the application to keep track of the thread context + * handles itself. + */ +void *ldap_pvt_thread_pool_context( ) +{ + void *ctx = NULL; + + ldap_pvt_thread_key_getdata( ldap_tpool_key, &ctx ); + return ctx ? ctx : (void *) &ldap_int_main_thrctx; +} + +/* + * Free the context's keys. + * Must not call functions taking a tpool argument (because this + * thread already holds ltp_mutex when called from pool_wrapper()). + */ +void ldap_pvt_thread_pool_context_reset( void *vctx ) +{ + ldap_int_thread_userctx_t *ctx = vctx; + int i; + + for ( i=MAXKEYS-1; i>=0; i--) { + if ( !ctx->ltu_key[i].ltk_key ) + continue; + if ( ctx->ltu_key[i].ltk_free ) + ctx->ltu_key[i].ltk_free( ctx->ltu_key[i].ltk_key, + ctx->ltu_key[i].ltk_data ); + ctx->ltu_key[i].ltk_key = NULL; + } +} + +ldap_pvt_thread_t ldap_pvt_thread_pool_tid( void *vctx ) +{ + ldap_int_thread_userctx_t *ctx = vctx; + + return ctx->ltu_id; +} +#endif /* LDAP_THREAD_HAVE_TPOOL */ + +#endif /* LDAP_R_COMPILE */ diff --git a/contrib/libs/openldap/libraries/libldap/turn.c b/contrib/libs/openldap/libraries/libldap/turn.c new file mode 100644 index 0000000000..7725f01d0d --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/turn.c @@ -0,0 +1,96 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +/* + * LDAPv3 Turn Operation Request + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +int +ldap_turn( + LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ +#ifdef LDAP_EXOP_X_TURN + BerElement *turnvalber = NULL; + struct berval turnval; + int rc; + + turnvalber = ber_alloc_t( LBER_USE_DER ); + if( mutual ) { + ber_printf( turnvalber, "{bs}", mutual, identifier ); + } else { + ber_printf( turnvalber, "{s}", identifier ); + } + ber_flatten2( turnvalber, &turnval, 0 ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_X_TURN, + &turnval, sctrls, cctrls, msgidp ); + ber_free( turnvalber, 1 ); + return rc; +#else + return LDAP_CONTROL_NOT_FOUND; +#endif +} + +int +ldap_turn_s( + LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ +#ifdef LDAP_EXOP_X_TURN + BerElement *turnvalber = NULL; + struct berval turnval; + int rc; + + turnvalber = ber_alloc_t( LBER_USE_DER ); + if( mutual ) { + ber_printf( turnvalber, "{bs}", 0xFF, identifier ); + } else { + ber_printf( turnvalber, "{s}", identifier ); + } + ber_flatten2( turnvalber, &turnval, 0 ); + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TURN, + &turnval, sctrls, cctrls, NULL, NULL ); + ber_free( turnvalber, 1 ); + return rc; +#else + return LDAP_CONTROL_NOT_FOUND; +#endif +} + diff --git a/contrib/libs/openldap/libraries/libldap/txn.c b/contrib/libs/openldap/libraries/libldap/txn.c new file mode 100644 index 0000000000..6409002344 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/txn.c @@ -0,0 +1,153 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. + */ + +/* + * LDAPv3 Transactions (draft-zeilenga-ldap-txn) + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +int +ldap_txn_start( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + return ldap_extended_operation( ld, LDAP_EXOP_TXN_START, + NULL, sctrls, cctrls, msgidp ); +} + +int +ldap_txn_start_s( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct berval **txnid ) +{ + assert( txnid != NULL ); + + return ldap_extended_operation_s( ld, LDAP_EXOP_TXN_START, + NULL, sctrls, cctrls, NULL, txnid ); +} + +int +ldap_txn_end( + LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *txnber = NULL; + struct berval txnval; + + assert( txnid != NULL ); + + txnber = ber_alloc_t( LBER_USE_DER ); + + if( commit ) { + ber_printf( txnber, "{ON}", txnid ); + } else { + ber_printf( txnber, "{bON}", commit, txnid ); + } + + ber_flatten2( txnber, &txnval, 0 ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_TXN_END, + &txnval, sctrls, cctrls, msgidp ); + + ber_free( txnber, 1 ); + return rc; +} + +int +ldap_txn_end_s( + LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *retidp ) +{ + int rc; + BerElement *txnber = NULL; + struct berval txnval; + struct berval *retdata = NULL; + + if ( retidp != NULL ) *retidp = -1; + + txnber = ber_alloc_t( LBER_USE_DER ); + + if( commit ) { + ber_printf( txnber, "{ON}", txnid ); + } else { + ber_printf( txnber, "{bON}", commit, txnid ); + } + + ber_flatten2( txnber, &txnval, 0 ); + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_TXN_END, + &txnval, sctrls, cctrls, NULL, &retdata ); + + ber_free( txnber, 1 ); + + /* parse retdata */ + if( retdata != NULL ) { + BerElement *ber; + ber_tag_t tag; + ber_int_t retid; + + if( retidp == NULL ) goto done; + + ber = ber_init( retdata ); + + if( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + tag = ber_scanf( ber, "i", &retid ); + ber_free( ber, 1 ); + + if ( tag != LBER_INTEGER ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + goto done; + } + + *retidp = (int) retid; + +done: + ber_bvfree( retdata ); + } + + return rc; +} diff --git a/contrib/libs/openldap/libraries/libldap/unbind.c b/contrib/libs/openldap/libraries/libldap/unbind.c new file mode 100644 index 0000000000..820d48feae --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/unbind.c @@ -0,0 +1,319 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* An Unbind Request looks like this: + * + * UnbindRequest ::= [APPLICATION 2] NULL + * + * and has no response. (Source: RFC 4511) + */ + +int +ldap_unbind_ext( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + return ldap_ld_free( ld, 1, sctrls, cctrls ); +} + +int +ldap_unbind_ext_s( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + return ldap_unbind_ext( ld, sctrls, cctrls ); +} + +int +ldap_unbind( LDAP *ld ) +{ + Debug0( LDAP_DEBUG_TRACE, "ldap_unbind\n" ); + + return( ldap_unbind_ext( ld, NULL, NULL ) ); +} + + +int +ldap_ld_free( + LDAP *ld, + int close, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + LDAPMessage *lm, *next; + int err = LDAP_SUCCESS; + + LDAP_MUTEX_LOCK( &ld->ld_ldcmutex ); + /* Someone else is still using this ld. */ + if (ld->ld_ldcrefcnt > 1) { /* but not last thread */ + /* clean up self only */ + ld->ld_ldcrefcnt--; + if ( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + if ( ld->ld_referrals != NULL) { + LDAP_VFREE(ld->ld_referrals); + ld->ld_referrals = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + LDAP_FREE( (char *) ld ); + return( err ); + } + + /* This ld is the last thread. */ + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + + /* free LDAP structure and outstanding requests/responses */ + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + ldap_tavl_free( ld->ld_requests, ldap_do_free_request ); + ld->ld_requests = NULL; + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + + /* free and unbind from all open connections */ + while ( ld->ld_conns != NULL ) { + ldap_free_connection( ld, ld->ld_conns, 1, close ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + for ( lm = ld->ld_responses; lm != NULL; lm = next ) { + next = lm->lm_next; + ldap_msgfree( lm ); + } + + if ( ld->ld_abandoned != NULL ) { + LDAP_FREE( ld->ld_abandoned ); + ld->ld_abandoned = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + + /* Should already be closed by ldap_free_connection which knows not to free + * this one */ + ber_int_sb_destroy( ld->ld_sb ); + LBER_FREE( ld->ld_sb ); + + LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex ); + + /* final close callbacks */ + { + ldaplist *ll, *next; + + for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) { + ldap_conncb *cb = ll->ll_data; + next = ll->ll_next; + cb->lc_del( ld, NULL, cb ); + LDAP_FREE( ll ); + } + } + + if ( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if ( ld->ld_referrals != NULL) { + LDAP_VFREE(ld->ld_referrals); + ld->ld_referrals = NULL; + } + + if ( ld->ld_selectinfo != NULL ) { + ldap_free_select_info( ld->ld_selectinfo ); + ld->ld_selectinfo = NULL; + } + + if ( ld->ld_options.ldo_defludp != NULL ) { + ldap_free_urllist( ld->ld_options.ldo_defludp ); + ld->ld_options.ldo_defludp = NULL; + } + + if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) { + LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ); + memset( & ld->ld_options.ldo_local_ip_addrs, 0, + sizeof( ldapsourceip ) ); + } + +#ifdef LDAP_CONNECTIONLESS + if ( ld->ld_options.ldo_peer != NULL ) { + LDAP_FREE( ld->ld_options.ldo_peer ); + ld->ld_options.ldo_peer = NULL; + } + + if ( ld->ld_options.ldo_cldapdn != NULL ) { + LDAP_FREE( ld->ld_options.ldo_cldapdn ); + ld->ld_options.ldo_cldapdn = NULL; + } +#endif + + if ( ld->ld_options.ldo_defbase != NULL ) { + LDAP_FREE( ld->ld_options.ldo_defbase ); + ld->ld_options.ldo_defbase = NULL; + } + +#ifdef HAVE_CYRUS_SASL + if ( ld->ld_options.ldo_def_sasl_mech != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_mech ); + ld->ld_options.ldo_def_sasl_mech = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_realm != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_realm ); + ld->ld_options.ldo_def_sasl_realm = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_authcid != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid ); + ld->ld_options.ldo_def_sasl_authcid = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_authzid != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid ); + ld->ld_options.ldo_def_sasl_authzid = NULL; + } +#endif + +#ifdef HAVE_TLS + ldap_int_tls_destroy( &ld->ld_options ); +#endif + + if ( ld->ld_options.ldo_sctrls != NULL ) { + ldap_controls_free( ld->ld_options.ldo_sctrls ); + ld->ld_options.ldo_sctrls = NULL; + } + + if ( ld->ld_options.ldo_cctrls != NULL ) { + ldap_controls_free( ld->ld_options.ldo_cctrls ); + ld->ld_options.ldo_cctrls = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex ); + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex ); +#endif +#ifndef NDEBUG + LDAP_TRASH(ld); +#endif + LDAP_FREE( (char *) ld->ldc ); + LDAP_FREE( (char *) ld ); + + return( err ); +} + +int +ldap_destroy( LDAP *ld ) +{ + return ( ldap_ld_free( ld, 1, NULL, NULL ) ); +} + +int +ldap_unbind_s( LDAP *ld ) +{ + return( ldap_unbind_ext( ld, NULL, NULL ) ); +} + +/* FIXME: this function is called only by ldap_free_connection(), + * which, most of the times, is called with ld_req_mutex locked */ +int +ldap_send_unbind( + LDAP *ld, + Sockbuf *sb, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + BerElement *ber; + ber_int_t id; + + Debug0( LDAP_DEBUG_TRACE, "ldap_send_unbind\n" ); + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) + return LDAP_SUCCESS; +#endif + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( ld->ld_errno ); + } + + LDAP_NEXT_MSGID(ld, id); + + /* fill it in */ + if ( ber_printf( ber, "{itn" /*}*/, id, + LDAP_REQ_UNBIND ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( ld->ld_errno ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return ld->ld_errno; + } + + if ( ber_printf( ber, /*{*/ "N}", LDAP_REQ_UNBIND ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( ld->ld_errno ); + } + + ld->ld_errno = LDAP_SUCCESS; + /* send the message */ + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) { + ld->ld_errno = LDAP_SERVER_DOWN; + } + + return( ld->ld_errno ); +} diff --git a/contrib/libs/openldap/libraries/libldap/url.c b/contrib/libs/openldap/libraries/libldap/url.c new file mode 100644 index 0000000000..493fd7ce47 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/url.c @@ -0,0 +1,1653 @@ +/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + */ + + +/* + * LDAP URLs look like this: + * [p]ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]] + * + * where: + * attributes is a comma separated list + * scope is one of these three strings: base one sub (default=base) + * filter is an string-represented filter as in RFC 4515 + * + * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension + * + * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl> + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* local functions */ +static const char* skip_url_prefix LDAP_P(( + const char *url, + int *enclosedp, + const char **scheme )); + +int ldap_pvt_url_scheme2proto( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return LDAP_PROTO_IPC; + } + + if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PROTO_UDP; + } +#endif + + return -1; +} + +int ldap_pvt_url_scheme_port( const char *scheme, int port ) +{ + assert( scheme != NULL ); + + if( port ) return port; + if( scheme == NULL ) return port; + + if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) { + return LDAP_PORT; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return -1; + } + + if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) { + return LDAPS_PORT; + } + +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PORT; + } +#endif + + return -1; +} + +int +ldap_pvt_url_scheme2tls( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + return strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0; +} + +int +ldap_pvt_url_scheme2proxied( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + return strcmp("pldap", scheme) == 0 || strcmp("pldaps", scheme) == 0; +} + +int +ldap_is_ldap_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return 1; +} + +int +ldap_is_ldaps_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "ldaps") == 0 || strcmp(scheme, "pldaps") == 0; +} + +int +ldap_is_ldapi_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "ldapi") == 0; +} + +#ifdef LDAP_CONNECTIONLESS +int +ldap_is_ldapc_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "cldap") == 0; +} +#endif + +static const char* +skip_url_prefix( + const char *url, + int *enclosedp, + const char **scheme ) +{ + /* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + */ + const char *p; + + if ( url == NULL ) { + return( NULL ); + } + + p = url; + + /* skip leading '<' (if any) */ + if ( *p == '<' ) { + *enclosedp = 1; + ++p; + } else { + *enclosedp = 0; + } + + /* skip leading "URL:" (if any) */ + if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { + p += LDAP_URL_URLCOLON_LEN; + } + + /* check for "ldap://" prefix */ + if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldap://" prefix and return success */ + p += LDAP_URL_PREFIX_LEN; + *scheme = "ldap"; + return( p ); + } + + /* check for "pldap://" prefix */ + if ( strncasecmp( p, PLDAP_URL_PREFIX, PLDAP_URL_PREFIX_LEN ) == 0 ) { + /* skip over "pldap://" prefix and return success */ + p += PLDAP_URL_PREFIX_LEN; + *scheme = "pldap"; + return( p ); + } + + /* check for "ldaps://" prefix */ + if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldaps://" prefix and return success */ + p += LDAPS_URL_PREFIX_LEN; + *scheme = "ldaps"; + return( p ); + } + + /* check for "pldaps://" prefix */ + if ( strncasecmp( p, PLDAPS_URL_PREFIX, PLDAPS_URL_PREFIX_LEN ) == 0 ) { + /* skip over "pldaps://" prefix and return success */ + p += PLDAPS_URL_PREFIX_LEN; + *scheme = "pldaps"; + return( p ); + } + + /* check for "ldapi://" prefix */ + if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldapi://" prefix and return success */ + p += LDAPI_URL_PREFIX_LEN; + *scheme = "ldapi"; + return( p ); + } + +#ifdef LDAP_CONNECTIONLESS + /* check for "cldap://" prefix */ + if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) { + /* skip over "cldap://" prefix and return success */ + p += LDAPC_URL_PREFIX_LEN; + *scheme = "cldap"; + return( p ); + } +#endif + + return( NULL ); +} + +int +ldap_pvt_scope2bv( int scope, struct berval *bv ) +{ + switch ( scope ) { + case LDAP_SCOPE_BASE: + BER_BVSTR( bv, "base" ); + break; + + case LDAP_SCOPE_ONELEVEL: + BER_BVSTR( bv, "one" ); + break; + + case LDAP_SCOPE_SUBTREE: + BER_BVSTR( bv, "sub" ); + break; + + case LDAP_SCOPE_SUBORDINATE: + BER_BVSTR( bv, "subordinate" ); + break; + + default: + return LDAP_OTHER; + } + + return LDAP_SUCCESS; +} + +const char * +ldap_pvt_scope2str( int scope ) +{ + struct berval bv; + + if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) { + return bv.bv_val; + } + + return NULL; +} + +int +ldap_pvt_bv2scope( struct berval *bv ) +{ + static struct { + struct berval bv; + int scope; + } v[] = { + { BER_BVC( "one" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "onelevel" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "base" ), LDAP_SCOPE_BASE }, + { BER_BVC( "sub" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subtree" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subord" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "children" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVNULL, -1 } + }; + int i; + + for ( i = 0; v[ i ].scope != -1; i++ ) { + if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) { + return v[ i ].scope; + } + } + + return( -1 ); +} + +int +ldap_pvt_str2scope( const char *p ) +{ + struct berval bv; + + ber_str2bv( p, 0, 0, &bv ); + + return ldap_pvt_bv2scope( &bv ); +} + +static const char hex[] = "0123456789ABCDEF"; + +#define URLESC_NONE 0x0000U +#define URLESC_COMMA 0x0001U +#define URLESC_SLASH 0x0002U + +static int +hex_escape_len( const char *s, unsigned list ) +{ + int len; + + if ( s == NULL ) { + return 0; + } + + for ( len = 0; s[0]; s++ ) { + switch ( s[0] ) { + /* RFC 2396: reserved */ + case '?': + len += 3; + break; + + case ',': + if ( list & URLESC_COMMA ) { + len += 3; + } else { + len++; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + len += 3; + } else { + len++; + } + break; + + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + len++; + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[0] ) ) { + len += 3; + } else { + len++; + } + break; + } + } + + return len; +} + +static int +hex_escape( char *buf, int len, const char *s, unsigned list ) +{ + int i; + int pos; + + if ( s == NULL ) { + return 0; + } + + for ( pos = 0, i = 0; s[i] && pos < len; i++ ) { + int escape = 0; + + switch ( s[i] ) { + /* RFC 2396: reserved */ + case '?': + escape = 1; + break; + + case ',': + if ( list & URLESC_COMMA ) { + escape = 1; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + escape = 1; + } + break; + + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[i] ) ) { + escape = 1; + } + break; + } + + if ( escape ) { + buf[pos++] = '%'; + buf[pos++] = hex[ (s[i] >> 4) & 0x0f ]; + buf[pos++] = hex[ s[i] & 0x0f ]; + + } else { + buf[pos++] = s[i]; + } + } + + buf[pos] = '\0'; + + return pos; +} + +static int +hex_escape_len_list( char **s, unsigned flags ) +{ + int len; + int i; + + if ( s == NULL ) { + return 0; + } + + len = 0; + for ( i = 0; s[i] != NULL; i++ ) { + if ( len ) { + len++; + } + len += hex_escape_len( s[i], flags ); + } + + return len; +} + +static int +hex_escape_list( char *buf, int len, char **s, unsigned flags ) +{ + int pos; + int i; + + if ( s == NULL ) { + return 0; + } + + pos = 0; + for ( i = 0; s[i] != NULL; i++ ) { + int curlen; + + if ( pos ) { + buf[pos++] = ','; + len--; + } + curlen = hex_escape( &buf[pos], len, s[i], flags ); + len -= curlen; + pos += curlen; + } + + return pos; +} + +static int +desc2str_len( LDAPURLDesc *u ) +{ + int sep = 0; + int len = 0; + int is_ipc = 0; + struct berval scope; + + if ( u == NULL || u->lud_scheme == NULL ) { + return -1; + } + + if ( !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + if ( u->lud_exts ) { + len += hex_escape_len_list( u->lud_exts, URLESC_COMMA ); + if ( !sep ) { + sep = 5; + } + } + + if ( u->lud_filter ) { + len += hex_escape_len( u->lud_filter, URLESC_NONE ); + if ( !sep ) { + sep = 4; + } + } + + if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) { + len += scope.bv_len; + if ( !sep ) { + sep = 3; + } + } + + if ( u->lud_attrs ) { + len += hex_escape_len_list( u->lud_attrs, URLESC_NONE ); + if ( !sep ) { + sep = 2; + } + } + + if ( u->lud_dn && u->lud_dn[0] ) { + len += hex_escape_len( u->lud_dn, URLESC_NONE ); + if ( !sep ) { + sep = 1; + } + }; + + len += sep; + + if ( u->lud_port ) { + unsigned p = u->lud_port; + if ( p > 65535 ) + return -1; + + len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9)); + } + + if ( u->lud_host && u->lud_host[0] ) { + char *ptr; + len += hex_escape_len( u->lud_host, URLESC_SLASH ); + if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) { + if ( strchr( ptr+1, ':' )) + len += 2; /* IPv6, [] */ + } + } + + len += strlen( u->lud_scheme ) + STRLENOF( "://" ); + + return len; +} + +static int +desc2str( LDAPURLDesc *u, char *s, int len ) +{ + int i; + int sep = 0; + int sofar = 0; + int is_v6 = 0; + int is_ipc = 0; + struct berval scope = BER_BVNULL; + char *ptr; + + if ( u == NULL ) { + return -1; + } + + if ( s == NULL ) { + return -1; + } + + if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + ldap_pvt_scope2bv( u->lud_scope, &scope ); + + if ( u->lud_exts ) { + sep = 5; + } else if ( u->lud_filter ) { + sep = 4; + } else if ( !BER_BVISEMPTY( &scope ) ) { + sep = 3; + } else if ( u->lud_attrs ) { + sep = 2; + } else if ( u->lud_dn && u->lud_dn[0] ) { + sep = 1; + } + + if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) { + if ( strchr( ptr+1, ':' )) + is_v6 = 1; + } + + if ( u->lud_port ) { + sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme, + is_v6 ? "[" : "", + u->lud_host ? u->lud_host : "", + is_v6 ? "]" : "", + u->lud_port ); + len -= sofar; + + } else { + sofar = sprintf( s, "%s://", u->lud_scheme ); + len -= sofar; + if ( u->lud_host && u->lud_host[0] ) { + if ( is_v6 ) { + s[sofar++] = '['; + len--; + } + i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH ); + sofar += i; + len -= i; + if ( is_v6 ) { + s[sofar++] = ']'; + len--; + } + } + } + + assert( len >= 0 ); + + if ( sep < 1 ) { + goto done; + } + + s[sofar++] = '/'; + len--; + + assert( len >= 0 ); + + if ( u->lud_dn && u->lud_dn[0] ) { + i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + } + + if ( sep < 2 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 3 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + if ( !BER_BVISNULL( &scope ) ) { + strcpy( &s[sofar], scope.bv_val ); + sofar += scope.bv_len; + len -= scope.bv_len; + } + + assert( len >= 0 ); + + if ( sep < 4 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 5 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA ); + sofar += i; + len -= i; + + assert( len >= 0 ); + +done: + if ( len < 0 ) { + return -1; + } + + return sofar; +} + +char * +ldap_url_desc2str( LDAPURLDesc *u ) +{ + int len; + char *s; + + if ( u == NULL ) { + return NULL; + } + + len = desc2str_len( u ); + if ( len < 0 ) { + return NULL; + } + + /* allocate enough to hex escape everything -- overkill */ + s = LDAP_MALLOC( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + if ( desc2str( u, s, len ) != len ) { + LDAP_FREE( s ); + return NULL; + } + + s[len] = '\0'; + + return s; +} + +int +ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags ) +{ +/* + * Pick apart the pieces of an LDAP URL. + */ + + LDAPURLDesc *ludp; + char *p, *q, *r; + int i, enclosed, proto, is_v6 = 0; + const char *scheme = NULL; + const char *url_tmp; + char *url; + + int check_dn = 1; + + if( url_in == NULL || ludpp == NULL ) { + return LDAP_URL_ERR_PARAM; + } + +#ifndef LDAP_INT_IN_KERNEL + /* Global options may not be created yet + * We can't test if the global options are initialized + * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate + * the options and cause infinite recursion + */ + Debug1( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in ); +#endif + + *ludpp = NULL; /* pessimistic */ + + url_tmp = skip_url_prefix( url_in, &enclosed, &scheme ); + + if ( url_tmp == NULL ) { + return LDAP_URL_ERR_BADSCHEME; + } + + assert( scheme != NULL ); + + proto = ldap_pvt_url_scheme2proto( scheme ); + if ( proto == -1 ) { + return LDAP_URL_ERR_BADSCHEME; + } + + /* make working copy of the remainder of the URL */ + url = LDAP_STRDUP( url_tmp ); + if ( url == NULL ) { + return LDAP_URL_ERR_MEM; + } + + if ( enclosed ) { + p = &url[strlen(url)-1]; + + if( *p != '>' ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_BADENCLOSURE; + } + + *p = '\0'; + } + + /* allocate return struct */ + ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc )); + + if ( ludp == NULL ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_MEM; + } + + ludp->lud_next = NULL; + ludp->lud_host = NULL; + ludp->lud_port = 0; + ludp->lud_dn = NULL; + ludp->lud_attrs = NULL; + ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT; + ludp->lud_filter = NULL; + ludp->lud_exts = NULL; + + ludp->lud_scheme = LDAP_STRDUP( scheme ); + + if ( ludp->lud_scheme == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + /* scan forward for '/' that marks end of hostport and begin. of dn */ + p = strchr( url, '/' ); + q = NULL; + + if( p != NULL ) { + /* terminate hostport; point to start of dn */ + *p++ = '\0'; + } else { + /* check for Novell kludge, see below */ + p = strchr( url, '?' ); + if ( p ) { + *p++ = '\0'; + q = p; + p = NULL; + } + } + + if ( proto != LDAP_PROTO_IPC ) { + /* IPv6 syntax with [ip address]:port */ + if ( *url == '[' ) { + r = strchr( url, ']' ); + if ( r == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + *r++ = '\0'; + q = strchr( r, ':' ); + if ( q && q != r ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + is_v6 = 1; + } else { + q = strchr( url, ':' ); + } + + if ( q != NULL ) { + char *next; + + *q++ = '\0'; + ldap_pvt_hex_unescape( q ); + + if( *q == '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + ludp->lud_port = strtol( q, &next, 10 ); + if ( next == q || next[0] != '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + /* check for Novell kludge */ + if ( !p ) { + if ( *next != '\0' ) { + q = &next[1]; + } else { + q = NULL; + } + } + } + + if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) { + if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) { + ludp->lud_port = LDAPS_PORT; + } else { + ludp->lud_port = LDAP_PORT; + } + } + } + + ldap_pvt_hex_unescape( url ); + + /* If [ip address]:port syntax, url is [ip and we skip the [ */ + ludp->lud_host = LDAP_STRDUP( url + is_v6 ); + + if( ludp->lud_host == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST ) + && ludp->lud_host != NULL + && *ludp->lud_host == '\0' ) + { + LDAP_FREE( ludp->lud_host ); + ludp->lud_host = NULL; + } + + /* + * Kludge. ldap://111.222.333.444:389??cn=abc,o=company + * + * On early Novell releases, search references/referrals were returned + * in this format, i.e., the dn was kind of in the scope position, + * but the required slash is missing. The whole thing is illegal syntax, + * but we need to account for it. Fortunately it can't be confused with + * anything real. + */ + if( (p == NULL) && (q != NULL) && (*q == '?') ) { + /* ? immediately followed by question */ + q++; + if( *q != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( q ); + ludp->lud_dn = LDAP_STRDUP( q ); + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { + ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; + } + + if ( check_dn && ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + + if( p == NULL ) { + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of dn */ + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate dn part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( p ); + ludp->lud_dn = LDAP_STRDUP( p ); + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { + ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; + } + + if( check_dn && ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + if( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of attributes */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate attributes part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse attributes */ + ldap_pvt_hex_unescape( p ); + ludp->lud_attrs = ldap_str2charray( p, "," ); + + if( ludp->lud_attrs == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADATTRS; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of scope */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the scope part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the scope */ + ldap_pvt_hex_unescape( p ); + ludp->lud_scope = ldap_pvt_str2scope( p ); + + if( ludp->lud_scope == -1 ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADSCOPE; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of filter */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the filter part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the filter */ + ldap_pvt_hex_unescape( p ); + + if( ! *p ) { + /* missing filter */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADFILTER; + } + + ludp->lud_filter = LDAP_STRDUP( p ); + + if( ludp->lud_filter == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of extensions */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* extra '?' */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + /* parse the extensions */ + ludp->lud_exts = ldap_str2charray( p, "," ); + + if( ludp->lud_exts == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + for( i=0; ludp->lud_exts[i] != NULL; i++ ) { + ldap_pvt_hex_unescape( ludp->lud_exts[i] ); + + if( *ludp->lud_exts[i] == '!' ) { + /* count the number of critical extensions */ + ludp->lud_crit_exts++; + } + } + + if( i == 0 ) { + /* must have 1 or more */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + /* no more */ + *ludpp = ludp; + LDAP_FREE( url ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +{ + return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC ); +} + +LDAPURLDesc * +ldap_url_dup ( LDAPURLDesc *ludp ) +{ + LDAPURLDesc *dest; + + if ( ludp == NULL ) { + return NULL; + } + + dest = LDAP_MALLOC( sizeof(LDAPURLDesc) ); + if (dest == NULL) + return NULL; + + *dest = *ludp; + dest->lud_scheme = NULL; + dest->lud_host = NULL; + dest->lud_dn = NULL; + dest->lud_filter = NULL; + dest->lud_attrs = NULL; + dest->lud_exts = NULL; + dest->lud_next = NULL; + + if ( ludp->lud_scheme != NULL ) { + dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme ); + if (dest->lud_scheme == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_host != NULL ) { + dest->lud_host = LDAP_STRDUP( ludp->lud_host ); + if (dest->lud_host == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_dn != NULL ) { + dest->lud_dn = LDAP_STRDUP( ludp->lud_dn ); + if (dest->lud_dn == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_filter != NULL ) { + dest->lud_filter = LDAP_STRDUP( ludp->lud_filter ); + if (dest->lud_filter == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_attrs != NULL ) { + dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs ); + if (dest->lud_attrs == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_exts != NULL ) { + dest->lud_exts = ldap_charray_dup( ludp->lud_exts ); + if (dest->lud_exts == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + return dest; +} + +LDAPURLDesc * +ldap_url_duplist (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *dest, *tail, *ludp, *newludp; + + dest = NULL; + tail = NULL; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + newludp = ldap_url_dup(ludp); + if (newludp == NULL) { + ldap_free_urllist(dest); + return NULL; + } + if (tail == NULL) + dest = newludp; + else + tail->lud_next = newludp; + tail = newludp; + } + return dest; +} + +static int +ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) + +{ + int i, rc; + LDAPURLDesc *ludp; + char **urls; + + assert( ludlist != NULL ); + assert( url != NULL ); + + *ludlist = NULL; + + if ( sep == NULL ) { + sep = ", "; + } + + urls = ldap_str2charray( url, sep ); + if (urls == NULL) + return LDAP_URL_ERR_MEM; + + /* count the URLs... */ + for (i = 0; urls[i] != NULL; i++) ; + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + rc = ldap_url_parse_ext( urls[i], &ludp, flags ); + if ( rc != 0 ) { + ldap_charray_free( urls ); + ldap_free_urllist( *ludlist ); + *ludlist = NULL; + return rc; + } + ludp->lud_next = *ludlist; + *ludlist = ludp; + } + ldap_charray_free( urls ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parselist (LDAPURLDesc **ludlist, const char *url ) +{ + return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC ); +} + +int +ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) +{ + return ldap_url_parselist_int( ludlist, url, sep, flags ); +} + +int +ldap_url_parsehosts( + LDAPURLDesc **ludlist, + const char *hosts, + int port ) +{ + int i; + LDAPURLDesc *ludp; + char **specs, *p; + + assert( ludlist != NULL ); + assert( hosts != NULL ); + + *ludlist = NULL; + + specs = ldap_str2charray(hosts, ", "); + if (specs == NULL) + return LDAP_NO_MEMORY; + + /* count the URLs... */ + for (i = 0; specs[i] != NULL; i++) /* EMPTY */; + + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) ); + if (ludp == NULL) { + ldap_charray_free(specs); + ldap_free_urllist(*ludlist); + *ludlist = NULL; + return LDAP_NO_MEMORY; + } + ludp->lud_port = port; + ludp->lud_host = specs[i]; + p = strchr(ludp->lud_host, ':'); + if (p != NULL) { + /* more than one :, IPv6 address */ + if ( strchr(p+1, ':') != NULL ) { + /* allow [address] and [address]:port */ + if ( *ludp->lud_host == '[' ) { + p = strchr( ludp->lud_host+1, ']' ); + if ( p == NULL ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + /* Truncate trailing ']' and shift hostname down 1 char */ + *p = '\0'; + AC_MEMCPY( ludp->lud_host, ludp->lud_host+1, p - ludp->lud_host ); + p++; + if ( *p != ':' ) { + if ( *p != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + p = NULL; + } + } else { + p = NULL; + } + } + if (p != NULL) { + char *next; + + *p++ = 0; + ldap_pvt_hex_unescape(p); + ludp->lud_port = strtol( p, &next, 10 ); + if ( next == p || next[0] != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + } + } + ludp->lud_scheme = LDAP_STRDUP("ldap"); + if ( ludp->lud_scheme == NULL ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_NO_MEMORY; + } + specs[i] = NULL; + ldap_pvt_hex_unescape(ludp->lud_host); + ludp->lud_next = *ludlist; + *ludlist = ludp; + } + + /* this should be an array of NULLs now */ + ldap_charray_free(specs); + return LDAP_SUCCESS; +} + +char * +ldap_url_list2hosts (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *ludp; + int size; + char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */ + + if (ludlist == NULL) + return NULL; + + /* figure out how big the string is */ + size = 1; /* nul-term */ + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + if ( ludp->lud_host == NULL ) continue; + size += strlen(ludp->lud_host) + 1; /* host and space */ + if (strchr(ludp->lud_host, ':')) /* will add [ ] below */ + size += 2; + if (ludp->lud_port != 0) + size += sprintf(buf, ":%d", ludp->lud_port); + } + s = LDAP_MALLOC(size); + if (s == NULL) + return NULL; + + p = s; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + if ( ludp->lud_host == NULL ) continue; + if (strchr(ludp->lud_host, ':')) { + p += sprintf(p, "[%s]", ludp->lud_host); + } else { + strcpy(p, ludp->lud_host); + p += strlen(ludp->lud_host); + } + if (ludp->lud_port != 0) + p += sprintf(p, ":%d", ludp->lud_port); + *p++ = ' '; + } + if (p != s) + p--; /* nuke that extra space */ + *p = '\0'; + return s; +} + +char * +ldap_url_list2urls( + LDAPURLDesc *ludlist ) +{ + LDAPURLDesc *ludp; + int size, sofar; + char *s; + + if ( ludlist == NULL ) { + return NULL; + } + + /* figure out how big the string is */ + for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len = desc2str_len( ludp ); + if ( len < 0 ) { + return NULL; + } + size += len + 1; + } + + s = LDAP_MALLOC( size ); + + if ( s == NULL ) { + return NULL; + } + + for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len; + + len = desc2str( ludp, &s[sofar], size ); + + if ( len < 0 ) { + LDAP_FREE( s ); + return NULL; + } + + sofar += len; + size -= len; + + s[sofar++] = ' '; + size--; + + assert( size >= 0 ); + } + + s[sofar - 1] = '\0'; + + return s; +} + +void +ldap_free_urllist( LDAPURLDesc *ludlist ) +{ + LDAPURLDesc *ludp, *next; + + for (ludp = ludlist; ludp != NULL; ludp = next) { + next = ludp->lud_next; + ldap_free_urldesc(ludp); + } +} + +void +ldap_free_urldesc( LDAPURLDesc *ludp ) +{ + if ( ludp == NULL ) { + return; + } + + if ( ludp->lud_scheme != NULL ) { + LDAP_FREE( ludp->lud_scheme ); + } + + if ( ludp->lud_host != NULL ) { + LDAP_FREE( ludp->lud_host ); + } + + if ( ludp->lud_dn != NULL ) { + LDAP_FREE( ludp->lud_dn ); + } + + if ( ludp->lud_filter != NULL ) { + LDAP_FREE( ludp->lud_filter); + } + + if ( ludp->lud_attrs != NULL ) { + LDAP_VFREE( ludp->lud_attrs ); + } + + if ( ludp->lud_exts != NULL ) { + LDAP_VFREE( ludp->lud_exts ); + } + + LDAP_FREE( ludp ); +} + +static int +ldap_int_is_hexpair( char *s ) +{ + int i; + + for ( i = 0; i < 2; i++ ) { + if ( s[i] >= '0' && s[i] <= '9' ) { + continue; + } + + if ( s[i] >= 'A' && s[i] <= 'F' ) { + continue; + } + + if ( s[i] >= 'a' && s[i] <= 'f' ) { + continue; + } + + return 0; + } + + return 1; +} + +static int +ldap_int_unhex( int c ) +{ + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} + +void +ldap_pvt_hex_unescape( char *s ) +{ + /* + * Remove URL hex escapes from s... done in place. The basic concept for + * this routine is borrowed from the WWW library HTUnEscape() routine. + */ + char *p, + *save_s = s; + + for ( p = s; *s != '\0'; ++s ) { + if ( *s == '%' ) { + /* + * FIXME: what if '%' is followed + * by non-hexpair chars? + */ + if ( !ldap_int_is_hexpair( s + 1 ) ) { + p = save_s; + break; + } + + if ( *++s == '\0' ) { + break; + } + *p = ldap_int_unhex( *s ) << 4; + if ( *++s == '\0' ) { + break; + } + *p++ += ldap_int_unhex( *s ); + } else { + *p++ = *s; + } + } + + *p = '\0'; +} + diff --git a/contrib/libs/openldap/libraries/libldap/utf-8-conv.c b/contrib/libs/openldap/libraries/libldap/utf-8-conv.c new file mode 100644 index 0000000000..9d8f9c18bb --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/utf-8-conv.c @@ -0,0 +1,485 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +/* + * UTF-8 Conversion Routines + * + * These routines convert between Wide Character and UTF-8, + * or between MultiByte and UTF-8 encodings. + * + * Both single character and string versions of the functions are provided. + * All functions return -1 if the character or string cannot be converted. + */ + +#include "portable.h" + +#if SIZEOF_WCHAR_T >= 4 +/* These routines assume ( sizeof(wchar_t) >= 4 ) */ + +#include <stdio.h> +#include <ac/stdlib.h> /* For wctomb, wcstombs, mbtowc, mbstowcs */ +#include <ac/string.h> +#include <ac/time.h> /* for time_t */ + +#include "ldap-int.h" + +#include <ldap_utf8.h> + +static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + + +/*----------------------------------------------------------------------------- + UTF-8 Format Summary + +ASCII chars 7 bits + 0xxxxxxx + +2-character UTF-8 sequence: 11 bits + 110xxxxx 10xxxxxx + +3-character UTF-8 16 bits + 1110xxxx 10xxxxxx 10xxxxxx + +4-char UTF-8 21 bits + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +5-char UTF-8 26 bits + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + +6-char UTF-8 31 bits + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + +Unicode address space (0 - 0x10FFFF) 21 bits +ISO-10646 address space (0 - 0x7FFFFFFF) 31 bits + +Note: This code does not prevent UTF-8 sequences which are longer than + necessary from being decoded. +*/ + +/*----------------------------------------------------------------------------- + Convert a UTF-8 character to a wide char. + Return the length of the UTF-8 input character in bytes. +*/ +int +ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char ) +{ + int utflen, i; + wchar_t ch; + + if (utf8char == NULL) return -1; + + /* Get UTF-8 sequence length from 1st byte */ + utflen = LDAP_UTF8_CHARLEN2(utf8char, utflen); + + if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1; + + /* First byte minus length tag */ + ch = (wchar_t)(utf8char[0] & mask[utflen]); + + for(i=1; i < utflen; i++) { + /* Subsequent bytes must start with 10 */ + if ((utf8char[i] & 0xc0) != 0x80) return -1; + + ch <<= 6; /* 6 bits of data in each subsequent byte */ + ch |= (wchar_t)(utf8char[i] & 0x3f); + } + + if (wchar) *wchar = ch; + + return utflen; +} + +/*----------------------------------------------------------------------------- + Convert a UTF-8 string to a wide char string. + No more than 'count' wide chars will be written to the output buffer. + Return the size of the converted string in wide chars, excl null terminator. +*/ +int +ldap_x_utf8s_to_wcs ( wchar_t *wcstr, const char *utf8str, size_t count ) +{ + size_t wclen = 0; + int utflen, i; + wchar_t ch; + + + /* If input ptr is NULL or empty... */ + if (utf8str == NULL || !*utf8str) { + if ( wcstr ) + *wcstr = 0; + return 0; + } + + /* Examine next UTF-8 character. If output buffer is NULL, ignore count */ + while ( *utf8str && (wcstr==NULL || wclen<count) ) { + /* Get UTF-8 sequence length from 1st byte */ + utflen = LDAP_UTF8_CHARLEN2(utf8str, utflen); + + if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1; + + /* First byte minus length tag */ + ch = (wchar_t)(utf8str[0] & mask[utflen]); + + for(i=1; i < utflen; i++) { + /* Subsequent bytes must start with 10 */ + if ((utf8str[i] & 0xc0) != 0x80) return -1; + + ch <<= 6; /* 6 bits of data in each subsequent byte */ + ch |= (wchar_t)(utf8str[i] & 0x3f); + } + + if (wcstr) wcstr[wclen] = ch; + + utf8str += utflen; /* Move to next UTF-8 character */ + wclen++; /* Count number of wide chars stored/required */ + } + + /* Add null terminator if there's room in the buffer. */ + if (wcstr && wclen < count) wcstr[wclen] = 0; + + return wclen; +} + + +/*----------------------------------------------------------------------------- + Convert one wide char to a UTF-8 character. + Return the length of the converted UTF-8 character in bytes. + No more than 'count' bytes will be written to the output buffer. +*/ +int +ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, size_t count ) +{ + int len=0; + + if (utf8char == NULL) /* Just determine the required UTF-8 char length. */ + { /* Ignore count */ + if( wchar < 0 ) + return -1; + if( wchar < 0x80 ) + return 1; + if( wchar < 0x800 ) + return 2; + if( wchar < 0x10000 ) + return 3; + if( wchar < 0x200000 ) + return 4; + if( wchar < 0x4000000 ) + return 5; +#if SIZEOF_WCHAR_T > 4 + /* UL is not strictly needed by ANSI C */ + if( wchar < (wchar_t)0x80000000UL ) +#endif /* SIZEOF_WCHAR_T > 4 */ + return 6; + return -1; + } + + + if ( wchar < 0 ) { /* Invalid wide character */ + len = -1; + + } else if( wchar < 0x80 ) { + if (count >= 1) { + utf8char[len++] = (char)wchar; + } + + } else if( wchar < 0x800 ) { + if (count >=2) { + utf8char[len++] = 0xc0 | ( wchar >> 6 ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x10000 ) { + if (count >= 3) { + utf8char[len++] = 0xe0 | ( wchar >> 12 ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x200000 ) { + if (count >= 4) { + utf8char[len++] = 0xf0 | ( wchar >> 18 ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x4000000 ) { + if (count >= 5) { + utf8char[len++] = 0xf8 | ( wchar >> 24 ); + utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else +#if SIZEOF_WCHAR_T > 4 + /* UL is not strictly needed by ANSI C */ + if( wchar < (wchar_t)0x80000000UL ) +#endif /* SIZEOF_WCHAR_T > 4 */ + { + if (count >= 6) { + utf8char[len++] = 0xfc | ( wchar >> 30 ); + utf8char[len++] = 0x80 | ( (wchar >> 24) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + +#if SIZEOF_WCHAR_T > 4 + } else { + len = -1; +#endif /* SIZEOF_WCHAR_T > 4 */ + } + + return len; + +} + + +/*----------------------------------------------------------------------------- + Convert a wide char string to a UTF-8 string. + No more than 'count' bytes will be written to the output buffer. + Return the # of bytes written to the output buffer, excl null terminator. +*/ +int +ldap_x_wcs_to_utf8s ( char *utf8str, const wchar_t *wcstr, size_t count ) +{ + int len = 0; + int n; + char *p = utf8str; + wchar_t empty = 0; /* To avoid use of L"" construct */ + + if (wcstr == NULL) /* Treat input ptr NULL as an empty string */ + wcstr = ∅ + + if (utf8str == NULL) /* Just compute size of output, excl null */ + { + while (*wcstr) + { + /* Get UTF-8 size of next wide char */ + n = ldap_x_wc_to_utf8( NULL, *wcstr++, LDAP_MAX_UTF8_LEN); + if (n == -1) + return -1; + len += n; + } + + return len; + } + + + /* Do the actual conversion. */ + + n = 1; /* In case of empty wcstr */ + while (*wcstr) + { + n = ldap_x_wc_to_utf8( p, *wcstr++, count); + + if (n <= 0) /* If encoding error (-1) or won't fit (0), quit */ + break; + + p += n; + count -= n; /* Space left in output buffer */ + } + + /* If not enough room for last character, pad remainder with null + so that return value = original count, indicating buffer full. */ + if (n == 0) + { + while (count--) + *p++ = 0; + } + + /* Add a null terminator if there's room. */ + else if (count) + *p = 0; + + if (n == -1) /* Conversion encountered invalid wide char. */ + return -1; + + /* Return the number of bytes written to output buffer, excl null. */ + return (p - utf8str); +} + +#ifdef ANDROID +int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); } +int mbtowc(wchar_t *pwc, const char *s, size_t n) { return mbrtowc(pwc, s, n, NULL); } +#endif + +/*----------------------------------------------------------------------------- + Convert a UTF-8 character to a MultiByte character. + Return the size of the converted character in bytes. +*/ +int +ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char, + int (*f_wctomb)(char *mbchar, wchar_t wchar) ) +{ + wchar_t wchar; + int n; + char tmp[6]; /* Large enough for biggest multibyte char */ + + if (f_wctomb == NULL) /* If no conversion function was given... */ + f_wctomb = wctomb; /* use the local ANSI C function */ + + /* First convert UTF-8 char to a wide char */ + n = ldap_x_utf8_to_wc( &wchar, utf8char); + + if (n == -1) + return -1; /* Invalid UTF-8 character */ + + if (mbchar == NULL) + n = f_wctomb( tmp, wchar ); + else + n = f_wctomb( mbchar, wchar); + + return n; +} + +/*----------------------------------------------------------------------------- + Convert a UTF-8 string to a MultiByte string. + No more than 'count' bytes will be written to the output buffer. + Return the size of the converted string in bytes, excl null terminator. +*/ +int +ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count, + size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) ) +{ + wchar_t *wcs; + size_t wcsize; + int n; + + if (f_wcstombs == NULL) /* If no conversion function was given... */ + f_wcstombs = wcstombs; /* use the local ANSI C function */ + + if (utf8str == NULL || *utf8str == 0) /* NULL or empty input string */ + { + if (mbstr) + *mbstr = 0; + return 0; + } + +/* Allocate memory for the maximum size wchar string that we could get. */ + wcsize = strlen(utf8str) + 1; + wcs = (wchar_t *)LDAP_MALLOC(wcsize * sizeof(wchar_t)); + if (wcs == NULL) + return -1; /* Memory allocation failure. */ + + /* First convert the UTF-8 string to a wide char string */ + n = ldap_x_utf8s_to_wcs( wcs, utf8str, wcsize); + + /* Then convert wide char string to multi-byte string */ + if (n != -1) + { + n = f_wcstombs(mbstr, wcs, count); + } + + LDAP_FREE(wcs); + + return n; +} + +/*----------------------------------------------------------------------------- + Convert a MultiByte character to a UTF-8 character. + 'mbsize' indicates the number of bytes of 'mbchar' to check. + Returns the number of bytes written to the output character. +*/ +int +ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize, + int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) ) +{ + wchar_t wchar; + int n; + + if (f_mbtowc == NULL) /* If no conversion function was given... */ + f_mbtowc = mbtowc; /* use the local ANSI C function */ + + if (mbsize == 0) /* 0 is not valid. */ + return -1; + + if (mbchar == NULL || *mbchar == 0) + { + if (utf8char) + *utf8char = 0; + return 1; + } + + /* First convert the MB char to a Wide Char */ + n = f_mbtowc( &wchar, mbchar, mbsize); + + if (n == -1) + return -1; + + /* Convert the Wide Char to a UTF-8 character. */ + n = ldap_x_wc_to_utf8( utf8char, wchar, LDAP_MAX_UTF8_LEN); + + return n; +} + + +/*----------------------------------------------------------------------------- + Convert a MultiByte string to a UTF-8 string. + No more than 'count' bytes will be written to the output buffer. + Return the size of the converted string in bytes, excl null terminator. +*/ +int +ldap_x_mbs_to_utf8s ( char *utf8str, const char *mbstr, size_t count, + size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count) ) +{ + wchar_t *wcs; + int n; + size_t wcsize; + + if (mbstr == NULL) /* Treat NULL input string as an empty string */ + mbstr = ""; + + if (f_mbstowcs == NULL) /* If no conversion function was given... */ + f_mbstowcs = mbstowcs; /* use the local ANSI C function */ + + /* Allocate memory for the maximum size wchar string that we could get. */ + wcsize = strlen(mbstr) + 1; + wcs = (wchar_t *)LDAP_MALLOC( wcsize * sizeof(wchar_t) ); + if (wcs == NULL) + return -1; + + /* First convert multi-byte string to a wide char string */ + n = f_mbstowcs(wcs, mbstr, wcsize); + + /* Convert wide char string to UTF-8 string */ + if (n != -1) + { + n = ldap_x_wcs_to_utf8s( utf8str, wcs, count); + } + + LDAP_FREE(wcs); + + return n; +} + +#endif /* SIZEOF_WCHAR_T >= 4 */ diff --git a/contrib/libs/openldap/libraries/libldap/utf-8.c b/contrib/libs/openldap/libraries/libldap/utf-8.c new file mode 100644 index 0000000000..c383e41413 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/utf-8.c @@ -0,0 +1,562 @@ +/* utf-8.c -- Basic UTF-8 routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Basic UTF-8 routines + * + * These routines are "dumb". Though they understand UTF-8, + * they don't grok Unicode. That is, they can push bits, + * but don't have a clue what the bits represent. That's + * good enough for use with the LDAP Client SDK. + * + * These routines are not optimized. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap_utf8.h" + +#include "ldap-int.h" +#include "ldap_defaults.h" + +/* + * return the number of bytes required to hold the + * NULL-terminated UTF-8 string NOT INCLUDING the + * termination. + */ +ber_len_t ldap_utf8_bytes( const char * p ) +{ + ber_len_t bytes; + + for( bytes=0; p[bytes]; bytes++ ) { + /* EMPTY */ ; + } + + return bytes; +} + +ber_len_t ldap_utf8_chars( const char * p ) +{ + /* could be optimized and could check for invalid sequences */ + ber_len_t chars=0; + + for( ; *p ; LDAP_UTF8_INCR(p) ) { + chars++; + } + + return chars; +} + +/* return offset to next character */ +int ldap_utf8_offset( const char * p ) +{ + return LDAP_UTF8_NEXT(p) - p; +} + +/* + * Returns length indicated by first byte. + */ +const char ldap_utf8_lentab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 }; + +int ldap_utf8_charlen( const char * p ) +{ + if (!(*p & 0x80)) + return 1; + + return ldap_utf8_lentab[*(const unsigned char *)p ^ 0x80]; +} + +/* + * Make sure the UTF-8 char used the shortest possible encoding + * returns charlen if valid, 0 if not. + * + * Here are the valid UTF-8 encodings, taken from RFC 2279 page 4. + * The table is slightly modified from that of the RFC. + * + * UCS-4 range (hex) UTF-8 sequence (binary) + * 0000 0000-0000 007F 0....... + * 0000 0080-0000 07FF 110++++. 10...... + * 0000 0800-0000 FFFF 1110++++ 10+..... 10...... + * 0001 0000-001F FFFF 11110+++ 10++.... 10...... 10...... + * 0020 0000-03FF FFFF 111110++ 10+++... 10...... 10...... 10...... + * 0400 0000-7FFF FFFF 1111110+ 10++++.. 10...... 10...... 10...... 10...... + * + * The '.' bits are "don't cares". When validating a UTF-8 sequence, + * at least one of the '+' bits must be set, otherwise the character + * should have been encoded in fewer octets. Note that in the two-octet + * case, only the first octet needs to be validated, and this is done + * in the ldap_utf8_lentab[] above. + */ + +/* mask of required bits in second octet */ +#undef c +#define c const char +c ldap_utf8_mintab[] = { + (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x38, (c)0x80, (c)0x80, (c)0x80, (c)0x3c, (c)0x80, (c)0x00, (c)0x00 }; +#undef c + +int ldap_utf8_charlen2( const char * p ) +{ + int i = LDAP_UTF8_CHARLEN( p ); + + if ( i > 2 ) { + if ( !( ldap_utf8_mintab[*p & 0x1f] & p[1] ) ) + i = 0; + } + return i; +} + +/* conv UTF-8 to UCS-4, useful for comparisons */ +ldap_ucs4_t ldap_x_utf8_to_ucs4( const char * p ) +{ + const unsigned char *c = (const unsigned char *) p; + ldap_ucs4_t ch; + int len, i; + static unsigned char mask[] = { + 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + + len = LDAP_UTF8_CHARLEN2(p, len); + + if( len == 0 ) return LDAP_UCS4_INVALID; + + ch = c[0] & mask[len]; + + for(i=1; i < len; i++) { + if ((c[i] & 0xc0) != 0x80) { + return LDAP_UCS4_INVALID; + } + + ch <<= 6; + ch |= c[i] & 0x3f; + } + + return ch; +} + +/* conv UCS-4 to UTF-8, not used */ +int ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf ) +{ + int len=0; + unsigned char* p = (unsigned char *) buf; + + /* not a valid Unicode character */ + if ( c < 0 ) return 0; + + /* Just return length, don't convert */ + if(buf == NULL) { + if( c < 0x80 ) return 1; + else if( c < 0x800 ) return 2; + else if( c < 0x10000 ) return 3; + else if( c < 0x200000 ) return 4; + else if( c < 0x4000000 ) return 5; + else return 6; + } + + if( c < 0x80 ) { + p[len++] = c; + + } else if( c < 0x800 ) { + p[len++] = 0xc0 | ( c >> 6 ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x10000 ) { + p[len++] = 0xe0 | ( c >> 12 ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x200000 ) { + p[len++] = 0xf0 | ( c >> 18 ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x4000000 ) { + p[len++] = 0xf8 | ( c >> 24 ); + p[len++] = 0x80 | ( (c >> 18) & 0x3f ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else /* if( c < 0x80000000 ) */ { + p[len++] = 0xfc | ( c >> 30 ); + p[len++] = 0x80 | ( (c >> 24) & 0x3f ); + p[len++] = 0x80 | ( (c >> 18) & 0x3f ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + } + + return len; +} + +#define LDAP_UCS_UTF8LEN(c) \ + c < 0 ? 0 : (c < 0x80 ? 1 : (c < 0x800 ? 2 : (c < 0x10000 ? 3 : \ + (c < 0x200000 ? 4 : (c < 0x4000000 ? 5 : 6))))) + +/* Convert a string to UTF-8 format. The input string is expected to + * have characters of 1, 2, or 4 octets (in network byte order) + * corresponding to the ASN.1 T61STRING, BMPSTRING, and UNIVERSALSTRING + * types respectively. (Here T61STRING just means that there is one + * octet per character and characters may use the high bit of the octet. + * The characters are assumed to use ISO mappings, no provision is made + * for converting from T.61 coding rules to Unicode.) + */ + +int +ldap_ucs_to_utf8s( struct berval *ucs, int csize, struct berval *utf8s ) +{ + unsigned char *in, *end; + char *ptr; + ldap_ucs4_t u; + int i, l = 0; + + utf8s->bv_val = NULL; + utf8s->bv_len = 0; + + in = (unsigned char *)ucs->bv_val; + + /* Make sure we stop at an even multiple of csize */ + end = in + ( ucs->bv_len & ~(csize-1) ); + + for (; in < end; ) { + u = *in++; + if (csize > 1) { + u <<= 8; + u |= *in++; + } + if (csize > 2) { + u <<= 8; + u |= *in++; + u <<= 8; + u |= *in++; + } + i = LDAP_UCS_UTF8LEN(u); + if (i == 0) + return LDAP_INVALID_SYNTAX; + l += i; + } + + utf8s->bv_val = LDAP_MALLOC( l+1 ); + if (utf8s->bv_val == NULL) + return LDAP_NO_MEMORY; + utf8s->bv_len = l; + + ptr = utf8s->bv_val; + for (in = (unsigned char *)ucs->bv_val; in < end; ) { + u = *in++; + if (csize > 1) { + u <<= 8; + u |= *in++; + } + if (csize > 2) { + u <<= 8; + u |= *in++; + u <<= 8; + u |= *in++; + } + ptr += ldap_x_ucs4_to_utf8(u, ptr); + } + *ptr = '\0'; + return LDAP_SUCCESS; +} + +/* + * Advance to the next UTF-8 character + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +char* ldap_utf8_next( const char * p ) +{ + int i; + const unsigned char *u = (const unsigned char *) p; + + if( LDAP_UTF8_ISASCII(u) ) { + return (char *) &p[1]; + } + + for( i=1; i<6; i++ ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return (char *) &p[i]; + } + } + + return (char *) &p[i]; +} + +/* + * Advance to the previous UTF-8 character + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +char* ldap_utf8_prev( const char * p ) +{ + int i; + const unsigned char *u = (const unsigned char *) p; + + for( i=-1; i>-6 ; i-- ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return (char *) &p[i]; + } + } + + return (char *) &p[i]; +} + +/* + * Copy one UTF-8 character from src to dst returning + * number of bytes copied. + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +int ldap_utf8_copy( char* dst, const char *src ) +{ + int i; + const unsigned char *u = (const unsigned char *) src; + + dst[0] = src[0]; + + if( LDAP_UTF8_ISASCII(u) ) { + return 1; + } + + for( i=1; i<6; i++ ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return i; + } + dst[i] = src[i]; + } + + return i; +} + +#ifndef UTF8_ALPHA_CTYPE +/* + * UTF-8 ctype routines + * Only deals with characters < 0x80 (ie: US-ASCII) + */ + +int ldap_utf8_isascii( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + return LDAP_ASCII(c); +} + +int ldap_utf8_isdigit( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_DIGIT( c ); +} + +int ldap_utf8_isxdigit( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_HEX(c); +} + +int ldap_utf8_isspace( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + switch(c) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\v': + case '\f': + return 1; + } + + return 0; +} + +/* + * These are not needed by the C SDK and are + * not "good enough" for general use. + */ +int ldap_utf8_isalpha( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_ALPHA(c); +} + +int ldap_utf8_isalnum( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_ALNUM(c); +} + +int ldap_utf8_islower( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_LOWER(c); +} + +int ldap_utf8_isupper( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_UPPER(c); +} +#endif + + +/* + * UTF-8 string routines + */ + +/* like strchr() */ +char * (ldap_utf8_strchr)( const char *str, const char *chr ) +{ + for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) { + if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( chr ) ) { + return (char *) str; + } + } + + return NULL; +} + +/* like strcspn() but returns number of bytes, not characters */ +ber_len_t (ldap_utf8_strcspn)( const char *str, const char *set ) +{ + const char *cstr; + const char *cset; + + for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) { + for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) { + if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) { + return cstr - str; + } + } + } + + return cstr - str; +} + +/* like strspn() but returns number of bytes, not characters */ +ber_len_t (ldap_utf8_strspn)( const char *str, const char *set ) +{ + const char *cstr; + const char *cset; + + for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) { + for( cset = set; ; LDAP_UTF8_INCR(cset) ) { + if( *cset == '\0' ) { + return cstr - str; + } + + if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) { + break; + } + } + } + + return cstr - str; +} + +/* like strpbrk(), replaces strchr() as well */ +char *(ldap_utf8_strpbrk)( const char *str, const char *set ) +{ + for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) { + const char *cset; + + for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) { + if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( cset ) ) { + return (char *) str; + } + } + } + + return NULL; +} + +/* like strtok_r(), not strtok() */ +char *(ldap_utf8_strtok)(char *str, const char *sep, char **last) +{ + char *begin; + char *end; + + if( last == NULL ) return NULL; + + begin = str ? str : *last; + + begin += ldap_utf8_strspn( begin, sep ); + + if( *begin == '\0' ) { + *last = NULL; + return NULL; + } + + end = &begin[ ldap_utf8_strcspn( begin, sep ) ]; + + if( *end != '\0' ) { + char *next = LDAP_UTF8_NEXT( end ); + *end = '\0'; + end = next; + } + + *last = end; + return begin; +} diff --git a/contrib/libs/openldap/libraries/libldap/util-int.c b/contrib/libs/openldap/libraries/libldap/util-int.c new file mode 100644 index 0000000000..57c6523381 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/util-int.c @@ -0,0 +1,1026 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 1998 A. Hartgers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Bart Hartgers for inclusion in + * OpenLDAP Software. + */ + +/* + * util-int.c Various functions to replace missing threadsafe ones. + * Without the real *_r funcs, things will + * work, but might not be threadsafe. + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" + +#ifndef h_errno +/* newer systems declare this in <netdb.h> for you, older ones don't. + * harmless to declare it again (unless defined by a macro). + */ +extern int h_errno; +#endif + +#ifdef HAVE_HSTRERROR +# define HSTRERROR(e) hstrerror(e) +#else +# define HSTRERROR(e) hp_strerror(e) +#endif + +#ifndef LDAP_R_COMPILE +# undef HAVE_REENTRANT_FUNCTIONS +# undef HAVE_CTIME_R +# undef HAVE_GETHOSTBYNAME_R +# undef HAVE_GETHOSTBYADDR_R + +#else +# include <ldap_pvt_thread.h> + ldap_pvt_thread_mutex_t ldap_int_resolv_mutex; + ldap_pvt_thread_mutex_t ldap_int_hostname_mutex; + static ldap_pvt_thread_mutex_t ldap_int_gettime_mutex; + +# if (defined( HAVE_CTIME_R ) || defined( HAVE_REENTRANT_FUNCTIONS)) \ + && defined( CTIME_R_NARGS ) +# define USE_CTIME_R +# else + static ldap_pvt_thread_mutex_t ldap_int_ctime_mutex; +# endif + +/* USE_GMTIME_R and USE_LOCALTIME_R defined in ldap_pvt.h */ + +#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) + /* we use the same mutex for gmtime(3) and localtime(3) + * because implementations may use the same buffer + * for both functions */ + static ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex; +#endif + +# if defined(HAVE_GETHOSTBYNAME_R) && \ + (GETHOSTBYNAME_R_NARGS < 5) || (6 < GETHOSTBYNAME_R_NARGS) + /* Don't know how to handle this version, pretend it's not there */ +# undef HAVE_GETHOSTBYNAME_R +# endif +# if defined(HAVE_GETHOSTBYADDR_R) && \ + (GETHOSTBYADDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS) + /* Don't know how to handle this version, pretend it's not there */ +# undef HAVE_GETHOSTBYADDR_R +# endif +#endif /* LDAP_R_COMPILE */ + +char *ldap_pvt_ctime( const time_t *tp, char *buf ) +{ +#ifdef USE_CTIME_R +# if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2) +# error "CTIME_R_NARGS should be 2 or 3" +# elif CTIME_R_NARGS > 2 && defined(CTIME_R_RETURNS_INT) + return( ctime_r(tp,buf,26) < 0 ? 0 : buf ); +# elif CTIME_R_NARGS > 2 + return ctime_r(tp,buf,26); +# else + return ctime_r(tp,buf); +# endif + +#else + + LDAP_MUTEX_LOCK( &ldap_int_ctime_mutex ); + AC_MEMCPY( buf, ctime(tp), 26 ); + LDAP_MUTEX_UNLOCK( &ldap_int_ctime_mutex ); + + return buf; +#endif +} + +#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) +int +ldap_pvt_gmtime_lock( void ) +{ +# ifndef LDAP_R_COMPILE + return 0; +# else /* LDAP_R_COMPILE */ + return ldap_pvt_thread_mutex_lock( &ldap_int_gmtime_mutex ); +# endif /* LDAP_R_COMPILE */ +} + +int +ldap_pvt_gmtime_unlock( void ) +{ +# ifndef LDAP_R_COMPILE + return 0; +# else /* LDAP_R_COMPILE */ + return ldap_pvt_thread_mutex_unlock( &ldap_int_gmtime_mutex ); +# endif /* LDAP_R_COMPILE */ +} +#endif /* !USE_GMTIME_R || !USE_LOCALTIME_R */ + +#ifndef USE_GMTIME_R +struct tm * +ldap_pvt_gmtime( const time_t *timep, struct tm *result ) +{ + struct tm *tm_ptr; + + LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); + tm_ptr = gmtime( timep ); + if ( tm_ptr == NULL ) { + result = NULL; + + } else { + *result = *tm_ptr; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); + + return result; +} +#endif /* !USE_GMTIME_R */ + +#ifndef USE_LOCALTIME_R +struct tm * +ldap_pvt_localtime( const time_t *timep, struct tm *result ) +{ + struct tm *tm_ptr; + + LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); + tm_ptr = localtime( timep ); + if ( tm_ptr == NULL ) { + result = NULL; + + } else { + *result = *tm_ptr; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); + + return result; +} +#endif /* !USE_LOCALTIME_R */ + +static int _ldap_pvt_gt_subs; + +#ifdef _WIN32 +/* Windows SYSTEMTIME only has 10 millisecond resolution, so we + * also need to use a high resolution timer to get nanoseconds. + * This is pretty clunky. + */ +static LARGE_INTEGER _ldap_pvt_gt_freq; +static LARGE_INTEGER _ldap_pvt_gt_prev; +static int _ldap_pvt_gt_offset; + +#define SEC_TO_UNIX_EPOCH 11644473600LL +#define TICKS_PER_SECOND 10000000 +#define BILLION 1000000000L + +static int +ldap_pvt_gettimensec(int *sec) +{ + LARGE_INTEGER count; + + QueryPerformanceCounter( &count ); + + /* It shouldn't ever go backwards, but multiple CPUs might + * be able to hit in the same tick. + */ + LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); + /* We assume Windows has at least a vague idea of + * when a second begins. So we align our nanosecond count + * with the Windows millisecond count using this offset. + * We retain the submillisecond portion of our own count. + * + * Note - this also assumes that the relationship between + * the PerformanceCounter and SystemTime stays constant; + * that assumption breaks if the SystemTime is adjusted by + * an external action. + */ + if ( !_ldap_pvt_gt_freq.QuadPart ) { + LARGE_INTEGER c2; + ULARGE_INTEGER ut; + FILETIME ft0, ft1; + long long t; + int nsec; + + /* Initialize our offset */ + QueryPerformanceFrequency( &_ldap_pvt_gt_freq ); + + /* Wait for a tick of the system time: 10-15ms */ + GetSystemTimeAsFileTime( &ft0 ); + do { + GetSystemTimeAsFileTime( &ft1 ); + } while ( ft1.dwLowDateTime == ft0.dwLowDateTime ); + + ut.LowPart = ft1.dwLowDateTime; + ut.HighPart = ft1.dwHighDateTime; + QueryPerformanceCounter( &c2 ); + + /* get second and fraction portion of counter */ + t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10); + + /* convert to nanoseconds */ + t *= BILLION; + nsec = t / _ldap_pvt_gt_freq.QuadPart; + + ut.QuadPart /= 10; + ut.QuadPart %= (10 * BILLION); + _ldap_pvt_gt_offset = nsec - ut.QuadPart; + count = c2; + } + if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) { + _ldap_pvt_gt_subs++; + } else { + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prev = count; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); + + /* convert to nanoseconds */ + count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10; + count.QuadPart *= BILLION; + count.QuadPart /= _ldap_pvt_gt_freq.QuadPart; + count.QuadPart -= _ldap_pvt_gt_offset; + + /* We've extracted the 1s and nanoseconds. + * The 1sec digit is used to detect wraparound in nanosecnds. + */ + if (count.QuadPart < 0) + count.QuadPart += (10 * BILLION); + else if (count.QuadPart >= (10 * BILLION)) + count.QuadPart -= (10 * BILLION); + + *sec = count.QuadPart / BILLION; + return count.QuadPart % BILLION; +} + + +/* emulate POSIX clock_gettime */ +int +ldap_pvt_clock_gettime( int clk_id, struct timespec *tv ) +{ + FILETIME ft; + ULARGE_INTEGER ut; + int sec, sec0; + + GetSystemTimeAsFileTime( &ft ); + ut.LowPart = ft.dwLowDateTime; + ut.HighPart = ft.dwHighDateTime; + + /* convert to sec */ + ut.QuadPart /= TICKS_PER_SECOND; + + tv->tv_nsec = ldap_pvt_gettimensec(&sec); + tv->tv_sec = ut.QuadPart - SEC_TO_UNIX_EPOCH; + + /* check for carry from microseconds */ + sec0 = tv->tv_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) + tv->tv_sec++; + + return 0; +} + +/* emulate POSIX gettimeofday */ +int +ldap_pvt_gettimeofday( struct timeval *tv, void *unused ) +{ + struct timespec ts; + ldap_pvt_clock_gettime( 0, &ts ); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + return 0; +} + + +/* return a broken out time, with nanoseconds + */ +void +ldap_pvt_gettime( struct lutil_tm *tm ) +{ + SYSTEMTIME st; + int sec, sec0; + static const char daysPerMonth[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + GetSystemTime( &st ); + tm->tm_nsec = ldap_pvt_gettimensec(&sec); + tm->tm_usub = _ldap_pvt_gt_subs; + + /* any difference larger than nanoseconds is + * already reflected in st + */ + tm->tm_sec = st.wSecond; + tm->tm_min = st.wMinute; + tm->tm_hour = st.wHour; + tm->tm_mday = st.wDay; + tm->tm_mon = st.wMonth - 1; + tm->tm_year = st.wYear - 1900; + + /* check for carry from nanoseconds */ + sec0 = tm->tm_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) { + tm->tm_sec++; + /* FIXME: we don't handle leap seconds */ + if (tm->tm_sec > 59) { + tm->tm_sec = 0; + tm->tm_min++; + if (tm->tm_min > 59) { + tm->tm_min = 0; + tm->tm_hour++; + if (tm->tm_hour > 23) { + int days = daysPerMonth[tm->tm_mon]; + tm->tm_hour = 0; + tm->tm_mday++; + + /* if it's February of a leap year, + * add 1 day to this month + */ + if (tm->tm_mon == 1 && + ((!(st.wYear % 4) && (st.wYear % 100)) || + !(st.wYear % 400))) + days++; + + if (tm->tm_mday > days) { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon > 11) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + } + } + } + } +} +#else + +#ifdef HAVE_CLOCK_GETTIME +static struct timespec _ldap_pvt_gt_prevTv; +#else +static struct timeval _ldap_pvt_gt_prevTv; +#endif + +void +ldap_pvt_gettime( struct lutil_tm *ltm ) +{ + struct tm tm; + time_t t; +#ifdef HAVE_CLOCK_GETTIME +#define FRAC tv_nsec +#define NSECS(x) x + struct timespec tv; + + clock_gettime( CLOCK_REALTIME, &tv ); +#else +#define FRAC tv_usec +#define NSECS(x) x * 1000 + struct timeval tv; + + gettimeofday( &tv, NULL ); +#endif + t = tv.tv_sec; + + LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); + if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec + || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec + && tv.FRAC <= _ldap_pvt_gt_prevTv.FRAC )) { + _ldap_pvt_gt_subs++; + } else { + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prevTv = tv; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); + + ltm->tm_usub = _ldap_pvt_gt_subs; + + ldap_pvt_gmtime( &t, &tm ); + + ltm->tm_sec = tm.tm_sec; + ltm->tm_min = tm.tm_min; + ltm->tm_hour = tm.tm_hour; + ltm->tm_mday = tm.tm_mday; + ltm->tm_mon = tm.tm_mon; + ltm->tm_year = tm.tm_year; + ltm->tm_nsec = NSECS(tv.FRAC); +} +#endif + +size_t +ldap_pvt_csnstr(char *buf, size_t len, unsigned int replica, unsigned int mod) +{ + struct lutil_tm tm; + int n; + + ldap_pvt_gettime( &tm ); + + n = snprintf( buf, len, + "%4d%02d%02d%02d%02d%02d.%06dZ#%06x#%03x#%06x", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec, tm.tm_nsec / 1000, tm.tm_usub, replica, mod ); + + if( n < 0 ) return 0; + return ( (size_t) n < len ) ? n : 0; +} + +#define BUFSTART (1024-32) +#define BUFMAX (32*1024-32) + +#if defined(LDAP_R_COMPILE) +static char *safe_realloc( char **buf, int len ); + +#if !(defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETHOSTBYADDR_R)) +static int copy_hostent( struct hostent *res, + char **buf, struct hostent * src ); +#endif +#endif + +int ldap_pvt_gethostbyname_a( + const char *name, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr ) +{ +#if defined( HAVE_GETHOSTBYNAME_R ) + +# define NEED_SAFE_REALLOC 1 + int r=-1; + int buflen=BUFSTART; + *buf = NULL; + for(;buflen<BUFMAX;) { + if (safe_realloc( buf, buflen )==NULL) + return r; + +#if (GETHOSTBYNAME_R_NARGS < 6) + *result=gethostbyname_r( name, resbuf, *buf, buflen, herrno_ptr ); + r = (*result == NULL) ? -1 : 0; +#else + while((r = gethostbyname_r( name, resbuf, *buf, buflen, result, herrno_ptr )) == ERANGE) { + /* Increase the buffer */ + buflen*=2; + if (safe_realloc(buf, buflen) == NULL) + return -1; + } +#endif + + Debug2( LDAP_DEBUG_TRACE, "ldap_pvt_gethostbyname_a: host=%s, r=%d\n", + name, r ); + +#ifdef NETDB_INTERNAL + if ((r<0) && + (*herrno_ptr==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + return r; + } + return -1; +#elif defined( LDAP_R_COMPILE ) +# define NEED_COPY_HOSTENT + struct hostent *he; + int retval; + *buf = NULL; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + + he = gethostbyname( name ); + + if (he==NULL) { + *herrno_ptr = h_errno; + retval = -1; + } else if (copy_hostent( resbuf, buf, he )<0) { + *herrno_ptr = -1; + retval = -1; + } else { + *result = resbuf; + retval = 0; + } + + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + + return retval; +#else + *buf = NULL; + *result = gethostbyname( name ); + + if (*result!=NULL) { + return 0; + } + + *herrno_ptr = h_errno; + + return -1; +#endif +} + +#if !defined( HAVE_GETNAMEINFO ) && !defined( HAVE_HSTRERROR ) +static const char * +hp_strerror( int err ) +{ + switch (err) { + case HOST_NOT_FOUND: return _("Host not found (authoritative)"); + case TRY_AGAIN: return _("Host not found (server fail?)"); + case NO_RECOVERY: return _("Non-recoverable failure"); + case NO_DATA: return _("No data of requested type"); +#ifdef NETDB_INTERNAL + case NETDB_INTERNAL: return STRERROR( errno ); +#endif + } + return _("Unknown resolver error"); +} +#endif + +int ldap_pvt_get_hname( + const struct sockaddr *sa, + int len, + char *name, + int namelen, + char **err ) +{ + int rc; +#if defined( HAVE_GETNAMEINFO ) + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + rc = getnameinfo( sa, len, name, namelen, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + if ( rc ) *err = (char *)AC_GAI_STRERROR( rc ); + return rc; + +#else /* !HAVE_GETNAMEINFO */ + char *addr; + int alen; + struct hostent *hp = NULL; +#ifdef HAVE_GETHOSTBYADDR_R + struct hostent hb; + int buflen=BUFSTART, h_errno; + char *buf=NULL; +#endif + +#ifdef LDAP_PF_INET6 + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; + addr = (char *)&sin->sin6_addr; + alen = sizeof(sin->sin6_addr); + } else +#endif + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + addr = (char *)&sin->sin_addr; + alen = sizeof(sin->sin_addr); + } else { + rc = NO_RECOVERY; + *err = (char *)HSTRERROR( rc ); + return rc; + } +#if defined( HAVE_GETHOSTBYADDR_R ) + for(;buflen<BUFMAX;) { + if (safe_realloc( &buf, buflen )==NULL) { + *err = (char *)STRERROR( ENOMEM ); + return ENOMEM; + } +#if (GETHOSTBYADDR_R_NARGS < 8) + hp=gethostbyaddr_r( addr, alen, sa->sa_family, + &hb, buf, buflen, &h_errno ); + rc = (hp == NULL) ? -1 : 0; +#else + rc = gethostbyaddr_r( addr, alen, sa->sa_family, + &hb, buf, buflen, + &hp, &h_errno ); +#endif +#ifdef NETDB_INTERNAL + if ((rc<0) && + (h_errno==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + break; + } + if (hp) { + strncpy( name, hp->h_name, namelen ); + } else { + *err = (char *)HSTRERROR( h_errno ); + } + LDAP_FREE(buf); +#else /* HAVE_GETHOSTBYADDR_R */ + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + hp = gethostbyaddr( addr, alen, sa->sa_family ); + if (hp) { + strncpy( name, hp->h_name, namelen ); + rc = 0; + } else { + rc = h_errno; + *err = (char *)HSTRERROR( h_errno ); + } + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + +#endif /* !HAVE_GETHOSTBYADDR_R */ + return rc; +#endif /* !HAVE_GETNAMEINFO */ +} + +int ldap_pvt_gethostbyaddr_a( + const char *addr, + int len, + int type, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr ) +{ +#if defined( HAVE_GETHOSTBYADDR_R ) + +# undef NEED_SAFE_REALLOC +# define NEED_SAFE_REALLOC + int r=-1; + int buflen=BUFSTART; + *buf = NULL; + for(;buflen<BUFMAX;) { + if (safe_realloc( buf, buflen )==NULL) + return r; +#if (GETHOSTBYADDR_R_NARGS < 8) + *result=gethostbyaddr_r( addr, len, type, + resbuf, *buf, buflen, herrno_ptr ); + r = (*result == NULL) ? -1 : 0; +#else + r = gethostbyaddr_r( addr, len, type, + resbuf, *buf, buflen, + result, herrno_ptr ); +#endif + +#ifdef NETDB_INTERNAL + if ((r<0) && + (*herrno_ptr==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + return r; + } + return -1; +#elif defined( LDAP_R_COMPILE ) +# undef NEED_COPY_HOSTENT +# define NEED_COPY_HOSTENT + struct hostent *he; + int retval; + *buf = NULL; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + he = gethostbyaddr( addr, len, type ); + + if (he==NULL) { + *herrno_ptr = h_errno; + retval = -1; + } else if (copy_hostent( resbuf, buf, he )<0) { + *herrno_ptr = -1; + retval = -1; + } else { + *result = resbuf; + retval = 0; + } + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + + return retval; + +#else /* gethostbyaddr() */ + *buf = NULL; + *result = gethostbyaddr( addr, len, type ); + + if (*result!=NULL) { + return 0; + } + return -1; +#endif +} +/* + * ldap_int_utils_init() should be called before any other function. + */ + +void ldap_int_utils_init( void ) +{ + static int done=0; + if (done) + return; + done=1; + +#ifdef LDAP_R_COMPILE +#if !defined( USE_CTIME_R ) && !defined( HAVE_REENTRANT_FUNCTIONS ) + ldap_pvt_thread_mutex_init( &ldap_int_ctime_mutex ); +#endif +#if !defined( USE_GMTIME_R ) && !defined( USE_LOCALTIME_R ) + ldap_pvt_thread_mutex_init( &ldap_int_gmtime_mutex ); +#endif + ldap_pvt_thread_mutex_init( &ldap_int_resolv_mutex ); + + ldap_pvt_thread_mutex_init( &ldap_int_hostname_mutex ); + + ldap_pvt_thread_mutex_init( &ldap_int_gettime_mutex ); + +#endif + + /* call other module init functions here... */ +} + +#if defined( NEED_COPY_HOSTENT ) +# undef NEED_SAFE_REALLOC +#define NEED_SAFE_REALLOC + +static char *cpy_aliases( + char ***tgtio, + char *buf, + char **src ) +{ + int len; + char **tgt=*tgtio; + for( ; (*src) ; src++ ) { + len = strlen( *src ) + 1; + AC_MEMCPY( buf, *src, len ); + *tgt++=buf; + buf+=len; + } + *tgtio=tgt; + return buf; +} + +static char *cpy_addresses( + char ***tgtio, + char *buf, + char **src, + int len ) +{ + char **tgt=*tgtio; + for( ; (*src) ; src++ ) { + AC_MEMCPY( buf, *src, len ); + *tgt++=buf; + buf+=len; + } + *tgtio=tgt; + return buf; +} + +static int copy_hostent( + struct hostent *res, + char **buf, + struct hostent * src ) +{ + char **p; + char **tp; + char *tbuf; + int name_len; + int n_alias=0; + int total_alias_len=0; + int n_addr=0; + int total_addr_len=0; + int total_len; + + /* calculate the size needed for the buffer */ + name_len = strlen( src->h_name ) + 1; + + if( src->h_aliases != NULL ) { + for( p = src->h_aliases; (*p) != NULL; p++ ) { + total_alias_len += strlen( *p ) + 1; + n_alias++; + } + } + + if( src->h_addr_list != NULL ) { + for( p = src->h_addr_list; (*p) != NULL; p++ ) { + n_addr++; + } + total_addr_len = n_addr * src->h_length; + } + + total_len = (n_alias + n_addr + 2) * sizeof( char * ) + + total_addr_len + total_alias_len + name_len; + + if (safe_realloc( buf, total_len )) { + tp = (char **) *buf; + tbuf = *buf + (n_alias + n_addr + 2) * sizeof( char * ); + AC_MEMCPY( res, src, sizeof( struct hostent ) ); + /* first the name... */ + AC_MEMCPY( tbuf, src->h_name, name_len ); + res->h_name = tbuf; tbuf+=name_len; + /* now the aliases */ + res->h_aliases = tp; + if ( src->h_aliases != NULL ) { + tbuf = cpy_aliases( &tp, tbuf, src->h_aliases ); + } + *tp++=NULL; + /* finally the addresses */ + res->h_addr_list = tp; + if ( src->h_addr_list != NULL ) { + tbuf = cpy_addresses( &tp, tbuf, src->h_addr_list, src->h_length ); + } + *tp++=NULL; + return 0; + } + return -1; +} +#endif + +#if defined( NEED_SAFE_REALLOC ) +static char *safe_realloc( char **buf, int len ) +{ + char *tmpbuf; + tmpbuf = LDAP_REALLOC( *buf, len ); + if (tmpbuf) { + *buf=tmpbuf; + } + return tmpbuf; +} +#endif + +char * ldap_pvt_get_fqdn( char *name ) +{ +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res; +#else + char *ha_buf; + struct hostent *hp, he_buf; + int local_h_errno; +#endif + int rc; + char *fqdn, hostbuf[MAXHOSTNAMELEN+1]; + + if( name == NULL ) { + if( gethostname( hostbuf, MAXHOSTNAMELEN ) == 0 ) { + hostbuf[MAXHOSTNAMELEN] = '\0'; + name = hostbuf; + } else { + name = "localhost"; + } + } + +#ifdef HAVE_GETADDRINFO + memset( &hints, 0, sizeof( hints )); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + rc = getaddrinfo( name, NULL, &hints, &res ); + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + if ( rc == 0 && res->ai_canonname ) { + fqdn = LDAP_STRDUP( res->ai_canonname ); + } else { + fqdn = LDAP_STRDUP( name ); + } + if ( rc == 0 ) + freeaddrinfo( res ); +#else + rc = ldap_pvt_gethostbyname_a( name, + &he_buf, &ha_buf, &hp, &local_h_errno ); + + if( rc < 0 || hp == NULL || hp->h_name == NULL ) { + fqdn = LDAP_STRDUP( name ); + } else { + fqdn = LDAP_STRDUP( hp->h_name ); + } + + LDAP_FREE( ha_buf ); +#endif + return fqdn; +} + +#if ( defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) ) \ + && !defined( HAVE_GAI_STRERROR ) +char *ldap_pvt_gai_strerror (int code) { + static struct { + int code; + const char *msg; + } values[] = { +#ifdef EAI_ADDRFAMILY + { EAI_ADDRFAMILY, N_("Address family for hostname not supported") }, +#endif + { EAI_AGAIN, N_("Temporary failure in name resolution") }, + { EAI_BADFLAGS, N_("Bad value for ai_flags") }, + { EAI_FAIL, N_("Non-recoverable failure in name resolution") }, + { EAI_FAMILY, N_("ai_family not supported") }, + { EAI_MEMORY, N_("Memory allocation failure") }, +#ifdef EAI_NODATA + { EAI_NODATA, N_("No address associated with hostname") }, +#endif + { EAI_NONAME, N_("Name or service not known") }, + { EAI_SERVICE, N_("Servname not supported for ai_socktype") }, + { EAI_SOCKTYPE, N_("ai_socktype not supported") }, +#ifdef EAI_SYSTEM + { EAI_SYSTEM, N_("System error") }, +#endif + { 0, NULL } + }; + + int i; + + for ( i = 0; values[i].msg != NULL; i++ ) { + if ( values[i].code == code ) { + return (char *) _(values[i].msg); + } + } + + return _("Unknown error"); +} +#endif + +/* format a socket address as a string */ + +#ifdef HAVE_TCPD +# include <tcpd.h> +# define SOCKADDR_STRING_UNKNOWN STRING_UNKNOWN +#else /* ! TCP Wrappers */ +# define SOCKADDR_STRING_UNKNOWN "unknown" +#endif /* ! TCP Wrappers */ + +void +ldap_pvt_sockaddrstr( Sockaddr *sa, struct berval *addrbuf ) +{ + char *addr; + switch( sa->sa_addr.sa_family ) { +#ifdef LDAP_PF_LOCAL + case AF_LOCAL: + addrbuf->bv_len = snprintf( addrbuf->bv_val, addrbuf->bv_len, + "PATH=%s", sa->sa_un_addr.sun_path ); + break; +#endif +#ifdef LDAP_PF_INET6 + case AF_INET6: + strcpy(addrbuf->bv_val, "IP="); + if ( IN6_IS_ADDR_V4MAPPED(&sa->sa_in6_addr.sin6_addr) ) { +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + addr = (char *)inet_ntop( AF_INET, + ((struct in_addr *)&sa->sa_in6_addr.sin6_addr.s6_addr[12]), + addrbuf->bv_val+3, addrbuf->bv_len-3 ); +#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */ + addr = inet_ntoa( *((struct in_addr *) + &sa->sa_in6_addr.sin6_addr.s6_addr[12]) ); +#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */ + if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN; + if ( addr != addrbuf->bv_val+3 ) { + addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "%s:%d", addr, + (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + 3; + } else { + int len = strlen( addr ); + addrbuf->bv_len = sprintf( addr+len, ":%d", + (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + len + 3; + } + } else { + addr = (char *)inet_ntop( AF_INET6, + &sa->sa_in6_addr.sin6_addr, + addrbuf->bv_val+4, addrbuf->bv_len-4 ); + if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN; + if ( addr != addrbuf->bv_val+4 ) { + addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "[%s]:%d", addr, + (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + 3; + } else { + int len = strlen( addr ); + addrbuf->bv_val[3] = '['; + addrbuf->bv_len = sprintf( addr+len, "]:%d", + (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + len + 4; + } + } + break; +#endif /* LDAP_PF_INET6 */ + case AF_INET: + strcpy(addrbuf->bv_val, "IP="); +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + addr = (char *)inet_ntop( AF_INET, &sa->sa_in_addr.sin_addr, + addrbuf->bv_val+3, addrbuf->bv_len-3 ); +#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */ + addr = inet_ntoa( sa->sa_in_addr.sin_addr ); +#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */ + if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN; + if ( addr != addrbuf->bv_val+3 ) { + addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "%s:%d", addr, + (unsigned) ntohs( sa->sa_in_addr.sin_port ) ) + 3; + } else { + int len = strlen( addr ); + addrbuf->bv_len = sprintf( addr+len, ":%d", + (unsigned) ntohs( sa->sa_in_addr.sin_port ) ) + len + 3; + } + break; + default: + addrbuf->bv_val[0] = '\0'; + } +} diff --git a/contrib/libs/openldap/libraries/libldap/vc.c b/contrib/libs/openldap/libraries/libldap/vc.c new file mode 100644 index 0000000000..0fc29a8c9a --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/vc.c @@ -0,0 +1,367 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * LDAP Verify Credentials operation + * + * The request is an extended request with OID 1.3.6.1.4.1.4203.666.6.5 with value of + * the BER encoding of: + * + * VCRequest ::= SEQUENCE { + * cookie [0] OCTET STRING OPTIONAL, + * name LDAPDN, + * authentication AuthenticationChoice, + * controls [2] Controls OPTIONAL + * } + * + * where LDAPDN, AuthenticationChoice, and Controls are as defined in RFC 4511. + * + * The response is an extended response with no OID and a value of the BER encoding of + * + * VCResponse ::= SEQUENCE { + * resultCode ResultCode, + * diagnosticMessage LDAPString, + * cookie [0] OCTET STRING OPTIONAL, + * serverSaslCreds [1] OCTET STRING OPTIONAL, + * controls [2] Controls OPTIONAL + * } + * + * where ResultCode is the result code enumeration from RFC 4511, and LDAPString and Controls are as + * defined in RFC 4511. + */ + +int ldap_parse_verify_credentials( + LDAP *ld, + LDAPMessage *res, + int * code, + char ** diagmsg, + struct berval **cookie, + struct berval **screds, + LDAPControl ***ctrls) +{ + int rc; + char *retoid = NULL; + struct berval *retdata = NULL; + + assert(ld != NULL); + assert(LDAP_VALID(ld)); + assert(res != NULL); + assert(code != NULL); + assert(diagmsg != NULL); + + rc = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0); + + if( rc != LDAP_SUCCESS ) { + ldap_perror(ld, "ldap_parse_verify_credentials"); + return rc; + } + + if (retdata) { + ber_tag_t tag; + ber_len_t len; + ber_int_t i; + BerElement * ber = ber_init(retdata); + struct berval diagmsg_bv = BER_BVNULL; + if (!ber) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + rc = LDAP_DECODING_ERROR; + + if (ber_scanf(ber, "{im" /*"}"*/, &i, &diagmsg_bv) == LBER_ERROR) { + goto ber_done; + } + if ( diagmsg != NULL ) { + *diagmsg = LDAP_MALLOC( diagmsg_bv.bv_len + 1 ); + AC_MEMCPY( *diagmsg, diagmsg_bv.bv_val, diagmsg_bv.bv_len ); + (*diagmsg)[diagmsg_bv.bv_len] = '\0'; + } + *code = i; + + tag = ber_peek_tag(ber, &len); + if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE) { + if (ber_scanf(ber, "O", cookie) == LBER_ERROR) + goto ber_done; + tag = ber_peek_tag(ber, &len); + } + + if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS) { + if (ber_scanf(ber, "O", screds) == LBER_ERROR) + goto ber_done; + tag = ber_peek_tag(ber, &len); + } + + if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS) { + int nctrls = 0; + char * opaque; + + *ctrls = LDAP_MALLOC(1 * sizeof(LDAPControl *)); + + if (!*ctrls) { + rc = LDAP_NO_MEMORY; + goto ber_done; + } + + *ctrls[nctrls] = NULL; + + for(tag = ber_first_element(ber, &len, &opaque); + tag != LBER_ERROR; + tag = ber_next_element(ber, &len, opaque)) + { + LDAPControl *tctrl; + LDAPControl **tctrls; + + tctrl = LDAP_CALLOC(1, sizeof(LDAPControl)); + + /* allocate pointer space for current controls (nctrls) + * + this control + extra NULL + */ + tctrls = !tctrl ? NULL : LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *)); + + if (!tctrls) { + /* allocation failure */ + if (tctrl) LDAP_FREE(tctrl); + ldap_controls_free(*ctrls); + *ctrls = NULL; + rc = LDAP_NO_MEMORY; + goto ber_done; + } + + tctrls[nctrls++] = tctrl; + tctrls[nctrls] = NULL; + + tag = ber_scanf(ber, "{a" /*"}"*/, &tctrl->ldctl_oid); + if (tag == LBER_ERROR) { + *ctrls = NULL; + ldap_controls_free(tctrls); + goto ber_done; + } + + tag = ber_peek_tag(ber, &len); + if (tag == LBER_BOOLEAN) { + ber_int_t crit; + tag = ber_scanf(ber, "b", &crit); + tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0; + tag = ber_peek_tag(ber, &len); + } + + if (tag == LBER_OCTETSTRING) { + tag = ber_scanf( ber, "o", &tctrl->ldctl_value ); + } else { + BER_BVZERO( &tctrl->ldctl_value ); + } + + *ctrls = tctrls; + } + } + + rc = LDAP_SUCCESS; + + ber_done: + ber_free(ber, 1); + } + +done: + ber_bvfree(retdata); + ber_memfree(retoid); + return rc; +} + +int +ldap_verify_credentials(LDAP *ld, + struct berval *cookie, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **vcctrls, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp) +{ + int rc; + BerElement *ber; + struct berval reqdata; + + assert(ld != NULL); + assert(LDAP_VALID(ld)); + assert(msgidp != NULL); + + ber = ber_alloc_t(LBER_USE_DER); + if (dn == NULL) dn = ""; + + if (mechanism == LDAP_SASL_SIMPLE) { + assert(!cookie); + + rc = ber_printf(ber, "{stO" /*"}"*/, + dn, LDAP_AUTH_SIMPLE, cred); + + } else { + if (!cred || BER_BVISNULL(cred)) { + if (cookie) { + rc = ber_printf(ber, "{tOst{sN}" /*"}"*/, + LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, cookie, + dn, LDAP_AUTH_SASL, mechanism); + } else { + rc = ber_printf(ber, "{st{sN}N" /*"}"*/, + dn, LDAP_AUTH_SASL, mechanism); + } + } else { + if (cookie) { + rc = ber_printf(ber, "{tOst{sON}" /*"}"*/, + LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, cookie, + dn, LDAP_AUTH_SASL, mechanism, cred); + } else { + rc = ber_printf(ber, "{st{sON}" /*"}"*/, + dn, LDAP_AUTH_SASL, mechanism, cred); + } + } + } + + if (rc < 0) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if (vcctrls && *vcctrls) { + LDAPControl *const *c; + + rc = ber_printf(ber, "t{" /*"}"*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS); + + for (c=vcctrls; *c; c++) { + rc = ldap_pvt_put_control(*c, ber); + if (rc != LDAP_SUCCESS) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + rc = ber_printf(ber, /*"{{"*/ "}N}"); + + } else { + rc = ber_printf(ber, /*"{"*/ "N}"); + } + + if (rc < 0) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + + rc = ber_flatten2(ber, &reqdata, 0); + if (rc < 0) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + rc = ldap_extended_operation(ld, LDAP_EXOP_VERIFY_CREDENTIALS, + &reqdata, sctrls, cctrls, msgidp); + +done: + ber_free(ber, 1); + return rc; +} + +int +ldap_verify_credentials_s( + LDAP *ld, + struct berval *cookie, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **vcictrls, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *rcode, + char **diagmsg, + struct berval **scookie, + struct berval **scred, + LDAPControl ***vcoctrls) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_verify_credentials(ld, cookie, dn, mechanism, cred, vcictrls, sctrls, cctrls, &msgid); + if (rc != LDAP_SUCCESS) return rc; + + if (ldap_result(ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res) == -1 || !res) { + return ld->ld_errno; + } + + rc = ldap_parse_verify_credentials(ld, res, rcode, diagmsg, scookie, scred, vcoctrls); + if (rc != LDAP_SUCCESS) { + ldap_msgfree(res); + return rc; + } + + return( ldap_result2error(ld, res, 1)); +} + +#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE +int +ldap_verify_credentials_interactive ( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mech, + LDAPControl **vcControls, + LDAPControl **serverControls, + LDAPControl **clientControls, + + /* should be client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *proc, + void *defaults, + void *context; + + /* as obtained from ldap_result() */ + LDAPMessage *result, + + /* returned during bind processing */ + const char **rmech, + int *msgid ) +{ + if (!ld && context) { + assert(!dn); + assert(!mech); + assert(!vcControls); + assert(!serverControls); + assert(!defaults); + assert(!result); + assert(!rmech); + assert(!msgid); + + /* special case to avoid having to expose a separate dispose context API */ + sasl_dispose((sasl_conn_t)context); + return LDAP_SUCCESS; + } + + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; +} +#endif diff --git a/contrib/libs/openldap/libraries/libldap/version.c b/contrib/libs/openldap/libraries/libldap/version.c new file mode 100644 index 0000000000..fad5f1a830 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/version.c @@ -0,0 +1,22 @@ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +static const char copyright[] = +"Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.\n" +"COPYING RESTRICTIONS APPLY\n"; + +static const char __Version[] = +"@(#) $OpenLDAP: libldap.la 2.6.6 (" __DATE__ " " __TIME__ ") $\n" +"\topenldap\n"; + diff --git a/contrib/libs/openldap/libraries/libldap/vlvctrl.c b/contrib/libs/openldap/libraries/libldap/vlvctrl.c new file mode 100644 index 0000000000..db009df42f --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/vlvctrl.c @@ -0,0 +1,361 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_VLVBYINDEX_IDENTIFIER 0xa0L +#define LDAP_VLVBYVALUE_IDENTIFIER 0x81L +#define LDAP_VLVCONTEXT_IDENTIFIER 0x04L + + +/*--- + ldap_create_vlv_control + + Create and encode the Virtual List View control. + + ld (IN) An LDAP session handle. + + vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents + are used to construct the value of the control + that is created. + + value (OUT) A struct berval that contains the value to be assigned to the ldctl_value member + of an LDAPControl structure that contains the + VirtualListViewRequest control. + The bv_val member of the berval structure + SHOULD be freed when it is no longer in use by + calling ldap_memfree(). + + + Ber encoding + + VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER (0 .. maxInt), + afterCount INTEGER (0 .. maxInt), + CHOICE { + byoffset [0] SEQUENCE, { + offset INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt) } + [1] greaterThanOrEqual assertionValue } + contextID OCTET STRING OPTIONAL } + + + Note: The first time the VLV control is created, the ldvlv_context + field of the LDAPVLVInfo structure should be set to NULL. + The context obtained from calling ldap_parse_vlv_control() + should be used as the context in the next ldap_create_vlv_control + call. + + ---*/ + +int +ldap_create_vlv_control_value( + LDAP *ld, + LDAPVLVInfo *vlvinfop, + struct berval *value ) +{ + ber_tag_t tag; + BerElement *ber; + + if ( ld == NULL || vlvinfop == NULL || value == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{ii" /*}*/, + vlvinfop->ldvlv_before_count, + vlvinfop->ldvlv_after_count ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( vlvinfop->ldvlv_attrvalue == NULL ) { + tag = ber_printf( ber, "t{iiN}", + LDAP_VLVBYINDEX_IDENTIFIER, + vlvinfop->ldvlv_offset, + vlvinfop->ldvlv_count ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + } else { + tag = ber_printf( ber, "tO", + LDAP_VLVBYVALUE_IDENTIFIER, + vlvinfop->ldvlv_attrvalue ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + if ( vlvinfop->ldvlv_context ) { + tag = ber_printf( ber, "tO", + LDAP_VLVCONTEXT_IDENTIFIER, + vlvinfop->ldvlv_context ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +/*--- + ldap_create_vlv_control + + Create and encode the Virtual List View control. + + ld (IN) An LDAP session handle. + + vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents + are used to construct the value of the control + that is created. + + ctrlp (OUT) A result parameter that will be assigned the address + of an LDAPControl structure that contains the + VirtualListViewRequest control created by this function. + The memory occupied by the LDAPControl structure + SHOULD be freed when it is no longer in use by + calling ldap_control_free(). + + + Ber encoding + + VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER (0 .. maxInt), + afterCount INTEGER (0 .. maxInt), + CHOICE { + byoffset [0] SEQUENCE, { + offset INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt) } + [1] greaterThanOrEqual assertionValue } + contextID OCTET STRING OPTIONAL } + + + Note: The first time the VLV control is created, the ldvlv_context + field of the LDAPVLVInfo structure should be set to NULL. + The context obtained from calling ldap_parse_vlv_control() + should be used as the context in the next ldap_create_vlv_control + call. + + ---*/ + +int +ldap_create_vlv_control( + LDAP *ld, + LDAPVLVInfo *vlvinfop, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_vlv_control_value( ld, vlvinfop, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_VLVREQUEST, + 1, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/*--- + ldap_parse_vlvresponse_control + + Decode the Virtual List View control return information. + + ld (IN) An LDAP session handle. + + ctrl (IN) The address of the LDAPControl structure. + + target_posp (OUT) This result parameter is filled in with the list + index of the target entry. If this parameter is + NULL, the target position is not returned. + + list_countp (OUT) This result parameter is filled in with the server's + estimate of the size of the list. If this parameter + is NULL, the size is not returned. + + contextp (OUT) This result parameter is filled in with the address + of a struct berval that contains the server- + generated context identifier if one was returned by + the server. If the server did not return a context + identifier, this parameter will be set to NULL, even + if an error occurred. + The returned context SHOULD be used in the next call + to create a VLV sort control. The struct berval + returned SHOULD be disposed of by calling ber_bvfree() + when it is no longer needed. If NULL is passed for + contextp, the context identifier is not returned. + + errcodep (OUT) This result parameter is filled in with the VLV + result code. If this parameter is NULL, the result + code is not returned. + + + Ber encoding + + VirtualListViewResponse ::= SEQUENCE { + targetPosition INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt), + virtualListViewResult ENUMERATED { + success (0), + operationsError (1), + unwillingToPerform (53), + insufficientAccessRights (50), + busy (51), + timeLimitExceeded (3), + adminLimitExceeded (11), + sortControlMissing (60), + offsetRangeError (61), + other (80) }, + contextID OCTET STRING OPTIONAL } + +---*/ + +int +ldap_parse_vlvresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *target_posp, + ber_int_t *list_countp, + struct berval **contextp, + ber_int_t *errcodep ) +{ + BerElement *ber; + ber_int_t pos, count, err; + ber_tag_t tag, berTag; + ber_len_t berLen; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if (contextp) { + *contextp = NULL; /* Make sure we return a NULL if error occurs. */ + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if (strcmp(LDAP_CONTROL_VLVRESPONSE, ctrl->ldctl_oid) != 0) { + /* Not VLV Response control */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + /* Extract the data returned in the control. */ + tag = ber_scanf(ber, "{iie" /*}*/, &pos, &count, &err); + + if( tag == LBER_ERROR) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + + /* Since the context is the last item encoded, if caller doesn't want + it returned, don't decode it. */ + if (contextp) { + if (LDAP_VLVCONTEXT_IDENTIFIER == ber_peek_tag(ber, &berLen)) { + tag = ber_scanf(ber, "tO", &berTag, contextp); + + if( tag == LBER_ERROR) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + } + + ber_free(ber, 1); + + /* Return data to the caller for items that were requested. */ + if (target_posp) *target_posp = pos; + if (list_countp) *list_countp = count; + if (errcodep) *errcodep = err; + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/contrib/libs/openldap/libraries/libldap/whoami.c b/contrib/libs/openldap/libraries/libldap/whoami.c new file mode 100644 index 0000000000..f3ebdeb118 --- /dev/null +++ b/contrib/libs/openldap/libraries/libldap/whoami.c @@ -0,0 +1,102 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * LDAP Who Am I? (Extended) Operation <draft-zeilenga-ldap-authzid-xx.txt> + */ + +int ldap_parse_whoami( + LDAP *ld, + LDAPMessage *res, + struct berval **authzid ) +{ + int rc; + char *retoid = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( authzid != NULL ); + + *authzid = NULL; + + rc = ldap_parse_extended_result( ld, res, &retoid, authzid, 0 ); + + if( rc != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_parse_whoami" ); + return rc; + } + + ber_memfree( retoid ); + return rc; +} + +int +ldap_whoami( LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_WHO_AM_I, + NULL, sctrls, cctrls, msgidp ); + + return rc; +} + +int +ldap_whoami_s( + LDAP *ld, + struct berval **authzid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_whoami( ld, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) return rc; + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return ld->ld_errno; + } + + rc = ldap_parse_whoami( ld, res, authzid ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/contrib/libs/openldap/libraries/liblmdb/CHANGES b/contrib/libs/openldap/libraries/liblmdb/CHANGES new file mode 100644 index 0000000000..76dd591db7 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/CHANGES @@ -0,0 +1,276 @@ +LMDB 0.9 Change Log + +LMDB 0.9.31 Release (2023/07/10) + ITS#8447 - Fix cursor_put(MDB_CURRENT) on DUPSORT DB with different sized data + +LMDB 0.9.30 Release (2023/02/08) + ITS#9806 - LMDB page_split: key threshold depends on page size + ITS#9916 - avoid gcc optimization bug on sparc64 linux + ITS#9919 - Mark infrequently used functions as cold + ITS#9723 - clear C_EOF on cursor with MDB_FIRST_DUP + ITS#9030 - Use sys/cachectl.h rather than asm/cachectl.h on mips + +LMDB 0.9.29 Release (2021/03/16) + ITS#9461 refix ITS#9376 + ITS#9500 fix regression from ITS#8662 + +LMDB 0.9.28 Release (2021/02/04) + ITS#8662 add -a append option to mdb_load + +LMDB 0.9.27 Release (2020/10/26) + ITS#9376 fix repeated DUPSORT cursor deletes + +LMDB 0.9.26 Release (2020/08/11) + ITS#9278 fix robust mutex cleanup for FreeBSD + +LMDB 0.9.25 Release (2020/01/30) + ITS#9068 fix mdb_dump/load backslashes in printable content + ITS#9118 add MAP_NOSYNC for FreeBSD + ITS#9155 free mt_spill_pgs in non-nested txn on end + +LMDB 0.9.24 Release (2019/07/24) + ITS#8969 Tweak mdb_page_split + ITS#8975 WIN32 fix writemap set_mapsize crash + ITS#9007 Fix loose pages in WRITEMAP + +LMDB 0.9.23 Release (2018/12/19) + ITS#8756 Fix loose pages in dirty list + ITS#8831 Fix mdb_load flag init + ITS#8844 Fix mdb_env_close in forked process + Documentation + ITS#8857 mdb_cursor_del doesn't invalidate cursor + ITS#8908 GET_MULTIPLE etc don't change passed in key + +LMDB 0.9.22 Release (2018/03/22) + Fix MDB_DUPSORT alignment bug (ITS#8819) + Fix regression with new db from 0.9.19 (ITS#8760) + Fix liblmdb to build on Solaris (ITS#8612) + Fix delete behavior with DUPSORT DB (ITS#8622) + Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722) + +LMDB 0.9.21 Release (2017/06/01) + Fix xcursor after cursor_del (ITS#8622) + +LMDB 0.9.20 (Withdrawn) + Fix mdb_load with escaped plaintext (ITS#8558) + Fix mdb_cursor_last / mdb_put interaction (ITS#8557) + +LMDB 0.9.19 Release (2016/12/28) + Fix mdb_env_cwalk cursor init (ITS#8424) + Fix robust mutexes on Solaris 10/11 (ITS#8339) + Tweak Win32 error message buffer + Fix MDB_GET_BOTH on non-dup record (ITS#8393) + Optimize mdb_drop + Fix xcursors after mdb_cursor_del (ITS#8406) + Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412) + Fix mdb_cursor_put resetting C_EOF (ITS#8489) + Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504) + Fix mdb_env_copy with empty DB (ITS#8209) + Fix behaviors with fork (ITS#8505) + Fix mdb_dbi_open with mainDB cursors (ITS#8542) + Fix robust mutexes on kFreeBSD (ITS#8554) + Fix utf8_to_utf16 error checks (ITS#7992) + Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682) + Build + Make shared lib suffix overridable (ITS#8481) + Documentation + Cleanup doxygen nits + Note reserved vs actual mem/disk usage + + +LMDB 0.9.18 Release (2016/02/05) + Fix robust mutex detection on glibc 2.10-11 (ITS#8330) + Fix page_search_root assert on FreeDB (ITS#8336) + Fix MDB_APPENDDUP vs. rewrite(single item) (ITS#8334) + Fix mdb_copy of large files on Windows + Fix subcursor move after delete (ITS#8355) + Fix mdb_midl_shirnk off-by-one (ITS#8363) + Check for utf8_to_utf16 failures (ITS#7992) + Catch strdup failure in mdb_dbi_open + Build + Additional makefile var tweaks (ITS#8169) + Documentation + Add Getting Started page + Update WRITEMAP description + + +LMDB 0.9.17 Release (2015/11/30) + Fix ITS#7377 catch calloc failure + Fix ITS#8237 regression from ITS#7589 + Fix ITS#8238 page_split for DUPFIXED pages + Fix ITS#8221 MDB_PAGE_FULL on delete/rebalance + Fix ITS#8258 rebalance/split assert + Fix ITS#8263 cursor_put cursor tracking + Fix ITS#8264 cursor_del cursor tracking + Fix ITS#8310 cursor_del cursor tracking + Fix ITS#8299 mdb_del cursor tracking + Fix ITS#8300 mdb_del cursor tracking + Fix ITS#8304 mdb_del cursor tracking + Fix ITS#7771 fakepage cursor tracking + Fix ITS#7789 ensure mapsize >= pages in use + Fix ITS#7971 mdb_txn_renew0() new reader slots + Fix ITS#7969 use __sync_synchronize on non-x86 + Fix ITS#8311 page_split from update_key + Fix ITS#8312 loose pages in nested txn + Fix ITS#8313 mdb_rebalance dummy cursor + Fix ITS#8315 dirty_room in nested txn + Fix ITS#8323 dirty_list in nested txn + Fix ITS#8316 page_merge cursor tracking + Fix ITS#8321 cursor tracking + Fix ITS#8319 mdb_load error messages + Fix ITS#8320 mdb_load plaintext input + Added mdb_txn_id() (ITS#7994) + Added robust mutex support + Miscellaneous cleanup/simplification + Build + Create install dirs if needed (ITS#8256) + Fix ThreadProc decl on Win32/MSVC (ITS#8270) + Added ssize_t typedef for MSVC (ITS#8067) + Use ANSI apis on Windows (ITS#8069) + Use O_SYNC if O_DSYNC,MDB_DSYNC are not defined (ITS#7209) + Allow passing AR to make (ITS#8168) + Allow passing mandir to make install (ITS#8169) + +LMDB 0.9.16 Release (2015/08/14) + Fix cursor EOF bug (ITS#8190) + Fix handling of subDB records (ITS#8181) + Fix mdb_midl_shrink() usage (ITS#8200) + +LMDB 0.9.15 Release (2015/06/19) + Fix txn init (ITS#7961,#7987) + Fix MDB_PREV_DUP (ITS#7955,#7671) + Fix compact of empty env (ITS#7956) + Fix mdb_copy file mode + Fix mdb_env_close() after failed mdb_env_open() + Fix mdb_rebalance collapsing root (ITS#8062) + Fix mdb_load with large values (ITS#8066) + Fix to retry writes on EINTR (ITS#8106) + Fix mdb_cursor_del on empty DB (ITS#8109) + Fix MDB_INTEGERDUP key compare (ITS#8117) + Fix error handling (ITS#7959,#8157,etc.) + Fix race conditions (ITS#7969,7970) + Added workaround for fdatasync bug in ext3fs + Build + Don't use -fPIC for static lib + Update .gitignore (ITS#7952,#7953) + Cleanup for "make test" (ITS#7841), "make clean", mtest*.c + Misc. Android/Windows cleanup + Documentation + Fix MDB_APPEND doc + Fix MDB_MAXKEYSIZE doc (ITS#8156) + Fix mdb_cursor_put,mdb_cursor_del EACCES description + Fix mdb_env_sync(MDB_RDONLY env) doc (ITS#8021) + Clarify MDB_WRITEMAP doc (ITS#8021) + Clarify mdb_env_open doc + Clarify mdb_dbi_open doc + +LMDB 0.9.14 Release (2014/09/20) + Fix to support 64K page size (ITS#7713) + Fix to persist decreased as well as increased mapsizes (ITS#7789) + Fix cursor bug when deleting last node of a DUPSORT key + Fix mdb_env_info to return FIXEDMAP address + Fix ambiguous error code from writing to closed DBI (ITS#7825) + Fix mdb_copy copying past end of file (ITS#7886) + Fix cursor bugs from page_merge/rebalance + Fix to dirty fewer pages in deletes (mdb_page_loose()) + Fix mdb_dbi_open creating subDBs (ITS#7917) + Fix mdb_cursor_get(_DUP) with single value (ITS#7913) + Fix Windows compat issues in mtests (ITS#7879) + Add compacting variant of mdb_copy + Add BigEndian integer key compare code + Add mdb_dump/mdb_load utilities + +LMDB 0.9.13 Release (2014/06/18) + Fix mdb_page_alloc unlimited overflow page search + Documentation + Re-fix MDB_CURRENT doc (ITS#7793) + Fix MDB_GET_MULTIPLE/MDB_NEXT_MULTIPLE doc + +LMDB 0.9.12 Release (2014/06/13) + Fix MDB_GET_BOTH regression (ITS#7875,#7681) + Fix MDB_MULTIPLE writing multiple keys (ITS#7834) + Fix mdb_rebalance (ITS#7829) + Fix mdb_page_split (ITS#7815) + Fix md_entries count (ITS#7861,#7828,#7793) + Fix MDB_CURRENT (ITS#7793) + Fix possible crash on Windows DLL detach + Misc code cleanup + Documentation + mdb_cursor_put: cursor moves on error (ITS#7771) + + +LMDB 0.9.11 Release (2014/01/15) + Add mdb_env_set_assert() (ITS#7775) + Fix: invalidate txn on page allocation errors (ITS#7377) + Fix xcursor tracking in mdb_cursor_del0() (ITS#7771) + Fix corruption from deletes (ITS#7756) + Fix Windows/MSVC build issues + Raise safe limit of max MDB_MAXKEYSIZE + Misc code cleanup + Documentation + Remove spurious note about non-overlapping flags (ITS#7665) + +LMDB 0.9.10 Release (2013/11/12) + Add MDB_NOMEMINIT option + Fix mdb_page_split() again (ITS#7589) + Fix MDB_NORDAHEAD definition (ITS#7734) + Fix mdb_cursor_del() positioning (ITS#7733) + Partial fix for larger page sizes (ITS#7713) + Fix Windows64/MSVC build issues + +LMDB 0.9.9 Release (2013/10/24) + Add mdb_env_get_fd() + Add MDB_NORDAHEAD option + Add MDB_NOLOCK option + Avoid wasting space in mdb_page_split() (ITS#7589) + Fix mdb_page_merge() cursor fixup (ITS#7722) + Fix mdb_cursor_del() on last delete (ITS#7718) + Fix adding WRITEMAP on existing env (ITS#7715) + Fix nested txns (ITS#7515) + Fix mdb_env_copy() O_DIRECT bug (ITS#7682) + Fix mdb_cursor_set(SET_RANGE) return code (ITS#7681) + Fix mdb_rebalance() cursor fixup (ITS#7701) + Misc code cleanup + Documentation + Note that by default, readers need write access + + +LMDB 0.9.8 Release (2013/09/09) + Allow mdb_env_set_mapsize() on an open environment + Fix mdb_dbi_flags() (ITS#7672) + Fix mdb_page_unspill() in nested txns + Fix mdb_cursor_get(CURRENT|NEXT) after a delete + Fix mdb_cursor_get(DUP) to always return key (ITS#7671) + Fix mdb_cursor_del() to always advance to next item (ITS#7670) + Fix mdb_cursor_set(SET_RANGE) for tree with single page (ITS#7681) + Fix mdb_env_copy() retry open if O_DIRECT fails (ITS#7682) + Tweak mdb_page_spill() to be less aggressive + Documentation + Update caveats since mdb_reader_check() added in 0.9.7 + +LMDB 0.9.7 Release (2013/08/17) + Don't leave stale lockfile on failed RDONLY open (ITS#7664) + Fix mdb_page_split() ref beyond cursor depth + Fix read txn data race (ITS#7635) + Fix mdb_rebalance (ITS#7536, #7538) + Fix mdb_drop() (ITS#7561) + Misc DEBUG macro fixes + Add MDB_NOTLS envflag + Add mdb_env_copyfd() + Add mdb_txn_env() (ITS#7660) + Add mdb_dbi_flags() (ITS#7661) + Add mdb_env_get_maxkeysize() + Add mdb_env_reader_list()/mdb_env_reader_check() + Add mdb_page_spill/unspill, remove hard txn size limit + Use shorter names for semaphores (ITS#7615) + Build + Fix install target (ITS#7656) + Documentation + Misc updates for cursors, DB handles, data lifetime + +LMDB 0.9.6 Release (2013/02/25) + Many fixes/enhancements + +LMDB 0.9.5 Release (2012/11/30) + Renamed from libmdb to liblmdb + Many fixes/enhancements diff --git a/contrib/libs/openldap/libraries/liblmdb/COPYRIGHT b/contrib/libs/openldap/libraries/liblmdb/COPYRIGHT new file mode 100644 index 0000000000..14eb1493d6 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/COPYRIGHT @@ -0,0 +1,20 @@ +Copyright 2011-2021 Howard Chu, Symas Corp. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Individual files and/or contributed packages may be copyright by +other parties and/or subject to additional restrictions. + +This work also contains materials derived from public sources. + +Additional information about OpenLDAP can be obtained at +<http://www.openldap.org/>. diff --git a/contrib/libs/openldap/libraries/liblmdb/LICENSE b/contrib/libs/openldap/libraries/liblmdb/LICENSE new file mode 100644 index 0000000000..05ad7571e4 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/LICENSE @@ -0,0 +1,47 @@ +The OpenLDAP Public License + Version 2.8, 17 August 2003 + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions in source form must retain copyright statements + and notices, + +2. Redistributions in binary form must reproduce applicable copyright + statements and notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution, and + +3. Redistributions must contain a verbatim copy of this document. + +The OpenLDAP Foundation may revise this license from time to time. +Each revision is distinguished by a version number. You may use +this Software under terms of this license revision or under the +terms of any subsequent revision of the license. + +THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS +CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) +OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +The names of the authors and copyright holders must not be used in +advertising or otherwise to promote the sale, use or other dealing +in this Software without specific, written prior permission. Title +to copyright in this Software shall at all times remain with copyright +holders. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, +California, USA. All Rights Reserved. Permission to copy and +distribute verbatim copies of this document is granted. diff --git a/contrib/libs/openldap/libraries/liblmdb/lmdb.h b/contrib/libs/openldap/libraries/liblmdb/lmdb.h new file mode 100644 index 0000000000..ff03c224f2 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/lmdb.h @@ -0,0 +1,1608 @@ +/** @file lmdb.h + * @brief Lightning memory-mapped database library + * + * @mainpage Lightning Memory-Mapped Database Manager (LMDB) + * + * @section intro_sec Introduction + * LMDB is a Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. The entire database is exposed + * in a memory map, and all data fetches return data directly + * from the mapped memory, so no malloc's or memcpy's occur during + * data fetches. As such, the library is extremely simple because it + * requires no page caching layer of its own, and it is extremely high + * performance and memory-efficient. It is also fully transactional with + * full ACID semantics, and when the memory map is read-only, the + * database integrity cannot be corrupted by stray pointer writes from + * application code. + * + * The library is fully thread-aware and supports concurrent read/write + * access from multiple processes and threads. Data pages use a copy-on- + * write strategy so no active data pages are ever overwritten, which + * also provides resistance to corruption and eliminates the need of any + * special recovery procedures after a system crash. Writes are fully + * serialized; only one write transaction may be active at a time, which + * guarantees that writers can never deadlock. The database structure is + * multi-versioned so readers run with no locks; writers cannot block + * readers, and readers don't block writers. + * + * Unlike other well-known database mechanisms which use either write-ahead + * transaction logs or append-only data writes, LMDB requires no maintenance + * during operation. Both write-ahead loggers and append-only databases + * require periodic checkpointing and/or compaction of their log or database + * files otherwise they grow without bound. LMDB tracks free pages within + * the database and re-uses them for new write operations, so the database + * size does not grow without bound in normal use. + * + * The memory map can be used as a read-only or read-write map. It is + * read-only by default as this provides total immunity to corruption. + * Using read-write mode offers much higher write performance, but adds + * the possibility for stray application writes thru pointers to silently + * corrupt the database. Of course if your application code is known to + * be bug-free (...) then this is not an issue. + * + * If this is your first time using a transactional embedded key/value + * store, you may find the \ref starting page to be helpful. + * + * @section caveats_sec Caveats + * Troubleshooting the lock file, plus semaphores on BSD systems: + * + * - A broken lockfile can cause sync issues. + * Stale reader transactions left behind by an aborted program + * cause further writes to grow the database quickly, and + * stale locks can block further operation. + * + * Fix: Check for stale readers periodically, using the + * #mdb_reader_check function or the \ref mdb_stat_1 "mdb_stat" tool. + * Stale writers will be cleared automatically on some systems: + * - Windows - automatic + * - Linux, systems using POSIX mutexes with Robust option - automatic + * - not on BSD, systems using POSIX semaphores. + * Otherwise just make all programs using the database close it; + * the lockfile is always reset on first open of the environment. + * + * - On BSD systems or others configured with MDB_USE_POSIX_SEM, + * startup can fail due to semaphores owned by another userid. + * + * Fix: Open and close the database as the user which owns the + * semaphores (likely last user) or as root, while no other + * process is using the database. + * + * Restrictions/caveats (in addition to those listed for some functions): + * + * - Only the database owner should normally use the database on + * BSD systems or when otherwise configured with MDB_USE_POSIX_SEM. + * Multiple users can cause startup to fail later, as noted above. + * + * - There is normally no pure read-only mode, since readers need write + * access to locks and lock file. Exceptions: On read-only filesystems + * or with the #MDB_NOLOCK flag described under #mdb_env_open(). + * + * - An LMDB configuration will often reserve considerable \b unused + * memory address space and maybe file size for future growth. + * This does not use actual memory or disk space, but users may need + * to understand the difference so they won't be scared off. + * + * - By default, in versions before 0.9.10, unused portions of the data + * file might receive garbage data from memory freed by other code. + * (This does not happen when using the #MDB_WRITEMAP flag.) As of + * 0.9.10 the default behavior is to initialize such memory before + * writing to the data file. Since there may be a slight performance + * cost due to this initialization, applications may disable it using + * the #MDB_NOMEMINIT flag. Applications handling sensitive data + * which must not be written should not use this flag. This flag is + * irrelevant when using #MDB_WRITEMAP. + * + * - A thread can only use one transaction at a time, plus any child + * transactions. Each transaction belongs to one thread. See below. + * The #MDB_NOTLS flag changes this for read-only transactions. + * + * - Use an MDB_env* in the process which opened it, not after fork(). + * + * - Do not have open an LMDB database twice in the same process at + * the same time. Not even from a plain open() call - close()ing it + * breaks fcntl() advisory locking. (It is OK to reopen it after + * fork() - exec*(), since the lockfile has FD_CLOEXEC set.) + * + * - Avoid long-lived transactions. Read transactions prevent + * reuse of pages freed by newer write transactions, thus the + * database can grow quickly. Write transactions prevent + * other write transactions, since writes are serialized. + * + * - Avoid suspending a process with active transactions. These + * would then be "long-lived" as above. Also read transactions + * suspended when writers commit could sometimes see wrong data. + * + * ...when several processes can use a database concurrently: + * + * - Avoid aborting a process with an active transaction. + * The transaction becomes "long-lived" as above until a check + * for stale readers is performed or the lockfile is reset, + * since the process may not remove it from the lockfile. + * + * This does not apply to write transactions if the system clears + * stale writers, see above. + * + * - If you do that anyway, do a periodic check for stale readers. Or + * close the environment once in a while, so the lockfile can get reset. + * + * - Do not use LMDB databases on remote filesystems, even between + * processes on the same host. This breaks flock() on some OSes, + * possibly memory map sync, and certainly sync between programs + * on different hosts. + * + * - Opening a database can fail if another process is opening or + * closing it at exactly the same time. + * + * @author Howard Chu, Symas Corporation. + * + * @copyright Copyright 2011-2021 Howard Chu, Symas Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * @par Derived From: + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _LMDB_H_ +#define _LMDB_H_ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Unix permissions for creating files, or dummy definition for Windows */ +#ifdef _MSC_VER +typedef int mdb_mode_t; +#else +typedef mode_t mdb_mode_t; +#endif + +/** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#ifdef _WIN32 +typedef void *mdb_filehandle_t; +#else +typedef int mdb_filehandle_t; +#endif + +/** @defgroup mdb LMDB API + * @{ + * @brief OpenLDAP Lightning Memory-Mapped Database Manager + */ +/** @defgroup Version Version Macros + * @{ + */ +/** Library major version */ +#define MDB_VERSION_MAJOR 0 +/** Library minor version */ +#define MDB_VERSION_MINOR 9 +/** Library patch version */ +#define MDB_VERSION_PATCH 31 + +/** Combine args a,b,c into a single integer for easy version comparisons */ +#define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c)) + +/** The full library version as a single integer */ +#define MDB_VERSION_FULL \ + MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH) + +/** The release date of this library version */ +#define MDB_VERSION_DATE "July 10, 2023" + +/** A stringifier for the version info */ +#define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")" + +/** A helper for the stringifier macro */ +#define MDB_VERFOO(a,b,c,d) MDB_VERSTR(a,b,c,d) + +/** The full library version as a C string */ +#define MDB_VERSION_STRING \ + MDB_VERFOO(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH,MDB_VERSION_DATE) +/** @} */ + +/** @brief Opaque structure for a database environment. + * + * A DB environment supports multiple databases, all residing in the same + * shared-memory map. + */ +typedef struct MDB_env MDB_env; + +/** @brief Opaque structure for a transaction handle. + * + * All database operations require a transaction handle. Transactions may be + * read-only or read-write. + */ +typedef struct MDB_txn MDB_txn; + +/** @brief A handle for an individual database in the DB environment. */ +typedef unsigned int MDB_dbi; + +/** @brief Opaque structure for navigating through a database */ +typedef struct MDB_cursor MDB_cursor; + +/** @brief Generic structure used for passing keys and data in and out + * of the database. + * + * Values returned from the database are valid only until a subsequent + * update operation, or the end of the transaction. Do not modify or + * free them, they commonly point into the database itself. + * + * Key sizes must be between 1 and #mdb_env_get_maxkeysize() inclusive. + * The same applies to data sizes in databases with the #MDB_DUPSORT flag. + * Other data items can in theory be from 0 to 0xffffffff bytes long. + */ +typedef struct MDB_val { + size_t mv_size; /**< size of the data item */ + void *mv_data; /**< address of the data item */ +} MDB_val; + +/** @brief A callback function used to compare two keys in a database */ +typedef int (MDB_cmp_func)(const MDB_val *a, const MDB_val *b); + +/** @brief A callback function used to relocate a position-dependent data item + * in a fixed-address database. + * + * The \b newptr gives the item's desired address in + * the memory map, and \b oldptr gives its previous address. The item's actual + * data resides at the address in \b item. This callback is expected to walk + * through the fields of the record in \b item and modify any + * values based at the \b oldptr address to be relative to the \b newptr address. + * @param[in,out] item The item that is to be relocated. + * @param[in] oldptr The previous address. + * @param[in] newptr The new address to relocate to. + * @param[in] relctx An application-provided context, set by #mdb_set_relctx(). + * @todo This feature is currently unimplemented. + */ +typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx); + +/** @defgroup mdb_env Environment Flags + * @{ + */ + /** mmap at a fixed address (experimental) */ +#define MDB_FIXEDMAP 0x01 + /** no environment directory */ +#define MDB_NOSUBDIR 0x4000 + /** don't fsync after commit */ +#define MDB_NOSYNC 0x10000 + /** read only */ +#define MDB_RDONLY 0x20000 + /** don't fsync metapage after commit */ +#define MDB_NOMETASYNC 0x40000 + /** use writable mmap */ +#define MDB_WRITEMAP 0x80000 + /** use asynchronous msync when #MDB_WRITEMAP is used */ +#define MDB_MAPASYNC 0x100000 + /** tie reader locktable slots to #MDB_txn objects instead of to threads */ +#define MDB_NOTLS 0x200000 + /** don't do any locking, caller must manage their own locks */ +#define MDB_NOLOCK 0x400000 + /** don't do readahead (no effect on Windows) */ +#define MDB_NORDAHEAD 0x800000 + /** don't initialize malloc'd memory before writing to datafile */ +#define MDB_NOMEMINIT 0x1000000 +/** @} */ + +/** @defgroup mdb_dbi_open Database Flags + * @{ + */ + /** use reverse string keys */ +#define MDB_REVERSEKEY 0x02 + /** use sorted duplicates */ +#define MDB_DUPSORT 0x04 + /** numeric keys in native byte order: either unsigned int or size_t. + * The keys must all be of the same size. */ +#define MDB_INTEGERKEY 0x08 + /** with #MDB_DUPSORT, sorted dup items have fixed size */ +#define MDB_DUPFIXED 0x10 + /** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */ +#define MDB_INTEGERDUP 0x20 + /** with #MDB_DUPSORT, use reverse string dups */ +#define MDB_REVERSEDUP 0x40 + /** create DB if not already existing */ +#define MDB_CREATE 0x40000 +/** @} */ + +/** @defgroup mdb_put Write Flags + * @{ + */ +/** For put: Don't write if the key already exists. */ +#define MDB_NOOVERWRITE 0x10 +/** Only for #MDB_DUPSORT<br> + * For put: don't write if the key and data pair already exist.<br> + * For mdb_cursor_del: remove all duplicate data items. + */ +#define MDB_NODUPDATA 0x20 +/** For mdb_cursor_put: overwrite the current key/data pair */ +#define MDB_CURRENT 0x40 +/** For put: Just reserve space for data, don't copy it. Return a + * pointer to the reserved space. + */ +#define MDB_RESERVE 0x10000 +/** Data is being appended, don't split full pages. */ +#define MDB_APPEND 0x20000 +/** Duplicate data is being appended, don't split full pages. */ +#define MDB_APPENDDUP 0x40000 +/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */ +#define MDB_MULTIPLE 0x80000 +/* @} */ + +/** @defgroup mdb_copy Copy Flags + * @{ + */ +/** Compacting copy: Omit free space from copy, and renumber all + * pages sequentially. + */ +#define MDB_CP_COMPACT 0x01 +/* @} */ + +/** @brief Cursor Get operations. + * + * This is the set of all operations for retrieving data + * using a cursor. + */ +typedef enum MDB_cursor_op { + MDB_FIRST, /**< Position at first key/data item */ + MDB_FIRST_DUP, /**< Position at first data item of current key. + Only for #MDB_DUPSORT */ + MDB_GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */ + MDB_GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */ + MDB_GET_CURRENT, /**< Return key/data at current cursor position */ + MDB_GET_MULTIPLE, /**< Return up to a page of duplicate data items + from current cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_LAST, /**< Position at last key/data item */ + MDB_LAST_DUP, /**< Position at last data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT, /**< Position at next data item */ + MDB_NEXT_DUP, /**< Position at next data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT_MULTIPLE, /**< Return up to a page of duplicate data items + from next cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_NEXT_NODUP, /**< Position at first data item of next key */ + MDB_PREV, /**< Position at previous data item */ + MDB_PREV_DUP, /**< Position at previous data item of current key. + Only for #MDB_DUPSORT */ + MDB_PREV_NODUP, /**< Position at last data item of previous key */ + MDB_SET, /**< Position at specified key */ + MDB_SET_KEY, /**< Position at specified key, return key + data */ + MDB_SET_RANGE, /**< Position at first key greater than or equal to specified key. */ + MDB_PREV_MULTIPLE /**< Position at previous page and return up to + a page of duplicate data items. Only for #MDB_DUPFIXED */ +} MDB_cursor_op; + +/** @defgroup errors Return Codes + * + * BerkeleyDB uses -30800 to -30999, we'll go under them + * @{ + */ + /** Successful result */ +#define MDB_SUCCESS 0 + /** key/data pair already exists */ +#define MDB_KEYEXIST (-30799) + /** key/data pair not found (EOF) */ +#define MDB_NOTFOUND (-30798) + /** Requested page not found - this usually indicates corruption */ +#define MDB_PAGE_NOTFOUND (-30797) + /** Located page was wrong type */ +#define MDB_CORRUPTED (-30796) + /** Update of meta page failed or environment had fatal error */ +#define MDB_PANIC (-30795) + /** Environment version mismatch */ +#define MDB_VERSION_MISMATCH (-30794) + /** File is not a valid LMDB file */ +#define MDB_INVALID (-30793) + /** Environment mapsize reached */ +#define MDB_MAP_FULL (-30792) + /** Environment maxdbs reached */ +#define MDB_DBS_FULL (-30791) + /** Environment maxreaders reached */ +#define MDB_READERS_FULL (-30790) + /** Too many TLS keys in use - Windows only */ +#define MDB_TLS_FULL (-30789) + /** Txn has too many dirty pages */ +#define MDB_TXN_FULL (-30788) + /** Cursor stack too deep - internal error */ +#define MDB_CURSOR_FULL (-30787) + /** Page has not enough space - internal error */ +#define MDB_PAGE_FULL (-30786) + /** Database contents grew beyond environment mapsize */ +#define MDB_MAP_RESIZED (-30785) + /** Operation and DB incompatible, or DB type changed. This can mean: + * <ul> + * <li>The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database. + * <li>Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY. + * <li>Accessing a data record as a database, or vice versa. + * <li>The database was dropped and recreated with different flags. + * </ul> + */ +#define MDB_INCOMPATIBLE (-30784) + /** Invalid reuse of reader locktable slot */ +#define MDB_BAD_RSLOT (-30783) + /** Transaction must abort, has a child, or is invalid */ +#define MDB_BAD_TXN (-30782) + /** Unsupported size of key/DB name/data, or wrong DUPFIXED size */ +#define MDB_BAD_VALSIZE (-30781) + /** The specified DBI was changed unexpectedly */ +#define MDB_BAD_DBI (-30780) + /** The last defined error code */ +#define MDB_LAST_ERRCODE MDB_BAD_DBI +/** @} */ + +/** @brief Statistics for a database in the environment */ +typedef struct MDB_stat { + unsigned int ms_psize; /**< Size of a database page. + This is currently the same for all databases. */ + unsigned int ms_depth; /**< Depth (height) of the B-tree */ + size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */ + size_t ms_leaf_pages; /**< Number of leaf pages */ + size_t ms_overflow_pages; /**< Number of overflow pages */ + size_t ms_entries; /**< Number of data items */ +} MDB_stat; + +/** @brief Information about the environment */ +typedef struct MDB_envinfo { + void *me_mapaddr; /**< Address of map, if fixed */ + size_t me_mapsize; /**< Size of the data memory map */ + size_t me_last_pgno; /**< ID of the last used page */ + size_t me_last_txnid; /**< ID of the last committed transaction */ + unsigned int me_maxreaders; /**< max reader slots in the environment */ + unsigned int me_numreaders; /**< max reader slots used in the environment */ +} MDB_envinfo; + + /** @brief Return the LMDB library version information. + * + * @param[out] major if non-NULL, the library major version number is copied here + * @param[out] minor if non-NULL, the library minor version number is copied here + * @param[out] patch if non-NULL, the library patch version number is copied here + * @retval "version string" The library version as a string + */ +char *mdb_version(int *major, int *minor, int *patch); + + /** @brief Return a string describing a given error code. + * + * This function is a superset of the ANSI C X3.159-1989 (ANSI C) strerror(3) + * function. If the error code is greater than or equal to 0, then the string + * returned by the system function strerror(3) is returned. If the error code + * is less than 0, an error string corresponding to the LMDB library error is + * returned. See @ref errors for a list of LMDB-specific error codes. + * @param[in] err The error code + * @retval "error message" The description of the error + */ +char *mdb_strerror(int err); + + /** @brief Create an LMDB environment handle. + * + * This function allocates memory for a #MDB_env structure. To release + * the allocated memory and discard the handle, call #mdb_env_close(). + * Before the handle may be used, it must be opened using #mdb_env_open(). + * Various other options may also need to be set before opening the handle, + * e.g. #mdb_env_set_mapsize(), #mdb_env_set_maxreaders(), #mdb_env_set_maxdbs(), + * depending on usage requirements. + * @param[out] env The address where the new handle will be stored + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_create(MDB_env **env); + + /** @brief Open an environment handle. + * + * If this function fails, #mdb_env_close() must be called to discard the #MDB_env handle. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] path The directory in which the database files reside. This + * directory must already exist and be writable. + * @param[in] flags Special options for this environment. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * Flags set by mdb_env_set_flags() are also used. + * <ul> + * <li>#MDB_FIXEDMAP + * use a fixed address for the mmap region. This flag must be specified + * when creating the environment, and is stored persistently in the environment. + * If successful, the memory map will always reside at the same virtual address + * and pointers used to reference data items in the database will be constant + * across multiple invocations. This option may not always work, depending on + * how the operating system has allocated memory to shared libraries and other uses. + * The feature is highly experimental. + * <li>#MDB_NOSUBDIR + * By default, LMDB creates its environment in a directory whose + * pathname is given in \b path, and creates its data and lock files + * under that directory. With this option, \b path is used as-is for + * the database main data file. The database lock file is the \b path + * with "-lock" appended. + * <li>#MDB_RDONLY + * Open the environment in read-only mode. No write operations will be + * allowed. LMDB will still modify the lock file - except on read-only + * filesystems, where LMDB does not use locks. + * <li>#MDB_WRITEMAP + * Use a writeable memory map unless MDB_RDONLY is set. This uses + * fewer mallocs but loses protection from application bugs + * like wild pointer writes and other bad updates into the database. + * This may be slightly faster for DBs that fit entirely in RAM, but + * is slower for DBs larger than RAM. + * Incompatible with nested transactions. + * Do not mix processes with and without MDB_WRITEMAP on the same + * environment. This can defeat durability (#mdb_env_sync etc). + * <li>#MDB_NOMETASYNC + * Flush system buffers to disk only once per transaction, omit the + * metadata flush. Defer that until the system flushes files to disk, + * or next non-MDB_RDONLY commit or #mdb_env_sync(). This optimization + * maintains database integrity, but a system crash may undo the last + * committed transaction. I.e. it preserves the ACI (atomicity, + * consistency, isolation) but not D (durability) database property. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOSYNC + * Don't flush system buffers to disk when committing a transaction. + * This optimization means a system crash can corrupt the database or + * lose the last transactions if buffers are not yet flushed to disk. + * The risk is governed by how often the system flushes dirty buffers + * to disk and how often #mdb_env_sync() is called. However, if the + * filesystem preserves write order and the #MDB_WRITEMAP flag is not + * used, transactions exhibit ACI (atomicity, consistency, isolation) + * properties and only lose D (durability). I.e. database integrity + * is maintained, but a system crash may undo the final transactions. + * Note that (#MDB_NOSYNC | #MDB_WRITEMAP) leaves the system with no + * hint for when to write transactions to disk, unless #mdb_env_sync() + * is called. (#MDB_MAPASYNC | #MDB_WRITEMAP) may be preferable. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_MAPASYNC + * When using #MDB_WRITEMAP, use asynchronous flushes to disk. + * As with #MDB_NOSYNC, a system crash can then corrupt the + * database or lose the last transactions. Calling #mdb_env_sync() + * ensures on-disk database integrity until next commit. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOTLS + * Don't use Thread-Local Storage. Tie reader locktable slots to + * #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps + * the slot reserved for the #MDB_txn object. A thread may use parallel + * read-only transactions. A read-only transaction may span threads if + * the user synchronizes its use. Applications that multiplex many + * user threads over individual OS threads need this option. Such an + * application must also serialize the write transactions in an OS + * thread, since LMDB's write locking is unaware of the user threads. + * <li>#MDB_NOLOCK + * Don't do any locking. If concurrent access is anticipated, the + * caller must manage all concurrency itself. For proper operation + * the caller must enforce single-writer semantics, and must ensure + * that no readers are using old transactions while a writer is + * active. The simplest approach is to use an exclusive lock so that + * no readers may be active at all when a writer begins. + * <li>#MDB_NORDAHEAD + * Turn off readahead. Most operating systems perform readahead on + * read requests by default. This option turns it off if the OS + * supports it. Turning it off may help random read performance + * when the DB is larger than RAM and system RAM is full. + * The option is not implemented on Windows. + * <li>#MDB_NOMEMINIT + * Don't initialize malloc'd memory before writing to unused spaces + * in the data file. By default, memory for pages written to the data + * file is obtained using malloc. While these pages may be reused in + * subsequent transactions, freshly malloc'd pages will be initialized + * to zeroes before use. This avoids persisting leftover data from other + * code (that used the heap and subsequently freed the memory) into the + * data file. Note that many other system libraries may allocate + * and free memory from the heap for arbitrary uses. E.g., stdio may + * use the heap for file I/O buffers. This initialization step has a + * modest performance cost so some applications may want to disable + * it using this flag. This option can be a problem for applications + * which handle sensitive data like passwords, and it makes memory + * checkers like Valgrind noisy. This flag is not needed with #MDB_WRITEMAP, + * which writes directly to the mmap instead of using malloc for pages. The + * initialization is also skipped if #MDB_RESERVE is used; the + * caller is expected to overwrite all of the memory that was + * reserved in that case. + * This flag may be changed at any time using #mdb_env_set_flags(). + * </ul> + * @param[in] mode The UNIX permissions to set on created files and semaphores. + * This parameter is ignored on Windows. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_VERSION_MISMATCH - the version of the LMDB library doesn't match the + * version that created the database environment. + * <li>#MDB_INVALID - the environment file headers are corrupted. + * <li>ENOENT - the directory specified by the path parameter doesn't exist. + * <li>EACCES - the user didn't have permission to access the environment files. + * <li>EAGAIN - the environment was locked by another process. + * </ul> + */ +int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode); + + /** @brief Copy an LMDB environment to the specified path. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy(MDB_env *env, const char *path); + + /** @brief Copy an LMDB environment to the specified file descriptor. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd); + + /** @brief Copy an LMDB environment to the specified path, with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_CP_COMPACT - Perform compaction while copying: omit free + * pages and sequentially renumber all pages in output. This option + * consumes more CPU and runs more slowly than the default. + * Currently it fails if the environment has suffered a page leak. + * </ul> + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags); + + /** @brief Copy an LMDB environment to the specified file descriptor, + * with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. See + * #mdb_env_copy2() for further details. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @param[in] flags Special options for this operation. + * See #mdb_env_copy2() for options. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd2(MDB_env *env, mdb_filehandle_t fd, unsigned int flags); + + /** @brief Return statistics about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + */ +int mdb_env_stat(MDB_env *env, MDB_stat *stat); + + /** @brief Return information about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_envinfo structure + * where the information will be copied + */ +int mdb_env_info(MDB_env *env, MDB_envinfo *stat); + + /** @brief Flush the data buffers to disk. + * + * Data is always written to disk when #mdb_txn_commit() is called, + * but the operating system may keep it buffered. LMDB always flushes + * the OS buffers upon commit as well, unless the environment was + * opened with #MDB_NOSYNC or in part #MDB_NOMETASYNC. This call is + * not valid if the environment was opened with #MDB_RDONLY. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] force If non-zero, force a synchronous flush. Otherwise + * if the environment has the #MDB_NOSYNC flag set the flushes + * will be omitted, and with #MDB_MAPASYNC they will be asynchronous. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - the environment is read-only. + * <li>EINVAL - an invalid parameter was specified. + * <li>EIO - an error occurred during synchronization. + * </ul> + */ +int mdb_env_sync(MDB_env *env, int force); + + /** @brief Close the environment and release the memory map. + * + * Only a single thread may call this function. All transactions, databases, + * and cursors must already be closed before calling this function. Attempts to + * use any such handles after calling this function will cause a SIGSEGV. + * The environment handle will be freed and must not be used again after this call. + * @param[in] env An environment handle returned by #mdb_env_create() + */ +void mdb_env_close(MDB_env *env); + + /** @brief Set environment flags. + * + * This may be used to set some flags in addition to those from + * #mdb_env_open(), or to unset these flags. If several threads + * change the flags at the same time, the result is undefined. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] flags The flags to change, bitwise OR'ed together + * @param[in] onoff A non-zero value sets the flags, zero clears them. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_set_flags(MDB_env *env, unsigned int flags, int onoff); + + /** @brief Get environment flags. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] flags The address of an integer to store the flags + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_flags(MDB_env *env, unsigned int *flags); + + /** @brief Return the path that was used in #mdb_env_open(). + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] path Address of a string pointer to contain the path. This + * is the actual string in the environment, not a copy. It should not be + * altered in any way. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_path(MDB_env *env, const char **path); + + /** @brief Return the filedescriptor for the given environment. + * + * This function may be called after fork(), so the descriptor can be + * closed before exec*(). Other LMDB file descriptors have FD_CLOEXEC. + * (Until LMDB 0.9.18, only the lockfile had that.) + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] fd Address of a mdb_filehandle_t to contain the descriptor. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *fd); + + /** @brief Set the size of the memory map to use for this environment. + * + * The size should be a multiple of the OS page size. The default is + * 10485760 bytes. The size of the memory map is also the maximum size + * of the database. The value should be chosen as large as possible, + * to accommodate future growth of the database. + * This function should be called after #mdb_env_create() and before #mdb_env_open(). + * It may be called at later times if no transactions are active in + * this process. Note that the library does not check for this condition, + * the caller must ensure it explicitly. + * + * The new size takes effect immediately for the current process but + * will not be persisted to any others until a write transaction has been + * committed by the current process. Also, only mapsize increases are + * persisted into the environment. + * + * If the mapsize is increased by another process, and data has grown + * beyond the range of the current mapsize, #mdb_txn_begin() will + * return #MDB_MAP_RESIZED. This function may be called with a size + * of zero to adopt the new size. + * + * Any attempt to set a size smaller than the space already consumed + * by the environment will be silently changed to the current size of the used space. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] size The size in bytes + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment has + * an active write transaction. + * </ul> + */ +int mdb_env_set_mapsize(MDB_env *env, size_t size); + + /** @brief Set the maximum number of threads/reader slots for the environment. + * + * This defines the number of slots in the lock table that is used to track readers in the + * the environment. The default is 126. + * Starting a read-only transaction normally ties a lock table slot to the + * current thread until the environment closes or the thread exits. If + * MDB_NOTLS is in use, #mdb_txn_begin() instead ties the slot to the + * MDB_txn object until it or the #MDB_env object is destroyed. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] readers The maximum number of reader lock table slots + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers); + + /** @brief Get the maximum number of threads/reader slots for the environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] readers Address of an integer to store the number of readers + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers); + + /** @brief Set the maximum number of named databases for the environment. + * + * This function is only needed if multiple databases will be used in the + * environment. Simpler applications that use the environment as a single + * unnamed database can ignore this option. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * + * Currently a moderate number of slots are cheap but a huge number gets + * expensive: 7-120 words per transaction, and every #mdb_dbi_open() + * does a linear search of the opened slots. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbs The maximum number of databases + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs); + + /** @brief Get the maximum size of keys and #MDB_DUPSORT data we can write. + * + * Depends on the compile-time constant #MDB_MAXKEYSIZE. Default 511. + * See @ref MDB_val. + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The maximum size of a key we can write + */ +int mdb_env_get_maxkeysize(MDB_env *env); + + /** @brief Set application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_userctx(MDB_env *env, void *ctx); + + /** @brief Get the application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The pointer set by #mdb_env_set_userctx(). + */ +void *mdb_env_get_userctx(MDB_env *env); + + /** @brief A callback function for most LMDB assert() failures, + * called before printing the message and aborting. + * + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] msg The assertion message, not including newline. + */ +typedef void MDB_assert_func(MDB_env *env, const char *msg); + + /** Set or reset the assert() callback of the environment. + * Disabled if liblmdb is built with NDEBUG. + * @note This hack should become obsolete as lmdb's error handling matures. + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] func An #MDB_assert_func function, or 0. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func); + + /** @brief Create a transaction for use with the environment. + * + * The transaction handle may be discarded using #mdb_txn_abort() or #mdb_txn_commit(). + * @note A transaction and its cursors must only be used by a single + * thread, and a thread may only have a single transaction at a time. + * If #MDB_NOTLS is in use, this does not apply to read-only transactions. + * @note Cursors may not span transactions. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] parent If this parameter is non-NULL, the new transaction + * will be a nested transaction, with the transaction indicated by \b parent + * as its parent. Transactions may be nested to any level. A parent + * transaction and its cursors may not issue any other operations than + * mdb_txn_commit and mdb_txn_abort while it has active child transactions. + * @param[in] flags Special options for this transaction. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_RDONLY + * This transaction will not perform any write operations. + * </ul> + * @param[out] txn Address where the new #MDB_txn handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>#MDB_MAP_RESIZED - another process wrote data beyond this MDB_env's + * mapsize and this environment's map must be resized as well. + * See #mdb_env_set_mapsize(). + * <li>#MDB_READERS_FULL - a read-only transaction was requested and + * the reader lock table is full. See #mdb_env_set_maxreaders(). + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn); + + /** @brief Returns the transaction's #MDB_env + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +MDB_env *mdb_txn_env(MDB_txn *txn); + + /** @brief Return the transaction's ID. + * + * This returns the identifier associated with this transaction. For a + * read-only transaction, this corresponds to the snapshot being read; + * concurrent readers will frequently have the same transaction ID. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A transaction ID, valid if input is an active transaction. + */ +size_t mdb_txn_id(MDB_txn *txn); + + /** @brief Commit all the operations of a transaction into the database. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * <li>ENOSPC - no more disk space. + * <li>EIO - a low-level I/O error occurred while writing. + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_commit(MDB_txn *txn); + + /** @brief Abandon all the operations of the transaction instead of saving them. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_abort(MDB_txn *txn); + + /** @brief Reset a read-only transaction. + * + * Abort the transaction like #mdb_txn_abort(), but keep the transaction + * handle. #mdb_txn_renew() may reuse the handle. This saves allocation + * overhead if the process will start a new read-only transaction soon, + * and also locking overhead if #MDB_NOTLS is in use. The reader table + * lock is released, but the table slot stays tied to its thread or + * #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free + * its lock table slot if MDB_NOTLS is in use. + * Cursors opened within the transaction must not be used + * again after this call, except with #mdb_cursor_renew(). + * Reader locks generally don't interfere with writers, but they keep old + * versions of database pages allocated. Thus they prevent the old pages + * from being reused when writers commit new data, and so under heavy load + * the database size may grow much more rapidly than otherwise. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_reset(MDB_txn *txn); + + /** @brief Renew a read-only transaction. + * + * This acquires a new reader lock for a transaction handle that had been + * released by #mdb_txn_reset(). It must be called before a reset transaction + * may be used again. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_txn_renew(MDB_txn *txn); + +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_open(txn,name,flags,dbi) mdb_dbi_open(txn,name,flags,dbi) +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_close(env,dbi) mdb_dbi_close(env,dbi) + + /** @brief Open a database in the environment. + * + * A database handle denotes the name and parameters of a database, + * independently of whether such a database exists. + * The database handle may be discarded by calling #mdb_dbi_close(). + * The old database handle is returned if the database was already open. + * The handle may only be closed once. + * + * The database handle will be private to the current transaction until + * the transaction is successfully committed. If the transaction is + * aborted the handle will be closed automatically. + * After a successful commit the handle will reside in the shared + * environment, and may be used by other transactions. + * + * This function must not be called from multiple concurrent + * transactions in the same process. A transaction that uses + * this function must finish (either commit or abort) before + * any other transaction in the process may use this function. + * + * To use named databases (with name != NULL), #mdb_env_set_maxdbs() + * must be called before opening the environment. Database names are + * keys in the unnamed database, and may be read but not written. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] name The name of the database to open. If only a single + * database is needed in the environment, this value may be NULL. + * @param[in] flags Special options for this database. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_REVERSEKEY + * Keys are strings to be compared in reverse order, from the end + * of the strings to the beginning. By default, Keys are treated as strings and + * compared from beginning to end. + * <li>#MDB_DUPSORT + * Duplicate keys may be used in the database. (Or, from another perspective, + * keys may have multiple data items, stored in sorted order.) By default + * keys must be unique and may have only a single data item. + * <li>#MDB_INTEGERKEY + * Keys are binary integers in native byte order, either unsigned int + * or size_t, and will be sorted as such. + * The keys must all be of the same size. + * <li>#MDB_DUPFIXED + * This flag may only be used in combination with #MDB_DUPSORT. This option + * tells the library that the data items for this database are all the same + * size, which allows further optimizations in storage and retrieval. When + * all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE + * and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple + * items at once. + * <li>#MDB_INTEGERDUP + * This option specifies that duplicate data items are binary integers, + * similar to #MDB_INTEGERKEY keys. + * <li>#MDB_REVERSEDUP + * This option specifies that duplicate data items should be compared as + * strings in reverse order. + * <li>#MDB_CREATE + * Create the named database if it doesn't exist. This option is not + * allowed in a read-only transaction or a read-only environment. + * </ul> + * @param[out] dbi Address where the new #MDB_dbi handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the specified database doesn't exist in the environment + * and #MDB_CREATE was not specified. + * <li>#MDB_DBS_FULL - too many databases have been opened. See #mdb_env_set_maxdbs(). + * </ul> + */ +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi); + + /** @brief Retrieve statistics for a database. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat); + + /** @brief Retrieve the DB flags for a database handle. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] flags Address where the flags will be returned. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags); + + /** @brief Close a database handle. Normally unnecessary. Use with care: + * + * This call is not mutex protected. Handles should only be closed by + * a single thread, and only if no other threads are going to reference + * the database handle or one of its cursors any further. Do not close + * a handle if an existing transaction has modified its database. + * Doing so can cause misbehavior from database corruption to errors + * like MDB_BAD_VALSIZE (since the DB name is gone). + * + * Closing a database handle is not necessary, but lets #mdb_dbi_open() + * reuse the handle value. Usually it's better to set a bigger + * #mdb_env_set_maxdbs(), unless that value would be large. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi); + + /** @brief Empty or delete+close a database. + * + * See #mdb_dbi_close() for restrictions about closing the DB handle. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] del 0 to empty the DB, 1 to delete it from the + * environment and close the DB handle. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del); + + /** @brief Set a custom key comparison function for a database. + * + * The comparison function is called whenever it is necessary to compare a + * key specified by the application with a key currently stored in the database. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the keys are compared lexically, with shorter keys collating + * before longer keys. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a custom data comparison function for a #MDB_DUPSORT database. + * + * This comparison function is called whenever it is necessary to compare a data + * item specified by the application with a data item currently stored in the database. + * This function only takes effect if the database was opened with the #MDB_DUPSORT + * flag. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the data items are compared lexically, with shorter items collating + * before longer items. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a relocation function for a #MDB_FIXEDMAP database. + * + * @todo The relocation function is called whenever it is necessary to move the data + * of an item to a different position in the database (e.g. through tree + * balancing operations, shifts as a result of adds or deletes, etc.). It is + * intended to allow address/position-dependent data items to be stored in + * a database in an environment opened with the #MDB_FIXEDMAP option. + * Currently the relocation feature is unimplemented and setting + * this function has no effect. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] rel A #MDB_rel_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel); + + /** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function. + * + * See #mdb_set_relfunc and #MDB_rel_func for more details. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * It will be passed to the callback function set by #mdb_set_relfunc + * as its \b relctx parameter whenever the callback is invoked. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx); + + /** @brief Get items from a database. + * + * This function retrieves key/data pairs from the database. The address + * and length of the data associated with the specified \b key are returned + * in the structure to which \b data refers. + * If the database supports duplicate keys (#MDB_DUPSORT) then the + * first data item for the key will be returned. Retrieval of other + * items requires the use of #mdb_cursor_get(). + * + * @note The memory pointed to by the returned values is owned by the + * database. The caller need not dispose of the memory, and may not + * modify it in any way. For values returned in a read-only transaction + * any modification attempts will cause a SIGSEGV. + * @note Values returned from the database are valid only until a + * subsequent update operation, or the end of the transaction. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to search for in the database + * @param[out] data The data corresponding to the key + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the key was not in the database. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Store items into a database. + * + * This function stores key/data pairs in the database. The default behavior + * is to enter the new key/data pair, replacing any previously existing key + * if duplicates are disallowed, or adding a duplicate data item if + * duplicates are allowed (#MDB_DUPSORT). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to store in the database + * @param[in,out] data The data to store + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). The \b data + * parameter will be set to point to the existing item. + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. + * LMDB does nothing else with this memory, the caller is expected + * to modify all of the space requested. This flag must not be + * specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. This option allows fast bulk loading when keys are + * already known to be in the correct order. Loading unsorted keys + * with this flag will cause a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete items from a database. + * + * This function removes key/data pairs from the database. + * If the database does not support sorted duplicate data items + * (#MDB_DUPSORT) the data parameter is ignored. + * If the database supports sorted duplicates and the data parameter + * is NULL, all of the duplicate data items for the key will be + * deleted. Otherwise, if the data parameter is non-NULL + * only the matching data item will be deleted. + * This function will return #MDB_NOTFOUND if the specified key/data + * pair is not in the database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to delete from the database + * @param[in] data The data to delete + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Create a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * A cursor cannot be used when its database handle is closed. Nor + * when its transaction has ended, except with #mdb_cursor_renew(). + * It can be discarded with #mdb_cursor_close(). + * A cursor in a write-transaction can be closed before its transaction + * ends, and will otherwise be closed when its transaction ends. + * A cursor in a read-only transaction must be closed explicitly, before + * or after its transaction ends. It can be reused with + * #mdb_cursor_renew() before finally closing it. + * @note Earlier documentation said that cursors in every transaction + * were closed when the transaction committed or aborted. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] cursor Address where the new #MDB_cursor handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor); + + /** @brief Close a cursor handle. + * + * The cursor handle will be freed and must not be used again after this call. + * Its transaction must still be live if it is a write-transaction. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +void mdb_cursor_close(MDB_cursor *cursor); + + /** @brief Renew a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * Cursors that are only used in read-only + * transactions may be re-used, to avoid unnecessary malloc/free overhead. + * The cursor may be associated with a new read-only transaction, and + * referencing the same database handle as it was created with. + * This may be done whether the previous transaction is live or dead. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_renew(MDB_txn *txn, MDB_cursor *cursor); + + /** @brief Return the cursor's transaction handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_txn *mdb_cursor_txn(MDB_cursor *cursor); + + /** @brief Return the cursor's database handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_dbi mdb_cursor_dbi(MDB_cursor *cursor); + + /** @brief Retrieve by cursor. + * + * This function retrieves key/data pairs from the database. The address and length + * of the key are returned in the object to which \b key refers (except for the + * case of the #MDB_SET option, in which the \b key object is unchanged), and + * the address and length of the data are returned in the object to which \b data + * refers. + * See #mdb_get() for restrictions on using the output values. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in,out] key The key for a retrieved item + * @param[in,out] data The data of a retrieved item + * @param[in] op A cursor operation #MDB_cursor_op + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - no matching key found. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + MDB_cursor_op op); + + /** @brief Store by cursor. + * + * This function stores key/data pairs into the database. + * The cursor is positioned at the new item, or on failure usually near it. + * @note Earlier documentation incorrectly said errors would leave the + * state of the cursor unchanged. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] key The key operated on. + * @param[in] data The data operated on. + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_CURRENT - replace the item at the current cursor position. + * The \b key parameter must still be provided, and must match it. + * If using sorted duplicates (#MDB_DUPSORT) the data item must still + * sort into the same place. This is intended to be used when the + * new data is the same size as the old. Otherwise it will simply + * perform a delete of the old record followed by an insert. + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. This flag + * must not be specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. No key comparisons are performed. This option allows + * fast bulk loading when keys are already known to be in the + * correct order. Loading unsorted keys with this flag will cause + * a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * <li>#MDB_MULTIPLE - store multiple contiguous data elements in a + * single request. This flag may only be specified if the database + * was opened with #MDB_DUPFIXED. The \b data argument must be an + * array of two MDB_vals. The mv_size of the first MDB_val must be + * the size of a single data element. The mv_data of the first MDB_val + * must point to the beginning of the array of contiguous data elements. + * The mv_size of the second MDB_val must be the count of the number + * of data elements to store. On return this field will be set to + * the count of the number of elements actually written. The mv_data + * of the second MDB_val is unused. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete current key/data pair + * + * This function deletes the key/data pair to which the cursor refers. + * This does not invalidate the cursor, so operations such as MDB_NEXT + * can still be used on it. + * Both MDB_NEXT and MDB_GET_CURRENT will return the same record after + * this operation. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_NODUPDATA - delete all of the data items for the current key. + * This flag may only be specified if the database was opened with #MDB_DUPSORT. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags); + + /** @brief Return count of duplicates for current key. + * + * This call is only valid on databases that support sorted duplicate + * data items #MDB_DUPSORT. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[out] countp Address where the count will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - cursor is not initialized, or an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_count(MDB_cursor *cursor, size_t *countp); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two data items were keys in the + * specified database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two items were data items of + * the specified database. The database must have the #MDB_DUPSORT flag. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief A callback function used to print a message from the library. + * + * @param[in] msg The string to be printed. + * @param[in] ctx An arbitrary context pointer for the callback. + * @return < 0 on failure, >= 0 on success. + */ +typedef int (MDB_msg_func)(const char *msg, void *ctx); + + /** @brief Dump the entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] func A #MDB_msg_func function + * @param[in] ctx Anything the message function needs + * @return < 0 on failure, >= 0 on success. + */ +int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx); + + /** @brief Check for stale entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] dead Number of stale slots that were cleared + * @return 0 on success, non-zero on failure. + */ +int mdb_reader_check(MDB_env *env, int *dead); +/** @} */ + +#ifdef __cplusplus +} +#endif +/** @page tools LMDB Command Line Tools + The following describes the command line tools that are available for LMDB. + \li \ref mdb_copy_1 + \li \ref mdb_dump_1 + \li \ref mdb_load_1 + \li \ref mdb_stat_1 +*/ + +#endif /* _LMDB_H_ */ diff --git a/contrib/libs/openldap/libraries/liblmdb/mdb.c b/contrib/libs/openldap/libraries/liblmdb/mdb.c new file mode 100644 index 0000000000..deb6779640 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/mdb.c @@ -0,0 +1,10354 @@ +/** @file mdb.c + * @brief Lightning memory-mapped database library + * + * A Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. + */ +/* + * Copyright 2011-2021 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#if defined(__WIN64__) +#define _FILE_OFFSET_BITS 64 +#endif +#ifdef _WIN32 +#include <malloc.h> +#include <windows.h> +#include <wchar.h> /* get wcscpy() */ + +/** getpid() returns int; MinGW defines pid_t but MinGW64 typedefs it + * as int64 which is wrong. MSVC doesn't define it at all, so just + * don't use it. + */ +#define MDB_PID_T int +#define MDB_THR_T DWORD +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __GNUC__ +# include <sys/param.h> +#else +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +# ifndef SSIZE_MAX +# define SSIZE_MAX INT_MAX +# endif +#endif +#else +#include <sys/types.h> +#include <sys/stat.h> +#define MDB_PID_T pid_t +#define MDB_THR_T pthread_t +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/mman.h> +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#include <fcntl.h> +#endif + +#if defined(__mips) && defined(__linux) +/* MIPS has cache coherency issues, requires explicit cache control */ +#include <sys/cachectl.h> +#define CACHEFLUSH(addr, bytes, cache) cacheflush(addr, bytes, cache) +#else +#define CACHEFLUSH(addr, bytes, cache) +#endif + +#if defined(__linux) && !defined(MDB_FDATASYNC_WORKS) +/** fdatasync is broken on ext3/ext4fs on older kernels, see + * description in #mdb_env_open2 comments. You can safely + * define MDB_FDATASYNC_WORKS if this code will only be run + * on kernels 3.6 and newer. + */ +#define BROKEN_FDATASYNC +#endif + +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef _MSC_VER +#include <io.h> +typedef SSIZE_T ssize_t; +#else +#include <unistd.h> +#endif + +#if defined(__sun) || defined(ANDROID) +/* Most platforms have posix_memalign, older may only have memalign */ +#define HAVE_MEMALIGN 1 +#include <malloc.h> +/* On Solaris, we need the POSIX sigwait function */ +#if defined (__sun) +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +#endif + +#if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER)) +#include <netinet/in.h> +#include <resolv.h> /* defines BYTE_ORDER on HPUX and Solaris */ +#endif + +#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1100110 +# define MDB_USE_POSIX_MUTEX 1 +# define MDB_USE_ROBUST 1 +#elif defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__) +# define MDB_USE_POSIX_SEM 1 +# define MDB_FDATASYNC fsync +#elif defined(ANDROID) +# define MDB_FDATASYNC fsync +#endif + +#ifndef _WIN32 +#include <pthread.h> +#include <signal.h> +#ifdef MDB_USE_POSIX_SEM +# define MDB_USE_HASH 1 +#include <semaphore.h> +#else +#define MDB_USE_POSIX_MUTEX 1 +#endif +#endif + +#if defined(_WIN32) + defined(MDB_USE_POSIX_SEM) \ + + defined(MDB_USE_POSIX_MUTEX) != 1 +# error "Ambiguous shared-lock implementation" +#endif + +#ifdef USE_VALGRIND +#include <valgrind/memcheck.h> +#define VGMEMP_CREATE(h,r,z) VALGRIND_CREATE_MEMPOOL(h,r,z) +#define VGMEMP_ALLOC(h,a,s) VALGRIND_MEMPOOL_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) VALGRIND_MEMPOOL_FREE(h,a) +#define VGMEMP_DESTROY(h) VALGRIND_DESTROY_MEMPOOL(h) +#define VGMEMP_DEFINED(a,s) VALGRIND_MAKE_MEM_DEFINED(a,s) +#else +#define VGMEMP_CREATE(h,r,z) +#define VGMEMP_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) +#define VGMEMP_DESTROY(h) +#define VGMEMP_DEFINED(a,s) +#endif + +#ifndef BYTE_ORDER +# if (defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) +/* Solaris just defines one or the other */ +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# ifdef _LITTLE_ENDIAN +# define BYTE_ORDER LITTLE_ENDIAN +# else +# define BYTE_ORDER BIG_ENDIAN +# endif +# else +# define BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#endif + +#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) +#define MISALIGNED_OK 1 +#endif + +#include "lmdb.h" +#include "midl.h" + +#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN) +# error "Unknown or unsupported endianness (BYTE_ORDER)" +#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF +# error "Two's complement, reasonably sized integer types, please" +#endif + +#if (((__clang_major__ << 8) | __clang_minor__) >= 0x0302) || (((__GNUC__ << 8) | __GNUC_MINOR__) >= 0x0403) +/** Mark infrequently used env functions as cold. This puts them in a separate + * section, and optimizes them for size */ +#define ESECT __attribute__ ((cold)) +#else +/* On older compilers, use a separate section */ +# ifdef __GNUC__ +# ifdef __APPLE__ +# define ESECT __attribute__ ((section("__TEXT,text_env"))) +# else +# define ESECT __attribute__ ((section("text_env"))) +# endif +# else +# define ESECT +# endif +#endif + +#ifdef _WIN32 +#define CALL_CONV WINAPI +#else +#define CALL_CONV +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup compat Compatibility Macros + * A bunch of macros to minimize the amount of platform-specific ifdefs + * needed throughout the rest of the code. When the features this library + * needs are similar enough to POSIX to be hidden in a one-or-two line + * replacement, this macro approach is used. + * @{ + */ + + /** Features under development */ +#ifndef MDB_DEVEL +#define MDB_DEVEL 0 +#endif + + /** Wrapper around __func__, which is a C99 feature */ +#if __STDC_VERSION__ >= 199901L +# define mdb_func_ __func__ +#elif __GNUC__ >= 2 || _MSC_VER >= 1300 +# define mdb_func_ __FUNCTION__ +#else +/* If a debug message says <mdb_unknown>(), update the #if statements above */ +# define mdb_func_ "<mdb_unknown>" +#endif + +/* Internal error codes, not exposed outside liblmdb */ +#define MDB_NO_ROOT (MDB_LAST_ERRCODE + 10) +#ifdef _WIN32 +#define MDB_OWNERDEAD ((int) WAIT_ABANDONED) +#elif defined(MDB_USE_POSIX_MUTEX) && defined(EOWNERDEAD) +#define MDB_OWNERDEAD EOWNERDEAD /**< #LOCK_MUTEX0() result if dead owner */ +#endif + +#ifdef __GLIBC__ +#define GLIBC_VER ((__GLIBC__ << 16 )| __GLIBC_MINOR__) +#endif +/** Some platforms define the EOWNERDEAD error code + * even though they don't support Robust Mutexes. + * Compile with -DMDB_USE_ROBUST=0, or use some other + * mechanism like -DMDB_USE_POSIX_SEM instead of + * -DMDB_USE_POSIX_MUTEX. + * (Posix semaphores are not robust.) + */ +#ifndef MDB_USE_ROBUST +/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */ +# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \ + (defined(__GLIBC__) && GLIBC_VER < 0x020004)) +# define MDB_USE_ROBUST 0 +# else +# define MDB_USE_ROBUST 1 +# endif +#endif /* !MDB_USE_ROBUST */ + +#if defined(MDB_USE_POSIX_MUTEX) && (MDB_USE_ROBUST) +/* glibc < 2.12 only provided _np API */ +# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \ + (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST)) +# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP +# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag) +# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex) +# endif +#endif /* MDB_USE_POSIX_MUTEX && MDB_USE_ROBUST */ + +#if defined(MDB_OWNERDEAD) && (MDB_USE_ROBUST) +#define MDB_ROBUST_SUPPORTED 1 +#endif + +#ifdef _WIN32 +#define MDB_USE_HASH 1 +#define MDB_PIDLOCK 0 +#define THREAD_RET DWORD +#define pthread_t HANDLE +#define pthread_mutex_t HANDLE +#define pthread_cond_t HANDLE +typedef HANDLE mdb_mutex_t, mdb_mutexref_t; +#define pthread_key_t DWORD +#define pthread_self() GetCurrentThreadId() +#define pthread_key_create(x,y) \ + ((*(x) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? ErrCode() : 0) +#define pthread_key_delete(x) TlsFree(x) +#define pthread_getspecific(x) TlsGetValue(x) +#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode()) +#define pthread_mutex_unlock(x) ReleaseMutex(*x) +#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE) +#define pthread_cond_signal(x) SetEvent(*x) +#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0) +#define THREAD_CREATE(thr,start,arg) \ + (((thr) = CreateThread(NULL, 0, start, arg, 0, NULL)) ? 0 : ErrCode()) +#define THREAD_FINISH(thr) \ + (WaitForSingleObject(thr, INFINITE) ? ErrCode() : 0) +#define LOCK_MUTEX0(mutex) WaitForSingleObject(mutex, INFINITE) +#define UNLOCK_MUTEX(mutex) ReleaseMutex(mutex) +#define mdb_mutex_consistent(mutex) 0 +#define getpid() GetCurrentProcessId() +#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd)) +#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len)) +#define ErrCode() GetLastError() +#define GET_PAGESIZE(x) {SYSTEM_INFO si; GetSystemInfo(&si); (x) = si.dwPageSize;} +#define close(fd) (CloseHandle(fd) ? 0 : -1) +#define munmap(ptr,len) UnmapViewOfFile(ptr) +#ifdef PROCESS_QUERY_LIMITED_INFORMATION +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION PROCESS_QUERY_LIMITED_INFORMATION +#else +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION 0x1000 +#endif +#define Z "I" +#else +#define THREAD_RET void * +#define THREAD_CREATE(thr,start,arg) pthread_create(&thr,NULL,start,arg) +#define THREAD_FINISH(thr) pthread_join(thr,NULL) +#define Z "z" /**< printf format modifier for size_t */ + + /** For MDB_LOCK_FORMAT: True if readers take a pid lock in the lockfile */ +#define MDB_PIDLOCK 1 + +#ifdef MDB_USE_POSIX_SEM + +typedef sem_t *mdb_mutex_t, *mdb_mutexref_t; +#define LOCK_MUTEX0(mutex) mdb_sem_wait(mutex) +#define UNLOCK_MUTEX(mutex) sem_post(mutex) + +static int +mdb_sem_wait(sem_t *sem) +{ + int rc; + while ((rc = sem_wait(sem)) && (rc = errno) == EINTR) ; + return rc; +} + +#else /* MDB_USE_POSIX_MUTEX: */ + /** Shared mutex/semaphore as the original is stored. + * + * Not for copies. Instead it can be assigned to an #mdb_mutexref_t. + * When mdb_mutexref_t is a pointer and mdb_mutex_t is not, then it + * is array[size 1] so it can be assigned to the pointer. + */ +typedef pthread_mutex_t mdb_mutex_t[1]; + /** Reference to an #mdb_mutex_t */ +typedef pthread_mutex_t *mdb_mutexref_t; + /** Lock the reader or writer mutex. + * Returns 0 or a code to give #mdb_mutex_failed(), as in #LOCK_MUTEX(). + */ +#define LOCK_MUTEX0(mutex) pthread_mutex_lock(mutex) + /** Unlock the reader or writer mutex. + */ +#define UNLOCK_MUTEX(mutex) pthread_mutex_unlock(mutex) + /** Mark mutex-protected data as repaired, after death of previous owner. + */ +#define mdb_mutex_consistent(mutex) pthread_mutex_consistent(mutex) +#endif /* MDB_USE_POSIX_SEM */ + + /** Get the error code for the last failed system function. + */ +#define ErrCode() errno + + /** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#define HANDLE int + + /** A value for an invalid file handle. + * Mainly used to initialize file variables and signify that they are + * unused. + */ +#define INVALID_HANDLE_VALUE (-1) + + /** Get the size of a memory page for the system. + * This is the basic size that the platform's memory manager uses, and is + * fundamental to the use of memory-mapped files. + */ +#define GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE)) +#endif + +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) +#define MNAME_LEN 32 +#else +#define MNAME_LEN (sizeof(pthread_mutex_t)) +#endif + +/** @} */ + +#ifdef MDB_ROBUST_SUPPORTED + /** Lock mutex, handle any error, set rc = result. + * Return 0 on success, nonzero (not rc) on error. + */ +#define LOCK_MUTEX(rc, env, mutex) \ + (((rc) = LOCK_MUTEX0(mutex)) && \ + ((rc) = mdb_mutex_failed(env, mutex, rc))) +static int mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc); +#else +#define LOCK_MUTEX(rc, env, mutex) ((rc) = LOCK_MUTEX0(mutex)) +#define mdb_mutex_failed(env, mutex, rc) (rc) +#endif + +#ifndef _WIN32 +/** A flag for opening a file and requesting synchronous data writes. + * This is only used when writing a meta page. It's not strictly needed; + * we could just do a normal write and then immediately perform a flush. + * But if this flag is available it saves us an extra system call. + * + * @note If O_DSYNC is undefined but exists in /usr/include, + * preferably set some compiler flag to get the definition. + */ +#ifndef MDB_DSYNC +# ifdef O_DSYNC +# define MDB_DSYNC O_DSYNC +# else +# define MDB_DSYNC O_SYNC +# endif +#endif +#endif + +/** Function for flushing the data of a file. Define this to fsync + * if fdatasync() is not supported. + */ +#ifndef MDB_FDATASYNC +# define MDB_FDATASYNC fdatasync +#endif + +#ifndef MDB_MSYNC +# define MDB_MSYNC(addr,len,flags) msync(addr,len,flags) +#endif + +#ifndef MS_SYNC +#define MS_SYNC 1 +#endif + +#ifndef MS_ASYNC +#define MS_ASYNC 0 +#endif + + /** A page number in the database. + * Note that 64 bit page numbers are overkill, since pages themselves + * already represent 12-13 bits of addressable memory, and the OS will + * always limit applications to a maximum of 63 bits of address space. + * + * @note In the #MDB_node structure, we only store 48 bits of this value, + * which thus limits us to only 60 bits of addressable data. + */ +typedef MDB_ID pgno_t; + + /** A transaction ID. + * See struct MDB_txn.mt_txnid for details. + */ +typedef MDB_ID txnid_t; + +/** @defgroup debug Debug Macros + * @{ + */ +#ifndef MDB_DEBUG + /** Enable debug output. Needs variable argument macros (a C99 feature). + * Set this to 1 for copious tracing. Set to 2 to add dumps of all IDLs + * read from and written to the database (used for free space management). + */ +#define MDB_DEBUG 0 +#endif + +#if MDB_DEBUG +static int mdb_debug; +static txnid_t mdb_debug_start; + + /** Print a debug message with printf formatting. + * Requires double parenthesis around 2 or more args. + */ +# define DPRINTF(args) ((void) ((mdb_debug) && DPRINTF0 args)) +# define DPRINTF0(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt "\n", mdb_func_, __LINE__, __VA_ARGS__) +#else +# define DPRINTF(args) ((void) 0) +#endif + /** Print a debug string. + * The string is printed literally, with no format processing. + */ +#define DPUTS(arg) DPRINTF(("%s", arg)) + /** Debugging output value of a cursor DBI: Negative in a sub-cursor. */ +#define DDBI(mc) \ + (((mc)->mc_flags & C_SUB) ? -(int)(mc)->mc_dbi : (int)(mc)->mc_dbi) +/** @} */ + + /** @brief The maximum size of a database page. + * + * It is 32k or 64k, since value-PAGEBASE must fit in + * #MDB_page.%mp_upper. + * + * LMDB will use database pages < OS pages if needed. + * That causes more I/O in write transactions: The OS must + * know (read) the whole page before writing a partial page. + * + * Note that we don't currently support Huge pages. On Linux, + * regular data files cannot use Huge pages, and in general + * Huge pages aren't actually pageable. We rely on the OS + * demand-pager to read our data and page it out when memory + * pressure from other processes is high. So until OSs have + * actual paging support for Huge pages, they're not viable. + */ +#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000) + + /** The minimum number of keys required in a database page. + * Setting this to a larger value will place a smaller bound on the + * maximum size of a data item. Data items larger than this size will + * be pushed into overflow pages instead of being stored directly in + * the B-tree node. This value used to default to 4. With a page size + * of 4096 bytes that meant that any item larger than 1024 bytes would + * go into an overflow page. That also meant that on average 2-3KB of + * each overflow page was wasted space. The value cannot be lower than + * 2 because then there would no longer be a tree structure. With this + * value, items larger than 2KB will go into overflow pages, and on + * average only 1KB will be wasted. + */ +#define MDB_MINKEYS 2 + + /** A stamp that identifies a file as an LMDB file. + * There's nothing special about this value other than that it is easily + * recognizable, and it will reflect any byte order mismatches. + */ +#define MDB_MAGIC 0xBEEFC0DE + + /** The version number for a database's datafile format. */ +#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1) + /** The version number for a database's lockfile format. */ +#define MDB_LOCK_VERSION 1 + + /** @brief The max size of a key we can write, or 0 for computed max. + * + * This macro should normally be left alone or set to 0. + * Note that a database with big keys or dupsort data cannot be + * reliably modified by a liblmdb which uses a smaller max. + * The default is 511 for backwards compat, or 0 when #MDB_DEVEL. + * + * Other values are allowed, for backwards compat. However: + * A value bigger than the computed max can break if you do not + * know what you are doing, and liblmdb <= 0.9.10 can break when + * modifying a DB with keys/dupsort data bigger than its max. + * + * Data items in an #MDB_DUPSORT database are also limited to + * this size, since they're actually keys of a sub-DB. Keys and + * #MDB_DUPSORT data items must fit on a node in a regular page. + */ +#ifndef MDB_MAXKEYSIZE +#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511) +#endif + + /** The maximum size of a key we can write to the environment. */ +#if MDB_MAXKEYSIZE +#define ENV_MAXKEY(env) (MDB_MAXKEYSIZE) +#else +#define ENV_MAXKEY(env) ((env)->me_maxkey) +#endif + + /** @brief The maximum size of a data item. + * + * We only store a 32 bit value for node sizes. + */ +#define MAXDATASIZE 0xffffffffUL + +#if MDB_DEBUG + /** Key size which fits in a #DKBUF. + * @ingroup debug + */ +#define DKBUF_MAXKEYSIZE ((MDB_MAXKEYSIZE) > 0 ? (MDB_MAXKEYSIZE) : 511) + /** A key buffer. + * @ingroup debug + * This is used for printing a hex dump of a key's contents. + */ +#define DKBUF char kbuf[DKBUF_MAXKEYSIZE*2+1] + /** Display a key in hex. + * @ingroup debug + * Invoke a function to display a key in hex. + */ +#define DKEY(x) mdb_dkey(x, kbuf) +#else +#define DKBUF +#define DKEY(x) 0 +#endif + + /** An invalid page number. + * Mainly used to denote an empty tree. + */ +#define P_INVALID (~(pgno_t)0) + + /** Test if the flags \b f are set in a flag word \b w. */ +#define F_ISSET(w, f) (((w) & (f)) == (f)) + + /** Round \b n up to an even number. */ +#define EVEN(n) (((n) + 1U) & -2) /* sign-extending -2 to match n+1U */ + + /** Used for offsets within a single page. + * Since memory pages are typically 4 or 8KB in size, 12-13 bits, + * this is plenty. + */ +typedef uint16_t indx_t; + + /** Default size of memory map. + * This is certainly too small for any actual applications. Apps should always set + * the size explicitly using #mdb_env_set_mapsize(). + */ +#define DEFAULT_MAPSIZE 1048576 + +/** @defgroup readers Reader Lock Table + * Readers don't acquire any locks for their data access. Instead, they + * simply record their transaction ID in the reader table. The reader + * mutex is needed just to find an empty slot in the reader table. The + * slot's address is saved in thread-specific data so that subsequent read + * transactions started by the same thread need no further locking to proceed. + * + * If #MDB_NOTLS is set, the slot address is not saved in thread-specific data. + * + * No reader table is used if the database is on a read-only filesystem, or + * if #MDB_NOLOCK is set. + * + * Since the database uses multi-version concurrency control, readers don't + * actually need any locking. This table is used to keep track of which + * readers are using data from which old transactions, so that we'll know + * when a particular old transaction is no longer in use. Old transactions + * that have discarded any data pages can then have those pages reclaimed + * for use by a later write transaction. + * + * The lock table is constructed such that reader slots are aligned with the + * processor's cache line size. Any slot is only ever used by one thread. + * This alignment guarantees that there will be no contention or cache + * thrashing as threads update their own slot info, and also eliminates + * any need for locking when accessing a slot. + * + * A writer thread will scan every slot in the table to determine the oldest + * outstanding reader transaction. Any freed pages older than this will be + * reclaimed by the writer. The writer doesn't use any locks when scanning + * this table. This means that there's no guarantee that the writer will + * see the most up-to-date reader info, but that's not required for correct + * operation - all we need is to know the upper bound on the oldest reader, + * we don't care at all about the newest reader. So the only consequence of + * reading stale information here is that old pages might hang around a + * while longer before being reclaimed. That's actually good anyway, because + * the longer we delay reclaiming old pages, the more likely it is that a + * string of contiguous pages can be found after coalescing old pages from + * many old transactions together. + * @{ + */ + /** Number of slots in the reader table. + * This value was chosen somewhat arbitrarily. 126 readers plus a + * couple mutexes fit exactly into 8KB on my development machine. + * Applications should set the table size using #mdb_env_set_maxreaders(). + */ +#define DEFAULT_READERS 126 + + /** The size of a CPU cache line in bytes. We want our lock structures + * aligned to this size to avoid false cache line sharing in the + * lock table. + * This value works for most CPUs. For Itanium this should be 128. + */ +#ifndef CACHELINE +#define CACHELINE 64 +#endif + + /** The information we store in a single slot of the reader table. + * In addition to a transaction ID, we also record the process and + * thread ID that owns a slot, so that we can detect stale information, + * e.g. threads or processes that went away without cleaning up. + * @note We currently don't check for stale records. We simply re-init + * the table when we know that we're the only process opening the + * lock file. + */ +typedef struct MDB_rxbody { + /** Current Transaction ID when this transaction began, or (txnid_t)-1. + * Multiple readers that start at the same time will probably have the + * same ID here. Again, it's not important to exclude them from + * anything; all we need to know is which version of the DB they + * started from so we can avoid overwriting any data used in that + * particular version. + */ + volatile txnid_t mrb_txnid; + /** The process ID of the process owning this reader txn. */ + volatile MDB_PID_T mrb_pid; + /** The thread ID of the thread owning this txn. */ + volatile MDB_THR_T mrb_tid; +} MDB_rxbody; + + /** The actual reader record, with cacheline padding. */ +typedef struct MDB_reader { + union { + MDB_rxbody mrx; + /** shorthand for mrb_txnid */ +#define mr_txnid mru.mrx.mrb_txnid +#define mr_pid mru.mrx.mrb_pid +#define mr_tid mru.mrx.mrb_tid + /** cache line alignment */ + char pad[(sizeof(MDB_rxbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mru; +} MDB_reader; + + /** The header for the reader table. + * The table resides in a memory-mapped file. (This is a different file + * than is used for the main database.) + * + * For POSIX the actual mutexes reside in the shared memory of this + * mapped file. On Windows, mutexes are named objects allocated by the + * kernel; we store the mutex names in this mapped file so that other + * processes can grab them. This same approach is also used on + * MacOSX/Darwin (using named semaphores) since MacOSX doesn't support + * process-shared POSIX mutexes. For these cases where a named object + * is used, the object name is derived from a 64 bit FNV hash of the + * environment pathname. As such, naming collisions are extremely + * unlikely. If a collision occurs, the results are unpredictable. + */ +typedef struct MDB_txbody { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mtb_magic; + /** Format of this lock file. Must be set to #MDB_LOCK_FORMAT. */ + uint32_t mtb_format; +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mtb_rmname[MNAME_LEN]; +#else + /** Mutex protecting access to this table. + * This is the reader table lock used with LOCK_MUTEX(). + */ + mdb_mutex_t mtb_rmutex; +#endif + /** The ID of the last transaction committed to the database. + * This is recorded here only for convenience; the value can always + * be determined by reading the main database meta pages. + */ + volatile txnid_t mtb_txnid; + /** The number of slots that have been used in the reader table. + * This always records the maximum count, it is not decremented + * when readers release their slots. + */ + volatile unsigned mtb_numreaders; +} MDB_txbody; + + /** The actual reader table definition. */ +typedef struct MDB_txninfo { + union { + MDB_txbody mtb; +#define mti_magic mt1.mtb.mtb_magic +#define mti_format mt1.mtb.mtb_format +#define mti_rmutex mt1.mtb.mtb_rmutex +#define mti_rmname mt1.mtb.mtb_rmname +#define mti_txnid mt1.mtb.mtb_txnid +#define mti_numreaders mt1.mtb.mtb_numreaders + char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mt1; + union { +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mt2_wmname[MNAME_LEN]; +#define mti_wmname mt2.mt2_wmname +#else + mdb_mutex_t mt2_wmutex; +#define mti_wmutex mt2.mt2_wmutex +#endif + char pad[(MNAME_LEN+CACHELINE-1) & ~(CACHELINE-1)]; + } mt2; + MDB_reader mti_readers[1]; +} MDB_txninfo; + + /** Lockfile format signature: version, features and field layout */ +#define MDB_LOCK_FORMAT \ + ((uint32_t) \ + ((MDB_LOCK_VERSION) \ + /* Flags which describe functionality */ \ + + (((MDB_PIDLOCK) != 0) << 16))) +/** @} */ + +/** Common header for all page types. The page type depends on #mp_flags. + * + * #P_BRANCH and #P_LEAF pages have unsorted '#MDB_node's at the end, with + * sorted #mp_ptrs[] entries referring to them. Exception: #P_LEAF2 pages + * omit mp_ptrs and pack sorted #MDB_DUPFIXED values after the page header. + * + * #P_OVERFLOW records occupy one or more contiguous pages where only the + * first has a page header. They hold the real data of #F_BIGDATA nodes. + * + * #P_SUBP sub-pages are small leaf "pages" with duplicate data. + * A node with flag #F_DUPDATA but not #F_SUBDATA contains a sub-page. + * (Duplicate data can also go in sub-databases, which use normal pages.) + * + * #P_META pages contain #MDB_meta, the start point of an LMDB snapshot. + * + * Each non-metapage up to #MDB_meta.%mm_last_pg is reachable exactly once + * in the snapshot: Either used by a database or listed in a freeDB record. + */ +typedef struct MDB_page { +#define mp_pgno mp_p.p_pgno +#define mp_next mp_p.p_next + union { + pgno_t p_pgno; /**< page number */ + struct MDB_page *p_next; /**< for in-memory list of freed pages */ + } mp_p; + uint16_t mp_pad; /**< key size if this is a LEAF2 page */ +/** @defgroup mdb_page Page Flags + * @ingroup internal + * Flags for the page headers. + * @{ + */ +#define P_BRANCH 0x01 /**< branch page */ +#define P_LEAF 0x02 /**< leaf page */ +#define P_OVERFLOW 0x04 /**< overflow page */ +#define P_META 0x08 /**< meta page */ +#define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */ +#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */ +#define P_SUBP 0x40 /**< for #MDB_DUPSORT sub-pages */ +#define P_LOOSE 0x4000 /**< page was dirtied then freed, can be reused */ +#define P_KEEP 0x8000 /**< leave this page alone during spill */ +/** @} */ + uint16_t mp_flags; /**< @ref mdb_page */ +#define mp_lower mp_pb.pb.pb_lower +#define mp_upper mp_pb.pb.pb_upper +#define mp_pages mp_pb.pb_pages + union { + struct { + indx_t pb_lower; /**< lower bound of free space */ + indx_t pb_upper; /**< upper bound of free space */ + } pb; + uint32_t pb_pages; /**< number of overflow pages */ + } mp_pb; + indx_t mp_ptrs[0]; /**< dynamic size */ +} MDB_page; + +/** Alternate page header, for 2-byte aligned access */ +typedef struct MDB_page2 { + uint16_t mp2_p[sizeof(pgno_t)/2]; + uint16_t mp2_pad; + uint16_t mp2_flags; + indx_t mp2_lower; + indx_t mp2_upper; + indx_t mp2_ptrs[0]; +} MDB_page2; + +#define MP_PGNO(p) (((MDB_page2 *)(void *)(p))->mp2_p) +#define MP_PAD(p) (((MDB_page2 *)(void *)(p))->mp2_pad) +#define MP_FLAGS(p) (((MDB_page2 *)(void *)(p))->mp2_flags) +#define MP_LOWER(p) (((MDB_page2 *)(void *)(p))->mp2_lower) +#define MP_UPPER(p) (((MDB_page2 *)(void *)(p))->mp2_upper) +#define MP_PTRS(p) (((MDB_page2 *)(void *)(p))->mp2_ptrs) + + /** Size of the page header, excluding dynamic data at the end */ +#define PAGEHDRSZ ((unsigned) offsetof(MDB_page, mp_ptrs)) + + /** Address of first usable data byte in a page, after the header */ +#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ)) + + /** ITS#7713, change PAGEBASE to handle 65536 byte pages */ +#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0) + + /** Number of nodes on a page */ +#define NUMKEYS(p) ((MP_LOWER(p) - (PAGEHDRSZ-PAGEBASE)) >> 1) + + /** The amount of space remaining in the page */ +#define SIZELEFT(p) (indx_t)(MP_UPPER(p) - MP_LOWER(p)) + + /** The percentage of space used in the page, in tenths of a percent. */ +#define PAGEFILL(env, p) (1000L * ((env)->me_psize - PAGEHDRSZ - SIZELEFT(p)) / \ + ((env)->me_psize - PAGEHDRSZ)) + /** The minimum page fill factor, in tenths of a percent. + * Pages emptier than this are candidates for merging. + */ +#define FILL_THRESHOLD 250 + + /** Test if a page is a leaf page */ +#define IS_LEAF(p) F_ISSET(MP_FLAGS(p), P_LEAF) + /** Test if a page is a LEAF2 page */ +#define IS_LEAF2(p) F_ISSET(MP_FLAGS(p), P_LEAF2) + /** Test if a page is a branch page */ +#define IS_BRANCH(p) F_ISSET(MP_FLAGS(p), P_BRANCH) + /** Test if a page is an overflow page */ +#define IS_OVERFLOW(p) F_ISSET(MP_FLAGS(p), P_OVERFLOW) + /** Test if a page is a sub page */ +#define IS_SUBP(p) F_ISSET(MP_FLAGS(p), P_SUBP) + + /** The number of overflow pages needed to store the given size. */ +#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1) + + /** Link in #MDB_txn.%mt_loose_pgs list. + * Kept outside the page header, which is needed when reusing the page. + */ +#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2)) + + /** Header for a single key/data pair within a page. + * Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2. + * We guarantee 2-byte alignment for 'MDB_node's. + * + * #mn_lo and #mn_hi are used for data size on leaf nodes, and for child + * pgno on branch nodes. On 64 bit platforms, #mn_flags is also used + * for pgno. (Branch nodes have no flags). Lo and hi are in host byte + * order in case some accesses can be optimized to 32-bit word access. + * + * Leaf node flags describe node contents. #F_BIGDATA says the node's + * data part is the page number of an overflow page with actual data. + * #F_DUPDATA and #F_SUBDATA can be combined giving duplicate data in + * a sub-page/sub-database, and named databases (just #F_SUBDATA). + */ +typedef struct MDB_node { + /** part of data size or pgno + * @{ */ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short mn_lo, mn_hi; +#else + unsigned short mn_hi, mn_lo; +#endif + /** @} */ +/** @defgroup mdb_node Node Flags + * @ingroup internal + * Flags for node headers. + * @{ + */ +#define F_BIGDATA 0x01 /**< data put on overflow page */ +#define F_SUBDATA 0x02 /**< data is a sub-database */ +#define F_DUPDATA 0x04 /**< data has duplicates */ + +/** valid flags for #mdb_node_add() */ +#define NODE_ADD_FLAGS (F_DUPDATA|F_SUBDATA|MDB_RESERVE|MDB_APPEND) + +/** @} */ + unsigned short mn_flags; /**< @ref mdb_node */ + unsigned short mn_ksize; /**< key size */ + char mn_data[1]; /**< key and data are appended here */ +} MDB_node; + + /** Size of the node header, excluding dynamic data at the end */ +#define NODESIZE offsetof(MDB_node, mn_data) + + /** Bit position of top word in page number, for shifting mn_flags */ +#define PGNO_TOPWORD ((pgno_t)-1 > 0xffffffffu ? 32 : 0) + + /** Size of a node in a branch page with a given key. + * This is just the node header plus the key, there is no data. + */ +#define INDXSIZE(k) (NODESIZE + ((k) == NULL ? 0 : (k)->mv_size)) + + /** Size of a node in a leaf page with a given key and data. + * This is node header plus key plus data size. + */ +#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size) + + /** Address of node \b i in page \b p */ +#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + MP_PTRS(p)[i] + PAGEBASE)) + + /** Address of the key for the node */ +#define NODEKEY(node) (void *)((node)->mn_data) + + /** Address of the data for a node */ +#define NODEDATA(node) (void *)((char *)(node)->mn_data + (node)->mn_ksize) + + /** Get the page number pointed to by a branch node */ +#define NODEPGNO(node) \ + ((node)->mn_lo | ((pgno_t) (node)->mn_hi << 16) | \ + (PGNO_TOPWORD ? ((pgno_t) (node)->mn_flags << PGNO_TOPWORD) : 0)) + /** Set the page number in a branch node */ +#define SETPGNO(node,pgno) do { \ + (node)->mn_lo = (pgno) & 0xffff; (node)->mn_hi = (pgno) >> 16; \ + if (PGNO_TOPWORD) (node)->mn_flags = (pgno) >> PGNO_TOPWORD; } while(0) + + /** Get the size of the data in a leaf node */ +#define NODEDSZ(node) ((node)->mn_lo | ((unsigned)(node)->mn_hi << 16)) + /** Set the size of the data for a leaf node */ +#define SETDSZ(node,size) do { \ + (node)->mn_lo = (size) & 0xffff; (node)->mn_hi = (size) >> 16;} while(0) + /** The size of a key in a node */ +#define NODEKSZ(node) ((node)->mn_ksize) + + /** Copy a page number from src to dst */ +#ifdef MISALIGNED_OK +#define COPY_PGNO(dst,src) dst = src +#undef MP_PGNO +#define MP_PGNO(p) ((p)->mp_pgno) +#else +#if SIZE_MAX > 4294967295UL +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d++ = *s++; \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#else +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#endif +#endif + /** The address of a key in a LEAF2 page. + * LEAF2 pages are used for #MDB_DUPFIXED sorted-duplicate sub-DBs. + * There are no node headers, keys are stored contiguously. + */ +#define LEAF2KEY(p, i, ks) ((char *)(p) + PAGEHDRSZ + ((i)*(ks))) + + /** Set the \b node's key into \b keyptr, if requested. */ +#define MDB_GET_KEY(node, keyptr) { if ((keyptr) != NULL) { \ + (keyptr)->mv_size = NODEKSZ(node); (keyptr)->mv_data = NODEKEY(node); } } + + /** Set the \b node's key into \b key. */ +#define MDB_GET_KEY2(node, key) { key.mv_size = NODEKSZ(node); key.mv_data = NODEKEY(node); } + + /** Information about a single database in the environment. */ +typedef struct MDB_db { + uint32_t md_pad; /**< also ksize for LEAF2 pages */ + uint16_t md_flags; /**< @ref mdb_dbi_open */ + uint16_t md_depth; /**< depth of this tree */ + pgno_t md_branch_pages; /**< number of internal pages */ + pgno_t md_leaf_pages; /**< number of leaf pages */ + pgno_t md_overflow_pages; /**< number of overflow pages */ + size_t md_entries; /**< number of data items */ + pgno_t md_root; /**< the root page of this tree */ +} MDB_db; + +#define MDB_VALID 0x8000 /**< DB handle is valid, for me_dbflags */ +#define PERSISTENT_FLAGS (0xffff & ~(MDB_VALID)) + /** #mdb_dbi_open() flags */ +#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\ + MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE) + + /** Handle for the DB used to track free pages. */ +#define FREE_DBI 0 + /** Handle for the default DB. */ +#define MAIN_DBI 1 + /** Number of DBs in metapage (free and main) - also hardcoded elsewhere */ +#define CORE_DBS 2 + + /** Number of meta pages - also hardcoded elsewhere */ +#define NUM_METAS 2 + + /** Meta page content. + * A meta page is the start point for accessing a database snapshot. + * Pages 0-1 are meta pages. Transaction N writes meta page #(N % 2). + */ +typedef struct MDB_meta { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mm_magic; + /** Version number of this file. Must be set to #MDB_DATA_VERSION. */ + uint32_t mm_version; + void *mm_address; /**< address for fixed mapping */ + size_t mm_mapsize; /**< size of mmap region */ + MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */ + /** The size of pages used in this DB */ +#define mm_psize mm_dbs[FREE_DBI].md_pad + /** Any persistent environment flags. @ref mdb_env */ +#define mm_flags mm_dbs[FREE_DBI].md_flags + /** Last used page in the datafile. + * Actually the file may be shorter if the freeDB lists the final pages. + */ + pgno_t mm_last_pg; + volatile txnid_t mm_txnid; /**< txnid that committed this page */ +} MDB_meta; + + /** Buffer for a stack-allocated meta page. + * The members define size and alignment, and silence type + * aliasing warnings. They are not used directly; that could + * mean incorrectly using several union members in parallel. + */ +typedef union MDB_metabuf { + MDB_page mb_page; + struct { + char mm_pad[PAGEHDRSZ]; + MDB_meta mm_meta; + } mb_metabuf; +} MDB_metabuf; + + /** Auxiliary DB info. + * The information here is mostly static/read-only. There is + * only a single copy of this record in the environment. + */ +typedef struct MDB_dbx { + MDB_val md_name; /**< name of the database */ + MDB_cmp_func *md_cmp; /**< function for comparing keys */ + MDB_cmp_func *md_dcmp; /**< function for comparing data items */ + MDB_rel_func *md_rel; /**< user relocate function */ + void *md_relctx; /**< user-provided context for md_rel */ +} MDB_dbx; + + /** A database transaction. + * Every operation requires a transaction handle. + */ +struct MDB_txn { + MDB_txn *mt_parent; /**< parent of a nested txn */ + /** Nested txn under this txn, set together with flag #MDB_TXN_HAS_CHILD */ + MDB_txn *mt_child; + pgno_t mt_next_pgno; /**< next unallocated page */ + /** The ID of this transaction. IDs are integers incrementing from 1. + * Only committed write transactions increment the ID. If a transaction + * aborts, the ID may be re-used by the next writer. + */ + txnid_t mt_txnid; + MDB_env *mt_env; /**< the DB environment */ + /** The list of pages that became unused during this transaction. + */ + MDB_IDL mt_free_pgs; + /** The list of loose pages that became unused and may be reused + * in this transaction, linked through #NEXT_LOOSE_PAGE(page). + */ + MDB_page *mt_loose_pgs; + /** Number of loose pages (#mt_loose_pgs) */ + int mt_loose_count; + /** The sorted list of dirty pages we temporarily wrote to disk + * because the dirty list was full. page numbers in here are + * shifted left by 1, deleted slots have the LSB set. + */ + MDB_IDL mt_spill_pgs; + union { + /** For write txns: Modified pages. Sorted when not MDB_WRITEMAP. */ + MDB_ID2L dirty_list; + /** For read txns: This thread/txn's reader table slot, or NULL. */ + MDB_reader *reader; + } mt_u; + /** Array of records for each DB known in the environment. */ + MDB_dbx *mt_dbxs; + /** Array of MDB_db records for each known DB */ + MDB_db *mt_dbs; + /** Array of sequence numbers for each DB handle */ + unsigned int *mt_dbiseqs; +/** @defgroup mt_dbflag Transaction DB Flags + * @ingroup internal + * @{ + */ +#define DB_DIRTY 0x01 /**< DB was written in this txn */ +#define DB_STALE 0x02 /**< Named-DB record is older than txnID */ +#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */ +#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */ +#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */ +#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */ +/** @} */ + /** In write txns, array of cursors for each DB */ + MDB_cursor **mt_cursors; + /** Array of flags for each DB */ + unsigned char *mt_dbflags; + /** Number of DB records in use, or 0 when the txn is finished. + * This number only ever increments until the txn finishes; we + * don't decrement it when individual DB handles are closed. + */ + MDB_dbi mt_numdbs; + +/** @defgroup mdb_txn Transaction Flags + * @ingroup internal + * @{ + */ + /** #mdb_txn_begin() flags */ +#define MDB_TXN_BEGIN_FLAGS MDB_RDONLY +#define MDB_TXN_RDONLY MDB_RDONLY /**< read-only transaction */ + /* internal txn flags */ +#define MDB_TXN_WRITEMAP MDB_WRITEMAP /**< copy of #MDB_env flag in writers */ +#define MDB_TXN_FINISHED 0x01 /**< txn is finished or never began */ +#define MDB_TXN_ERROR 0x02 /**< txn is unusable after an error */ +#define MDB_TXN_DIRTY 0x04 /**< must write, even if dirty list is empty */ +#define MDB_TXN_SPILLS 0x08 /**< txn or a parent has spilled pages */ +#define MDB_TXN_HAS_CHILD 0x10 /**< txn has an #MDB_txn.%mt_child */ + /** most operations on the txn are currently illegal */ +#define MDB_TXN_BLOCKED (MDB_TXN_FINISHED|MDB_TXN_ERROR|MDB_TXN_HAS_CHILD) +/** @} */ + unsigned int mt_flags; /**< @ref mdb_txn */ + /** #dirty_list room: Array size - \#dirty pages visible to this txn. + * Includes ancestor txns' dirty pages not hidden by other txns' + * dirty/spilled pages. Thus commit(nested txn) has room to merge + * dirty_list into mt_parent after freeing hidden mt_parent pages. + */ + unsigned int mt_dirty_room; +}; + +/** Enough space for 2^32 nodes with minimum of 2 keys per node. I.e., plenty. + * At 4 keys per node, enough for 2^64 nodes, so there's probably no need to + * raise this on a 64 bit machine. + */ +#define CURSOR_STACK 32 + +struct MDB_xcursor; + + /** Cursors are used for all DB operations. + * A cursor holds a path of (page pointer, key index) from the DB + * root to a position in the DB, plus other state. #MDB_DUPSORT + * cursors include an xcursor to the current data item. Write txns + * track their cursors and keep them up to date when data moves. + * Exception: An xcursor's pointer to a #P_SUBP page can be stale. + * (A node with #F_DUPDATA but no #F_SUBDATA contains a subpage). + */ +struct MDB_cursor { + /** Next cursor on this DB in this txn */ + MDB_cursor *mc_next; + /** Backup of the original cursor if this cursor is a shadow */ + MDB_cursor *mc_backup; + /** Context used for databases with #MDB_DUPSORT, otherwise NULL */ + struct MDB_xcursor *mc_xcursor; + /** The transaction that owns this cursor */ + MDB_txn *mc_txn; + /** The database handle this cursor operates on */ + MDB_dbi mc_dbi; + /** The database record for this cursor */ + MDB_db *mc_db; + /** The database auxiliary record for this cursor */ + MDB_dbx *mc_dbx; + /** The @ref mt_dbflag for this database */ + unsigned char *mc_dbflag; + unsigned short mc_snum; /**< number of pushed pages */ + unsigned short mc_top; /**< index of top page, normally mc_snum-1 */ +/** @defgroup mdb_cursor Cursor Flags + * @ingroup internal + * Cursor state flags. + * @{ + */ +#define C_INITIALIZED 0x01 /**< cursor has been initialized and is valid */ +#define C_EOF 0x02 /**< No more data */ +#define C_SUB 0x04 /**< Cursor is a sub-cursor */ +#define C_DEL 0x08 /**< last op was a cursor_del */ +#define C_UNTRACK 0x40 /**< Un-track cursor when closing */ +/** @} */ + unsigned int mc_flags; /**< @ref mdb_cursor */ + MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */ + indx_t mc_ki[CURSOR_STACK]; /**< stack of page indices */ +}; + + /** Context for sorted-dup records. + * We could have gone to a fully recursive design, with arbitrarily + * deep nesting of sub-databases. But for now we only handle these + * levels - main DB, optional sub-DB, sorted-duplicate DB. + */ +typedef struct MDB_xcursor { + /** A sub-cursor for traversing the Dup DB */ + MDB_cursor mx_cursor; + /** The database record for this Dup DB */ + MDB_db mx_db; + /** The auxiliary DB record for this Dup DB */ + MDB_dbx mx_dbx; + /** The @ref mt_dbflag for this Dup DB */ + unsigned char mx_dbflag; +} MDB_xcursor; + + /** Check if there is an inited xcursor */ +#define XCURSOR_INITED(mc) \ + ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + + /** Update the xcursor's sub-page pointer, if any, in \b mc. Needed + * when the node which contains the sub-page may have moved. Called + * with leaf page \b mp = mc->mc_pg[\b top]. + */ +#define XCURSOR_REFRESH(mc, top, mp) do { \ + MDB_page *xr_pg = (mp); \ + MDB_node *xr_node; \ + if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \ + xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \ + if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \ + (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \ +} while (0) + + /** State of FreeDB old pages, stored in the MDB_env */ +typedef struct MDB_pgstate { + pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */ + txnid_t mf_pglast; /**< ID of last used record, or 0 if !mf_pghead */ +} MDB_pgstate; + + /** The database environment. */ +struct MDB_env { + HANDLE me_fd; /**< The main data file */ + HANDLE me_lfd; /**< The lock file */ + HANDLE me_mfd; /**< For writing and syncing the meta pages */ + /** Failed to update the meta page. Probably an I/O error. */ +#define MDB_FATAL_ERROR 0x80000000U + /** Some fields are initialized. */ +#define MDB_ENV_ACTIVE 0x20000000U + /** me_txkey is set */ +#define MDB_ENV_TXKEY 0x10000000U + /** fdatasync is unreliable */ +#define MDB_FSYNCONLY 0x08000000U + uint32_t me_flags; /**< @ref mdb_env */ + unsigned int me_psize; /**< DB page size, inited from me_os_psize */ + unsigned int me_os_psize; /**< OS page size, from #GET_PAGESIZE */ + unsigned int me_maxreaders; /**< size of the reader table */ + /** Max #MDB_txninfo.%mti_numreaders of interest to #mdb_env_close() */ + volatile int me_close_readers; + MDB_dbi me_numdbs; /**< number of DBs opened */ + MDB_dbi me_maxdbs; /**< size of the DB table */ + MDB_PID_T me_pid; /**< process ID of this env */ + char *me_path; /**< path to the DB files */ + char *me_map; /**< the memory map of the data file */ + MDB_txninfo *me_txns; /**< the memory map of the lock file or NULL */ + MDB_meta *me_metas[NUM_METAS]; /**< pointers to the two meta pages */ + void *me_pbuf; /**< scratch area for DUPSORT put() */ + MDB_txn *me_txn; /**< current write transaction */ + MDB_txn *me_txn0; /**< prealloc'd write transaction */ + size_t me_mapsize; /**< size of the data memory map */ + off_t me_size; /**< current file size */ + pgno_t me_maxpg; /**< me_mapsize / me_psize */ + MDB_dbx *me_dbxs; /**< array of static DB info */ + uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */ + unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */ + pthread_key_t me_txkey; /**< thread-key for readers */ + txnid_t me_pgoldest; /**< ID of oldest reader last time we looked */ + MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ +# define me_pglast me_pgstate.mf_pglast +# define me_pghead me_pgstate.mf_pghead + MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */ + /** IDL of pages that became unused in a write txn */ + MDB_IDL me_free_pgs; + /** ID2L of pages written during a write txn. Length MDB_IDL_UM_SIZE. */ + MDB_ID2L me_dirty_list; + /** Max number of freelist items that can fit in a single overflow page */ + int me_maxfree_1pg; + /** Max size of a node on a page */ + unsigned int me_nodemax; +#if !(MDB_MAXKEYSIZE) + unsigned int me_maxkey; /**< max size of a key */ +#endif + int me_live_reader; /**< have liveness lock in reader table */ +#ifdef _WIN32 + int me_pidquery; /**< Used in OpenProcess */ +#endif +#ifdef MDB_USE_POSIX_MUTEX /* Posix mutexes reside in shared mem */ +# define me_rmutex me_txns->mti_rmutex /**< Shared reader lock */ +# define me_wmutex me_txns->mti_wmutex /**< Shared writer lock */ +#else + mdb_mutex_t me_rmutex; + mdb_mutex_t me_wmutex; +#endif + void *me_userctx; /**< User-settable context */ + MDB_assert_func *me_assert_func; /**< Callback for assertion failures */ +}; + + /** Nested transaction */ +typedef struct MDB_ntxn { + MDB_txn mnt_txn; /**< the transaction */ + MDB_pgstate mnt_pgstate; /**< parent transaction's saved freestate */ +} MDB_ntxn; + + /** max number of pages to commit in one writev() call */ +#define MDB_COMMIT_PAGES 64 +#if defined(IOV_MAX) && IOV_MAX < MDB_COMMIT_PAGES +#undef MDB_COMMIT_PAGES +#define MDB_COMMIT_PAGES IOV_MAX +#endif + + /** max bytes to write in one call */ +#define MAX_WRITE (0x40000000U >> (sizeof(ssize_t) == 4)) + + /** Check \b txn and \b dbi arguments to a function */ +#define TXN_DBI_EXIST(txn, dbi, validity) \ + ((txn) && (dbi)<(txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & (validity))) + + /** Check for misused \b dbi handles */ +#define TXN_DBI_CHANGED(txn, dbi) \ + ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi]) + +static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp); +static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp); +static int mdb_page_touch(MDB_cursor *mc); + +#define MDB_END_NAMES {"committed", "empty-commit", "abort", "reset", \ + "reset-tmp", "fail-begin", "fail-beginchild"} +enum { + /* mdb_txn_end operation number, for logging */ + MDB_END_COMMITTED, MDB_END_EMPTY_COMMIT, MDB_END_ABORT, MDB_END_RESET, + MDB_END_RESET_TMP, MDB_END_FAIL_BEGIN, MDB_END_FAIL_BEGINCHILD +}; +#define MDB_END_OPMASK 0x0F /**< mask for #mdb_txn_end() operation number */ +#define MDB_END_UPDATE 0x10 /**< update env state (DBIs) */ +#define MDB_END_FREE 0x20 /**< free txn unless it is #MDB_env.%me_txn0 */ +#define MDB_END_SLOT MDB_NOTLS /**< release any reader slot if #MDB_NOTLS */ +static void mdb_txn_end(MDB_txn *txn, unsigned mode); + +static int mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **mp, int *lvl); +static int mdb_page_search_root(MDB_cursor *mc, + MDB_val *key, int modify); +#define MDB_PS_MODIFY 1 +#define MDB_PS_ROOTONLY 2 +#define MDB_PS_FIRST 4 +#define MDB_PS_LAST 8 +static int mdb_page_search(MDB_cursor *mc, + MDB_val *key, int flags); +static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst); + +#define MDB_SPLIT_REPLACE MDB_APPENDDUP /**< newkey is not new */ +static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, + pgno_t newpgno, unsigned int nflags); + +static int mdb_env_read_header(MDB_env *env, MDB_meta *meta); +static MDB_meta *mdb_env_pick_meta(const MDB_env *env); +static int mdb_env_write_meta(MDB_txn *txn); +#if defined(MDB_USE_POSIX_MUTEX) && !defined(MDB_ROBUST_SUPPORTED) /* Drop unused excl arg */ +# define mdb_env_close0(env, excl) mdb_env_close1(env) +#endif +static void mdb_env_close0(MDB_env *env, int excl); + +static MDB_node *mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp); +static int mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags); +static void mdb_node_del(MDB_cursor *mc, int ksize); +static void mdb_node_shrink(MDB_page *mp, indx_t indx); +static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft); +static int mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data); +static size_t mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data); +static size_t mdb_branch_size(MDB_env *env, MDB_val *key); + +static int mdb_rebalance(MDB_cursor *mc); +static int mdb_update_key(MDB_cursor *mc, MDB_val *key); + +static void mdb_cursor_pop(MDB_cursor *mc); +static int mdb_cursor_push(MDB_cursor *mc, MDB_page *mp); + +static int mdb_cursor_del0(MDB_cursor *mc); +static int mdb_del0(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned flags); +static int mdb_cursor_sibling(MDB_cursor *mc, int move_right); +static int mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op, + int *exactp); +static int mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data); +static int mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data); + +static void mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx); +static void mdb_xcursor_init0(MDB_cursor *mc); +static void mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node); +static void mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int force); + +static int mdb_drop0(MDB_cursor *mc, int subs); +static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi); +static int mdb_reader_check0(MDB_env *env, int rlocked, int *dead); + +/** @cond */ +static MDB_cmp_func mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long; +/** @endcond */ + +/** Compare two items pointing at size_t's of unknown alignment. */ +#ifdef MISALIGNED_OK +# define mdb_cmp_clong mdb_cmp_long +#else +# define mdb_cmp_clong mdb_cmp_cint +#endif + +#ifdef _WIN32 +static SECURITY_DESCRIPTOR mdb_null_sd; +static SECURITY_ATTRIBUTES mdb_all_sa; +static int mdb_sec_inited; + +struct MDB_name; +static int utf8_to_utf16(const char *src, struct MDB_name *dst, int xtra); +#endif + +/** Return the library version info. */ +char * ESECT +mdb_version(int *major, int *minor, int *patch) +{ + if (major) *major = MDB_VERSION_MAJOR; + if (minor) *minor = MDB_VERSION_MINOR; + if (patch) *patch = MDB_VERSION_PATCH; + return MDB_VERSION_STRING; +} + +/** Table of descriptions for LMDB @ref errors */ +static char *const mdb_errstr[] = { + "MDB_KEYEXIST: Key/data pair already exists", + "MDB_NOTFOUND: No matching key/data pair found", + "MDB_PAGE_NOTFOUND: Requested page not found", + "MDB_CORRUPTED: Located page was wrong type", + "MDB_PANIC: Update of meta page failed or environment had fatal error", + "MDB_VERSION_MISMATCH: Database environment version mismatch", + "MDB_INVALID: File is not an LMDB file", + "MDB_MAP_FULL: Environment mapsize limit reached", + "MDB_DBS_FULL: Environment maxdbs limit reached", + "MDB_READERS_FULL: Environment maxreaders limit reached", + "MDB_TLS_FULL: Thread-local storage keys full - too many environments open", + "MDB_TXN_FULL: Transaction has too many dirty pages - transaction too big", + "MDB_CURSOR_FULL: Internal error - cursor stack limit reached", + "MDB_PAGE_FULL: Internal error - page has no more space", + "MDB_MAP_RESIZED: Database contents grew beyond environment mapsize", + "MDB_INCOMPATIBLE: Operation and DB incompatible, or DB flags changed", + "MDB_BAD_RSLOT: Invalid reuse of reader locktable slot", + "MDB_BAD_TXN: Transaction must abort, has a child, or is invalid", + "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size", + "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly", +}; + +char * +mdb_strerror(int err) +{ +#ifdef _WIN32 + /** HACK: pad 4KB on stack over the buf. Return system msgs in buf. + * This works as long as no function between the call to mdb_strerror + * and the actual use of the message uses more than 4K of stack. + */ +#define MSGSIZE 1024 +#define PADSIZE 4096 + char buf[MSGSIZE+PADSIZE], *ptr = buf; +#endif + int i; + if (!err) + return ("Successful return: 0"); + + if (err >= MDB_KEYEXIST && err <= MDB_LAST_ERRCODE) { + i = err - MDB_KEYEXIST; + return mdb_errstr[i]; + } + +#ifdef _WIN32 + /* These are the C-runtime error codes we use. The comment indicates + * their numeric value, and the Win32 error they would correspond to + * if the error actually came from a Win32 API. A major mess, we should + * have used LMDB-specific error codes for everything. + */ + switch(err) { + case ENOENT: /* 2, FILE_NOT_FOUND */ + case EIO: /* 5, ACCESS_DENIED */ + case ENOMEM: /* 12, INVALID_ACCESS */ + case EACCES: /* 13, INVALID_DATA */ + case EBUSY: /* 16, CURRENT_DIRECTORY */ + case EINVAL: /* 22, BAD_COMMAND */ + case ENOSPC: /* 28, OUT_OF_PAPER */ + return strerror(err); + default: + ; + } + buf[0] = 0; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE); + return ptr; +#else + if (err < 0) + return "Invalid error code"; + return strerror(err); +#endif +} + +/** assert(3) variant in cursor context */ +#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr) +/** assert(3) variant in transaction context */ +#define mdb_tassert(txn, expr) mdb_assert0((txn)->mt_env, expr, #expr) +/** assert(3) variant in environment context */ +#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr) + +#ifndef NDEBUG +# define mdb_assert0(env, expr, expr_txt) ((expr) ? (void)0 : \ + mdb_assert_fail(env, expr_txt, mdb_func_, __FILE__, __LINE__)) + +static void ESECT +mdb_assert_fail(MDB_env *env, const char *expr_txt, + const char *func, const char *file, int line) +{ + char buf[400]; + sprintf(buf, "%.100s:%d: Assertion '%.200s' failed in %.40s()", + file, line, expr_txt, func); + if (env->me_assert_func) + env->me_assert_func(env, buf); + fprintf(stderr, "%s\n", buf); + abort(); +} +#else +# define mdb_assert0(env, expr, expr_txt) ((void) 0) +#endif /* NDEBUG */ + +#if MDB_DEBUG +/** Return the page number of \b mp which may be sub-page, for debug output */ +static pgno_t +mdb_dbg_pgno(MDB_page *mp) +{ + pgno_t ret; + COPY_PGNO(ret, MP_PGNO(mp)); + return ret; +} + +/** Display a key in hexadecimal and return the address of the result. + * @param[in] key the key to display + * @param[in] buf the buffer to write into. Should always be #DKBUF. + * @return The key in hexadecimal form. + */ +char * +mdb_dkey(MDB_val *key, char *buf) +{ + char *ptr = buf; + unsigned char *c = key->mv_data; + unsigned int i; + + if (!key) + return ""; + + if (key->mv_size > DKBUF_MAXKEYSIZE) + return "MDB_MAXKEYSIZE"; + /* may want to make this a dynamic check: if the key is mostly + * printable characters, print it as-is instead of converting to hex. + */ +#if 1 + buf[0] = '\0'; + for (i=0; i<key->mv_size; i++) + ptr += sprintf(ptr, "%02x", *c++); +#else + sprintf(buf, "%.*s", key->mv_size, key->mv_data); +#endif + return buf; +} + +static const char * +mdb_leafnode_type(MDB_node *n) +{ + static char *const tp[2][2] = {{"", ": DB"}, {": sub-page", ": sub-DB"}}; + return F_ISSET(n->mn_flags, F_BIGDATA) ? ": overflow page" : + tp[F_ISSET(n->mn_flags, F_DUPDATA)][F_ISSET(n->mn_flags, F_SUBDATA)]; +} + +/** Display all the keys in the page. */ +void +mdb_page_list(MDB_page *mp) +{ + pgno_t pgno = mdb_dbg_pgno(mp); + const char *type, *state = (MP_FLAGS(mp) & P_DIRTY) ? ", dirty" : ""; + MDB_node *node; + unsigned int i, nkeys, nsize, total = 0; + MDB_val key; + DKBUF; + + switch (MP_FLAGS(mp) & (P_BRANCH|P_LEAF|P_LEAF2|P_META|P_OVERFLOW|P_SUBP)) { + case P_BRANCH: type = "Branch page"; break; + case P_LEAF: type = "Leaf page"; break; + case P_LEAF|P_SUBP: type = "Sub-page"; break; + case P_LEAF|P_LEAF2: type = "LEAF2 page"; break; + case P_LEAF|P_LEAF2|P_SUBP: type = "LEAF2 sub-page"; break; + case P_OVERFLOW: + fprintf(stderr, "Overflow page %"Z"u pages %u%s\n", + pgno, mp->mp_pages, state); + return; + case P_META: + fprintf(stderr, "Meta-page %"Z"u txnid %"Z"u\n", + pgno, ((MDB_meta *)METADATA(mp))->mm_txnid); + return; + default: + fprintf(stderr, "Bad page %"Z"u flags 0x%X\n", pgno, MP_FLAGS(mp)); + return; + } + + nkeys = NUMKEYS(mp); + fprintf(stderr, "%s %"Z"u numkeys %d%s\n", type, pgno, nkeys, state); + + for (i=0; i<nkeys; i++) { + if (IS_LEAF2(mp)) { /* LEAF2 pages have no mp_ptrs[] or node headers */ + key.mv_size = nsize = mp->mp_pad; + key.mv_data = LEAF2KEY(mp, i, nsize); + total += nsize; + fprintf(stderr, "key %d: nsize %d, %s\n", i, nsize, DKEY(&key)); + continue; + } + node = NODEPTR(mp, i); + key.mv_size = node->mn_ksize; + key.mv_data = node->mn_data; + nsize = NODESIZE + key.mv_size; + if (IS_BRANCH(mp)) { + fprintf(stderr, "key %d: page %"Z"u, %s\n", i, NODEPGNO(node), + DKEY(&key)); + total += nsize; + } else { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + nsize += sizeof(pgno_t); + else + nsize += NODEDSZ(node); + total += nsize; + nsize += sizeof(indx_t); + fprintf(stderr, "key %d: nsize %d, %s%s\n", + i, nsize, DKEY(&key), mdb_leafnode_type(node)); + } + total = EVEN(total); + } + fprintf(stderr, "Total: header %d + contents %d + unused %d\n", + IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + MP_LOWER(mp), total, SIZELEFT(mp)); +} + +void +mdb_cursor_chk(MDB_cursor *mc) +{ + unsigned int i; + MDB_node *node; + MDB_page *mp; + + if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return; + for (i=0; i<mc->mc_top; i++) { + mp = mc->mc_pg[i]; + node = NODEPTR(mp, mc->mc_ki[i]); + if (NODEPGNO(node) != mc->mc_pg[i+1]->mp_pgno) + printf("oops!\n"); + } + if (mc->mc_ki[i] >= NUMKEYS(mc->mc_pg[i])) + printf("ack!\n"); + if (XCURSOR_INITED(mc)) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) && + mc->mc_xcursor->mx_cursor.mc_pg[0] != NODEDATA(node)) { + printf("blah!\n"); + } + } +} +#endif + +#if (MDB_DEBUG) > 2 +/** Count all the pages in each DB and in the freelist + * and make sure it matches the actual number of pages + * being used. + * All named DBs must be open for a correct count. + */ +static void mdb_audit(MDB_txn *txn) +{ + MDB_cursor mc; + MDB_val key, data; + MDB_ID freecount, count; + MDB_dbi i; + int rc; + + freecount = 0; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + mdb_tassert(txn, rc == MDB_NOTFOUND); + + count = 0; + for (i = 0; i<txn->mt_numdbs; i++) { + MDB_xcursor mx; + if (!(txn->mt_dbflags[i] & DB_VALID)) + continue; + mdb_cursor_init(&mc, txn, i, &mx); + if (txn->mt_dbs[i].md_root == P_INVALID) + continue; + count += txn->mt_dbs[i].md_branch_pages + + txn->mt_dbs[i].md_leaf_pages + + txn->mt_dbs[i].md_overflow_pages; + if (txn->mt_dbs[i].md_flags & MDB_DUPSORT) { + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST); + for (; rc == MDB_SUCCESS; rc = mdb_cursor_sibling(&mc, 1)) { + unsigned j; + MDB_page *mp; + mp = mc.mc_pg[mc.mc_top]; + for (j=0; j<NUMKEYS(mp); j++) { + MDB_node *leaf = NODEPTR(mp, j); + if (leaf->mn_flags & F_SUBDATA) { + MDB_db db; + memcpy(&db, NODEDATA(leaf), sizeof(db)); + count += db.md_branch_pages + db.md_leaf_pages + + db.md_overflow_pages; + } + } + } + mdb_tassert(txn, rc == MDB_NOTFOUND); + } + } + if (freecount + count + NUM_METAS != txn->mt_next_pgno) { + fprintf(stderr, "audit: %"Z"u freecount: %"Z"u count: %"Z"u total: %"Z"u next_pgno: %"Z"u\n", + txn->mt_txnid, freecount, count+NUM_METAS, + freecount+count+NUM_METAS, txn->mt_next_pgno); + } +} +#endif + +int +mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + return txn->mt_dbxs[dbi].md_cmp(a, b); +} + +int +mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && a->mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + return dcmp(a, b); +} + +/** Allocate memory for a page. + * Re-use old malloc'd pages first for singletons, otherwise just malloc. + * Set #MDB_TXN_ERROR on failure. + */ +static MDB_page * +mdb_page_malloc(MDB_txn *txn, unsigned num) +{ + MDB_env *env = txn->mt_env; + MDB_page *ret = env->me_dpages; + size_t psize = env->me_psize, sz = psize, off; + /* For ! #MDB_NOMEMINIT, psize counts how much to init. + * For a single page alloc, we init everything after the page header. + * For multi-page, we init the final page; if the caller needed that + * many pages they will be filling in at least up to the last page. + */ + if (num == 1) { + if (ret) { + VGMEMP_ALLOC(env, ret, sz); + VGMEMP_DEFINED(ret, sizeof(ret->mp_next)); + env->me_dpages = ret->mp_next; + return ret; + } + psize -= off = PAGEHDRSZ; + } else { + sz *= num; + off = sz - psize; + } + if ((ret = malloc(sz)) != NULL) { + VGMEMP_ALLOC(env, ret, sz); + if (!(env->me_flags & MDB_NOMEMINIT)) { + memset((char *)ret + off, 0, psize); + ret->mp_pad = 0; + } + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + return ret; +} +/** Free a single page. + * Saves single pages to a list, for future reuse. + * (This is not used for multi-page overflow pages.) + */ +static void +mdb_page_free(MDB_env *env, MDB_page *mp) +{ + mp->mp_next = env->me_dpages; + VGMEMP_FREE(env, mp); + env->me_dpages = mp; +} + +/** Free a dirty page */ +static void +mdb_dpage_free(MDB_env *env, MDB_page *dp) +{ + if (!IS_OVERFLOW(dp) || dp->mp_pages == 1) { + mdb_page_free(env, dp); + } else { + /* large pages just get freed directly */ + VGMEMP_FREE(env, dp); + free(dp); + } +} + +/** Return all dirty pages to dpage list */ +static void +mdb_dlist_free(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned i, n = dl[0].mid; + + for (i = 1; i <= n; i++) { + mdb_dpage_free(env, dl[i].mptr); + } + dl[0].mid = 0; +} + +/** Loosen or free a single page. + * Saves single pages to a list for future reuse + * in this same txn. It has been pulled from the freeDB + * and already resides on the dirty list, but has been + * deleted. Use these pages first before pulling again + * from the freeDB. + * + * If the page wasn't dirtied in this txn, just add it + * to this txn's free list. + */ +static int +mdb_page_loose(MDB_cursor *mc, MDB_page *mp) +{ + int loose = 0; + pgno_t pgno = mp->mp_pgno; + MDB_txn *txn = mc->mc_txn; + + if ((mp->mp_flags & P_DIRTY) && mc->mc_dbi != FREE_DBI) { + if (txn->mt_parent) { + MDB_ID2 *dl = txn->mt_u.dirty_list; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + /* ok, it's ours */ + loose = 1; + } + } + } else { + /* no parent txn, so it's just ours */ + loose = 1; + } + } + if (loose) { + DPRINTF(("loosen db %d page %"Z"u", DDBI(mc), + mp->mp_pgno)); + NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs; + txn->mt_loose_pgs = mp; + txn->mt_loose_count++; + mp->mp_flags |= P_LOOSE; + } else { + int rc = mdb_midl_append(&txn->mt_free_pgs, pgno); + if (rc) + return rc; + } + + return MDB_SUCCESS; +} + +/** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn. + * @param[in] mc A cursor handle for the current operation. + * @param[in] pflags Flags of the pages to update: + * P_DIRTY to set P_KEEP, P_DIRTY|P_KEEP to clear it. + * @param[in] all No shortcuts. Needed except after a full #mdb_page_flush(). + * @return 0 on success, non-zero on failure. + */ +static int +mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all) +{ + enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP }; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m3, *m0 = mc; + MDB_xcursor *mx; + MDB_page *dp, *mp; + MDB_node *leaf; + unsigned i, j; + int rc = MDB_SUCCESS, level; + + /* Mark pages seen by cursors */ + if (mc->mc_flags & C_UNTRACK) + mc = NULL; /* will find mc in mt_cursors */ + for (i = txn->mt_numdbs;; mc = txn->mt_cursors[--i]) { + for (; mc; mc=mc->mc_next) { + if (!(mc->mc_flags & C_INITIALIZED)) + continue; + for (m3 = mc;; m3 = &mx->mx_cursor) { + mp = NULL; + for (j=0; j<m3->mc_snum; j++) { + mp = m3->mc_pg[j]; + if ((mp->mp_flags & Mask) == pflags) + mp->mp_flags ^= P_KEEP; + } + mx = m3->mc_xcursor; + /* Proceed to mx if it is at a sub-database */ + if (! (mx && (mx->mx_cursor.mc_flags & C_INITIALIZED))) + break; + if (! (mp && (mp->mp_flags & P_LEAF))) + break; + leaf = NODEPTR(mp, m3->mc_ki[j-1]); + if (!(leaf->mn_flags & F_SUBDATA)) + break; + } + } + if (i == 0) + break; + } + + if (all) { + /* Mark dirty root pages */ + for (i=0; i<txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + pgno_t pgno = txn->mt_dbs[i].md_root; + if (pgno == P_INVALID) + continue; + if ((rc = mdb_page_get(m0, pgno, &dp, &level)) != MDB_SUCCESS) + break; + if ((dp->mp_flags & Mask) == pflags && level <= 1) + dp->mp_flags ^= P_KEEP; + } + } + } + + return rc; +} + +static int mdb_page_flush(MDB_txn *txn, int keep); + +/** Spill pages from the dirty list back to disk. + * This is intended to prevent running into #MDB_TXN_FULL situations, + * but note that they may still occur in a few cases: + * 1) our estimate of the txn size could be too small. Currently this + * seems unlikely, except with a large number of #MDB_MULTIPLE items. + * 2) child txns may run out of space if their parents dirtied a + * lot of pages and never spilled them. TODO: we probably should do + * a preemptive spill during #mdb_txn_begin() of a child txn, if + * the parent's dirty_room is below a given threshold. + * + * Otherwise, if not using nested txns, it is expected that apps will + * not run into #MDB_TXN_FULL any more. The pages are flushed to disk + * the same way as for a txn commit, e.g. their P_DIRTY flag is cleared. + * If the txn never references them again, they can be left alone. + * If the txn only reads them, they can be used without any fuss. + * If the txn writes them again, they can be dirtied immediately without + * going thru all of the work of #mdb_page_touch(). Such references are + * handled by #mdb_page_unspill(). + * + * Also note, we never spill DB root pages, nor pages of active cursors, + * because we'll need these back again soon anyway. And in nested txns, + * we can't spill a page in a child txn if it was already spilled in a + * parent txn. That would alter the parent txns' data even though + * the child hasn't committed yet, and we'd have no way to undo it if + * the child aborted. + * + * @param[in] m0 cursor A cursor handle identifying the transaction and + * database for which we are checking space. + * @param[in] key For a put operation, the key being stored. + * @param[in] data For a put operation, the data being stored. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_spill(MDB_cursor *m0, MDB_val *key, MDB_val *data) +{ + MDB_txn *txn = m0->mc_txn; + MDB_page *dp; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned int i, j, need; + int rc; + + if (m0->mc_flags & C_SUB) + return MDB_SUCCESS; + + /* Estimate how much space this op will take */ + i = m0->mc_db->md_depth; + /* Named DBs also dirty the main DB */ + if (m0->mc_dbi >= CORE_DBS) + i += txn->mt_dbs[MAIN_DBI].md_depth; + /* For puts, roughly factor in the key+data size */ + if (key) + i += (LEAFSIZE(key, data) + txn->mt_env->me_psize) / txn->mt_env->me_psize; + i += i; /* double it for good measure */ + need = i; + + if (txn->mt_dirty_room > i) + return MDB_SUCCESS; + + if (!txn->mt_spill_pgs) { + txn->mt_spill_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX); + if (!txn->mt_spill_pgs) + return ENOMEM; + } else { + /* purge deleted slots */ + MDB_IDL sl = txn->mt_spill_pgs; + unsigned int num = sl[0]; + j=0; + for (i=1; i<=num; i++) { + if (!(sl[i] & 1)) + sl[++j] = sl[i]; + } + sl[0] = j; + } + + /* Preserve pages which may soon be dirtied again */ + if ((rc = mdb_pages_xkeep(m0, P_DIRTY, 1)) != MDB_SUCCESS) + goto done; + + /* Less aggressive spill - we originally spilled the entire dirty list, + * with a few exceptions for cursor pages and DB root pages. But this + * turns out to be a lot of wasted effort because in a large txn many + * of those pages will need to be used again. So now we spill only 1/8th + * of the dirty pages. Testing revealed this to be a good tradeoff, + * better than 1/2, 1/4, or 1/10. + */ + if (need < MDB_IDL_UM_MAX / 8) + need = MDB_IDL_UM_MAX / 8; + + /* Save the page IDs of all the pages we're flushing */ + /* flush from the tail forward, this saves a lot of shifting later on. */ + for (i=dl[0].mid; i && need; i--) { + MDB_ID pn = dl[i].mid << 1; + dp = dl[i].mptr; + if (dp->mp_flags & (P_LOOSE|P_KEEP)) + continue; + /* Can't spill twice, make sure it's not already in a parent's + * spill list. + */ + if (txn->mt_parent) { + MDB_txn *tx2; + for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) { + if (tx2->mt_spill_pgs) { + j = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (j <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[j] == pn) { + dp->mp_flags |= P_KEEP; + break; + } + } + } + if (tx2) + continue; + } + if ((rc = mdb_midl_append(&txn->mt_spill_pgs, pn))) + goto done; + need--; + } + mdb_midl_sort(txn->mt_spill_pgs); + + /* Flush the spilled part of dirty list */ + if ((rc = mdb_page_flush(txn, i)) != MDB_SUCCESS) + goto done; + + /* Reset any dirty pages we kept that page_flush didn't see */ + rc = mdb_pages_xkeep(m0, P_DIRTY|P_KEEP, i); + +done: + txn->mt_flags |= rc ? MDB_TXN_ERROR : MDB_TXN_SPILLS; + return rc; +} + +/** Find oldest txnid still referenced. Expects txn->mt_txnid > 0. */ +static txnid_t +mdb_find_oldest(MDB_txn *txn) +{ + int i; + txnid_t mr, oldest = txn->mt_txnid - 1; + if (txn->mt_env->me_txns) { + MDB_reader *r = txn->mt_env->me_txns->mti_readers; + for (i = txn->mt_env->me_txns->mti_numreaders; --i >= 0; ) { + if (r[i].mr_pid) { + mr = r[i].mr_txnid; + if (oldest > mr) + oldest = mr; + } + } + } + return oldest; +} + +/** Add a page to the txn's dirty list */ +static void +mdb_page_dirty(MDB_txn *txn, MDB_page *mp) +{ + MDB_ID2 mid; + int rc, (*insert)(MDB_ID2L, MDB_ID2 *); + + if (txn->mt_flags & MDB_TXN_WRITEMAP) { + insert = mdb_mid2l_append; + } else { + insert = mdb_mid2l_insert; + } + mid.mid = mp->mp_pgno; + mid.mptr = mp; + rc = insert(txn->mt_u.dirty_list, &mid); + mdb_tassert(txn, rc == 0); + txn->mt_dirty_room--; +} + +/** Allocate page numbers and memory for writing. Maintain me_pglast, + * me_pghead and mt_next_pgno. Set #MDB_TXN_ERROR on failure. + * + * If there are free pages available from older transactions, they + * are re-used first. Otherwise allocate a new page at mt_next_pgno. + * Do not modify the freedB, just merge freeDB records into me_pghead[] + * and move me_pglast to say which records were consumed. Only this + * function can create me_pghead and move me_pglast/mt_next_pgno. + * @param[in] mc cursor A cursor handle identifying the transaction and + * database for which we are allocating. + * @param[in] num the number of pages to allocate. + * @param[out] mp Address of the allocated page(s). Requests for multiple pages + * will always be satisfied by a single contiguous chunk of memory. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp) +{ +#ifdef MDB_PARANOID /* Seems like we can ignore this now */ + /* Get at most <Max_retries> more freeDB records once me_pghead + * has enough pages. If not enough, use new pages from the map. + * If <Paranoid> and mc is updating the freeDB, only get new + * records if me_pghead is empty. Then the freelist cannot play + * catch-up with itself by growing while trying to save it. + */ + enum { Paranoid = 1, Max_retries = 500 }; +#else + enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ }; +#endif + int rc, retry = num * 60; + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + pgno_t pgno, *mop = env->me_pghead; + unsigned i, j, mop_len = mop ? mop[0] : 0, n2 = num-1; + MDB_page *np; + txnid_t oldest = 0, last; + MDB_cursor_op op; + MDB_cursor m2; + int found_old = 0; + + /* If there are any loose pages, just use them */ + if (num == 1 && txn->mt_loose_pgs) { + np = txn->mt_loose_pgs; + txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np); + txn->mt_loose_count--; + DPRINTF(("db %d use loose page %"Z"u", DDBI(mc), + np->mp_pgno)); + *mp = np; + return MDB_SUCCESS; + } + + *mp = NULL; + + /* If our dirty list is already full, we can't do anything */ + if (txn->mt_dirty_room == 0) { + rc = MDB_TXN_FULL; + goto fail; + } + + for (op = MDB_FIRST;; op = MDB_NEXT) { + MDB_val key, data; + MDB_node *leaf; + pgno_t *idl; + + /* Seek a big enough contiguous page range. Prefer + * pages at the tail, just truncating the list. + */ + if (mop_len > n2) { + i = mop_len; + do { + pgno = mop[i]; + if (mop[i-n2] == pgno+n2) + goto search_done; + } while (--i > n2); + if (--retry < 0) + break; + } + + if (op == MDB_FIRST) { /* 1st iteration */ + /* Prepare to fetch more and coalesce */ + last = env->me_pglast; + oldest = env->me_pgoldest; + mdb_cursor_init(&m2, txn, FREE_DBI, NULL); + if (last) { + op = MDB_SET_RANGE; + key.mv_data = &last; /* will look up last+1 */ + key.mv_size = sizeof(last); + } + if (Paranoid && mc->mc_dbi == FREE_DBI) + retry = -1; + } + if (Paranoid && retry < 0 && mop_len) + break; + + last++; + /* Do not fetch more if the record will be too recent */ + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + rc = mdb_cursor_get(&m2, &key, NULL, op); + if (rc) { + if (rc == MDB_NOTFOUND) + break; + goto fail; + } + last = *(txnid_t*)key.mv_data; + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + np = m2.mc_pg[m2.mc_top]; + leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]); + if ((rc = mdb_node_read(&m2, leaf, &data)) != MDB_SUCCESS) + goto fail; + + idl = (MDB_ID *) data.mv_data; + i = idl[0]; + if (!mop) { + if (!(env->me_pghead = mop = mdb_midl_alloc(i))) { + rc = ENOMEM; + goto fail; + } + } else { + if ((rc = mdb_midl_need(&env->me_pghead, i)) != 0) + goto fail; + mop = env->me_pghead; + } + env->me_pglast = last; +#if (MDB_DEBUG) > 1 + DPRINTF(("IDL read txn %"Z"u root %"Z"u num %u", + last, txn->mt_dbs[FREE_DBI].md_root, i)); + for (j = i; j; j--) + DPRINTF(("IDL %"Z"u", idl[j])); +#endif + /* Merge in descending sorted order */ + mdb_midl_xmerge(mop, idl); + mop_len = mop[0]; + } + + /* Use new pages from the map when nothing suitable in the freeDB */ + i = 0; + pgno = txn->mt_next_pgno; + if (pgno + num >= env->me_maxpg) { + DPUTS("DB size maxed out"); + rc = MDB_MAP_FULL; + goto fail; + } + +search_done: + if (env->me_flags & MDB_WRITEMAP) { + np = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + if (!(np = mdb_page_malloc(txn, num))) { + rc = ENOMEM; + goto fail; + } + } + if (i) { + mop[0] = mop_len -= num; + /* Move any stragglers down */ + for (j = i-num; j < mop_len; ) + mop[++j] = mop[++i]; + } else { + txn->mt_next_pgno = pgno + num; + } + np->mp_pgno = pgno; + mdb_page_dirty(txn, np); + *mp = np; + + return MDB_SUCCESS; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Copy the used portions of a non-overflow page. + * @param[in] dst page to copy into + * @param[in] src page to copy from + * @param[in] psize size of a page + */ +static void +mdb_page_copy(MDB_page *dst, MDB_page *src, unsigned int psize) +{ + enum { Align = sizeof(pgno_t) }; + indx_t upper = src->mp_upper, lower = src->mp_lower, unused = upper-lower; + + /* If page isn't full, just copy the used portion. Adjust + * alignment so memcpy may copy words instead of bytes. + */ + if ((unused &= -Align) && !IS_LEAF2(src)) { + upper = (upper + PAGEBASE) & -Align; + memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align); + memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper), + psize - upper); + } else { + memcpy(dst, src, psize - unused); + } +} + +/** Pull a page off the txn's spill list, if present. + * If a page being referenced was spilled to disk in this txn, bring + * it back and make it dirty/writable again. + * @param[in] txn the transaction handle. + * @param[in] mp the page being referenced. It must not be dirty. + * @param[out] ret the writable page, if any. ret is unchanged if + * mp wasn't spilled. + */ +static int +mdb_page_unspill(MDB_txn *txn, MDB_page *mp, MDB_page **ret) +{ + MDB_env *env = txn->mt_env; + const MDB_txn *tx2; + unsigned x; + pgno_t pgno = mp->mp_pgno, pn = pgno << 1; + + for (tx2 = txn; tx2; tx2=tx2->mt_parent) { + if (!tx2->mt_spill_pgs) + continue; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + MDB_page *np; + int num; + if (txn->mt_dirty_room == 0) + return MDB_TXN_FULL; + if (IS_OVERFLOW(mp)) + num = mp->mp_pages; + else + num = 1; + if (env->me_flags & MDB_WRITEMAP) { + np = mp; + } else { + np = mdb_page_malloc(txn, num); + if (!np) + return ENOMEM; + if (num > 1) + memcpy(np, mp, num * env->me_psize); + else + mdb_page_copy(np, mp, env->me_psize); + } + if (tx2 == txn) { + /* If in current txn, this page is no longer spilled. + * If it happens to be the last page, truncate the spill list. + * Otherwise mark it as deleted by setting the LSB. + */ + if (x == txn->mt_spill_pgs[0]) + txn->mt_spill_pgs[0]--; + else + txn->mt_spill_pgs[x] |= 1; + } /* otherwise, if belonging to a parent txn, the + * page remains spilled until child commits + */ + + mdb_page_dirty(txn, np); + np->mp_flags |= P_DIRTY; + *ret = np; + break; + } + } + return MDB_SUCCESS; +} + +/** Touch a page: make it dirty and re-insert into tree with updated pgno. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc cursor pointing to the page to be touched + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_touch(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top], *np; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m2, *m3; + pgno_t pgno; + int rc; + + if (!F_ISSET(MP_FLAGS(mp), P_DIRTY)) { + if (txn->mt_flags & MDB_TXN_SPILLS) { + np = NULL; + rc = mdb_page_unspill(txn, mp, &np); + if (rc) + goto fail; + if (np) + goto done; + } + if ((rc = mdb_midl_need(&txn->mt_free_pgs, 1)) || + (rc = mdb_page_alloc(mc, 1, &np))) + goto fail; + pgno = np->mp_pgno; + DPRINTF(("touched db %d page %"Z"u -> %"Z"u", DDBI(mc), + mp->mp_pgno, pgno)); + mdb_cassert(mc, mp->mp_pgno != pgno); + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + /* Update the parent page, if any, to point to the new page */ + if (mc->mc_top) { + MDB_page *parent = mc->mc_pg[mc->mc_top-1]; + MDB_node *node = NODEPTR(parent, mc->mc_ki[mc->mc_top-1]); + SETPGNO(node, pgno); + } else { + mc->mc_db->md_root = pgno; + } + } else if (txn->mt_parent && !IS_SUBP(mp)) { + MDB_ID2 mid, *dl = txn->mt_u.dirty_list; + pgno = mp->mp_pgno; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + return 0; + } + } + mdb_cassert(mc, dl[0].mid < MDB_IDL_UM_MAX); + /* No - copy it */ + np = mdb_page_malloc(txn, 1); + if (!np) + return ENOMEM; + mid.mid = pgno; + mid.mptr = np; + rc = mdb_mid2l_insert(dl, &mid); + mdb_cassert(mc, rc == 0); + } else { + return 0; + } + + mdb_page_copy(np, mp, txn->mt_env->me_psize); + np->mp_pgno = pgno; + np->mp_flags |= P_DIRTY; + +done: + /* Adjust cursors pointing to mp */ + mc->mc_pg[mc->mc_top] = np; + m2 = txn->mt_cursors[mc->mc_dbi]; + if (mc->mc_flags & C_SUB) { + for (; m2; m2=m2->mc_next) { + m3 = &m2->mc_xcursor->mx_cursor; + if (m3->mc_snum < mc->mc_snum) continue; + if (m3->mc_pg[mc->mc_top] == mp) + m3->mc_pg[mc->mc_top] = np; + } + } else { + for (; m2; m2=m2->mc_next) { + if (m2->mc_snum < mc->mc_snum) continue; + if (m2 == mc) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + m2->mc_pg[mc->mc_top] = np; + if (IS_LEAF(np)) + XCURSOR_REFRESH(m2, mc->mc_top, np); + } + } + } + return 0; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_env_sync(MDB_env *env, int force) +{ + int rc = 0; + if (env->me_flags & MDB_RDONLY) + return EACCES; + if (force || !F_ISSET(env->me_flags, MDB_NOSYNC)) { + if (env->me_flags & MDB_WRITEMAP) { + int flags = ((env->me_flags & MDB_MAPASYNC) && !force) + ? MS_ASYNC : MS_SYNC; + if (MDB_MSYNC(env->me_map, env->me_mapsize, flags)) + rc = ErrCode(); +#ifdef _WIN32 + else if (flags == MS_SYNC && MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); +#endif + } else { +#ifdef BROKEN_FDATASYNC + if (env->me_flags & MDB_FSYNCONLY) { + if (fsync(env->me_fd)) + rc = ErrCode(); + } else +#endif + if (MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); + } + } + return rc; +} + +/** Back up parent txn's cursors, then grab the originals for tracking */ +static int +mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst) +{ + MDB_cursor *mc, *bk; + MDB_xcursor *mx; + size_t size; + int i; + + for (i = src->mt_numdbs; --i >= 0; ) { + if ((mc = src->mt_cursors[i]) != NULL) { + size = sizeof(MDB_cursor); + if (mc->mc_xcursor) + size += sizeof(MDB_xcursor); + for (; mc; mc = bk->mc_next) { + bk = malloc(size); + if (!bk) + return ENOMEM; + *bk = *mc; + mc->mc_backup = bk; + mc->mc_db = &dst->mt_dbs[i]; + /* Kill pointers into src to reduce abuse: The + * user may not use mc until dst ends. But we need a valid + * txn pointer here for cursor fixups to keep working. + */ + mc->mc_txn = dst; + mc->mc_dbflag = &dst->mt_dbflags[i]; + if ((mx = mc->mc_xcursor) != NULL) { + *(MDB_xcursor *)(bk+1) = *mx; + mx->mx_cursor.mc_txn = dst; + } + mc->mc_next = dst->mt_cursors[i]; + dst->mt_cursors[i] = mc; + } + } + } + return MDB_SUCCESS; +} + +/** Close this write txn's cursors, give parent txn's cursors back to parent. + * @param[in] txn the transaction handle. + * @param[in] merge true to keep changes to parent cursors, false to revert. + * @return 0 on success, non-zero on failure. + */ +static void +mdb_cursors_close(MDB_txn *txn, unsigned merge) +{ + MDB_cursor **cursors = txn->mt_cursors, *mc, *next, *bk; + MDB_xcursor *mx; + int i; + + for (i = txn->mt_numdbs; --i >= 0; ) { + for (mc = cursors[i]; mc; mc = next) { + next = mc->mc_next; + if ((bk = mc->mc_backup) != NULL) { + if (merge) { + /* Commit changes to parent txn */ + mc->mc_next = bk->mc_next; + mc->mc_backup = bk->mc_backup; + mc->mc_txn = bk->mc_txn; + mc->mc_db = bk->mc_db; + mc->mc_dbflag = bk->mc_dbflag; + if ((mx = mc->mc_xcursor) != NULL) + mx->mx_cursor.mc_txn = bk->mc_txn; + } else { + /* Abort nested txn */ + *mc = *bk; + if ((mx = mc->mc_xcursor) != NULL) + *mx = *(MDB_xcursor *)(bk+1); + } + mc = bk; + } + /* Only malloced cursors are permanently tracked. */ + free(mc); + } + cursors[i] = NULL; + } +} + +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ +enum Pidlock_op { + Pidset, Pidcheck +}; +#else +enum Pidlock_op { + Pidset = F_SETLK, Pidcheck = F_GETLK +}; +#endif + +/** Set or check a pid lock. Set returns 0 on success. + * Check returns 0 if the process is certainly dead, nonzero if it may + * be alive (the lock exists or an error happened so we do not know). + * + * On Windows Pidset is a no-op, we merely check for the existence + * of the process with the given pid. On POSIX we use a single byte + * lock on the lockfile, set at an offset equal to the pid. + */ +static int +mdb_reader_pid(MDB_env *env, enum Pidlock_op op, MDB_PID_T pid) +{ +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ + int ret = 0; + HANDLE h; + if (op == Pidcheck) { + h = OpenProcess(env->me_pidquery, FALSE, pid); + /* No documented "no such process" code, but other program use this: */ + if (!h) + return ErrCode() != ERROR_INVALID_PARAMETER; + /* A process exists until all handles to it close. Has it exited? */ + ret = WaitForSingleObject(h, 0) != 0; + CloseHandle(h); + } + return ret; +#else + for (;;) { + int rc; + struct flock lock_info; + memset(&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = pid; + lock_info.l_len = 1; + if ((rc = fcntl(env->me_lfd, op, &lock_info)) == 0) { + if (op == F_GETLK && lock_info.l_type != F_UNLCK) + rc = -1; + } else if ((rc = ErrCode()) == EINTR) { + continue; + } + return rc; + } +#endif +} + +/** Common code for #mdb_txn_begin() and #mdb_txn_renew(). + * @param[in] txn the transaction handle to initialize + * @return 0 on success, non-zero on failure. + */ +static int +mdb_txn_renew0(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_txninfo *ti = env->me_txns; + MDB_meta *meta; + unsigned int i, nr, flags = txn->mt_flags; + uint16_t x; + int rc, new_notls = 0; + + if ((flags &= MDB_TXN_RDONLY) != 0) { + if (!ti) { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + txn->mt_u.reader = NULL; + } else { + MDB_reader *r = (env->me_flags & MDB_NOTLS) ? txn->mt_u.reader : + pthread_getspecific(env->me_txkey); + if (r) { + if (r->mr_pid != env->me_pid || r->mr_txnid != (txnid_t)-1) + return MDB_BAD_RSLOT; + } else { + MDB_PID_T pid = env->me_pid; + MDB_THR_T tid = pthread_self(); + mdb_mutexref_t rmutex = env->me_rmutex; + + if (!env->me_live_reader) { + rc = mdb_reader_pid(env, Pidset, pid); + if (rc) + return rc; + env->me_live_reader = 1; + } + + if (LOCK_MUTEX(rc, env, rmutex)) + return rc; + nr = ti->mti_numreaders; + for (i=0; i<nr; i++) + if (ti->mti_readers[i].mr_pid == 0) + break; + if (i == env->me_maxreaders) { + UNLOCK_MUTEX(rmutex); + return MDB_READERS_FULL; + } + r = &ti->mti_readers[i]; + /* Claim the reader slot, carefully since other code + * uses the reader table un-mutexed: First reset the + * slot, next publish it in mti_numreaders. After + * that, it is safe for mdb_env_close() to touch it. + * When it will be closed, we can finally claim it. + */ + r->mr_pid = 0; + r->mr_txnid = (txnid_t)-1; + r->mr_tid = tid; + if (i == nr) + ti->mti_numreaders = ++nr; + env->me_close_readers = nr; + r->mr_pid = pid; + UNLOCK_MUTEX(rmutex); + + new_notls = (env->me_flags & MDB_NOTLS); + if (!new_notls && (rc=pthread_setspecific(env->me_txkey, r))) { + r->mr_pid = 0; + return rc; + } + } + do /* LY: Retry on a race, ITS#7970. */ + r->mr_txnid = ti->mti_txnid; + while(r->mr_txnid != ti->mti_txnid); + txn->mt_txnid = r->mr_txnid; + txn->mt_u.reader = r; + meta = env->me_metas[txn->mt_txnid & 1]; + } + + } else { + /* Not yet touching txn == env->me_txn0, it may be active */ + if (ti) { + if (LOCK_MUTEX(rc, env, env->me_wmutex)) + return rc; + txn->mt_txnid = ti->mti_txnid; + meta = env->me_metas[txn->mt_txnid & 1]; + } else { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + } + txn->mt_txnid++; +#if MDB_DEBUG + if (txn->mt_txnid == mdb_debug_start) + mdb_debug = 1; +#endif + txn->mt_child = NULL; + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + txn->mt_dirty_room = MDB_IDL_UM_MAX; + txn->mt_u.dirty_list = env->me_dirty_list; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_free_pgs = env->me_free_pgs; + txn->mt_free_pgs[0] = 0; + txn->mt_spill_pgs = NULL; + env->me_txn = txn; + memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int)); + } + + /* Copy the DB info and flags */ + memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDB_db)); + + /* Moved to here to avoid a data race in read TXNs */ + txn->mt_next_pgno = meta->mm_last_pg+1; + + txn->mt_flags = flags; + + /* Setup db info */ + txn->mt_numdbs = env->me_numdbs; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + x = env->me_dbflags[i]; + txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS; + txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0; + } + txn->mt_dbflags[MAIN_DBI] = DB_VALID|DB_USRVALID; + txn->mt_dbflags[FREE_DBI] = DB_VALID; + + if (env->me_flags & MDB_FATAL_ERROR) { + DPUTS("environment had fatal error, must shutdown!"); + rc = MDB_PANIC; + } else if (env->me_maxpg < txn->mt_next_pgno) { + rc = MDB_MAP_RESIZED; + } else { + return MDB_SUCCESS; + } + mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN); + return rc; +} + +int +mdb_txn_renew(MDB_txn *txn) +{ + int rc; + + if (!txn || !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY|MDB_TXN_FINISHED)) + return EINVAL; + + rc = mdb_txn_renew0(txn); + if (rc == MDB_SUCCESS) { + DPRINTF(("renew txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root)); + } + return rc; +} + +int +mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) +{ + MDB_txn *txn; + MDB_ntxn *ntxn; + int rc, size, tsize; + + flags &= MDB_TXN_BEGIN_FLAGS; + flags |= env->me_flags & MDB_WRITEMAP; + + if (env->me_flags & MDB_RDONLY & ~flags) /* write txn in RDONLY env */ + return EACCES; + + if (parent) { + /* Nested transactions: Max 1 child, write txns only, no writemap */ + flags |= parent->mt_flags; + if (flags & (MDB_RDONLY|MDB_WRITEMAP|MDB_TXN_BLOCKED)) { + return (parent->mt_flags & MDB_TXN_RDONLY) ? EINVAL : MDB_BAD_TXN; + } + /* Child txns save MDB_pgstate and use own copy of cursors */ + size = env->me_maxdbs * (sizeof(MDB_db)+sizeof(MDB_cursor *)+1); + size += tsize = sizeof(MDB_ntxn); + } else if (flags & MDB_RDONLY) { + size = env->me_maxdbs * (sizeof(MDB_db)+1); + size += tsize = sizeof(MDB_txn); + } else { + /* Reuse preallocated write txn. However, do not touch it until + * mdb_txn_renew0() succeeds, since it currently may be active. + */ + txn = env->me_txn0; + goto renew; + } + if ((txn = calloc(1, size)) == NULL) { + DPRINTF(("calloc: %s", strerror(errno))); + return ENOMEM; + } + txn->mt_dbxs = env->me_dbxs; /* static */ + txn->mt_dbs = (MDB_db *) ((char *)txn + tsize); + txn->mt_dbflags = (unsigned char *)txn + size - env->me_maxdbs; + txn->mt_flags = flags; + txn->mt_env = env; + + if (parent) { + unsigned int i; + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = parent->mt_dbiseqs; + txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE); + if (!txn->mt_u.dirty_list || + !(txn->mt_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX))) + { + free(txn->mt_u.dirty_list); + free(txn); + return ENOMEM; + } + txn->mt_txnid = parent->mt_txnid; + txn->mt_dirty_room = parent->mt_dirty_room; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_spill_pgs = NULL; + txn->mt_next_pgno = parent->mt_next_pgno; + parent->mt_flags |= MDB_TXN_HAS_CHILD; + parent->mt_child = txn; + txn->mt_parent = parent; + txn->mt_numdbs = parent->mt_numdbs; + memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + /* Copy parent's mt_dbflags, but clear DB_NEW */ + for (i=0; i<txn->mt_numdbs; i++) + txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~DB_NEW; + rc = 0; + ntxn = (MDB_ntxn *)txn; + ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */ + if (env->me_pghead) { + size = MDB_IDL_SIZEOF(env->me_pghead); + env->me_pghead = mdb_midl_alloc(env->me_pghead[0]); + if (env->me_pghead) + memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size); + else + rc = ENOMEM; + } + if (!rc) + rc = mdb_cursor_shadow(parent, txn); + if (rc) + mdb_txn_end(txn, MDB_END_FAIL_BEGINCHILD); + } else { /* MDB_RDONLY */ + txn->mt_dbiseqs = env->me_dbiseqs; +renew: + rc = mdb_txn_renew0(txn); + } + if (rc) { + if (txn != env->me_txn0) + free(txn); + } else { + txn->mt_flags |= flags; /* could not change txn=me_txn0 earlier */ + *ret = txn; + DPRINTF(("begin txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (flags & MDB_RDONLY) ? 'r' : 'w', + (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root)); + } + + return rc; +} + +MDB_env * +mdb_txn_env(MDB_txn *txn) +{ + if(!txn) return NULL; + return txn->mt_env; +} + +size_t +mdb_txn_id(MDB_txn *txn) +{ + if(!txn) return 0; + return txn->mt_txnid; +} + +/** Export or close DBI handles opened in this txn. */ +static void +mdb_dbis_update(MDB_txn *txn, int keep) +{ + int i; + MDB_dbi n = txn->mt_numdbs; + MDB_env *env = txn->mt_env; + unsigned char *tdbflags = txn->mt_dbflags; + + for (i = n; --i >= CORE_DBS;) { + if (tdbflags[i] & DB_NEW) { + if (keep) { + env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID; + } else { + char *ptr = env->me_dbxs[i].md_name.mv_data; + if (ptr) { + env->me_dbxs[i].md_name.mv_data = NULL; + env->me_dbxs[i].md_name.mv_size = 0; + env->me_dbflags[i] = 0; + env->me_dbiseqs[i]++; + free(ptr); + } + } + } + } + if (keep && env->me_numdbs < n) + env->me_numdbs = n; +} + +/** End a transaction, except successful commit of a nested transaction. + * May be called twice for readonly txns: First reset it, then abort. + * @param[in] txn the transaction handle to end + * @param[in] mode why and how to end the transaction + */ +static void +mdb_txn_end(MDB_txn *txn, unsigned mode) +{ + MDB_env *env = txn->mt_env; +#if MDB_DEBUG + static const char *const names[] = MDB_END_NAMES; +#endif + + /* Export or close DBI handles opened in this txn */ + mdb_dbis_update(txn, mode & MDB_END_UPDATE); + + DPRINTF(("%s txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + names[mode & MDB_END_OPMASK], + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *) txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root)); + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + if (txn->mt_u.reader) { + txn->mt_u.reader->mr_txnid = (txnid_t)-1; + if (!(env->me_flags & MDB_NOTLS)) { + txn->mt_u.reader = NULL; /* txn does not own reader */ + } else if (mode & MDB_END_SLOT) { + txn->mt_u.reader->mr_pid = 0; + txn->mt_u.reader = NULL; + } /* else txn owns the slot until it does MDB_END_SLOT */ + } + txn->mt_numdbs = 0; /* prevent further DBI activity */ + txn->mt_flags |= MDB_TXN_FINISHED; + + } else if (!F_ISSET(txn->mt_flags, MDB_TXN_FINISHED)) { + pgno_t *pghead = env->me_pghead; + + if (!(mode & MDB_END_UPDATE)) /* !(already closed cursors) */ + mdb_cursors_close(txn, 0); + if (!(env->me_flags & MDB_WRITEMAP)) { + mdb_dlist_free(txn); + } + + txn->mt_numdbs = 0; + txn->mt_flags = MDB_TXN_FINISHED; + + if (!txn->mt_parent) { + mdb_midl_shrink(&txn->mt_free_pgs); + env->me_free_pgs = txn->mt_free_pgs; + /* me_pgstate: */ + env->me_pghead = NULL; + env->me_pglast = 0; + + env->me_txn = NULL; + mode = 0; /* txn == env->me_txn0, do not free() it */ + + /* The writer mutex was locked in mdb_txn_begin. */ + if (env->me_txns) + UNLOCK_MUTEX(env->me_wmutex); + } else { + txn->mt_parent->mt_child = NULL; + txn->mt_parent->mt_flags &= ~MDB_TXN_HAS_CHILD; + env->me_pgstate = ((MDB_ntxn *)txn)->mnt_pgstate; + mdb_midl_free(txn->mt_free_pgs); + free(txn->mt_u.dirty_list); + } + mdb_midl_free(txn->mt_spill_pgs); + + mdb_midl_free(pghead); + } + + if (mode & MDB_END_FREE) + free(txn); +} + +void +mdb_txn_reset(MDB_txn *txn) +{ + if (txn == NULL) + return; + + /* This call is only valid for read-only txns */ + if (!(txn->mt_flags & MDB_TXN_RDONLY)) + return; + + mdb_txn_end(txn, MDB_END_RESET); +} + +void +mdb_txn_abort(MDB_txn *txn) +{ + if (txn == NULL) + return; + + if (txn->mt_child) + mdb_txn_abort(txn->mt_child); + + mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE); +} + +/** Save the freelist as of this transaction to the freeDB. + * This changes the freelist. Keep trying until it stabilizes. + */ +static int +mdb_freelist_save(MDB_txn *txn) +{ + /* env->me_pghead[] can grow and shrink during this call. + * env->me_pglast and txn->mt_free_pgs[] can only grow. + * Page numbers cannot disappear from txn->mt_free_pgs[]. + */ + MDB_cursor mc; + MDB_env *env = txn->mt_env; + int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1; + txnid_t pglast = 0, head_id = 0; + pgno_t freecnt = 0, *free_pgs, *mop; + ssize_t head_room = 0, total_room = 0, mop_len, clean_limit; + + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + + if (env->me_pghead) { + /* Make sure first page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (!env->me_pghead && txn->mt_loose_pgs) { + /* Put loose page numbers in mt_free_pgs, since + * we may be unable to return them to me_pghead. + */ + MDB_page *mp = txn->mt_loose_pgs; + MDB_ID2 *dl = txn->mt_u.dirty_list; + unsigned x; + if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0) + return rc; + for (; mp; mp = NEXT_LOOSE_PAGE(mp)) { + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + /* must also remove from dirty list */ + if (txn->mt_flags & MDB_TXN_WRITEMAP) { + for (x=1; x<=dl[0].mid; x++) + if (dl[x].mid == mp->mp_pgno) + break; + mdb_tassert(txn, x <= dl[0].mid); + } else { + x = mdb_mid2l_search(dl, mp->mp_pgno); + mdb_tassert(txn, dl[x].mid == mp->mp_pgno); + mdb_dpage_free(env, mp); + } + dl[x].mptr = NULL; + } + { + /* squash freed slots out of the dirty list */ + unsigned y; + for (y=1; dl[y].mptr && y <= dl[0].mid; y++); + if (y <= dl[0].mid) { + for(x=y, y++;;) { + while (!dl[y].mptr && y <= dl[0].mid) y++; + if (y > dl[0].mid) break; + dl[x++] = dl[y++]; + } + dl[0].mid = x-1; + } else { + /* all slots freed */ + dl[0].mid = 0; + } + } + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + } + + /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ + clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP)) + ? SSIZE_MAX : maxfree_1pg; + + for (;;) { + /* Come back here after each Put() in case freelist changed */ + MDB_val key, data; + pgno_t *pgs; + ssize_t j; + + /* If using records from freeDB which we have not yet + * deleted, delete them and any we reserved for me_pghead. + */ + while (pglast < env->me_pglast) { + rc = mdb_cursor_first(&mc, &key, NULL); + if (rc) + return rc; + pglast = head_id = *(txnid_t *)key.mv_data; + total_room = head_room = 0; + mdb_tassert(txn, pglast <= env->me_pglast); + rc = mdb_cursor_del(&mc, 0); + if (rc) + return rc; + } + + /* Save the IDL of pages freed by this txn, to a single record */ + if (freecnt < txn->mt_free_pgs[0]) { + if (!freecnt) { + /* Make sure last page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_LAST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + free_pgs = txn->mt_free_pgs; + /* Write to last page of freeDB */ + key.mv_size = sizeof(txn->mt_txnid); + key.mv_data = &txn->mt_txnid; + do { + freecnt = free_pgs[0]; + data.mv_size = MDB_IDL_SIZEOF(free_pgs); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* Retry if mt_free_pgs[] grew during the Put() */ + free_pgs = txn->mt_free_pgs; + } while (freecnt < free_pgs[0]); + mdb_midl_sort(free_pgs); + memcpy(data.mv_data, free_pgs, data.mv_size); +#if (MDB_DEBUG) > 1 + { + unsigned int i = free_pgs[0]; + DPRINTF(("IDL write txn %"Z"u root %"Z"u num %u", + txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, i)); + for (; i; i--) + DPRINTF(("IDL %"Z"u", free_pgs[i])); + } +#endif + continue; + } + + mop = env->me_pghead; + mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count; + + /* Reserve records for me_pghead[]. Split it if multi-page, + * to avoid searching freeDB for a page range. Use keys in + * range [1,me_pglast]: Smaller than txnid of oldest reader. + */ + if (total_room >= mop_len) { + if (total_room == mop_len || --more < 0) + break; + } else if (head_room >= maxfree_1pg && head_id > 1) { + /* Keep current record (overflow page), add a new one */ + head_id--; + head_room = 0; + } + /* (Re)write {key = head_id, IDL length = head_room} */ + total_room -= head_room; + head_room = mop_len - total_room; + if (head_room > maxfree_1pg && head_id > 1) { + /* Overflow multi-page for part of me_pghead */ + head_room /= head_id; /* amortize page sizes */ + head_room += maxfree_1pg - head_room % (maxfree_1pg + 1); + } else if (head_room < 0) { + /* Rare case, not bothering to delete this record */ + head_room = 0; + } + key.mv_size = sizeof(head_id); + key.mv_data = &head_id; + data.mv_size = (head_room + 1) * sizeof(pgno_t); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* IDL is initially empty, zero out at least the length */ + pgs = (pgno_t *)data.mv_data; + j = head_room > clean_limit ? head_room : 0; + do { + pgs[j] = 0; + } while (--j >= 0); + total_room += head_room; + } + + /* Return loose page numbers to me_pghead, though usually none are + * left at this point. The pages themselves remain in dirty_list. + */ + if (txn->mt_loose_pgs) { + MDB_page *mp = txn->mt_loose_pgs; + unsigned count = txn->mt_loose_count; + MDB_IDL loose; + /* Room for loose pages + temp IDL with same */ + if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0) + return rc; + mop = env->me_pghead; + loose = mop + MDB_IDL_ALLOCLEN(mop) - count; + for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp)) + loose[ ++count ] = mp->mp_pgno; + loose[0] = count; + mdb_midl_sort(loose); + mdb_midl_xmerge(mop, loose); + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + mop_len = mop[0]; + } + + /* Fill in the reserved me_pghead records */ + rc = MDB_SUCCESS; + if (mop_len) { + MDB_val key, data; + + mop += mop_len; + rc = mdb_cursor_first(&mc, &key, &data); + for (; !rc; rc = mdb_cursor_next(&mc, &key, &data, MDB_NEXT)) { + txnid_t id = *(txnid_t *)key.mv_data; + ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1; + MDB_ID save; + + mdb_tassert(txn, len >= 0 && id <= env->me_pglast); + key.mv_data = &id; + if (len > mop_len) { + len = mop_len; + data.mv_size = (len + 1) * sizeof(MDB_ID); + } + data.mv_data = mop -= len; + save = mop[0]; + mop[0] = len; + rc = mdb_cursor_put(&mc, &key, &data, MDB_CURRENT); + mop[0] = save; + if (rc || !(mop_len -= len)) + break; + } + } + return rc; +} + +/** Flush (some) dirty pages to the map, after clearing their dirty flag. + * @param[in] txn the transaction that's being committed + * @param[in] keep number of initial pages in dirty_list to keep dirty. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_flush(MDB_txn *txn, int keep) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned psize = env->me_psize, j; + int i, pagecount = dl[0].mid, rc; + size_t size = 0, pos = 0; + pgno_t pgno = 0; + MDB_page *dp = NULL; +#ifdef _WIN32 + OVERLAPPED ov; +#else + struct iovec iov[MDB_COMMIT_PAGES]; + ssize_t wpos = 0, wsize = 0, wres; + size_t next_pos = 1; /* impossible pos, so pos != next_pos */ + int n = 0; +#endif + + j = i = keep; + + if (env->me_flags & MDB_WRITEMAP) { + /* Clear dirty flags */ + while (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[++j] = dl[i]; + continue; + } + dp->mp_flags &= ~P_DIRTY; + } + goto done; + } + + /* Write the pages */ + for (;;) { + if (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[i].mid = 0; + continue; + } + pgno = dl[i].mid; + /* clear dirty flag */ + dp->mp_flags &= ~P_DIRTY; + pos = pgno * psize; + size = psize; + if (IS_OVERFLOW(dp)) size *= dp->mp_pages; + } +#ifdef _WIN32 + else break; + + /* Windows actually supports scatter/gather I/O, but only on + * unbuffered file handles. Since we're relying on the OS page + * cache for all our data, that's self-defeating. So we just + * write pages one at a time. We use the ov structure to set + * the write offset, to at least save the overhead of a Seek + * system call. + */ + DPRINTF(("committing page %"Z"u", pgno)); + memset(&ov, 0, sizeof(ov)); + ov.Offset = pos & 0xffffffff; + ov.OffsetHigh = pos >> 16 >> 16; + if (!WriteFile(env->me_fd, dp, size, NULL, &ov)) { + rc = ErrCode(); + DPRINTF(("WriteFile: %d", rc)); + return rc; + } +#else + /* Write up to MDB_COMMIT_PAGES dirty pages at a time. */ + if (pos!=next_pos || n==MDB_COMMIT_PAGES || wsize+size>MAX_WRITE) { + if (n) { +retry_write: + /* Write previous page(s) */ +#ifdef MDB_USE_PWRITEV + wres = pwritev(env->me_fd, iov, n, wpos); +#else + if (n == 1) { + wres = pwrite(env->me_fd, iov[0].iov_base, wsize, wpos); + } else { +retry_seek: + if (lseek(env->me_fd, wpos, SEEK_SET) == -1) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_seek; + DPRINTF(("lseek: %s", strerror(rc))); + return rc; + } + wres = writev(env->me_fd, iov, n); + } +#endif + if (wres != wsize) { + if (wres < 0) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_write; + DPRINTF(("Write error: %s", strerror(rc))); + } else { + rc = EIO; /* TODO: Use which error code? */ + DPUTS("short write, filesystem full?"); + } + return rc; + } + n = 0; + } + if (i > pagecount) + break; + wpos = pos; + wsize = 0; + } + DPRINTF(("committing page %"Z"u", pgno)); + next_pos = pos + size; + iov[n].iov_len = size; + iov[n].iov_base = (char *)dp; + wsize += size; + n++; +#endif /* _WIN32 */ + } + + /* MIPS has cache coherency issues, this is a no-op everywhere else + * Note: for any size >= on-chip cache size, entire on-chip cache is + * flushed. + */ + CACHEFLUSH(env->me_map, txn->mt_next_pgno * env->me_psize, DCACHE); + + for (i = keep; ++i <= pagecount; ) { + dp = dl[i].mptr; + /* This is a page we skipped above */ + if (!dl[i].mid) { + dl[++j] = dl[i]; + dl[j].mid = dp->mp_pgno; + continue; + } + mdb_dpage_free(env, dp); + } + +done: + i--; + txn->mt_dirty_room += i - j; + dl[0].mid = j; + return MDB_SUCCESS; +} + +int +mdb_txn_commit(MDB_txn *txn) +{ + int rc; + unsigned int i, end_mode; + MDB_env *env; + + if (txn == NULL) + return EINVAL; + + /* mdb_txn_end() mode for a commit which writes nothing */ + end_mode = MDB_END_EMPTY_COMMIT|MDB_END_UPDATE|MDB_END_SLOT|MDB_END_FREE; + + if (txn->mt_child) { + rc = mdb_txn_commit(txn->mt_child); + if (rc) + goto fail; + } + + env = txn->mt_env; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + goto done; + } + + if (txn->mt_flags & (MDB_TXN_FINISHED|MDB_TXN_ERROR)) { + DPUTS("txn has failed/finished, can't commit"); + if (txn->mt_parent) + txn->mt_parent->mt_flags |= MDB_TXN_ERROR; + rc = MDB_BAD_TXN; + goto fail; + } + + if (txn->mt_parent) { + MDB_txn *parent = txn->mt_parent; + MDB_page **lp; + MDB_ID2L dst, src; + MDB_IDL pspill; + unsigned x, y, len, ps_len; + + /* Append our free list to parent's */ + rc = mdb_midl_append_list(&parent->mt_free_pgs, txn->mt_free_pgs); + if (rc) + goto fail; + mdb_midl_free(txn->mt_free_pgs); + /* Failures after this must either undo the changes + * to the parent or set MDB_TXN_ERROR in the parent. + */ + + parent->mt_next_pgno = txn->mt_next_pgno; + parent->mt_flags = txn->mt_flags; + + /* Merge our cursors into parent's and close them */ + mdb_cursors_close(txn, 1); + + /* Update parent's DB table. */ + memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + parent->mt_numdbs = txn->mt_numdbs; + parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI]; + parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI]; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + /* preserve parent's DB_NEW status */ + x = parent->mt_dbflags[i] & DB_NEW; + parent->mt_dbflags[i] = txn->mt_dbflags[i] | x; + } + + dst = parent->mt_u.dirty_list; + src = txn->mt_u.dirty_list; + /* Remove anything in our dirty list from parent's spill list */ + if ((pspill = parent->mt_spill_pgs) && (ps_len = pspill[0])) { + x = y = ps_len; + pspill[0] = (pgno_t)-1; + /* Mark our dirty pages as deleted in parent spill list */ + for (i=0, len=src[0].mid; ++i <= len; ) { + MDB_ID pn = src[i].mid << 1; + while (pn > pspill[x]) + x--; + if (pn == pspill[x]) { + pspill[x] = 1; + y = --x; + } + } + /* Squash deleted pagenums if we deleted any */ + for (x=y; ++x <= ps_len; ) + if (!(pspill[x] & 1)) + pspill[++y] = pspill[x]; + pspill[0] = y; + } + + /* Remove anything in our spill list from parent's dirty list */ + if (txn->mt_spill_pgs && txn->mt_spill_pgs[0]) { + for (i=1; i<=txn->mt_spill_pgs[0]; i++) { + MDB_ID pn = txn->mt_spill_pgs[i]; + if (pn & 1) + continue; /* deleted spillpg */ + pn >>= 1; + y = mdb_mid2l_search(dst, pn); + if (y <= dst[0].mid && dst[y].mid == pn) { + free(dst[y].mptr); + while (y < dst[0].mid) { + dst[y] = dst[y+1]; + y++; + } + dst[0].mid--; + } + } + } + + /* Find len = length of merging our dirty list with parent's */ + x = dst[0].mid; + dst[0].mid = 0; /* simplify loops */ + if (parent->mt_parent) { + len = x + src[0].mid; + y = mdb_mid2l_search(src, dst[x].mid + 1) - 1; + for (i = x; y && i; y--) { + pgno_t yp = src[y].mid; + while (yp < dst[i].mid) + i--; + if (yp == dst[i].mid) { + i--; + len--; + } + } + } else { /* Simplify the above for single-ancestor case */ + len = MDB_IDL_UM_MAX - txn->mt_dirty_room; + } + /* Merge our dirty list with parent's */ + y = src[0].mid; + for (i = len; y; dst[i--] = src[y--]) { + pgno_t yp = src[y].mid; + while (yp < dst[x].mid) + dst[i--] = dst[x--]; + if (yp == dst[x].mid) + free(dst[x--].mptr); + } + mdb_tassert(txn, i == x); + dst[0].mid = len; + free(txn->mt_u.dirty_list); + parent->mt_dirty_room = txn->mt_dirty_room; + if (txn->mt_spill_pgs) { + if (parent->mt_spill_pgs) { + /* TODO: Prevent failure here, so parent does not fail */ + rc = mdb_midl_append_list(&parent->mt_spill_pgs, txn->mt_spill_pgs); + if (rc) + parent->mt_flags |= MDB_TXN_ERROR; + mdb_midl_free(txn->mt_spill_pgs); + mdb_midl_sort(parent->mt_spill_pgs); + } else { + parent->mt_spill_pgs = txn->mt_spill_pgs; + } + } + + /* Append our loose page list to parent's */ + for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp)) + ; + *lp = txn->mt_loose_pgs; + parent->mt_loose_count += txn->mt_loose_count; + + parent->mt_child = NULL; + mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead); + free(txn); + return rc; + } + + if (txn != env->me_txn) { + DPUTS("attempt to commit unknown transaction"); + rc = EINVAL; + goto fail; + } + + mdb_cursors_close(txn, 0); + + if (!txn->mt_u.dirty_list[0].mid && + !(txn->mt_flags & (MDB_TXN_DIRTY|MDB_TXN_SPILLS))) + goto done; + + DPRINTF(("committing txn %"Z"u %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root)); + + /* Update DB root pointers */ + if (txn->mt_numdbs > CORE_DBS) { + MDB_cursor mc; + MDB_dbi i; + MDB_val data; + data.mv_size = sizeof(MDB_db); + + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + for (i = CORE_DBS; i < txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + if (TXN_DBI_CHANGED(txn, i)) { + rc = MDB_BAD_DBI; + goto fail; + } + data.mv_data = &txn->mt_dbs[i]; + rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, + F_SUBDATA); + if (rc) + goto fail; + } + } + } + + rc = mdb_freelist_save(txn); + if (rc) + goto fail; + + mdb_midl_free(env->me_pghead); + env->me_pghead = NULL; + mdb_midl_shrink(&txn->mt_free_pgs); + +#if (MDB_DEBUG) > 2 + mdb_audit(txn); +#endif + + if ((rc = mdb_page_flush(txn, 0)) || + (rc = mdb_env_sync(env, 0)) || + (rc = mdb_env_write_meta(txn))) + goto fail; + end_mode = MDB_END_COMMITTED|MDB_END_UPDATE; + +done: + mdb_txn_end(txn, end_mode); + return MDB_SUCCESS; + +fail: + mdb_txn_abort(txn); + return rc; +} + +/** Read the environment parameters of a DB environment before + * mapping it into memory. + * @param[in] env the environment handle + * @param[out] meta address of where to store the meta information + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_read_header(MDB_env *env, MDB_meta *meta) +{ + MDB_metabuf pbuf; + MDB_page *p; + MDB_meta *m; + int i, rc, off; + enum { Size = sizeof(pbuf) }; + + /* We don't know the page size yet, so use a minimum value. + * Read both meta pages so we can use the latest one. + */ + + for (i=off=0; i<NUM_METAS; i++, off += meta->mm_psize) { +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + rc = ReadFile(env->me_fd, &pbuf, Size, &len, &ov) ? (int)len : -1; + if (rc == -1 && ErrCode() == ERROR_HANDLE_EOF) + rc = 0; +#else + rc = pread(env->me_fd, &pbuf, Size, off); +#endif + if (rc != Size) { + if (rc == 0 && off == 0) + return ENOENT; + rc = rc < 0 ? (int) ErrCode() : MDB_INVALID; + DPRINTF(("read: %s", mdb_strerror(rc))); + return rc; + } + + p = (MDB_page *)&pbuf; + + if (!F_ISSET(p->mp_flags, P_META)) { + DPRINTF(("page %"Z"u not a meta page", p->mp_pgno)); + return MDB_INVALID; + } + + m = METADATA(p); + if (m->mm_magic != MDB_MAGIC) { + DPUTS("meta has invalid magic"); + return MDB_INVALID; + } + + if (m->mm_version != MDB_DATA_VERSION) { + DPRINTF(("database is version %u, expected version %u", + m->mm_version, MDB_DATA_VERSION)); + return MDB_VERSION_MISMATCH; + } + + if (off == 0 || m->mm_txnid > meta->mm_txnid) + *meta = *m; + } + return 0; +} + +/** Fill in most of the zeroed #MDB_meta for an empty database environment */ +static void ESECT +mdb_env_init_meta0(MDB_env *env, MDB_meta *meta) +{ + meta->mm_magic = MDB_MAGIC; + meta->mm_version = MDB_DATA_VERSION; + meta->mm_mapsize = env->me_mapsize; + meta->mm_psize = env->me_psize; + meta->mm_last_pg = NUM_METAS-1; + meta->mm_flags = env->me_flags & 0xffff; + meta->mm_flags |= MDB_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */ + meta->mm_dbs[FREE_DBI].md_root = P_INVALID; + meta->mm_dbs[MAIN_DBI].md_root = P_INVALID; +} + +/** Write the environment parameters of a freshly created DB environment. + * @param[in] env the environment handle + * @param[in] meta the #MDB_meta to write + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_init_meta(MDB_env *env, MDB_meta *meta) +{ + MDB_page *p, *q; + int rc; + unsigned int psize; +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + ov.Offset = pos; \ + rc = WriteFile(fd, ptr, size, &len, &ov); } while(0) +#else + int len; +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + len = pwrite(fd, ptr, size, pos); \ + if (len == -1 && ErrCode() == EINTR) continue; \ + rc = (len >= 0); break; } while(1) +#endif + + DPUTS("writing new meta page"); + + psize = env->me_psize; + + p = calloc(NUM_METAS, psize); + if (!p) + return ENOMEM; + + p->mp_pgno = 0; + p->mp_flags = P_META; + *(MDB_meta *)METADATA(p) = *meta; + + q = (MDB_page *)((char *)p + psize); + q->mp_pgno = 1; + q->mp_flags = P_META; + *(MDB_meta *)METADATA(q) = *meta; + + DO_PWRITE(rc, env->me_fd, p, psize * NUM_METAS, len, 0); + if (!rc) + rc = ErrCode(); + else if ((unsigned) len == psize * NUM_METAS) + rc = MDB_SUCCESS; + else + rc = ENOSPC; + free(p); + return rc; +} + +/** Update the environment info to commit a transaction. + * @param[in] txn the transaction that's being committed + * @return 0 on success, non-zero on failure. + */ +static int +mdb_env_write_meta(MDB_txn *txn) +{ + MDB_env *env; + MDB_meta meta, metab, *mp; + unsigned flags; + size_t mapsize; + off_t off; + int rc, len, toggle; + char *ptr; + HANDLE mfd; +#ifdef _WIN32 + OVERLAPPED ov; +#else + int r2; +#endif + + toggle = txn->mt_txnid & 1; + DPRINTF(("writing meta page %d for root page %"Z"u", + toggle, txn->mt_dbs[MAIN_DBI].md_root)); + + env = txn->mt_env; + flags = env->me_flags; + mp = env->me_metas[toggle]; + mapsize = env->me_metas[toggle ^ 1]->mm_mapsize; + /* Persist any increases of mapsize config */ + if (mapsize < env->me_mapsize) + mapsize = env->me_mapsize; + + if (flags & MDB_WRITEMAP) { + mp->mm_mapsize = mapsize; + mp->mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + mp->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mp->mm_last_pg = txn->mt_next_pgno - 1; +#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) && /* TODO: portability */ \ + !(defined(__i386__) || defined(__x86_64__)) + /* LY: issue a memory barrier, if not x86. ITS#7969 */ + __sync_synchronize(); +#endif + mp->mm_txnid = txn->mt_txnid; + if (!(flags & (MDB_NOMETASYNC|MDB_NOSYNC))) { + unsigned meta_size = env->me_psize; + rc = (env->me_flags & MDB_MAPASYNC) ? MS_ASYNC : MS_SYNC; + ptr = (char *)mp - PAGEHDRSZ; +#ifndef _WIN32 /* POSIX msync() requires ptr = start of OS page */ + r2 = (ptr - env->me_map) & (env->me_os_psize - 1); + ptr -= r2; + meta_size += r2; +#endif + if (MDB_MSYNC(ptr, meta_size, rc)) { + rc = ErrCode(); + goto fail; + } + } + goto done; + } + metab.mm_txnid = mp->mm_txnid; + metab.mm_last_pg = mp->mm_last_pg; + + meta.mm_mapsize = mapsize; + meta.mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + meta.mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + meta.mm_last_pg = txn->mt_next_pgno - 1; + meta.mm_txnid = txn->mt_txnid; + + off = offsetof(MDB_meta, mm_mapsize); + ptr = (char *)&meta + off; + len = sizeof(MDB_meta) - off; + off += (char *)mp - env->me_map; + + /* Write to the SYNC fd unless MDB_NOSYNC/MDB_NOMETASYNC. + * (me_mfd goes to the same file as me_fd, but writing to it + * also syncs to disk. Avoids a separate fdatasync() call.) + */ + mfd = (flags & (MDB_NOSYNC|MDB_NOMETASYNC)) ? env->me_fd : env->me_mfd; +#ifdef _WIN32 + { + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + if (!WriteFile(mfd, ptr, len, (DWORD *)&rc, &ov)) + rc = -1; + } +#else +retry_write: + rc = pwrite(mfd, ptr, len, off); +#endif + if (rc != len) { + rc = rc < 0 ? ErrCode() : EIO; +#ifndef _WIN32 + if (rc == EINTR) + goto retry_write; +#endif + DPUTS("write failed, disk error?"); + /* On a failure, the pagecache still contains the new data. + * Write some old data back, to prevent it from being used. + * Use the non-SYNC fd; we know it will fail anyway. + */ + meta.mm_last_pg = metab.mm_last_pg; + meta.mm_txnid = metab.mm_txnid; +#ifdef _WIN32 + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + WriteFile(env->me_fd, ptr, len, NULL, &ov); +#else + r2 = pwrite(env->me_fd, ptr, len, off); + (void)r2; /* Silence warnings. We don't care about pwrite's return value */ +#endif +fail: + env->me_flags |= MDB_FATAL_ERROR; + return rc; + } + /* MIPS has cache coherency issues, this is a no-op everywhere else */ + CACHEFLUSH(env->me_map + off, len, DCACHE); +done: + /* Memory ordering issues are irrelevant; since the entire writer + * is wrapped by wmutex, all of these changes will become visible + * after the wmutex is unlocked. Since the DB is multi-version, + * readers will get consistent data regardless of how fresh or + * how stale their view of these values is. + */ + if (env->me_txns) + env->me_txns->mti_txnid = txn->mt_txnid; + + return MDB_SUCCESS; +} + +/** Check both meta pages to see which one is newer. + * @param[in] env the environment handle + * @return newest #MDB_meta. + */ +static MDB_meta * +mdb_env_pick_meta(const MDB_env *env) +{ + MDB_meta *const *metas = env->me_metas; + return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ]; +} + +int ESECT +mdb_env_create(MDB_env **env) +{ + MDB_env *e; + + e = calloc(1, sizeof(MDB_env)); + if (!e) + return ENOMEM; + + e->me_maxreaders = DEFAULT_READERS; + e->me_maxdbs = e->me_numdbs = CORE_DBS; + e->me_fd = INVALID_HANDLE_VALUE; + e->me_lfd = INVALID_HANDLE_VALUE; + e->me_mfd = INVALID_HANDLE_VALUE; +#ifdef MDB_USE_POSIX_SEM + e->me_rmutex = SEM_FAILED; + e->me_wmutex = SEM_FAILED; +#endif + e->me_pid = getpid(); + GET_PAGESIZE(e->me_os_psize); + VGMEMP_CREATE(e,0,0); + *env = e; + return MDB_SUCCESS; +} + +static int ESECT +mdb_env_map(MDB_env *env, void *addr) +{ + MDB_page *p; + unsigned int flags = env->me_flags; +#ifdef _WIN32 + int rc; + HANDLE mh; + LONG sizelo, sizehi; + size_t msize; + + if (flags & MDB_RDONLY) { + /* Don't set explicit map size, use whatever exists */ + msize = 0; + sizelo = 0; + sizehi = 0; + } else { + msize = env->me_mapsize; + sizelo = msize & 0xffffffff; + sizehi = msize >> 16 >> 16; /* only needed on Win64 */ + + /* Windows won't create mappings for zero length files. + * and won't map more than the file size. + * Just set the maxsize right now. + */ + if (!(flags & MDB_WRITEMAP) && (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo + || !SetEndOfFile(env->me_fd) + || SetFilePointer(env->me_fd, 0, NULL, 0) != 0)) + return ErrCode(); + } + + mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ? + PAGE_READWRITE : PAGE_READONLY, + sizehi, sizelo, NULL); + if (!mh) + return ErrCode(); + env->me_map = MapViewOfFileEx(mh, flags & MDB_WRITEMAP ? + FILE_MAP_WRITE : FILE_MAP_READ, + 0, 0, msize, addr); + rc = env->me_map ? 0 : ErrCode(); + CloseHandle(mh); + if (rc) + return rc; +#else + int mmap_flags = MAP_SHARED; + int prot = PROT_READ; +#ifdef MAP_NOSYNC /* Used on FreeBSD */ + if (flags & MDB_NOSYNC) + mmap_flags |= MAP_NOSYNC; +#endif + if (flags & MDB_WRITEMAP) { + prot |= PROT_WRITE; + if (ftruncate(env->me_fd, env->me_mapsize) < 0) + return ErrCode(); + } + env->me_map = mmap(addr, env->me_mapsize, prot, mmap_flags, + env->me_fd, 0); + if (env->me_map == MAP_FAILED) { + env->me_map = NULL; + return ErrCode(); + } + + if (flags & MDB_NORDAHEAD) { + /* Turn off readahead. It's harmful when the DB is larger than RAM. */ +#ifdef MADV_RANDOM + madvise(env->me_map, env->me_mapsize, MADV_RANDOM); +#else +#ifdef POSIX_MADV_RANDOM + posix_madvise(env->me_map, env->me_mapsize, POSIX_MADV_RANDOM); +#endif /* POSIX_MADV_RANDOM */ +#endif /* MADV_RANDOM */ + } +#endif /* _WIN32 */ + + /* Can happen because the address argument to mmap() is just a + * hint. mmap() can pick another, e.g. if the range is in use. + * The MAP_FIXED flag would prevent that, but then mmap could + * instead unmap existing pages to make room for the new map. + */ + if (addr && env->me_map != addr) + return EBUSY; /* TODO: Make a new MDB_* error code? */ + + p = (MDB_page *)env->me_map; + env->me_metas[0] = METADATA(p); + env->me_metas[1] = (MDB_meta *)((char *)env->me_metas[0] + env->me_psize); + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_mapsize(MDB_env *env, size_t size) +{ + /* If env is already open, caller is responsible for making + * sure there are no active txns. + */ + if (env->me_map) { + int rc; + MDB_meta *meta; + void *old; + if (env->me_txn) + return EINVAL; + meta = mdb_env_pick_meta(env); + if (!size) + size = meta->mm_mapsize; + { + /* Silently round up to minimum if the size is too small */ + size_t minsize = (meta->mm_last_pg + 1) * env->me_psize; + if (size < minsize) + size = minsize; + } + munmap(env->me_map, env->me_mapsize); + env->me_mapsize = size; + old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL; + rc = mdb_env_map(env, old); + if (rc) + return rc; + } + env->me_mapsize = size; + if (env->me_psize) + env->me_maxpg = env->me_mapsize / env->me_psize; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs) +{ + if (env->me_map) + return EINVAL; + env->me_maxdbs = dbs + CORE_DBS; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxreaders(MDB_env *env, unsigned int readers) +{ + if (env->me_map || readers < 1) + return EINVAL; + env->me_maxreaders = readers; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers) +{ + if (!env || !readers) + return EINVAL; + *readers = env->me_maxreaders; + return MDB_SUCCESS; +} + +static int ESECT +mdb_fsize(HANDLE fd, size_t *size) +{ +#ifdef _WIN32 + LARGE_INTEGER fsize; + + if (!GetFileSizeEx(fd, &fsize)) + return ErrCode(); + + *size = fsize.QuadPart; +#else + struct stat st; + + if (fstat(fd, &st)) + return ErrCode(); + + *size = st.st_size; +#endif + return MDB_SUCCESS; +} + + +#ifdef _WIN32 +typedef wchar_t mdb_nchar_t; +# define MDB_NAME(str) L##str +# define mdb_name_cpy wcscpy +#else +/** Character type for file names: char on Unix, wchar_t on Windows */ +typedef char mdb_nchar_t; +# define MDB_NAME(str) str /**< #mdb_nchar_t[] string literal */ +# define mdb_name_cpy strcpy /**< Copy name (#mdb_nchar_t string) */ +#endif + +/** Filename - string of #mdb_nchar_t[] */ +typedef struct MDB_name { + int mn_len; /**< Length */ + int mn_alloced; /**< True if #mn_val was malloced */ + mdb_nchar_t *mn_val; /**< Contents */ +} MDB_name; + +/** Filename suffixes [datafile,lockfile][without,with MDB_NOSUBDIR] */ +static const mdb_nchar_t *const mdb_suffixes[2][2] = { + { MDB_NAME("/data.mdb"), MDB_NAME("") }, + { MDB_NAME("/lock.mdb"), MDB_NAME("-lock") } +}; + +#define MDB_SUFFLEN 9 /**< Max string length in #mdb_suffixes[] */ + +/** Set up filename + scratch area for filename suffix, for opening files. + * It should be freed with #mdb_fname_destroy(). + * On Windows, paths are converted from char *UTF-8 to wchar_t *UTF-16. + * + * @param[in] path Pathname for #mdb_env_open(). + * @param[in] envflags Whether a subdir and/or lockfile will be used. + * @param[out] fname Resulting filename, with room for a suffix if necessary. + */ +static int ESECT +mdb_fname_init(const char *path, unsigned envflags, MDB_name *fname) +{ + int no_suffix = F_ISSET(envflags, MDB_NOSUBDIR|MDB_NOLOCK); + fname->mn_alloced = 0; +#ifdef _WIN32 + return utf8_to_utf16(path, fname, no_suffix ? 0 : MDB_SUFFLEN); +#else + fname->mn_len = strlen(path); + if (no_suffix) + fname->mn_val = (char *) path; + else if ((fname->mn_val = malloc(fname->mn_len + MDB_SUFFLEN+1)) != NULL) { + fname->mn_alloced = 1; + strcpy(fname->mn_val, path); + } + else + return ENOMEM; + return MDB_SUCCESS; +#endif +} + +/** Destroy \b fname from #mdb_fname_init() */ +#define mdb_fname_destroy(fname) \ + do { if ((fname).mn_alloced) free((fname).mn_val); } while (0) + +#ifdef O_CLOEXEC /* POSIX.1-2008: Set FD_CLOEXEC atomically at open() */ +# define MDB_CLOEXEC O_CLOEXEC +#else +# define MDB_CLOEXEC 0 +#endif + +/** File type, access mode etc. for #mdb_fopen() */ +enum mdb_fopen_type { +#ifdef _WIN32 + MDB_O_RDONLY, MDB_O_RDWR, MDB_O_META, MDB_O_COPY, MDB_O_LOCKS +#else + /* A comment in mdb_fopen() explains some O_* flag choices. */ + MDB_O_RDONLY= O_RDONLY, /**< for RDONLY me_fd */ + MDB_O_RDWR = O_RDWR |O_CREAT, /**< for me_fd */ + MDB_O_META = O_WRONLY|MDB_DSYNC |MDB_CLOEXEC, /**< for me_mfd */ + MDB_O_COPY = O_WRONLY|O_CREAT|O_EXCL|MDB_CLOEXEC, /**< for #mdb_env_copy() */ + /** Bitmask for open() flags in enum #mdb_fopen_type. The other bits + * distinguish otherwise-equal MDB_O_* constants from each other. + */ + MDB_O_MASK = MDB_O_RDWR|MDB_CLOEXEC | MDB_O_RDONLY|MDB_O_META|MDB_O_COPY, + MDB_O_LOCKS = MDB_O_RDWR|MDB_CLOEXEC | ((MDB_O_MASK+1) & ~MDB_O_MASK) /**< for me_lfd */ +#endif +}; + +/** Open an LMDB file. + * @param[in] env The LMDB environment. + * @param[in,out] fname Path from from #mdb_fname_init(). A suffix is + * appended if necessary to create the filename, without changing mn_len. + * @param[in] which Determines file type, access mode, etc. + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[out] res Resulting file handle. + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_fopen(const MDB_env *env, MDB_name *fname, + enum mdb_fopen_type which, mdb_mode_t mode, + HANDLE *res) +{ + int rc = MDB_SUCCESS; + HANDLE fd; +#ifdef _WIN32 + DWORD acc, share, disp, attrs; +#else + int flags; +#endif + + if (fname->mn_alloced) /* modifiable copy */ + mdb_name_cpy(fname->mn_val + fname->mn_len, + mdb_suffixes[which==MDB_O_LOCKS][F_ISSET(env->me_flags, MDB_NOSUBDIR)]); + + /* The directory must already exist. Usually the file need not. + * MDB_O_META requires the file because we already created it using + * MDB_O_RDWR. MDB_O_COPY must not overwrite an existing file. + * + * With MDB_O_COPY we do not want the OS to cache the writes, since + * the source data is already in the OS cache. + * + * The lockfile needs FD_CLOEXEC (close file descriptor on exec*()) + * to avoid the flock() issues noted under Caveats in lmdb.h. + * Also set it for other filehandles which the user cannot get at + * and close himself, which he may need after fork(). I.e. all but + * me_fd, which programs do use via mdb_env_get_fd(). + */ + +#ifdef _WIN32 + acc = GENERIC_READ|GENERIC_WRITE; + share = FILE_SHARE_READ|FILE_SHARE_WRITE; + disp = OPEN_ALWAYS; + attrs = FILE_ATTRIBUTE_NORMAL; + switch (which) { + case MDB_O_RDONLY: /* read-only datafile */ + acc = GENERIC_READ; + disp = OPEN_EXISTING; + break; + case MDB_O_META: /* for writing metapages */ + acc = GENERIC_WRITE; + disp = OPEN_EXISTING; + attrs = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH; + break; + case MDB_O_COPY: /* mdb_env_copy() & co */ + acc = GENERIC_WRITE; + share = 0; + disp = CREATE_NEW; + attrs = FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH; + break; + default: break; /* silence gcc -Wswitch (not all enum values handled) */ + } + fd = CreateFileW(fname->mn_val, acc, share, NULL, disp, attrs, NULL); +#else + fd = open(fname->mn_val, which & MDB_O_MASK, mode); +#endif + + if (fd == INVALID_HANDLE_VALUE) + rc = ErrCode(); +#ifndef _WIN32 + else { + if (which != MDB_O_RDONLY && which != MDB_O_RDWR) { + /* Set CLOEXEC if we could not pass it to open() */ + if (!MDB_CLOEXEC && (flags = fcntl(fd, F_GETFD)) != -1) + (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } + if (which == MDB_O_COPY && env->me_psize >= env->me_os_psize) { + /* This may require buffer alignment. There is no portable + * way to ask how much, so we require OS pagesize alignment. + */ +# ifdef F_NOCACHE /* __APPLE__ */ + (void) fcntl(fd, F_NOCACHE, 1); +# elif defined O_DIRECT + /* open(...O_DIRECT...) would break on filesystems without + * O_DIRECT support (ITS#7682). Try to set it here instead. + */ + if ((flags = fcntl(fd, F_GETFL)) != -1) + (void) fcntl(fd, F_SETFL, flags | O_DIRECT); +# endif + } + } +#endif /* !_WIN32 */ + + *res = fd; + return rc; +} + + +#ifdef BROKEN_FDATASYNC +#include <sys/utsname.h> +#include <sys/vfs.h> +#endif + +/** Further setup required for opening an LMDB environment + */ +static int ESECT +mdb_env_open2(MDB_env *env) +{ + unsigned int flags = env->me_flags; + int i, newenv = 0, rc; + MDB_meta meta; + +#ifdef _WIN32 + /* See if we should use QueryLimited */ + rc = GetVersion(); + if ((rc & 0xff) > 5) + env->me_pidquery = MDB_PROCESS_QUERY_LIMITED_INFORMATION; + else + env->me_pidquery = PROCESS_QUERY_INFORMATION; +#endif /* _WIN32 */ + +#ifdef BROKEN_FDATASYNC + /* ext3/ext4 fdatasync is broken on some older Linux kernels. + * https://lkml.org/lkml/2012/9/3/83 + * Kernels after 3.6-rc6 are known good. + * https://lkml.org/lkml/2012/9/10/556 + * See if the DB is on ext3/ext4, then check for new enough kernel + * Kernels 2.6.32.60, 2.6.34.15, 3.2.30, and 3.5.4 are also known + * to be patched. + */ + { + struct statfs st; + fstatfs(env->me_fd, &st); + while (st.f_type == 0xEF53) { + struct utsname uts; + int i; + uname(&uts); + if (uts.release[0] < '3') { + if (!strncmp(uts.release, "2.6.32.", 7)) { + i = atoi(uts.release+7); + if (i >= 60) + break; /* 2.6.32.60 and newer is OK */ + } else if (!strncmp(uts.release, "2.6.34.", 7)) { + i = atoi(uts.release+7); + if (i >= 15) + break; /* 2.6.34.15 and newer is OK */ + } + } else if (uts.release[0] == '3') { + i = atoi(uts.release+2); + if (i > 5) + break; /* 3.6 and newer is OK */ + if (i == 5) { + i = atoi(uts.release+4); + if (i >= 4) + break; /* 3.5.4 and newer is OK */ + } else if (i == 2) { + i = atoi(uts.release+4); + if (i >= 30) + break; /* 3.2.30 and newer is OK */ + } + } else { /* 4.x and newer is OK */ + break; + } + env->me_flags |= MDB_FSYNCONLY; + break; + } + } +#endif + + if ((i = mdb_env_read_header(env, &meta)) != 0) { + if (i != ENOENT) + return i; + DPUTS("new mdbenv"); + newenv = 1; + env->me_psize = env->me_os_psize; + if (env->me_psize > MAX_PAGESIZE) + env->me_psize = MAX_PAGESIZE; + memset(&meta, 0, sizeof(meta)); + mdb_env_init_meta0(env, &meta); + meta.mm_mapsize = DEFAULT_MAPSIZE; + } else { + env->me_psize = meta.mm_psize; + } + + /* Was a mapsize configured? */ + if (!env->me_mapsize) { + env->me_mapsize = meta.mm_mapsize; + } + { + /* Make sure mapsize >= committed data size. Even when using + * mm_mapsize, which could be broken in old files (ITS#7789). + */ + size_t minsize = (meta.mm_last_pg + 1) * meta.mm_psize; + if (env->me_mapsize < minsize) + env->me_mapsize = minsize; + } + meta.mm_mapsize = env->me_mapsize; + + if (newenv && !(flags & MDB_FIXEDMAP)) { + /* mdb_env_map() may grow the datafile. Write the metapages + * first, so the file will be valid if initialization fails. + * Except with FIXEDMAP, since we do not yet know mm_address. + * We could fill in mm_address later, but then a different + * program might end up doing that - one with a memory layout + * and map address which does not suit the main program. + */ + rc = mdb_env_init_meta(env, &meta); + if (rc) + return rc; + newenv = 0; + } + + rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL); + if (rc) + return rc; + + if (newenv) { + if (flags & MDB_FIXEDMAP) + meta.mm_address = env->me_map; + i = mdb_env_init_meta(env, &meta); + if (i != MDB_SUCCESS) { + return i; + } + } + + env->me_maxfree_1pg = (env->me_psize - PAGEHDRSZ) / sizeof(pgno_t) - 1; + env->me_nodemax = (((env->me_psize - PAGEHDRSZ) / MDB_MINKEYS) & -2) + - sizeof(indx_t); +#if !(MDB_MAXKEYSIZE) + env->me_maxkey = env->me_nodemax - (NODESIZE + sizeof(MDB_db)); +#endif + env->me_maxpg = env->me_mapsize / env->me_psize; + +#if MDB_DEBUG + { + MDB_meta *meta = mdb_env_pick_meta(env); + MDB_db *db = &meta->mm_dbs[MAIN_DBI]; + + DPRINTF(("opened database version %u, pagesize %u", + meta->mm_version, env->me_psize)); + DPRINTF(("using meta page %d", (int) (meta->mm_txnid & 1))); + DPRINTF(("depth: %u", db->md_depth)); + DPRINTF(("entries: %"Z"u", db->md_entries)); + DPRINTF(("branch pages: %"Z"u", db->md_branch_pages)); + DPRINTF(("leaf pages: %"Z"u", db->md_leaf_pages)); + DPRINTF(("overflow pages: %"Z"u", db->md_overflow_pages)); + DPRINTF(("root: %"Z"u", db->md_root)); + } +#endif + + return MDB_SUCCESS; +} + + +/** Release a reader thread's slot in the reader lock table. + * This function is called automatically when a thread exits. + * @param[in] ptr This points to the slot in the reader lock table. + */ +static void +mdb_env_reader_dest(void *ptr) +{ + MDB_reader *reader = ptr; + +#ifndef _WIN32 + if (reader->mr_pid == getpid()) /* catch pthread_exit() in child process */ +#endif + /* We omit the mutex, so do this atomically (i.e. skip mr_txnid) */ + reader->mr_pid = 0; +} + +#ifdef _WIN32 +/** Junk for arranging thread-specific callbacks on Windows. This is + * necessarily platform and compiler-specific. Windows supports up + * to 1088 keys. Let's assume nobody opens more than 64 environments + * in a single process, for now. They can override this if needed. + */ +#ifndef MAX_TLS_KEYS +#define MAX_TLS_KEYS 64 +#endif +static pthread_key_t mdb_tls_keys[MAX_TLS_KEYS]; +static int mdb_tls_nkeys; + +static void NTAPI mdb_tls_callback(PVOID module, DWORD reason, PVOID ptr) +{ + int i; + switch(reason) { + case DLL_PROCESS_ATTACH: break; + case DLL_THREAD_ATTACH: break; + case DLL_THREAD_DETACH: + for (i=0; i<mdb_tls_nkeys; i++) { + MDB_reader *r = pthread_getspecific(mdb_tls_keys[i]); + if (r) { + mdb_env_reader_dest(r); + } + } + break; + case DLL_PROCESS_DETACH: break; + } +} +#ifdef __GNUC__ +#ifdef _WIN64 +const PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#else +PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#endif +#else +#ifdef _WIN64 +/* Force some symbol references. + * _tls_used forces the linker to create the TLS directory if not already done + * mdb_tls_cbp prevents whole-program-optimizer from dropping the symbol. + */ +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:mdb_tls_cbp") +#pragma const_seg(".CRT$XLB") +extern const PIMAGE_TLS_CALLBACK mdb_tls_cbp; +const PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma const_seg() +#else /* _WIN32 */ +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_mdb_tls_cbp") +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma data_seg() +#endif /* WIN 32/64 */ +#endif /* !__GNUC__ */ +#endif + +/** Downgrade the exclusive lock on the region back to shared */ +static int ESECT +mdb_env_share_locks(MDB_env *env, int *excl) +{ + int rc = 0; + MDB_meta *meta = mdb_env_pick_meta(env); + + env->me_txns->mti_txnid = meta->mm_txnid; + +#ifdef _WIN32 + { + OVERLAPPED ov; + /* First acquire a shared lock. The Unlock will + * then release the existing exclusive lock. + */ + memset(&ov, 0, sizeof(ov)); + if (!LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + rc = ErrCode(); + } else { + UnlockFile(env->me_lfd, 0, 0, 1, 0); + *excl = 0; + } + } +#else + { + struct flock lock_info; + /* The shared lock replaces the existing lock */ + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_RDLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + *excl = rc ? -1 : 0; /* error may mean we lost the lock */ + } +#endif + + return rc; +} + +/** Try to get exclusive lock, otherwise shared. + * Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive. + */ +static int ESECT +mdb_env_excl_lock(MDB_env *env, int *excl) +{ + int rc = 0; +#ifdef _WIN32 + if (LockFile(env->me_lfd, 0, 0, 1, 0)) { + *excl = 1; + } else { + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + if (LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + *excl = 0; + } else { + rc = ErrCode(); + } + } +#else + struct flock lock_info; + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (!rc) { + *excl = 1; + } else +# ifndef MDB_USE_POSIX_MUTEX + if (*excl < 0) /* always true when MDB_USE_POSIX_MUTEX */ +# endif + { + lock_info.l_type = F_RDLCK; + while ((rc = fcntl(env->me_lfd, F_SETLKW, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (rc == 0) + *excl = 0; + } +#endif + return rc; +} + +#ifdef MDB_USE_HASH +/* + * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code + * + * @(#) $Revision: 5.1 $ + * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $ + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo <Landon Curt Noll> /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +typedef unsigned long long mdb_hash_t; +#define MDB_HASH_INIT ((mdb_hash_t)0xcbf29ce484222325ULL) + +/** perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * @param[in] val value to hash + * @param[in] hval initial value for hash + * @return 64 bit hash + * + * NOTE: To use the recommended 64 bit FNV-1a hash, use MDB_HASH_INIT as the + * hval arg on the first call. + */ +static mdb_hash_t +mdb_hash_val(MDB_val *val, mdb_hash_t hval) +{ + unsigned char *s = (unsigned char *)val->mv_data; /* unsigned string */ + unsigned char *end = s + val->mv_size; + /* + * FNV-1a hash each octet of the string + */ + while (s < end) { + /* xor the bottom with the current octet */ + hval ^= (mdb_hash_t)*s++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + /* return our new hash value */ + return hval; +} + +/** Hash the string and output the encoded hash. + * This uses modified RFC1924 Ascii85 encoding to accommodate systems with + * very short name limits. We don't care about the encoding being reversible, + * we just want to preserve as many bits of the input as possible in a + * small printable string. + * @param[in] str string to hash + * @param[out] encbuf an array of 11 chars to hold the hash + */ +static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; + +static void ESECT +mdb_pack85(unsigned long l, char *out) +{ + int i; + + for (i=0; i<5; i++) { + *out++ = mdb_a85[l % 85]; + l /= 85; + } +} + +static void ESECT +mdb_hash_enc(MDB_val *val, char *encbuf) +{ + mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT); + + mdb_pack85(h, encbuf); + mdb_pack85(h>>32, encbuf+5); + encbuf[10] = '\0'; +} +#endif + +/** Open and/or initialize the lock region for the environment. + * @param[in] env The LMDB environment. + * @param[in] fname Filename + scratch area, from #mdb_fname_init(). + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl) +{ +#ifdef _WIN32 +# define MDB_ERRCODE_ROFS ERROR_WRITE_PROTECT +#else +# define MDB_ERRCODE_ROFS EROFS +#endif + int rc; + off_t size, rsize; + + rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd); + if (rc) { + /* Omit lockfile if read-only env on read-only filesystem */ + if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) { + return MDB_SUCCESS; + } + goto fail; + } + + if (!(env->me_flags & MDB_NOTLS)) { + rc = pthread_key_create(&env->me_txkey, mdb_env_reader_dest); + if (rc) + goto fail; + env->me_flags |= MDB_ENV_TXKEY; +#ifdef _WIN32 + /* Windows TLS callbacks need help finding their TLS info. */ + if (mdb_tls_nkeys >= MAX_TLS_KEYS) { + rc = MDB_TLS_FULL; + goto fail; + } + mdb_tls_keys[mdb_tls_nkeys++] = env->me_txkey; +#endif + } + + /* Try to get exclusive lock. If we succeed, then + * nobody is using the lock region and we should initialize it. + */ + if ((rc = mdb_env_excl_lock(env, excl))) goto fail; + +#ifdef _WIN32 + size = GetFileSize(env->me_lfd, NULL); +#else + size = lseek(env->me_lfd, 0, SEEK_END); + if (size == -1) goto fail_errno; +#endif + rsize = (env->me_maxreaders-1) * sizeof(MDB_reader) + sizeof(MDB_txninfo); + if (size < rsize && *excl > 0) { +#ifdef _WIN32 + if (SetFilePointer(env->me_lfd, rsize, NULL, FILE_BEGIN) != (DWORD)rsize + || !SetEndOfFile(env->me_lfd)) + goto fail_errno; +#else + if (ftruncate(env->me_lfd, rsize) != 0) goto fail_errno; +#endif + } else { + rsize = size; + size = rsize - sizeof(MDB_txninfo); + env->me_maxreaders = size/sizeof(MDB_reader) + 1; + } + { +#ifdef _WIN32 + HANDLE mh; + mh = CreateFileMapping(env->me_lfd, NULL, PAGE_READWRITE, + 0, 0, NULL); + if (!mh) goto fail_errno; + env->me_txns = MapViewOfFileEx(mh, FILE_MAP_WRITE, 0, 0, rsize, NULL); + CloseHandle(mh); + if (!env->me_txns) goto fail_errno; +#else + void *m = mmap(NULL, rsize, PROT_READ|PROT_WRITE, MAP_SHARED, + env->me_lfd, 0); + if (m == MAP_FAILED) goto fail_errno; + env->me_txns = m; +#endif + } + if (*excl > 0) { +#ifdef _WIN32 + BY_HANDLE_FILE_INFORMATION stbuf; + struct { + DWORD volume; + DWORD nhigh; + DWORD nlow; + } idbuf; + MDB_val val; + char encbuf[11]; + + if (!mdb_sec_inited) { + InitializeSecurityDescriptor(&mdb_null_sd, + SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&mdb_null_sd, TRUE, 0, FALSE); + mdb_all_sa.nLength = sizeof(SECURITY_ATTRIBUTES); + mdb_all_sa.bInheritHandle = FALSE; + mdb_all_sa.lpSecurityDescriptor = &mdb_null_sd; + mdb_sec_inited = 1; + } + if (!GetFileInformationByHandle(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.volume = stbuf.dwVolumeSerialNumber; + idbuf.nhigh = stbuf.nFileIndexHigh; + idbuf.nlow = stbuf.nFileIndexLow; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); + sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", encbuf); + env->me_rmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + struct stat stbuf; + struct { + dev_t dev; + ino_t ino; + } idbuf; + MDB_val val; + char encbuf[11]; + +#if defined(__NetBSD__) +#define MDB_SHORT_SEMNAMES 1 /* limited to 14 chars */ +#endif + if (fstat(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.dev = stbuf.st_dev; + idbuf.ino = stbuf.st_ino; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); +#ifdef MDB_SHORT_SEMNAMES + encbuf[9] = '\0'; /* drop name from 15 chars to 14 chars */ +#endif + sprintf(env->me_txns->mti_rmname, "/MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "/MDBw%s", encbuf); + /* Clean up after a previous run, if needed: Try to + * remove both semaphores before doing anything else. + */ + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + env->me_rmutex = sem_open(env->me_txns->mti_rmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#else /* MDB_USE_POSIX_MUTEX: */ + pthread_mutexattr_t mattr; + + /* Solaris needs this before initing a robust mutex. Otherwise + * it may skip the init and return EBUSY "seems someone already + * inited" or EINVAL "it was inited differently". + */ + memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex)); + memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex)); + + if ((rc = pthread_mutexattr_init(&mattr))) + goto fail; + + rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); +#ifdef MDB_ROBUST_SUPPORTED + if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); +#endif + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr); + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr); + pthread_mutexattr_destroy(&mattr); + if (rc) + goto fail; +#endif /* _WIN32 || MDB_USE_POSIX_SEM */ + + env->me_txns->mti_magic = MDB_MAGIC; + env->me_txns->mti_format = MDB_LOCK_FORMAT; + env->me_txns->mti_txnid = 0; + env->me_txns->mti_numreaders = 0; + + } else { + if (env->me_txns->mti_magic != MDB_MAGIC) { + DPUTS("lock region has invalid magic"); + rc = MDB_INVALID; + goto fail; + } + if (env->me_txns->mti_format != MDB_LOCK_FORMAT) { + DPRINTF(("lock region has format+version 0x%x, expected 0x%x", + env->me_txns->mti_format, MDB_LOCK_FORMAT)); + rc = MDB_VERSION_MISMATCH; + goto fail; + } + rc = ErrCode(); + if (rc && rc != EACCES && rc != EAGAIN) { + goto fail; + } +#ifdef _WIN32 + env->me_rmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, 0); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#endif + } + return MDB_SUCCESS; + +fail_errno: + rc = ErrCode(); +fail: + return rc; +} + + /** Only a subset of the @ref mdb_env flags can be changed + * at runtime. Changing other flags requires closing the + * environment and re-opening it with the new flags. + */ +#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT) +#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \ + MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD) + +#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS) +# error "Persistent DB flags & env flags overlap, but both go in mm_flags" +#endif + +int ESECT +mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode) +{ + int rc, excl = -1; + MDB_name fname; + + if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS))) + return EINVAL; + + flags |= env->me_flags; + + rc = mdb_fname_init(path, flags, &fname); + if (rc) + return rc; + + if (flags & MDB_RDONLY) { + /* silently ignore WRITEMAP when we're only getting read access */ + flags &= ~MDB_WRITEMAP; + } else { + if (!((env->me_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)) && + (env->me_dirty_list = calloc(MDB_IDL_UM_SIZE, sizeof(MDB_ID2))))) + rc = ENOMEM; + } + env->me_flags = flags |= MDB_ENV_ACTIVE; + if (rc) + goto leave; + + env->me_path = strdup(path); + env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx)); + env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t)); + env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int)); + if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) { + rc = ENOMEM; + goto leave; + } + env->me_dbxs[FREE_DBI].md_cmp = mdb_cmp_long; /* aligned MDB_INTEGERKEY */ + + /* For RDONLY, get lockfile after we know datafile exists */ + if (!(flags & (MDB_RDONLY|MDB_NOLOCK))) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + rc = mdb_fopen(env, &fname, + (flags & MDB_RDONLY) ? MDB_O_RDONLY : MDB_O_RDWR, + mode, &env->me_fd); + if (rc) + goto leave; + + if ((flags & (MDB_RDONLY|MDB_NOLOCK)) == MDB_RDONLY) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) { + if (!(flags & (MDB_RDONLY|MDB_WRITEMAP))) { + /* Synchronous fd for meta writes. Needed even with + * MDB_NOSYNC/MDB_NOMETASYNC, in case these get reset. + */ + rc = mdb_fopen(env, &fname, MDB_O_META, mode, &env->me_mfd); + if (rc) + goto leave; + } + DPRINTF(("opened dbenv %p", (void *) env)); + if (excl > 0) { + rc = mdb_env_share_locks(env, &excl); + if (rc) + goto leave; + } + if (!(flags & MDB_RDONLY)) { + MDB_txn *txn; + int tsize = sizeof(MDB_txn), size = tsize + env->me_maxdbs * + (sizeof(MDB_db)+sizeof(MDB_cursor *)+sizeof(unsigned int)+1); + if ((env->me_pbuf = calloc(1, env->me_psize)) && + (txn = calloc(1, size))) + { + txn->mt_dbs = (MDB_db *)((char *)txn + tsize); + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs); + txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs); + txn->mt_env = env; + txn->mt_dbxs = env->me_dbxs; + txn->mt_flags = MDB_TXN_FINISHED; + env->me_txn0 = txn; + } else { + rc = ENOMEM; + } + } + } + +leave: + if (rc) { + mdb_env_close0(env, excl); + } + mdb_fname_destroy(fname); + return rc; +} + +/** Destroy resources from mdb_env_open(), clear our readers & DBIs */ +static void ESECT +mdb_env_close0(MDB_env *env, int excl) +{ + int i; + + if (!(env->me_flags & MDB_ENV_ACTIVE)) + return; + + /* Doing this here since me_dbxs may not exist during mdb_env_close */ + if (env->me_dbxs) { + for (i = env->me_maxdbs; --i >= CORE_DBS; ) + free(env->me_dbxs[i].md_name.mv_data); + free(env->me_dbxs); + } + + free(env->me_pbuf); + free(env->me_dbiseqs); + free(env->me_dbflags); + free(env->me_path); + free(env->me_dirty_list); + free(env->me_txn0); + mdb_midl_free(env->me_free_pgs); + + if (env->me_flags & MDB_ENV_TXKEY) { + pthread_key_delete(env->me_txkey); +#ifdef _WIN32 + /* Delete our key from the global list */ + for (i=0; i<mdb_tls_nkeys; i++) + if (mdb_tls_keys[i] == env->me_txkey) { + mdb_tls_keys[i] = mdb_tls_keys[mdb_tls_nkeys-1]; + mdb_tls_nkeys--; + break; + } +#endif + } + + if (env->me_map) { + munmap(env->me_map, env->me_mapsize); + } + if (env->me_mfd != INVALID_HANDLE_VALUE) + (void) close(env->me_mfd); + if (env->me_fd != INVALID_HANDLE_VALUE) + (void) close(env->me_fd); + if (env->me_txns) { + MDB_PID_T pid = getpid(); + /* Clearing readers is done in this function because + * me_txkey with its destructor must be disabled first. + * + * We skip the the reader mutex, so we touch only + * data owned by this process (me_close_readers and + * our readers), and clear each reader atomically. + */ + for (i = env->me_close_readers; --i >= 0; ) + if (env->me_txns->mti_readers[i].mr_pid == pid) + env->me_txns->mti_readers[i].mr_pid = 0; +#ifdef _WIN32 + if (env->me_rmutex) { + CloseHandle(env->me_rmutex); + if (env->me_wmutex) CloseHandle(env->me_wmutex); + } + /* Windows automatically destroys the mutexes when + * the last handle closes. + */ +#elif defined(MDB_USE_POSIX_SEM) + if (env->me_rmutex != SEM_FAILED) { + sem_close(env->me_rmutex); + if (env->me_wmutex != SEM_FAILED) + sem_close(env->me_wmutex); + /* If we have the filelock: If we are the + * only remaining user, clean up semaphores. + */ + if (excl == 0) + mdb_env_excl_lock(env, &excl); + if (excl > 0) { + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + } + } +#elif defined(MDB_ROBUST_SUPPORTED) + /* If we have the filelock: If we are the + * only remaining user, clean up robust + * mutexes. + */ + if (excl == 0) + mdb_env_excl_lock(env, &excl); + if (excl > 0) { + pthread_mutex_destroy(env->me_txns->mti_rmutex); + pthread_mutex_destroy(env->me_txns->mti_wmutex); + } +#endif + munmap((void *)env->me_txns, (env->me_maxreaders-1)*sizeof(MDB_reader)+sizeof(MDB_txninfo)); + } + if (env->me_lfd != INVALID_HANDLE_VALUE) { +#ifdef _WIN32 + if (excl >= 0) { + /* Unlock the lockfile. Windows would have unlocked it + * after closing anyway, but not necessarily at once. + */ + UnlockFile(env->me_lfd, 0, 0, 1, 0); + } +#endif + (void) close(env->me_lfd); + } + + env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY); +} + +void ESECT +mdb_env_close(MDB_env *env) +{ + MDB_page *dp; + + if (env == NULL) + return; + + VGMEMP_DESTROY(env); + while ((dp = env->me_dpages) != NULL) { + VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next)); + env->me_dpages = dp->mp_next; + free(dp); + } + + mdb_env_close0(env, 0); + free(env); +} + +/** Compare two items pointing at aligned size_t's */ +static int +mdb_cmp_long(const MDB_val *a, const MDB_val *b) +{ + return (*(size_t *)a->mv_data < *(size_t *)b->mv_data) ? -1 : + *(size_t *)a->mv_data > *(size_t *)b->mv_data; +} + +/** Compare two items pointing at aligned unsigned int's. + * + * This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp, + * but #mdb_cmp_clong() is called instead if the data type is size_t. + */ +static int +mdb_cmp_int(const MDB_val *a, const MDB_val *b) +{ + return (*(unsigned int *)a->mv_data < *(unsigned int *)b->mv_data) ? -1 : + *(unsigned int *)a->mv_data > *(unsigned int *)b->mv_data; +} + +/** Compare two items pointing at unsigned ints of unknown alignment. + * Nodes and keys are guaranteed to be 2-byte aligned. + */ +static int +mdb_cmp_cint(const MDB_val *a, const MDB_val *b) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short *u, *c; + int x; + + u = (unsigned short *) ((char *) a->mv_data + a->mv_size); + c = (unsigned short *) ((char *) b->mv_data + a->mv_size); + do { + x = *--u - *--c; + } while(!x && u > (unsigned short *)a->mv_data); + return x; +#else + unsigned short *u, *c, *end; + int x; + + end = (unsigned short *) ((char *) a->mv_data + a->mv_size); + u = (unsigned short *)a->mv_data; + c = (unsigned short *)b->mv_data; + do { + x = *u++ - *c++; + } while(!x && u < end); + return x; +#endif +} + +/** Compare two items lexically */ +static int +mdb_cmp_memn(const MDB_val *a, const MDB_val *b) +{ + int diff; + ssize_t len_diff; + unsigned int len; + + len = a->mv_size; + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + len = b->mv_size; + len_diff = 1; + } + + diff = memcmp(a->mv_data, b->mv_data, len); + return diff ? diff : len_diff<0 ? -1 : len_diff; +} + +/** Compare two items in reverse byte order */ +static int +mdb_cmp_memnr(const MDB_val *a, const MDB_val *b) +{ + const unsigned char *p1, *p2, *p1_lim; + ssize_t len_diff; + int diff; + + p1_lim = (const unsigned char *)a->mv_data; + p1 = (const unsigned char *)a->mv_data + a->mv_size; + p2 = (const unsigned char *)b->mv_data + b->mv_size; + + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + p1_lim += len_diff; + len_diff = 1; + } + + while (p1 > p1_lim) { + diff = *--p1 - *--p2; + if (diff) + return diff; + } + return len_diff<0 ? -1 : len_diff; +} + +/** Search for key within a page, using binary search. + * Returns the smallest entry larger or equal to the key. + * If exactp is non-null, stores whether the found entry was an exact match + * in *exactp (1 or 0). + * Updates the cursor index with the index of the found entry. + * If no entry larger or equal to the key is found, returns NULL. + */ +static MDB_node * +mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp) +{ + unsigned int i = 0, nkeys; + int low, high; + int rc = 0; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NULL; + MDB_val nodekey; + MDB_cmp_func *cmp; + DKBUF; + + nkeys = NUMKEYS(mp); + + DPRINTF(("searching %u keys in %s %spage %"Z"u", + nkeys, IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp))); + + low = IS_LEAF(mp) ? 0 : 1; + high = nkeys - 1; + cmp = mc->mc_dbx->md_cmp; + + /* Branch pages have no data, so if using integer keys, + * alignment is guaranteed. Use faster mdb_cmp_int. + */ + if (cmp == mdb_cmp_cint && IS_BRANCH(mp)) { + if (NODEPTR(mp, 1)->mn_ksize == sizeof(size_t)) + cmp = mdb_cmp_long; + else + cmp = mdb_cmp_int; + } + + if (IS_LEAF2(mp)) { + nodekey.mv_size = mc->mc_db->md_pad; + node = NODEPTR(mp, 0); /* fake */ + while (low <= high) { + i = (low + high) >> 1; + nodekey.mv_data = LEAF2KEY(mp, i, nodekey.mv_size); + rc = cmp(key, &nodekey); + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } else { + while (low <= high) { + i = (low + high) >> 1; + + node = NODEPTR(mp, i); + nodekey.mv_size = NODEKSZ(node); + nodekey.mv_data = NODEKEY(node); + + rc = cmp(key, &nodekey); +#if MDB_DEBUG + if (IS_LEAF(mp)) + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + else + DPRINTF(("found branch index %u [%s -> %"Z"u], rc = %i", + i, DKEY(&nodekey), NODEPGNO(node), rc)); +#endif + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } + + if (rc > 0) { /* Found entry is less than the key. */ + i++; /* Skip to get the smallest entry larger than key. */ + if (!IS_LEAF2(mp)) + node = NODEPTR(mp, i); + } + if (exactp) + *exactp = (rc == 0 && nkeys > 0); + /* store the key index */ + mc->mc_ki[mc->mc_top] = i; + if (i >= nkeys) + /* There is no entry larger or equal to the key. */ + return NULL; + + /* nodeptr is fake for LEAF2 */ + return node; +} + +#if 0 +static void +mdb_cursor_adjust(MDB_cursor *mc, func) +{ + MDB_cursor *m2; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2->mc_pg[m2->mc_top] == mc->mc_pg[mc->mc_top]) { + func(mc, m2); + } + } +} +#endif + +/** Pop a page off the top of the cursor's stack. */ +static void +mdb_cursor_pop(MDB_cursor *mc) +{ + if (mc->mc_snum) { + DPRINTF(("popping page %"Z"u off db %d cursor %p", + mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc)); + + mc->mc_snum--; + if (mc->mc_snum) { + mc->mc_top--; + } else { + mc->mc_flags &= ~C_INITIALIZED; + } + } +} + +/** Push a page onto the top of the cursor's stack. + * Set #MDB_TXN_ERROR on failure. + */ +static int +mdb_cursor_push(MDB_cursor *mc, MDB_page *mp) +{ + DPRINTF(("pushing page %"Z"u on db %d cursor %p", mp->mp_pgno, + DDBI(mc), (void *) mc)); + + if (mc->mc_snum >= CURSOR_STACK) { + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CURSOR_FULL; + } + + mc->mc_top = mc->mc_snum++; + mc->mc_pg[mc->mc_top] = mp; + mc->mc_ki[mc->mc_top] = 0; + + return MDB_SUCCESS; +} + +/** Find the address of the page corresponding to a given page number. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc the cursor accessing the page. + * @param[in] pgno the page number for the page to retrieve. + * @param[out] ret address of a pointer where the page's address will be stored. + * @param[out] lvl dirty_list inheritance level of found page. 1=current txn, 0=mapped page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl) +{ + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + MDB_page *p = NULL; + int level; + + if (! (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_WRITEMAP))) { + MDB_txn *tx2 = txn; + level = 1; + do { + MDB_ID2L dl = tx2->mt_u.dirty_list; + unsigned x; + /* Spilled pages were dirtied in this txn and flushed + * because the dirty list got full. Bring this page + * back in from the map (but don't unspill it here, + * leave that unless page_touch happens again). + */ + if (tx2->mt_spill_pgs) { + MDB_ID pn = pgno << 1; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + goto done; + } + } + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + p = dl[x].mptr; + goto done; + } + } + level++; + } while ((tx2 = tx2->mt_parent) != NULL); + } + + if (pgno < txn->mt_next_pgno) { + level = 0; + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + DPRINTF(("page %"Z"u not found", pgno)); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_NOTFOUND; + } + +done: + *ret = p; + if (lvl) + *lvl = level; + return MDB_SUCCESS; +} + +/** Finish #mdb_page_search() / #mdb_page_search_lowest(). + * The cursor is at the root page, set up the rest of it. + */ +static int +mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int rc; + DKBUF; + + while (IS_BRANCH(mp)) { + MDB_node *node; + indx_t i; + + DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp))); + /* Don't assert on branch pages in the FreeDB. We can get here + * while in the process of rebalancing a FreeDB branch page; we must + * let that proceed. ITS#8336 + */ + mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1); + DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0)))); + + if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) { + i = 0; + if (flags & MDB_PS_LAST) { + i = NUMKEYS(mp) - 1; + /* if already init'd, see if we're already in right place */ + if (mc->mc_flags & C_INITIALIZED) { + if (mc->mc_ki[mc->mc_top] == i) { + mc->mc_top = mc->mc_snum++; + mp = mc->mc_pg[mc->mc_top]; + goto ready; + } + } + } + } else { + int exact; + node = mdb_node_search(mc, key, &exact); + if (node == NULL) + i = NUMKEYS(mp) - 1; + else { + i = mc->mc_ki[mc->mc_top]; + if (!exact) { + mdb_cassert(mc, i > 0); + i--; + } + } + DPRINTF(("following index %u for key [%s]", i, DKEY(key))); + } + + mdb_cassert(mc, i < NUMKEYS(mp)); + node = NODEPTR(mp, i); + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = i; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + +ready: + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc)) != 0) + return rc; + mp = mc->mc_pg[mc->mc_top]; + } + } + + if (!IS_LEAF(mp)) { + DPRINTF(("internal error, index points to a %02X page!?", + mp->mp_flags)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + + DPRINTF(("found leaf page %"Z"u for key [%s]", mp->mp_pgno, + key ? DKEY(key) : "null")); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + return MDB_SUCCESS; +} + +/** Search for the lowest key under the current branch page. + * This just bypasses a NUMKEYS check in the current page + * before calling mdb_page_search_root(), because the callers + * are all in situations where the current page is known to + * be underfilled. + */ +static int +mdb_page_search_lowest(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NODEPTR(mp, 0); + int rc; + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = 0; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + return mdb_page_search_root(mc, NULL, MDB_PS_FIRST); +} + +/** Search for the page a given key should be in. + * Push it and its parent pages on the cursor stack. + * @param[in,out] mc the cursor for this operation. + * @param[in] key the key to search for, or NULL for first/last page. + * @param[in] flags If MDB_PS_MODIFY is set, visited pages in the DB + * are touched (updated with new page numbers). + * If MDB_PS_FIRST or MDB_PS_LAST is set, find first or last leaf. + * This is used by #mdb_cursor_first() and #mdb_cursor_last(). + * If MDB_PS_ROOTONLY set, just fetch root node, no further lookups. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags) +{ + int rc; + pgno_t root; + + /* Make sure the txn is still viable, then find the root from + * the txn's db table and set it as the root of the cursor's stack. + */ + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) { + DPUTS("transaction may not be used now"); + return MDB_BAD_TXN; + } else { + /* Make sure we're using an up-to-date root */ + if (*mc->mc_dbflag & DB_STALE) { + MDB_cursor mc2; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0); + if (rc) + return rc; + { + MDB_val data; + int exact = 0; + uint16_t flags; + MDB_node *leaf = mdb_node_search(&mc2, + &mc->mc_dbx->md_name, &exact); + if (!exact) + return MDB_NOTFOUND; + if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; /* not a named DB */ + rc = mdb_node_read(&mc2, leaf, &data); + if (rc) + return rc; + memcpy(&flags, ((char *) data.mv_data + offsetof(MDB_db, md_flags)), + sizeof(uint16_t)); + /* The txn may not know this DBI, or another process may + * have dropped and recreated the DB with other flags. + */ + if ((mc->mc_db->md_flags & PERSISTENT_FLAGS) != flags) + return MDB_INCOMPATIBLE; + memcpy(mc->mc_db, data.mv_data, sizeof(MDB_db)); + } + *mc->mc_dbflag &= ~DB_STALE; + } + root = mc->mc_db->md_root; + + if (root == P_INVALID) { /* Tree is empty. */ + DPUTS("tree is empty"); + return MDB_NOTFOUND; + } + } + + mdb_cassert(mc, root > 1); + if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root) + if ((rc = mdb_page_get(mc, root, &mc->mc_pg[0], NULL)) != 0) + return rc; + + mc->mc_snum = 1; + mc->mc_top = 0; + + DPRINTF(("db %d root page %"Z"u has flags 0x%X", + DDBI(mc), root, mc->mc_pg[0]->mp_flags)); + + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc))) + return rc; + } + + if (flags & MDB_PS_ROOTONLY) + return MDB_SUCCESS; + + return mdb_page_search_root(mc, key, flags); +} + +static int +mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp) +{ + MDB_txn *txn = mc->mc_txn; + pgno_t pg = mp->mp_pgno; + unsigned x = 0, ovpages = mp->mp_pages; + MDB_env *env = txn->mt_env; + MDB_IDL sl = txn->mt_spill_pgs; + MDB_ID pn = pg << 1; + int rc; + + DPRINTF(("free ov page %"Z"u (%d)", pg, ovpages)); + /* If the page is dirty or on the spill list we just acquired it, + * so we should give it back to our current free list, if any. + * Otherwise put it onto the list of pages we freed in this txn. + * + * Won't create me_pghead: me_pglast must be inited along with it. + * Unsupported in nested txns: They would need to hide the page + * range in ancestor txns' dirty and spilled lists. + */ + if (env->me_pghead && + !txn->mt_parent && + ((mp->mp_flags & P_DIRTY) || + (sl && (x = mdb_midl_search(sl, pn)) <= sl[0] && sl[x] == pn))) + { + unsigned i, j; + pgno_t *mop; + MDB_ID2 *dl, ix, iy; + rc = mdb_midl_need(&env->me_pghead, ovpages); + if (rc) + return rc; + if (!(mp->mp_flags & P_DIRTY)) { + /* This page is no longer spilled */ + if (x == sl[0]) + sl[0]--; + else + sl[x] |= 1; + goto release; + } + /* Remove from dirty list */ + dl = txn->mt_u.dirty_list; + x = dl[0].mid--; + for (ix = dl[x]; ix.mptr != mp; ix = iy) { + if (x > 1) { + x--; + iy = dl[x]; + dl[x] = ix; + } else { + mdb_cassert(mc, x > 1); + j = ++(dl[0].mid); + dl[j] = ix; /* Unsorted. OK when MDB_TXN_ERROR. */ + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + } + txn->mt_dirty_room++; + if (!(env->me_flags & MDB_WRITEMAP)) + mdb_dpage_free(env, mp); +release: + /* Insert in me_pghead */ + mop = env->me_pghead; + j = mop[0] + ovpages; + for (i = mop[0]; i && mop[i] < pg; i--) + mop[j--] = mop[i]; + while (j>i) + mop[j--] = pg++; + mop[0] += ovpages; + } else { + rc = mdb_midl_append_range(&txn->mt_free_pgs, pg, ovpages); + if (rc) + return rc; + } + mc->mc_db->md_overflow_pages -= ovpages; + return 0; +} + +/** Return the data associated with a given node. + * @param[in] mc The cursor for this operation. + * @param[in] leaf The node being read. + * @param[out] data Updated to point to the node's data. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data) +{ + MDB_page *omp; /* overflow page */ + pgno_t pgno; + int rc; + + if (!F_ISSET(leaf->mn_flags, F_BIGDATA)) { + data->mv_size = NODEDSZ(leaf); + data->mv_data = NODEDATA(leaf); + return MDB_SUCCESS; + } + + /* Read overflow data. + */ + data->mv_size = NODEDSZ(leaf); + memcpy(&pgno, NODEDATA(leaf), sizeof(pgno)); + if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) { + DPRINTF(("read overflow page %"Z"u failed", pgno)); + return rc; + } + data->mv_data = METADATA(omp); + + return MDB_SUCCESS; +} + +int +mdb_get(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + MDB_cursor mc; + MDB_xcursor mx; + int exact = 0; + DKBUF; + + DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key))); + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + return mdb_cursor_set(&mc, key, data, MDB_SET, &exact); +} + +/** Find a sibling for a page. + * Replaces the page at the top of the cursor's stack with the + * specified sibling, if one exists. + * @param[in] mc The cursor for this operation. + * @param[in] move_right Non-zero if the right sibling is requested, + * otherwise the left sibling. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_cursor_sibling(MDB_cursor *mc, int move_right) +{ + int rc; + MDB_node *indx; + MDB_page *mp; + + if (mc->mc_snum < 2) { + return MDB_NOTFOUND; /* root has no siblings */ + } + + mdb_cursor_pop(mc); + DPRINTF(("parent page is page %"Z"u, index %u", + mc->mc_pg[mc->mc_top]->mp_pgno, mc->mc_ki[mc->mc_top])); + + if (move_right ? (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mc->mc_pg[mc->mc_top])) + : (mc->mc_ki[mc->mc_top] == 0)) { + DPRINTF(("no more keys left, moving to %s sibling", + move_right ? "right" : "left")); + if ((rc = mdb_cursor_sibling(mc, move_right)) != MDB_SUCCESS) { + /* undo cursor_pop before returning */ + mc->mc_top++; + mc->mc_snum++; + return rc; + } + } else { + if (move_right) + mc->mc_ki[mc->mc_top]++; + else + mc->mc_ki[mc->mc_top]--; + DPRINTF(("just moving to %s index key %u", + move_right ? "right" : "left", mc->mc_ki[mc->mc_top])); + } + mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top])); + + indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if ((rc = mdb_page_get(mc, NODEPGNO(indx), &mp, NULL)) != 0) { + /* mc will be inconsistent if caller does mc_snum++ as above */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + return rc; + } + + mdb_cursor_push(mc, mp); + if (!move_right) + mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1; + + return MDB_SUCCESS; +} + +/** Move the cursor to the next data item. */ +static int +mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if ((mc->mc_flags & C_DEL && op == MDB_NEXT_DUP)) + return MDB_NOTFOUND; + + if (!(mc->mc_flags & C_INITIALIZED)) + return mdb_cursor_first(mc, key, data); + + mp = mc->mc_pg[mc->mc_top]; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mp)-1) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + if (mc->mc_db->md_flags & MDB_DUPSORT) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_NEXT || op == MDB_NEXT_DUP) { + rc = mdb_cursor_next(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_NEXT); + if (op != MDB_NEXT || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) + MDB_GET_KEY(leaf, key); + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_NEXT_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_next: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + if (mc->mc_flags & C_DEL) { + mc->mc_flags ^= C_DEL; + goto skip; + } + + if (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mp)) { + DPUTS("=====> move to next sibling page"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + DPRINTF(("next page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]++; + +skip: + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the previous data item. */ +static int +mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = mdb_cursor_last(mc, key, data); + if (rc) + return rc; + mc->mc_ki[mc->mc_top]++; + } + + mp = mc->mc_pg[mc->mc_top]; + + if ((mc->mc_db->md_flags & MDB_DUPSORT) && + mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_PREV || op == MDB_PREV_DUP) { + rc = mdb_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_PREV); + if (op != MDB_PREV || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) { + MDB_GET_KEY(leaf, key); + mc->mc_flags &= ~C_EOF; + } + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_PREV_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_prev: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + + mc->mc_flags &= ~(C_EOF|C_DEL); + + if (mc->mc_ki[mc->mc_top] == 0) { + DPUTS("=====> move to prev sibling page"); + if ((rc = mdb_cursor_sibling(mc, 0)) != MDB_SUCCESS) { + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; + DPRINTF(("prev page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]--; + + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (!IS_LEAF(mp)) + return MDB_CORRUPTED; + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Set the cursor on a specific data item. */ +static int +mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op, int *exactp) +{ + int rc; + MDB_page *mp; + MDB_node *leaf = NULL; + DKBUF; + + if (key->mv_size == 0) + return MDB_BAD_VALSIZE; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + /* See if we're already on the right page */ + if (mc->mc_flags & C_INITIALIZED) { + MDB_val nodekey; + + mp = mc->mc_pg[mc->mc_top]; + if (!NUMKEYS(mp)) { + mc->mc_ki[mc->mc_top] = 0; + return MDB_NOTFOUND; + } + if (MP_FLAGS(mp) & P_LEAF2) { + nodekey.mv_size = mc->mc_db->md_pad; + nodekey.mv_data = LEAF2KEY(mp, 0, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, 0); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* Probably happens rarely, but first node on the page + * was the one we wanted. + */ + mc->mc_ki[mc->mc_top] = 0; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc > 0) { + unsigned int i; + unsigned int nkeys = NUMKEYS(mp); + if (nkeys > 1) { + if (MP_FLAGS(mp) & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + nkeys-1, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, nkeys-1); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* last node was the one we wanted */ + mc->mc_ki[mc->mc_top] = nkeys-1; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc < 0) { + if (mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) { + /* This is definitely the right page, skip search_page */ + if (MP_FLAGS(mp) & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + mc->mc_ki[mc->mc_top], nodekey.mv_size); + } else { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* current node was the one we wanted */ + if (exactp) + *exactp = 1; + goto set1; + } + } + rc = 0; + mc->mc_flags &= ~C_EOF; + goto set2; + } + } + /* If any parents have right-sibs, search. + * Otherwise, there's nothing further. + */ + for (i=0; i<mc->mc_top; i++) + if (mc->mc_ki[i] < + NUMKEYS(mc->mc_pg[i])-1) + break; + if (i == mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = nkeys; + return MDB_NOTFOUND; + } + } + if (!mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = 0; + if (op == MDB_SET_RANGE && !exactp) { + rc = 0; + goto set1; + } else + return MDB_NOTFOUND; + } + } else { + mc->mc_pg[0] = 0; + } + + rc = mdb_page_search(mc, key, 0); + if (rc != MDB_SUCCESS) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + +set2: + leaf = mdb_node_search(mc, key, exactp); + if (exactp != NULL && !*exactp) { + /* MDB_SET specified and not an exact match. */ + return MDB_NOTFOUND; + } + + if (leaf == NULL) { + DPUTS("===> inexact leaf not found, goto sibling"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; /* no entries matched */ + } + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, 0); + } + +set1: + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + if (IS_LEAF2(mp)) { + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + if (op == MDB_SET || op == MDB_SET_KEY || op == MDB_SET_RANGE) { + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + } else { + int ex2, *ex2p; + if (op == MDB_GET_BOTH) { + ex2p = &ex2; + ex2 = 0; + } else { + ex2p = NULL; + } + rc = mdb_cursor_set(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_SET_RANGE, ex2p); + if (rc != MDB_SUCCESS) + return rc; + } + } else if (data) { + if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) { + MDB_val olddata; + MDB_cmp_func *dcmp; + if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS) + return rc; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + rc = dcmp(data, &olddata); + if (rc) { + if (op == MDB_GET_BOTH || rc > 0) + return MDB_NOTFOUND; + rc = 0; + } + *data = olddata; + + } else { + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + } + + /* The key already matches in all other cases */ + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) + MDB_GET_KEY(leaf, key); + DPRINTF(("==> cursor placed on key [%s]", DKEY(key))); + + return rc; +} + +/** Move the cursor to the first item in the database. */ +static int +mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + mc->mc_ki[mc->mc_top] = 0; + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + if ( key ) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], 0, key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the last item in the database. */ +static int +mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_LAST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1; + mc->mc_flags |= C_INITIALIZED|C_EOF; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + if (key) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +int +mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op) +{ + int rc; + int exact = 0; + int (*mfunc)(MDB_cursor *mc, MDB_val *key, MDB_val *data); + + if (mc == NULL) + return EINVAL; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + switch (op) { + case MDB_GET_CURRENT: + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + } else { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int nkeys = NUMKEYS(mp); + if (!nkeys || mc->mc_ki[mc->mc_top] >= nkeys) { + mc->mc_ki[mc->mc_top] = nkeys; + rc = MDB_NOTFOUND; + break; + } + rc = MDB_SUCCESS; + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } else { + MDB_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY(leaf, key); + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + rc = mdb_cursor_get(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_GET_CURRENT); + } else { + rc = mdb_node_read(mc, leaf, data); + } + } + } + } + break; + case MDB_GET_BOTH: + case MDB_GET_BOTH_RANGE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + /* FALLTHRU */ + case MDB_SET: + case MDB_SET_KEY: + case MDB_SET_RANGE: + if (key == NULL) { + rc = EINVAL; + } else { + rc = mdb_cursor_set(mc, key, data, op, + op == MDB_SET_RANGE ? NULL : &exact); + } + break; + case MDB_GET_MULTIPLE: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = MDB_SUCCESS; + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) || + (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF)) + break; + goto fetchm; + case MDB_NEXT_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP); + if (rc == MDB_SUCCESS) { + if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + MDB_cursor *mx; +fetchm: + mx = &mc->mc_xcursor->mx_cursor; + data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) * + mx->mc_db->md_pad; + data->mv_data = METADATA(mx->mc_pg[mx->mc_top]); + mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_PREV_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + if (!(mc->mc_flags & C_INITIALIZED)) + rc = mdb_cursor_last(mc, key, data); + else + rc = MDB_SUCCESS; + if (rc == MDB_SUCCESS) { + MDB_cursor *mx = &mc->mc_xcursor->mx_cursor; + if (mx->mc_flags & C_INITIALIZED) { + rc = mdb_cursor_sibling(mx, 0); + if (rc == MDB_SUCCESS) + goto fetchm; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_NEXT: + case MDB_NEXT_DUP: + case MDB_NEXT_NODUP: + rc = mdb_cursor_next(mc, key, data, op); + break; + case MDB_PREV: + case MDB_PREV_DUP: + case MDB_PREV_NODUP: + rc = mdb_cursor_prev(mc, key, data, op); + break; + case MDB_FIRST: + rc = mdb_cursor_first(mc, key, data); + break; + case MDB_FIRST_DUP: + mfunc = mdb_cursor_first; + mmove: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) { + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + rc = MDB_NOTFOUND; + break; + } + mc->mc_flags &= ~C_EOF; + { + MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_GET_KEY(leaf, key); + rc = mdb_node_read(mc, leaf, data); + break; + } + } + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + rc = mfunc(&mc->mc_xcursor->mx_cursor, data, NULL); + break; + case MDB_LAST: + rc = mdb_cursor_last(mc, key, data); + break; + case MDB_LAST_DUP: + mfunc = mdb_cursor_last; + goto mmove; + default: + DPRINTF(("unhandled/unimplemented cursor operation %u", op)); + rc = EINVAL; + break; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + return rc; +} + +/** Touch all the pages in the cursor stack. Set mc_top. + * Makes sure all the pages are writable, before attempting a write operation. + * @param[in] mc The cursor to operate on. + */ +static int +mdb_cursor_touch(MDB_cursor *mc) +{ + int rc = MDB_SUCCESS; + + if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) { + /* Touch DB record of named DB */ + MDB_cursor mc2; + MDB_xcursor mcx; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY); + if (rc) + return rc; + *mc->mc_dbflag |= DB_DIRTY; + } + mc->mc_top = 0; + if (mc->mc_snum) { + do { + rc = mdb_page_touch(mc); + } while (!rc && ++(mc->mc_top) < mc->mc_snum); + mc->mc_top = mc->mc_snum-1; + } + return rc; +} + +/** Do not spill pages to disk if txn is getting full, may fail instead */ +#define MDB_NOSPILL 0x8000 + +int +mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data, + unsigned int flags) +{ + MDB_env *env; + MDB_node *leaf = NULL; + MDB_page *fp, *mp, *sub_root = NULL; + uint16_t fp_flags; + MDB_val xdata, *rdata, dkey, olddata; + MDB_db dummy; + int do_sub = 0, insert_key, insert_data; + unsigned int mcount = 0, dcount = 0, nospill; + size_t nsize; + int rc, rc2; + unsigned int nflags; + DKBUF; + + if (mc == NULL || key == NULL) + return EINVAL; + + env = mc->mc_txn->mt_env; + + /* Check this first so counter will always be zero on any + * early failures. + */ + if (flags & MDB_MULTIPLE) { + dcount = data[1].mv_size; + data[1].mv_size = 0; + if (!F_ISSET(mc->mc_db->md_flags, MDB_DUPFIXED)) + return MDB_INCOMPATIBLE; + } + + nospill = flags & MDB_NOSPILL; + flags &= ~MDB_NOSPILL; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (key->mv_size-1 >= ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; + +#if SIZE_MAX > MAXDATASIZE + if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE)) + return MDB_BAD_VALSIZE; +#else + if ((mc->mc_db->md_flags & MDB_DUPSORT) && data->mv_size > ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; +#endif + + DPRINTF(("==> put db %d key [%s], size %"Z"u, data size %"Z"u", + DDBI(mc), DKEY(key), key ? key->mv_size : 0, data->mv_size)); + + dkey.mv_size = 0; + + if (flags & MDB_CURRENT) { + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + rc = MDB_SUCCESS; + } else if (mc->mc_db->md_root == P_INVALID) { + /* new database, cursor has nothing to point to */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + rc = MDB_NO_ROOT; + } else { + int exact = 0; + MDB_val d2; + if (flags & MDB_APPEND) { + MDB_val k2; + rc = mdb_cursor_last(mc, &k2, &d2); + if (rc == 0) { + rc = mc->mc_dbx->md_cmp(key, &k2); + if (rc > 0) { + rc = MDB_NOTFOUND; + mc->mc_ki[mc->mc_top]++; + } else { + /* new key is <= last key */ + rc = MDB_KEYEXIST; + } + } + } else { + rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact); + } + if ((flags & MDB_NOOVERWRITE) && rc == 0) { + DPRINTF(("duplicate key [%s]", DKEY(key))); + *data = d2; + return MDB_KEYEXIST; + } + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + /* Cursor is positioned, check for room in the dirty list */ + if (!nospill) { + if (flags & MDB_MULTIPLE) { + rdata = &xdata; + xdata.mv_size = data->mv_size * dcount; + } else { + rdata = data; + } + if ((rc2 = mdb_page_spill(mc, key, rdata))) + return rc2; + } + + if (rc == MDB_NO_ROOT) { + MDB_page *np; + /* new database, write a root leaf page */ + DPUTS("allocating new root leaf page"); + if ((rc2 = mdb_page_new(mc, P_LEAF, 1, &np))) { + return rc2; + } + mdb_cursor_push(mc, np); + mc->mc_db->md_root = np->mp_pgno; + mc->mc_db->md_depth++; + *mc->mc_dbflag |= DB_DIRTY; + if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED)) + == MDB_DUPFIXED) + MP_FLAGS(np) |= P_LEAF2; + mc->mc_flags |= C_INITIALIZED; + } else { + /* make sure all cursor pages are writable */ + rc2 = mdb_cursor_touch(mc); + if (rc2) + return rc2; + } + + insert_key = insert_data = rc; + if (insert_key) { + /* The key does not exist */ + DPRINTF(("inserting key at index %i", mc->mc_ki[mc->mc_top])); + if ((mc->mc_db->md_flags & MDB_DUPSORT) && + LEAFSIZE(key, data) > env->me_nodemax) + { + /* Too big for a node, insert in sub-DB. Set up an empty + * "old sub-page" for prep_subDB to expand to a full page. + */ + fp_flags = P_LEAF|P_DIRTY; + fp = env->me_pbuf; + fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */ + MP_LOWER(fp) = MP_UPPER(fp) = (PAGEHDRSZ-PAGEBASE); + olddata.mv_size = PAGEHDRSZ; + goto prep_subDB; + } + } else { + /* there's only a key anyway, so this is a no-op */ + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + char *ptr; + unsigned int ksize = mc->mc_db->md_pad; + if (key->mv_size != ksize) + return MDB_BAD_VALSIZE; + ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize); + memcpy(ptr, key->mv_data, ksize); +fix_parent: + /* if overwriting slot 0 of leaf, need to + * update branch key if there is a parent page + */ + if (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + unsigned short dtop = 1; + mc->mc_top--; + /* slot 0 is always an empty key, find real slot */ + while (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + mc->mc_top--; + dtop++; + } + if (mc->mc_ki[mc->mc_top]) + rc2 = mdb_update_key(mc, key); + else + rc2 = MDB_SUCCESS; + mc->mc_top += dtop; + if (rc2) + return rc2; + } + return MDB_SUCCESS; + } + +more: + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + olddata.mv_size = NODEDSZ(leaf); + olddata.mv_data = NODEDATA(leaf); + + /* DB has dups? */ + if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) { + /* Prepare (sub-)page/sub-DB to accept the new item, + * if needed. fp: old sub-page or a header faking + * it. mp: new (sub-)page. offset: growth in page + * size. xdata: node data with new page or DB. + */ + unsigned i, offset = 0; + mp = fp = xdata.mv_data = env->me_pbuf; + mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno; + + /* Was a single item before, must convert now */ + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_cmp_func *dcmp; + /* Just overwrite the current item */ + if (flags == MDB_CURRENT) + goto current; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + /* does data match? */ + if (!dcmp(data, &olddata)) { + if (flags & (MDB_NODUPDATA|MDB_APPENDDUP)) + return MDB_KEYEXIST; + /* overwrite it */ + goto current; + } + + /* Back up original data item */ + dkey.mv_size = olddata.mv_size; + dkey.mv_data = memcpy(fp+1, olddata.mv_data, olddata.mv_size); + + /* Make sub-page header for the dup items, with dummy body */ + MP_FLAGS(fp) = P_LEAF|P_DIRTY|P_SUBP; + MP_LOWER(fp) = (PAGEHDRSZ-PAGEBASE); + xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + MP_FLAGS(fp) |= P_LEAF2; + fp->mp_pad = data->mv_size; + xdata.mv_size += 2 * data->mv_size; /* leave space for 2 more */ + } else { + xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) + + (dkey.mv_size & 1) + (data->mv_size & 1); + } + MP_UPPER(fp) = xdata.mv_size - PAGEBASE; + olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */ + } else if (leaf->mn_flags & F_SUBDATA) { + /* Data is on sub-DB, just store it */ + flags |= F_DUPDATA|F_SUBDATA; + goto put_sub; + } else { + /* Data is on sub-page */ + fp = olddata.mv_data; + switch (flags) { + default: + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + offset = EVEN(NODESIZE + sizeof(indx_t) + + data->mv_size); + break; + } + offset = fp->mp_pad; + if (SIZELEFT(fp) < offset) { + offset *= 4; /* space for 4 more */ + break; + } + /* FALLTHRU */ /* Big enough MDB_DUPFIXED sub-page */ + case MDB_CURRENT: + MP_FLAGS(fp) |= P_DIRTY; + COPY_PGNO(MP_PGNO(fp), MP_PGNO(mp)); + mc->mc_xcursor->mx_cursor.mc_pg[0] = fp; + flags |= F_DUPDATA; + goto put_sub; + } + xdata.mv_size = olddata.mv_size + offset; + } + + fp_flags = MP_FLAGS(fp); + if (NODESIZE + NODEKSZ(leaf) + xdata.mv_size > env->me_nodemax) { + /* Too big for a sub-page, convert to sub-DB */ + fp_flags &= ~P_SUBP; +prep_subDB: + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + fp_flags |= P_LEAF2; + dummy.md_pad = fp->mp_pad; + dummy.md_flags = MDB_DUPFIXED; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + dummy.md_flags |= MDB_INTEGERKEY; + } else { + dummy.md_pad = 0; + dummy.md_flags = 0; + } + dummy.md_depth = 1; + dummy.md_branch_pages = 0; + dummy.md_leaf_pages = 1; + dummy.md_overflow_pages = 0; + dummy.md_entries = NUMKEYS(fp); + xdata.mv_size = sizeof(MDB_db); + xdata.mv_data = &dummy; + if ((rc = mdb_page_alloc(mc, 1, &mp))) + return rc; + offset = env->me_psize - olddata.mv_size; + flags |= F_DUPDATA|F_SUBDATA; + dummy.md_root = mp->mp_pgno; + sub_root = mp; + } + if (mp != fp) { + MP_FLAGS(mp) = fp_flags | P_DIRTY; + MP_PAD(mp) = MP_PAD(fp); + MP_LOWER(mp) = MP_LOWER(fp); + MP_UPPER(mp) = MP_UPPER(fp) + offset; + if (fp_flags & P_LEAF2) { + memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad); + } else { + memcpy((char *)mp + MP_UPPER(mp) + PAGEBASE, (char *)fp + MP_UPPER(fp) + PAGEBASE, + olddata.mv_size - MP_UPPER(fp) - PAGEBASE); + memcpy((char *)MP_PTRS(mp), (char *)MP_PTRS(fp), NUMKEYS(fp) * sizeof(mp->mp_ptrs[0])); + for (i=0; i<NUMKEYS(fp); i++) + mp->mp_ptrs[i] += offset; + } + } + + rdata = &xdata; + flags |= F_DUPDATA; + do_sub = 1; + if (!insert_key) + mdb_node_del(mc, 0); + goto new_sub; + } +current: + /* LMDB passes F_SUBDATA in 'flags' to write a DB record */ + if ((leaf->mn_flags ^ flags) & F_SUBDATA) + return MDB_INCOMPATIBLE; + /* overflow page overwrites need special handling */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + int level, ovpages, dpages = OVPAGES(data->mv_size, env->me_psize); + + memcpy(&pg, olddata.mv_data, sizeof(pg)); + if ((rc2 = mdb_page_get(mc, pg, &omp, &level)) != 0) + return rc2; + ovpages = omp->mp_pages; + + /* Is the ov page large enough? */ + if (ovpages >= dpages) { + if (!(omp->mp_flags & P_DIRTY) && + (level || (env->me_flags & MDB_WRITEMAP))) + { + rc = mdb_page_unspill(mc->mc_txn, omp, &omp); + if (rc) + return rc; + level = 0; /* dirty in this txn or clean */ + } + /* Is it dirty? */ + if (omp->mp_flags & P_DIRTY) { + /* yes, overwrite it. Note in this case we don't + * bother to try shrinking the page if the new data + * is smaller than the overflow threshold. + */ + if (level > 1) { + /* It is writable only in a parent txn */ + size_t sz = (size_t) env->me_psize * ovpages, off; + MDB_page *np = mdb_page_malloc(mc->mc_txn, ovpages); + MDB_ID2 id2; + if (!np) + return ENOMEM; + id2.mid = pg; + id2.mptr = np; + /* Note - this page is already counted in parent's dirty_room */ + rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2); + mdb_cassert(mc, rc2 == 0); + /* Currently we make the page look as with put() in the + * parent txn, in case the user peeks at MDB_RESERVEd + * or unused parts. Some users treat ovpages specially. + */ + if (!(flags & MDB_RESERVE)) { + /* Skip the part where LMDB will put *data. + * Copy end of page, adjusting alignment so + * compiler may copy words instead of bytes. + */ + off = (PAGEHDRSZ + data->mv_size) & -(int)sizeof(size_t); + memcpy((size_t *)((char *)np + off), + (size_t *)((char *)omp + off), sz - off); + sz = PAGEHDRSZ; + } + memcpy(np, omp, sz); /* Copy beginning of page */ + omp = np; + } + SETDSZ(leaf, data->mv_size); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = METADATA(omp); + else + memcpy(METADATA(omp), data->mv_data, data->mv_size); + return MDB_SUCCESS; + } + } + if ((rc2 = mdb_ovpage_free(mc, omp)) != MDB_SUCCESS) + return rc2; + } else if (data->mv_size == olddata.mv_size) { + /* same size, just replace it. Note that we could + * also reuse this node if the new data is smaller, + * but instead we opt to shrink the node in that case. + */ + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = olddata.mv_data; + else if (!(mc->mc_flags & C_SUB)) + memcpy(olddata.mv_data, data->mv_data, data->mv_size); + else { + if (key->mv_size != NODEKSZ(leaf)) + goto new_ksize; + memcpy(NODEKEY(leaf), key->mv_data, key->mv_size); + goto fix_parent; + } + return MDB_SUCCESS; + } +new_ksize: + mdb_node_del(mc, 0); + } + + rdata = data; + +new_sub: + nflags = flags & NODE_ADD_FLAGS; + nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(env, key, rdata); + if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) { + if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA ) + nflags &= ~MDB_APPEND; /* sub-page may need room to grow */ + if (!insert_key) + nflags |= MDB_SPLIT_REPLACE; + rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags); + } else { + /* There is room already in this leaf page. */ + rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags); + if (rc == 0) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue; + if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) { + m3->mc_ki[i]++; + } + XCURSOR_REFRESH(m3, i, mp); + } + } + } + + if (rc == MDB_SUCCESS) { + /* Now store the actual data in the child DB. Note that we're + * storing the user data in the keys field, so there are strict + * size limits on dupdata. The actual data fields of the child + * DB are all zero size. + */ + if (do_sub) { + int xflags, new_dupdata; + size_t ecount; +put_sub: + xdata.mv_size = 0; + xdata.mv_data = ""; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if ((flags & (MDB_CURRENT|MDB_APPENDDUP)) == MDB_CURRENT) { + xflags = MDB_CURRENT|MDB_NOSPILL; + } else { + mdb_xcursor_init1(mc, leaf); + xflags = (flags & MDB_NODUPDATA) ? + MDB_NOOVERWRITE|MDB_NOSPILL : MDB_NOSPILL; + } + if (sub_root) + mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root; + new_dupdata = (int)dkey.mv_size; + /* converted, write the original data first */ + if (dkey.mv_size) { + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags); + if (rc) + goto bad_sub; + /* we've done our job */ + dkey.mv_size = 0; + } + if (!(leaf->mn_flags & F_SUBDATA) || sub_root) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2; + MDB_xcursor *mx = mc->mc_xcursor; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[i] == mp) { + if (m2->mc_ki[i] == mc->mc_ki[i]) { + mdb_xcursor_init2(m2, mx, new_dupdata); + } else if (!insert_key) { + XCURSOR_REFRESH(m2, i, mp); + } + } + } + } + ecount = mc->mc_xcursor->mx_db.md_entries; + if (flags & MDB_APPENDDUP) + xflags |= MDB_APPEND; + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags); + if (flags & F_SUBDATA) { + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } + insert_data = mc->mc_xcursor->mx_db.md_entries - ecount; + } + /* Increment count unless we just replaced an existing item. */ + if (insert_data) + mc->mc_db->md_entries++; + if (insert_key) { + /* Invalidate txn if we created an empty sub-DB */ + if (rc) + goto bad_sub; + /* If we succeeded and the key didn't exist before, + * make sure the cursor is marked valid. + */ + mc->mc_flags |= C_INITIALIZED; + } + if (flags & MDB_MULTIPLE) { + if (!rc) { + mcount++; + /* let caller know how many succeeded, if any */ + data[1].mv_size = mcount; + if (mcount < dcount) { + data[0].mv_data = (char *)data[0].mv_data + data[0].mv_size; + insert_key = insert_data = 0; + goto more; + } + } + } + return rc; +bad_sub: + if (rc == MDB_KEYEXIST) /* should not happen, we deleted that item */ + rc = MDB_CORRUPTED; + } + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_cursor_del(MDB_cursor *mc, unsigned int flags) +{ + MDB_node *leaf; + MDB_page *mp; + int rc; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + + if (!(flags & MDB_NOSPILL) && (rc = mdb_page_spill(mc, NULL, NULL))) + return rc; + + rc = mdb_cursor_touch(mc); + if (rc) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + if (!IS_LEAF(mp)) + return MDB_CORRUPTED; + if (IS_LEAF2(mp)) + goto del_key; + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (flags & MDB_NODUPDATA) { + /* mdb_cursor_del0() will subtract the final entry */ + mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1; + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } else { + if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) { + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + } + rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, MDB_NOSPILL); + if (rc) + return rc; + /* If sub-DB still has entries, we're done */ + if (mc->mc_xcursor->mx_db.md_entries) { + if (leaf->mn_flags & F_SUBDATA) { + /* update subDB info */ + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } else { + MDB_cursor *m2; + /* shrink fake page */ + mdb_node_shrink(mp, mc->mc_ki[mc->mc_top]); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + /* fix other sub-DB cursors pointed at fake pages on this page */ + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + XCURSOR_REFRESH(m2, mc->mc_top, mp); + } + } + } + mc->mc_db->md_entries--; + return rc; + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } + /* otherwise fall thru and delete the sub-DB */ + } + + if (leaf->mn_flags & F_SUBDATA) { + /* add all the child DB's pages to the free list */ + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto fail; + } + } + /* LMDB passes F_SUBDATA in 'flags' to delete a DB record */ + else if ((leaf->mn_flags ^ flags) & F_SUBDATA) { + rc = MDB_INCOMPATIBLE; + goto fail; + } + + /* add overflow pages to free list */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + + memcpy(&pg, NODEDATA(leaf), sizeof(pg)); + if ((rc = mdb_page_get(mc, pg, &omp, NULL)) || + (rc = mdb_ovpage_free(mc, omp))) + goto fail; + } + +del_key: + return mdb_cursor_del0(mc); + +fail: + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Allocate and initialize new pages for a database. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc a cursor on the database being added to. + * @param[in] flags flags defining what type of page is being allocated. + * @param[in] num the number of pages to allocate. This is usually 1, + * unless allocating overflow pages for a large record. + * @param[out] mp Address of a page, or NULL on failure. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp) +{ + MDB_page *np; + int rc; + + if ((rc = mdb_page_alloc(mc, num, &np))) + return rc; + DPRINTF(("allocated new mpage %"Z"u, page size %u", + np->mp_pgno, mc->mc_txn->mt_env->me_psize)); + np->mp_flags = flags | P_DIRTY; + np->mp_lower = (PAGEHDRSZ-PAGEBASE); + np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE; + + if (IS_BRANCH(np)) + mc->mc_db->md_branch_pages++; + else if (IS_LEAF(np)) + mc->mc_db->md_leaf_pages++; + else if (IS_OVERFLOW(np)) { + mc->mc_db->md_overflow_pages += num; + np->mp_pages = num; + } + *mp = np; + + return 0; +} + +/** Calculate the size of a leaf node. + * The size depends on the environment's page size; if a data item + * is too large it will be put onto an overflow page and the node + * size will only include the key and not the data. Sizes are always + * rounded up to an even number of bytes, to guarantee 2-byte alignment + * of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @param[in] data The data for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data) +{ + size_t sz; + + sz = LEAFSIZE(key, data); + if (sz > env->me_nodemax) { + /* put on overflow page */ + sz -= data->mv_size - sizeof(pgno_t); + } + + return EVEN(sz + sizeof(indx_t)); +} + +/** Calculate the size of a branch node. + * The size should depend on the environment's page size but since + * we currently don't support spilling large keys onto overflow + * pages, it's simply the size of the #MDB_node header plus the + * size of the key. Sizes are always rounded up to an even number + * of bytes, to guarantee 2-byte alignment of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_branch_size(MDB_env *env, MDB_val *key) +{ + size_t sz; + + sz = INDXSIZE(key); + if (sz > env->me_nodemax) { + /* put on overflow page */ + /* not implemented */ + /* sz -= key->size - sizeof(pgno_t); */ + } + + return sz + sizeof(indx_t); +} + +/** Add a node to the page pointed to by the cursor. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc The cursor for this operation. + * @param[in] indx The index on the page where the new node should be added. + * @param[in] key The key for the new node. + * @param[in] data The data for the new node, if any. + * @param[in] pgno The page number, if adding a branch node. + * @param[in] flags Flags for the node. + * @return 0 on success, non-zero on failure. Possible errors are: + * <ul> + * <li>ENOMEM - failed to allocate overflow pages for the node. + * <li>MDB_PAGE_FULL - there is insufficient room in the page. This error + * should never happen since all callers already calculate the + * page's free space before calling this function. + * </ul> + */ +static int +mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags) +{ + unsigned int i; + size_t node_size = NODESIZE; + ssize_t room; + indx_t ofs; + MDB_node *node; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_page *ofp = NULL; /* overflow page */ + void *ndata; + DKBUF; + + mdb_cassert(mc, MP_UPPER(mp) >= MP_LOWER(mp)); + + DPRINTF(("add to %s %spage %"Z"u index %i, data size %"Z"u key size %"Z"u [%s]", + IS_LEAF(mp) ? "leaf" : "branch", + IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp), indx, data ? data->mv_size : 0, + key ? key->mv_size : 0, key ? DKEY(key) : "null")); + + if (IS_LEAF2(mp)) { + /* Move higher keys up one slot. */ + int ksize = mc->mc_db->md_pad, dif; + char *ptr = LEAF2KEY(mp, indx, ksize); + dif = NUMKEYS(mp) - indx; + if (dif > 0) + memmove(ptr+ksize, ptr, dif*ksize); + /* insert new key */ + memcpy(ptr, key->mv_data, ksize); + + /* Just using these for counting */ + MP_LOWER(mp) += sizeof(indx_t); + MP_UPPER(mp) -= ksize - sizeof(indx_t); + return MDB_SUCCESS; + } + + room = (ssize_t)SIZELEFT(mp) - (ssize_t)sizeof(indx_t); + if (key != NULL) + node_size += key->mv_size; + if (IS_LEAF(mp)) { + mdb_cassert(mc, key && data); + if (F_ISSET(flags, F_BIGDATA)) { + /* Data already on overflow page. */ + node_size += sizeof(pgno_t); + } else if (node_size + data->mv_size > mc->mc_txn->mt_env->me_nodemax) { + int ovpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize); + int rc; + /* Put data on overflow page. */ + DPRINTF(("data size is %"Z"u, node would be %"Z"u, put data on overflow page", + data->mv_size, node_size+data->mv_size)); + node_size = EVEN(node_size + sizeof(pgno_t)); + if ((ssize_t)node_size > room) + goto full; + if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp))) + return rc; + DPRINTF(("allocated overflow page %"Z"u", ofp->mp_pgno)); + flags |= F_BIGDATA; + goto update; + } else { + node_size += data->mv_size; + } + } + node_size = EVEN(node_size); + if ((ssize_t)node_size > room) + goto full; + +update: + /* Move higher pointers up one slot. */ + for (i = NUMKEYS(mp); i > indx; i--) + MP_PTRS(mp)[i] = MP_PTRS(mp)[i - 1]; + + /* Adjust free space offsets. */ + ofs = MP_UPPER(mp) - node_size; + mdb_cassert(mc, ofs >= MP_LOWER(mp) + sizeof(indx_t)); + MP_PTRS(mp)[indx] = ofs; + MP_UPPER(mp) = ofs; + MP_LOWER(mp) += sizeof(indx_t); + + /* Write the node data. */ + node = NODEPTR(mp, indx); + node->mn_ksize = (key == NULL) ? 0 : key->mv_size; + node->mn_flags = flags; + if (IS_LEAF(mp)) + SETDSZ(node,data->mv_size); + else + SETPGNO(node,pgno); + + if (key) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + if (IS_LEAF(mp)) { + ndata = NODEDATA(node); + if (ofp == NULL) { + if (F_ISSET(flags, F_BIGDATA)) + memcpy(ndata, data->mv_data, sizeof(pgno_t)); + else if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } else { + memcpy(ndata, &ofp->mp_pgno, sizeof(pgno_t)); + ndata = METADATA(ofp); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } + } + + return MDB_SUCCESS; + +full: + DPRINTF(("not enough room in page %"Z"u, got %u ptrs", + mdb_dbg_pgno(mp), NUMKEYS(mp))); + DPRINTF(("upper-lower = %u - %u = %"Z"d", MP_UPPER(mp),MP_LOWER(mp),room)); + DPRINTF(("node size = %"Z"u", node_size)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_FULL; +} + +/** Delete the specified node from a page. + * @param[in] mc Cursor pointing to the node to delete. + * @param[in] ksize The size of a node. Only used if the page is + * part of a #MDB_DUPFIXED database. + */ +static void +mdb_node_del(MDB_cursor *mc, int ksize) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + indx_t indx = mc->mc_ki[mc->mc_top]; + unsigned int sz; + indx_t i, j, numkeys, ptr; + MDB_node *node; + char *base; + + DPRINTF(("delete node %u on %s page %"Z"u", indx, + IS_LEAF(mp) ? "leaf" : "branch", mdb_dbg_pgno(mp))); + numkeys = NUMKEYS(mp); + mdb_cassert(mc, indx < numkeys); + + if (IS_LEAF2(mp)) { + int x = numkeys - 1 - indx; + base = LEAF2KEY(mp, indx, ksize); + if (x) + memmove(base, base + ksize, x * ksize); + MP_LOWER(mp) -= sizeof(indx_t); + MP_UPPER(mp) += ksize - sizeof(indx_t); + return; + } + + node = NODEPTR(mp, indx); + sz = NODESIZE + node->mn_ksize; + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + sz += sizeof(pgno_t); + else + sz += NODEDSZ(node); + } + sz = EVEN(sz); + + ptr = MP_PTRS(mp)[indx]; + for (i = j = 0; i < numkeys; i++) { + if (i != indx) { + MP_PTRS(mp)[j] = MP_PTRS(mp)[i]; + if (MP_PTRS(mp)[i] < ptr) + MP_PTRS(mp)[j] += sz; + j++; + } + } + + base = (char *)mp + MP_UPPER(mp) + PAGEBASE; + memmove(base + sz, base, ptr - MP_UPPER(mp)); + + MP_LOWER(mp) -= sizeof(indx_t); + MP_UPPER(mp) += sz; +} + +/** Compact the main page after deleting a node on a subpage. + * @param[in] mp The main page to operate on. + * @param[in] indx The index of the subpage on the main page. + */ +static void +mdb_node_shrink(MDB_page *mp, indx_t indx) +{ + MDB_node *node; + MDB_page *sp, *xp; + char *base; + indx_t delta, nsize, len, ptr; + int i; + + node = NODEPTR(mp, indx); + sp = (MDB_page *)NODEDATA(node); + delta = SIZELEFT(sp); + nsize = NODEDSZ(node) - delta; + + /* Prepare to shift upward, set len = length(subpage part to shift) */ + if (IS_LEAF2(sp)) { + len = nsize; + if (nsize & 1) + return; /* do not make the node uneven-sized */ + } else { + xp = (MDB_page *)((char *)sp + delta); /* destination subpage */ + for (i = NUMKEYS(sp); --i >= 0; ) + MP_PTRS(xp)[i] = MP_PTRS(sp)[i] - delta; + len = PAGEHDRSZ; + } + MP_UPPER(sp) = MP_LOWER(sp); + COPY_PGNO(MP_PGNO(sp), mp->mp_pgno); + SETDSZ(node, nsize); + + /* Shift <lower nodes...initial part of subpage> upward */ + base = (char *)mp + mp->mp_upper + PAGEBASE; + memmove(base + delta, base, (char *)sp + len - base); + + ptr = mp->mp_ptrs[indx]; + for (i = NUMKEYS(mp); --i >= 0; ) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] += delta; + } + mp->mp_upper += delta; +} + +/** Initial setup of a sorted-dups cursor. + * Sorted duplicates are implemented as a sub-database for the given key. + * The duplicate data items are actually keys of the sub-database. + * Operations on the duplicate data items are performed using a sub-cursor + * initialized when the sub-database is first accessed. This function does + * the preliminary setup of the sub-cursor, filling in the fields that + * depend only on the parent DB. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + */ +static void +mdb_xcursor_init0(MDB_cursor *mc) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + mx->mx_cursor.mc_xcursor = NULL; + mx->mx_cursor.mc_txn = mc->mc_txn; + mx->mx_cursor.mc_db = &mx->mx_db; + mx->mx_cursor.mc_dbx = &mx->mx_dbx; + mx->mx_cursor.mc_dbi = mc->mc_dbi; + mx->mx_cursor.mc_dbflag = &mx->mx_dbflag; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + mx->mx_dbx.md_name.mv_size = 0; + mx->mx_dbx.md_name.mv_data = NULL; + mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp; + mx->mx_dbx.md_dcmp = NULL; + mx->mx_dbx.md_rel = mc->mc_dbx->md_rel; +} + +/** Final setup of a sorted-dups cursor. + * Sets up the fields that depend on the data from the main cursor. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + * @param[in] node The data containing the #MDB_db record for the + * sorted-dup database. + */ +static void +mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (node->mn_flags & F_SUBDATA) { + memcpy(&mx->mx_db, NODEDATA(node), sizeof(MDB_db)); + mx->mx_cursor.mc_pg[0] = 0; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + } else { + MDB_page *fp = NODEDATA(node); + mx->mx_db.md_pad = 0; + mx->mx_db.md_flags = 0; + mx->mx_db.md_depth = 1; + mx->mx_db.md_branch_pages = 0; + mx->mx_db.md_leaf_pages = 1; + mx->mx_db.md_overflow_pages = 0; + mx->mx_db.md_entries = NUMKEYS(fp); + COPY_PGNO(mx->mx_db.md_root, MP_PGNO(fp)); + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_INITIALIZED|C_SUB; + mx->mx_cursor.mc_pg[0] = fp; + mx->mx_cursor.mc_ki[0] = 0; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + mx->mx_db.md_flags = MDB_DUPFIXED; + mx->mx_db.md_pad = fp->mp_pad; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + mx->mx_db.md_flags |= MDB_INTEGERKEY; + } + } + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t)) + mx->mx_dbx.md_cmp = mdb_cmp_clong; +#endif +} + + +/** Fixup a sorted-dups cursor due to underlying update. + * Sets up some fields that depend on the data from the main cursor. + * Almost the same as init1, but skips initialization steps if the + * xcursor had already been used. + * @param[in] mc The main cursor whose sorted-dups cursor is to be fixed up. + * @param[in] src_mx The xcursor of an up-to-date cursor. + * @param[in] new_dupdata True if converting from a non-#F_DUPDATA item. + */ +static void +mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (new_dupdata) { + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags |= C_INITIALIZED; + mx->mx_cursor.mc_ki[0] = 0; + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp; +#endif + } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) { + return; + } + mx->mx_db = src_mx->mx_db; + mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0]; + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); +} + +/** Initialize a cursor for a given transaction and database. */ +static void +mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx) +{ + mc->mc_next = NULL; + mc->mc_backup = NULL; + mc->mc_dbi = dbi; + mc->mc_txn = txn; + mc->mc_db = &txn->mt_dbs[dbi]; + mc->mc_dbx = &txn->mt_dbxs[dbi]; + mc->mc_dbflag = &txn->mt_dbflags[dbi]; + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_pg[0] = 0; + mc->mc_ki[0] = 0; + mc->mc_flags = 0; + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) { + mdb_tassert(txn, mx != NULL); + mc->mc_xcursor = mx; + mdb_xcursor_init0(mc); + } else { + mc->mc_xcursor = NULL; + } + if (*mc->mc_dbflag & DB_STALE) { + mdb_page_search(mc, NULL, MDB_PS_ROOTONLY); + } +} + +int +mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret) +{ + MDB_cursor *mc; + size_t size = sizeof(MDB_cursor); + + if (!ret || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EINVAL; + + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) + size += sizeof(MDB_xcursor); + + if ((mc = malloc(size)) != NULL) { + mdb_cursor_init(mc, txn, dbi, (MDB_xcursor *)(mc + 1)); + if (txn->mt_cursors) { + mc->mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = mc; + mc->mc_flags |= C_UNTRACK; + } + } else { + return ENOMEM; + } + + *ret = mc; + + return MDB_SUCCESS; +} + +int +mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc) +{ + if (!mc || !TXN_DBI_EXIST(txn, mc->mc_dbi, DB_VALID)) + return EINVAL; + + if ((mc->mc_flags & C_UNTRACK) || txn->mt_cursors) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); + return MDB_SUCCESS; +} + +/* Return the count of duplicate data items for the current key */ +int +mdb_cursor_count(MDB_cursor *mc, size_t *countp) +{ + MDB_node *leaf; + + if (mc == NULL || countp == NULL) + return EINVAL; + + if (mc->mc_xcursor == NULL) + return MDB_INCOMPATIBLE; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (!mc->mc_snum) + return MDB_NOTFOUND; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + *countp = 1; + } else { + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + return EINVAL; + + *countp = mc->mc_xcursor->mx_db.md_entries; + } + return MDB_SUCCESS; +} + +void +mdb_cursor_close(MDB_cursor *mc) +{ + if (mc && !mc->mc_backup) { + /* remove from txn, if tracked */ + if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) { + MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi]; + while (*prev && *prev != mc) prev = &(*prev)->mc_next; + if (*prev == mc) + *prev = mc->mc_next; + } + free(mc); + } +} + +MDB_txn * +mdb_cursor_txn(MDB_cursor *mc) +{ + if (!mc) return NULL; + return mc->mc_txn; +} + +MDB_dbi +mdb_cursor_dbi(MDB_cursor *mc) +{ + return mc->mc_dbi; +} + +/** Replace the key for a branch node with a new key. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc Cursor pointing to the node to operate on. + * @param[in] key The new key to use. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_update_key(MDB_cursor *mc, MDB_val *key) +{ + MDB_page *mp; + MDB_node *node; + char *base; + size_t len; + int delta, ksize, oksize; + indx_t ptr, i, numkeys, indx; + DKBUF; + + indx = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + node = NODEPTR(mp, indx); + ptr = mp->mp_ptrs[indx]; +#if MDB_DEBUG + { + MDB_val k2; + char kbuf2[DKBUF_MAXKEYSIZE*2+1]; + k2.mv_data = NODEKEY(node); + k2.mv_size = node->mn_ksize; + DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Z"u", + indx, ptr, + mdb_dkey(&k2, kbuf2), + DKEY(key), + mp->mp_pgno)); + } +#endif + + /* Sizes must be 2-byte aligned. */ + ksize = EVEN(key->mv_size); + oksize = EVEN(node->mn_ksize); + delta = ksize - oksize; + + /* Shift node contents if EVEN(key length) changed. */ + if (delta) { + if (delta > 0 && SIZELEFT(mp) < delta) { + pgno_t pgno; + /* not enough space left, do a delete and split */ + DPRINTF(("Not enough room, delta = %d, splitting...", delta)); + pgno = NODEPGNO(node); + mdb_node_del(mc, 0); + return mdb_page_split(mc, key, NULL, pgno, MDB_SPLIT_REPLACE); + } + + numkeys = NUMKEYS(mp); + for (i = 0; i < numkeys; i++) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] -= delta; + } + + base = (char *)mp + mp->mp_upper + PAGEBASE; + len = ptr - mp->mp_upper + NODESIZE; + memmove(base - delta, base, len); + mp->mp_upper -= delta; + + node = NODEPTR(mp, indx); + } + + /* But even if no shift was needed, update ksize */ + if (node->mn_ksize != key->mv_size) + node->mn_ksize = key->mv_size; + + if (key->mv_size) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + return MDB_SUCCESS; +} + +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst); + +/** Perform \b act while tracking temporary cursor \b mn */ +#define WITH_CURSOR_TRACKING(mn, act) do { \ + MDB_cursor dummy, *tracked, **tp = &(mn).mc_txn->mt_cursors[mn.mc_dbi]; \ + if ((mn).mc_flags & C_SUB) { \ + dummy.mc_flags = C_INITIALIZED; \ + dummy.mc_xcursor = (MDB_xcursor *)&(mn); \ + tracked = &dummy; \ + } else { \ + tracked = &(mn); \ + } \ + tracked->mc_next = *tp; \ + *tp = tracked; \ + { act; } \ + *tp = tracked->mc_next; \ +} while (0) + +/** Move a node from csrc to cdst. + */ +static int +mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft) +{ + MDB_node *srcnode; + MDB_val key, data; + pgno_t srcpg; + MDB_cursor mn; + int rc; + unsigned short flags; + + DKBUF; + + /* Mark src and dst as dirty. */ + if ((rc = mdb_page_touch(csrc)) || + (rc = mdb_page_touch(cdst))) + return rc; + + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top], key.mv_size); + data.mv_size = 0; + data.mv_data = NULL; + srcpg = 0; + flags = 0; + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top]); + mdb_cassert(csrc, !((size_t)srcnode & 1)); + srcpg = NODEPGNO(srcnode); + flags = srcnode->mn_flags; + if (csrc->mc_ki[csrc->mc_top] == 0 && IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + unsigned int snum = csrc->mc_snum; + MDB_node *s2; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(csrc); + if (rc) + return rc; + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + csrc->mc_snum = snum--; + csrc->mc_top = snum; + } else { + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + } + mn.mc_xcursor = NULL; + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top]) && cdst->mc_ki[cdst->mc_top] == 0) { + unsigned int snum = cdst->mc_snum; + MDB_node *s2; + MDB_val bkey; + /* must find the lowest key below dst */ + mdb_cursor_copy(cdst, &mn); + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + bkey.mv_size = mn.mc_db->md_pad; + bkey.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, bkey.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + bkey.mv_size = NODEKSZ(s2); + bkey.mv_data = NODEKEY(s2); + } + mn.mc_snum = snum--; + mn.mc_top = snum; + mn.mc_ki[snum] = 0; + rc = mdb_update_key(&mn, &bkey); + if (rc) + return rc; + } + + DPRINTF(("moving %s node %u [%s] on page %"Z"u to node %u on page %"Z"u", + IS_LEAF(csrc->mc_pg[csrc->mc_top]) ? "leaf" : "branch", + csrc->mc_ki[csrc->mc_top], + DKEY(&key), + csrc->mc_pg[csrc->mc_top]->mp_pgno, + cdst->mc_ki[cdst->mc_top], cdst->mc_pg[cdst->mc_top]->mp_pgno)); + + /* Add the node to the destination page. + */ + rc = mdb_node_add(cdst, cdst->mc_ki[cdst->mc_top], &key, &data, srcpg, flags); + if (rc != MDB_SUCCESS) + return rc; + + /* Delete the node from the source page. + */ + mdb_node_del(csrc, key.mv_size); + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + MDB_page *mpd, *mps; + + mps = csrc->mc_pg[csrc->mc_top]; + /* If we're adding on the left, bump others up */ + if (fromleft) { + mpd = cdst->mc_pg[csrc->mc_top]; + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3 != cdst && + m3->mc_pg[csrc->mc_top] == mpd && + m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) { + m3->mc_ki[csrc->mc_top]++; + } + if (m3 !=csrc && + m3->mc_pg[csrc->mc_top] == mps && + m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]++; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } else + /* Adding on the right, bump others down */ + { + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3->mc_pg[csrc->mc_top] == mps) { + if (!m3->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]--; + } else { + m3->mc_ki[csrc->mc_top]--; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } + } + } + + /* Update the parent separators. + */ + if (csrc->mc_ki[csrc->mc_top] == 0) { + if (csrc->mc_ki[csrc->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for source page %"Z"u to [%s]", + csrc->mc_pg[csrc->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(csrc, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + MDB_val nullkey; + indx_t ix = csrc->mc_ki[csrc->mc_top]; + nullkey.mv_size = 0; + csrc->mc_ki[csrc->mc_top] = 0; + rc = mdb_update_key(csrc, &nullkey); + csrc->mc_ki[csrc->mc_top] = ix; + mdb_cassert(csrc, rc == MDB_SUCCESS); + } + } + + if (cdst->mc_ki[cdst->mc_top] == 0) { + if (cdst->mc_ki[cdst->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(cdst->mc_pg[cdst->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for destination page %"Z"u to [%s]", + cdst->mc_pg[cdst->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(cdst, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) { + MDB_val nullkey; + indx_t ix = cdst->mc_ki[cdst->mc_top]; + nullkey.mv_size = 0; + cdst->mc_ki[cdst->mc_top] = 0; + rc = mdb_update_key(cdst, &nullkey); + cdst->mc_ki[cdst->mc_top] = ix; + mdb_cassert(cdst, rc == MDB_SUCCESS); + } + } + + return MDB_SUCCESS; +} + +/** Merge one page into another. + * The nodes from the page pointed to by \b csrc will + * be copied to the page pointed to by \b cdst and then + * the \b csrc page will be freed. + * @param[in] csrc Cursor pointing to the source page. + * @param[in] cdst Cursor pointing to the destination page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst) +{ + MDB_page *psrc, *pdst; + MDB_node *srcnode; + MDB_val key, data; + unsigned nkeys; + int rc; + indx_t i, j; + + psrc = csrc->mc_pg[csrc->mc_top]; + pdst = cdst->mc_pg[cdst->mc_top]; + + DPRINTF(("merging page %"Z"u into %"Z"u", psrc->mp_pgno, pdst->mp_pgno)); + + mdb_cassert(csrc, csrc->mc_snum > 1); /* can't merge root page */ + mdb_cassert(csrc, cdst->mc_snum > 1); + + /* Mark dst as dirty. */ + if ((rc = mdb_page_touch(cdst))) + return rc; + + /* get dst page again now that we've touched it. */ + pdst = cdst->mc_pg[cdst->mc_top]; + + /* Move all nodes from src to dst. + */ + j = nkeys = NUMKEYS(pdst); + if (IS_LEAF2(psrc)) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = METADATA(psrc); + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + rc = mdb_node_add(cdst, j, &key, NULL, 0, 0); + if (rc != MDB_SUCCESS) + return rc; + key.mv_data = (char *)key.mv_data + key.mv_size; + } + } else { + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + srcnode = NODEPTR(psrc, i); + if (i == 0 && IS_BRANCH(psrc)) { + MDB_cursor mn; + MDB_node *s2; + mdb_cursor_copy(csrc, &mn); + mn.mc_xcursor = NULL; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + key.mv_size = mn.mc_db->md_pad; + key.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + } else { + key.mv_size = srcnode->mn_ksize; + key.mv_data = NODEKEY(srcnode); + } + + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + rc = mdb_node_add(cdst, j, &key, &data, NODEPGNO(srcnode), srcnode->mn_flags); + if (rc != MDB_SUCCESS) + return rc; + } + } + + DPRINTF(("dst page %"Z"u now has %u keys (%.1f%% filled)", + pdst->mp_pgno, NUMKEYS(pdst), + (float)PAGEFILL(cdst->mc_txn->mt_env, pdst) / 10)); + + /* Unlink the src page from parent and add to free list. + */ + csrc->mc_top--; + mdb_node_del(csrc, 0); + if (csrc->mc_ki[csrc->mc_top] == 0) { + key.mv_size = 0; + rc = mdb_update_key(csrc, &key); + if (rc) { + csrc->mc_top++; + return rc; + } + } + csrc->mc_top++; + + psrc = csrc->mc_pg[csrc->mc_top]; + /* If not operating on FreeDB, allow this page to be reused + * in this txn. Otherwise just add to free list. + */ + rc = mdb_page_loose(csrc, psrc); + if (rc) + return rc; + if (IS_LEAF(psrc)) + csrc->mc_db->md_leaf_pages--; + else + csrc->mc_db->md_branch_pages--; + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + unsigned int top = csrc->mc_top; + + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (m3->mc_snum < csrc->mc_snum) continue; + if (m3->mc_pg[top] == psrc) { + m3->mc_pg[top] = pdst; + m3->mc_ki[top] += nkeys; + m3->mc_ki[top-1] = cdst->mc_ki[top-1]; + } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] && + m3->mc_ki[top-1] > csrc->mc_ki[top-1]) { + m3->mc_ki[top-1]--; + } + if (IS_LEAF(psrc)) + XCURSOR_REFRESH(m3, top, m3->mc_pg[top]); + } + } + { + unsigned int snum = cdst->mc_snum; + uint16_t depth = cdst->mc_db->md_depth; + mdb_cursor_pop(cdst); + rc = mdb_rebalance(cdst); + /* Did the tree height change? */ + if (depth != cdst->mc_db->md_depth) + snum += cdst->mc_db->md_depth - depth; + cdst->mc_snum = snum; + cdst->mc_top = snum-1; + } + return rc; +} + +/** Copy the contents of a cursor. + * @param[in] csrc The cursor to copy from. + * @param[out] cdst The cursor to copy to. + */ +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst) +{ + unsigned int i; + + cdst->mc_txn = csrc->mc_txn; + cdst->mc_dbi = csrc->mc_dbi; + cdst->mc_db = csrc->mc_db; + cdst->mc_dbx = csrc->mc_dbx; + cdst->mc_snum = csrc->mc_snum; + cdst->mc_top = csrc->mc_top; + cdst->mc_flags = csrc->mc_flags; + + for (i=0; i<csrc->mc_snum; i++) { + cdst->mc_pg[i] = csrc->mc_pg[i]; + cdst->mc_ki[i] = csrc->mc_ki[i]; + } +} + +/** Rebalance the tree after a delete operation. + * @param[in] mc Cursor pointing to the page where rebalancing + * should begin. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_rebalance(MDB_cursor *mc) +{ + MDB_node *node; + int rc, fromleft; + unsigned int ptop, minkeys, thresh; + MDB_cursor mn; + indx_t oldki; + + if (IS_BRANCH(mc->mc_pg[mc->mc_top])) { + minkeys = 2; + thresh = 1; + } else { + minkeys = 1; + thresh = FILL_THRESHOLD; + } + DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)", + IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10)); + + if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh && + NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) { + DPRINTF(("no need to rebalance page %"Z"u, above fill threshold", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]))); + return MDB_SUCCESS; + } + + if (mc->mc_snum < 2) { + MDB_page *mp = mc->mc_pg[0]; + if (IS_SUBP(mp)) { + DPUTS("Can't rebalance a subpage, ignoring"); + return MDB_SUCCESS; + } + if (NUMKEYS(mp) == 0) { + DPUTS("tree is completely empty"); + mc->mc_db->md_root = P_INVALID; + mc->mc_db->md_depth = 0; + mc->mc_db->md_leaf_pages = 0; + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + /* Adjust cursors pointing to mp */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + { + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum)) + continue; + if (m3->mc_pg[0] == mp) { + m3->mc_snum = 0; + m3->mc_top = 0; + m3->mc_flags &= ~C_INITIALIZED; + } + } + } + } else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) { + int i; + DPUTS("collapsing root page!"); + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + mc->mc_db->md_root = NODEPGNO(NODEPTR(mp, 0)); + rc = mdb_page_get(mc, mc->mc_db->md_root, &mc->mc_pg[0], NULL); + if (rc) + return rc; + mc->mc_db->md_depth--; + mc->mc_db->md_branch_pages--; + mc->mc_ki[0] = mc->mc_ki[1]; + for (i = 1; i<mc->mc_db->md_depth; i++) { + mc->mc_pg[i] = mc->mc_pg[i+1]; + mc->mc_ki[i] = mc->mc_ki[i+1]; + } + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) continue; + if (!(m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_pg[0] == mp) { + for (i=0; i<mc->mc_db->md_depth; i++) { + m3->mc_pg[i] = m3->mc_pg[i+1]; + m3->mc_ki[i] = m3->mc_ki[i+1]; + } + m3->mc_snum--; + m3->mc_top--; + } + } + } + } else + DPUTS("root page doesn't need rebalancing"); + return MDB_SUCCESS; + } + + /* The parent (branch page) must have at least 2 pointers, + * otherwise the tree is invalid. + */ + ptop = mc->mc_top-1; + mdb_cassert(mc, NUMKEYS(mc->mc_pg[ptop]) > 1); + + /* Leaf page fill factor is below the threshold. + * Try to move keys from left or right neighbor, or + * merge with a neighbor page. + */ + + /* Find neighbors. + */ + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + + oldki = mc->mc_ki[mc->mc_top]; + if (mc->mc_ki[ptop] == 0) { + /* We're the leftmost leaf in our parent. + */ + DPUTS("reading right neighbor"); + mn.mc_ki[ptop]++; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = 0; + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + fromleft = 0; + } else { + /* There is at least one neighbor to the left. + */ + DPUTS("reading left neighbor"); + mn.mc_ki[ptop]--; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1; + mc->mc_ki[mc->mc_top] = 0; + fromleft = 1; + } + + DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)", + mn.mc_pg[mn.mc_top]->mp_pgno, NUMKEYS(mn.mc_pg[mn.mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) / 10)); + + /* If the neighbor page is above threshold and has enough keys, + * move one key from it. Otherwise we should try to merge them. + * (A branch page must never have less than 2 keys.) + */ + if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) { + rc = mdb_node_move(&mn, mc, fromleft); + if (fromleft) { + /* if we inserted on left, bump position up */ + oldki++; + } + } else { + if (!fromleft) { + rc = mdb_page_merge(&mn, mc); + } else { + oldki += NUMKEYS(mn.mc_pg[mn.mc_top]); + mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_merge(mc, &mn)); + mdb_cursor_copy(&mn, mc); + } + mc->mc_flags &= ~C_EOF; + } + mc->mc_ki[mc->mc_top] = oldki; + return rc; +} + +/** Complete a delete operation started by #mdb_cursor_del(). */ +static int +mdb_cursor_del0(MDB_cursor *mc) +{ + int rc; + MDB_page *mp; + indx_t ki; + unsigned int nkeys; + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + ki = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + mdb_node_del(mc, mc->mc_db->md_pad); + mc->mc_db->md_entries--; + { + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3 == mc || m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] == ki) { + m3->mc_flags |= C_DEL; + if (mc->mc_db->md_flags & MDB_DUPSORT) { + /* Sub-cursor referred into dataset which is gone */ + m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + } + continue; + } else if (m3->mc_ki[mc->mc_top] > ki) { + m3->mc_ki[mc->mc_top]--; + } + XCURSOR_REFRESH(m3, mc->mc_top, mp); + } + } + } + rc = mdb_rebalance(mc); + if (rc) + goto fail; + + /* DB is totally empty now, just bail out. + * Other cursors adjustments were already done + * by mdb_rebalance and aren't needed here. + */ + if (!mc->mc_snum) { + mc->mc_flags |= C_EOF; + return rc; + } + + mp = mc->mc_pg[mc->mc_top]; + nkeys = NUMKEYS(mp); + + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) { + /* if m3 points past last node in page, find next sibling */ + if (m3->mc_ki[mc->mc_top] >= nkeys) { + rc = mdb_cursor_sibling(m3, 1); + if (rc == MDB_NOTFOUND) { + m3->mc_flags |= C_EOF; + rc = MDB_SUCCESS; + continue; + } + if (rc) + goto fail; + } + if (m3->mc_xcursor && !(m3->mc_flags & C_EOF)) { + MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]); + /* If this node has dupdata, it may need to be reinited + * because its data has moved. + * If the xcursor was not initd it must be reinited. + * Else if node points to a subDB, nothing is needed. + * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset. + */ + if (node->mn_flags & F_DUPDATA) { + if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + if (!(node->mn_flags & F_SUBDATA)) + m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node); + } else { + mdb_xcursor_init1(m3, node); + rc = mdb_cursor_first(&m3->mc_xcursor->mx_cursor, NULL, NULL); + if (rc) + goto fail; + } + } + m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; + } + } + } + } + mc->mc_flags |= C_DEL; + +fail: + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_del(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + if (!key || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!F_ISSET(txn->mt_dbs[dbi].md_flags, MDB_DUPSORT)) { + /* must ignore any data */ + data = NULL; + } + + return mdb_del0(txn, dbi, key, data, 0); +} + +static int +mdb_del0(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + MDB_cursor_op op; + MDB_val rdata, *xdata; + int rc, exact = 0; + DKBUF; + + DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key))); + + mdb_cursor_init(&mc, txn, dbi, &mx); + + if (data) { + op = MDB_GET_BOTH; + rdata = *data; + xdata = &rdata; + } else { + op = MDB_SET; + xdata = NULL; + flags |= MDB_NODUPDATA; + } + rc = mdb_cursor_set(&mc, key, xdata, op, &exact); + if (rc == 0) { + /* let mdb_page_split know about this cursor if needed: + * delete will trigger a rebalance; if it needs to move + * a node from one page to another, it will have to + * update the parent's separator key(s). If the new sepkey + * is larger than the current one, the parent page may + * run out of space, triggering a split. We need this + * cursor to be consistent until the end of the rebalance. + */ + mc.mc_flags |= C_UNTRACK; + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_del(&mc, flags); + txn->mt_cursors[dbi] = mc.mc_next; + } + return rc; +} + +/** Split a page and insert a new node. + * Set #MDB_TXN_ERROR on failure. + * @param[in,out] mc Cursor pointing to the page and desired insertion index. + * The cursor will be updated to point to the actual page and index where + * the node got inserted after the split. + * @param[in] newkey The key for the newly inserted node. + * @param[in] newdata The data for the newly inserted node. + * @param[in] newpgno The page number, if the new node is a branch node. + * @param[in] nflags The #NODE_ADD_FLAGS for the new node. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno, + unsigned int nflags) +{ + unsigned int flags; + int rc = MDB_SUCCESS, new_root = 0, did_split = 0; + indx_t newindx; + pgno_t pgno = 0; + int i, j, split_indx, nkeys, pmax; + MDB_env *env = mc->mc_txn->mt_env; + MDB_node *node; + MDB_val sepkey, rkey, xdata, *rdata = &xdata; + MDB_page *copy = NULL; + MDB_page *mp, *rp, *pp; + int ptop; + MDB_cursor mn; + DKBUF; + + mp = mc->mc_pg[mc->mc_top]; + newindx = mc->mc_ki[mc->mc_top]; + nkeys = NUMKEYS(mp); + + DPRINTF(("-----> splitting %s page %"Z"u and adding [%s] at index %i/%i", + IS_LEAF(mp) ? "leaf" : "branch", mp->mp_pgno, + DKEY(newkey), mc->mc_ki[mc->mc_top], nkeys)); + + /* Create a right sibling. */ + if ((rc = mdb_page_new(mc, mp->mp_flags, 1, &rp))) + return rc; + rp->mp_pad = mp->mp_pad; + DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno)); + + /* Usually when splitting the root page, the cursor + * height is 1. But when called from mdb_update_key, + * the cursor height may be greater because it walks + * up the stack while finding the branch slot to update. + */ + if (mc->mc_top < 1) { + if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp))) + goto done; + /* shift current top to make room for new parent */ + for (i=mc->mc_snum; i>0; i--) { + mc->mc_pg[i] = mc->mc_pg[i-1]; + mc->mc_ki[i] = mc->mc_ki[i-1]; + } + mc->mc_pg[0] = pp; + mc->mc_ki[0] = 0; + mc->mc_db->md_root = pp->mp_pgno; + DPRINTF(("root split! new root = %"Z"u", pp->mp_pgno)); + new_root = mc->mc_db->md_depth++; + + /* Add left (implicit) pointer. */ + if ((rc = mdb_node_add(mc, 0, NULL, NULL, mp->mp_pgno, 0)) != MDB_SUCCESS) { + /* undo the pre-push */ + mc->mc_pg[0] = mc->mc_pg[1]; + mc->mc_ki[0] = mc->mc_ki[1]; + mc->mc_db->md_root = mp->mp_pgno; + mc->mc_db->md_depth--; + goto done; + } + mc->mc_snum++; + mc->mc_top++; + ptop = 0; + } else { + ptop = mc->mc_top-1; + DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno)); + } + + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + mn.mc_pg[mn.mc_top] = rp; + mn.mc_ki[ptop] = mc->mc_ki[ptop]+1; + + if (nflags & MDB_APPEND) { + mn.mc_ki[mn.mc_top] = 0; + sepkey = *newkey; + split_indx = newindx; + nkeys = 0; + } else { + + split_indx = (nkeys+1) / 2; + + if (IS_LEAF2(rp)) { + char *split, *ins; + int x; + unsigned int lsize, rsize, ksize; + /* Move half of the keys to the right sibling */ + x = mc->mc_ki[mc->mc_top] - split_indx; + ksize = mc->mc_db->md_pad; + split = LEAF2KEY(mp, split_indx, ksize); + rsize = (nkeys - split_indx) * ksize; + lsize = (nkeys - split_indx) * sizeof(indx_t); + mp->mp_lower -= lsize; + rp->mp_lower += lsize; + mp->mp_upper += rsize - lsize; + rp->mp_upper -= rsize - lsize; + sepkey.mv_size = ksize; + if (newindx == split_indx) { + sepkey.mv_data = newkey->mv_data; + } else { + sepkey.mv_data = split; + } + if (x<0) { + ins = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], ksize); + memcpy(rp->mp_ptrs, split, rsize); + sepkey.mv_data = rp->mp_ptrs; + memmove(ins+ksize, ins, (split_indx - mc->mc_ki[mc->mc_top]) * ksize); + memcpy(ins, newkey->mv_data, ksize); + mp->mp_lower += sizeof(indx_t); + mp->mp_upper -= ksize - sizeof(indx_t); + } else { + if (x) + memcpy(rp->mp_ptrs, split, x * ksize); + ins = LEAF2KEY(rp, x, ksize); + memcpy(ins, newkey->mv_data, ksize); + memcpy(ins+ksize, split + x * ksize, rsize - x * ksize); + rp->mp_lower += sizeof(indx_t); + rp->mp_upper -= ksize - sizeof(indx_t); + mc->mc_ki[mc->mc_top] = x; + } + } else { + int psize, nsize, k, keythresh; + + /* Maximum free space in an empty page */ + pmax = env->me_psize - PAGEHDRSZ; + /* Threshold number of keys considered "small" */ + keythresh = env->me_psize >> 7; + + if (IS_LEAF(mp)) + nsize = mdb_leaf_size(env, newkey, newdata); + else + nsize = mdb_branch_size(env, newkey); + nsize = EVEN(nsize); + + /* grab a page to hold a temporary copy */ + copy = mdb_page_malloc(mc->mc_txn, 1); + if (copy == NULL) { + rc = ENOMEM; + goto done; + } + copy->mp_pgno = mp->mp_pgno; + copy->mp_flags = mp->mp_flags; + copy->mp_lower = (PAGEHDRSZ-PAGEBASE); + copy->mp_upper = env->me_psize - PAGEBASE; + + /* prepare to insert */ + for (i=0, j=0; i<nkeys; i++) { + if (i == newindx) { + copy->mp_ptrs[j++] = 0; + } + copy->mp_ptrs[j++] = mp->mp_ptrs[i]; + } + + /* When items are relatively large the split point needs + * to be checked, because being off-by-one will make the + * difference between success or failure in mdb_node_add. + * + * It's also relevant if a page happens to be laid out + * such that one half of its nodes are all "small" and + * the other half of its nodes are "large." If the new + * item is also "large" and falls on the half with + * "large" nodes, it also may not fit. + * + * As a final tweak, if the new item goes on the last + * spot on the page (and thus, onto the new page), bias + * the split so the new page is emptier than the old page. + * This yields better packing during sequential inserts. + */ + if (nkeys < keythresh || nsize > pmax/16 || newindx >= nkeys) { + /* Find split point */ + psize = 0; + if (newindx <= split_indx || newindx >= nkeys) { + i = 0; j = 1; + k = newindx >= nkeys ? nkeys : split_indx+1+IS_LEAF(mp); + } else { + i = nkeys; j = -1; + k = split_indx-1; + } + for (; i!=k; i+=j) { + if (i == newindx) { + psize += nsize; + node = NULL; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t); + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + psize += sizeof(pgno_t); + else + psize += NODEDSZ(node); + } + psize = EVEN(psize); + } + if (psize > pmax || i == k-j) { + split_indx = i + (j<0); + break; + } + } + } + if (split_indx == newindx) { + sepkey.mv_size = newkey->mv_size; + sepkey.mv_data = newkey->mv_data; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE); + sepkey.mv_size = node->mn_ksize; + sepkey.mv_data = NODEKEY(node); + } + } + } + + DPRINTF(("separator is %d [%s]", split_indx, DKEY(&sepkey))); + + /* Copy separator key to the parent. + */ + if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) { + int snum = mc->mc_snum; + mn.mc_snum--; + mn.mc_top--; + did_split = 1; + /* We want other splits to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0)); + if (rc) + goto done; + + /* root split? */ + if (mc->mc_snum > snum) { + ptop++; + } + /* Right page might now have changed parent. + * Check if left page also changed parent. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + mc->mc_pg[ptop] = mn.mc_pg[ptop]; + if (mn.mc_ki[ptop]) { + mc->mc_ki[ptop] = mn.mc_ki[ptop] - 1; + } else { + /* find right page's left sibling */ + mc->mc_ki[ptop] = mn.mc_ki[ptop]; + mdb_cursor_sibling(mc, 0); + } + } + } else { + mn.mc_top--; + rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0); + mn.mc_top++; + } + if (rc != MDB_SUCCESS) { + goto done; + } + if (nflags & MDB_APPEND) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[mc->mc_top] = 0; + rc = mdb_node_add(mc, 0, newkey, newdata, newpgno, nflags); + if (rc) + goto done; + for (i=0; i<mc->mc_top; i++) + mc->mc_ki[i] = mn.mc_ki[i]; + } else if (!IS_LEAF2(mp)) { + /* Move nodes */ + mc->mc_pg[mc->mc_top] = rp; + i = split_indx; + j = 0; + do { + if (i == newindx) { + rkey.mv_data = newkey->mv_data; + rkey.mv_size = newkey->mv_size; + if (IS_LEAF(mp)) { + rdata = newdata; + } else + pgno = newpgno; + flags = nflags; + /* Update index for the new key. */ + mc->mc_ki[mc->mc_top] = j; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + rkey.mv_data = NODEKEY(node); + rkey.mv_size = node->mn_ksize; + if (IS_LEAF(mp)) { + xdata.mv_data = NODEDATA(node); + xdata.mv_size = NODEDSZ(node); + rdata = &xdata; + } else + pgno = NODEPGNO(node); + flags = node->mn_flags; + } + + if (!IS_LEAF(mp) && j == 0) { + /* First branch index doesn't need key data. */ + rkey.mv_size = 0; + } + + rc = mdb_node_add(mc, j, &rkey, rdata, pgno, flags); + if (rc) + goto done; + if (i == nkeys) { + i = 0; + j = 0; + mc->mc_pg[mc->mc_top] = copy; + } else { + i++; + j++; + } + } while (i != split_indx); + + nkeys = NUMKEYS(copy); + for (i=0; i<nkeys; i++) + mp->mp_ptrs[i] = copy->mp_ptrs[i]; + mp->mp_lower = copy->mp_lower; + mp->mp_upper = copy->mp_upper; + memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1), + env->me_psize - copy->mp_upper - PAGEBASE); + + /* reset back to original page */ + if (newindx < split_indx) { + mc->mc_pg[mc->mc_top] = mp; + } else { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + if (nflags & MDB_RESERVE) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!(node->mn_flags & F_BIGDATA)) + newdata->mv_data = NODEDATA(node); + } + } else { + if (newindx >= split_indx) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + } + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + nkeys = NUMKEYS(mp); + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) + continue; + if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (new_root) { + int k; + /* sub cursors may be on different DB */ + if (m3->mc_pg[0] != mp) + continue; + /* root split */ + for (k=new_root; k>=0; k--) { + m3->mc_ki[k+1] = m3->mc_ki[k]; + m3->mc_pg[k+1] = m3->mc_pg[k]; + } + if (m3->mc_ki[0] >= nkeys) { + m3->mc_ki[0] = 1; + } else { + m3->mc_ki[0] = 0; + } + m3->mc_pg[0] = mc->mc_pg[0]; + m3->mc_snum++; + m3->mc_top++; + } + if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE)) + m3->mc_ki[mc->mc_top]++; + if (m3->mc_ki[mc->mc_top] >= nkeys) { + m3->mc_pg[mc->mc_top] = rp; + m3->mc_ki[mc->mc_top] -= nkeys; + for (i=0; i<mc->mc_top; i++) { + m3->mc_ki[i] = mn.mc_ki[i]; + m3->mc_pg[i] = mn.mc_pg[i]; + } + } + } else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] && + m3->mc_ki[ptop] >= mc->mc_ki[ptop]) { + m3->mc_ki[ptop]++; + } + if (IS_LEAF(mp)) + XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]); + } + } + DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp))); + +done: + if (copy) /* tmp page */ + mdb_page_free(env, copy); + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_put(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned int flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + int rc; + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_put(&mc, key, data, flags); + txn->mt_cursors[dbi] = mc.mc_next; + return rc; +} + +#ifndef MDB_WBUF +#define MDB_WBUF (1024*1024) +#endif +#define MDB_EOF 0x10 /**< #mdb_env_copyfd1() is done reading */ + + /** State needed for a double-buffering compacting copy. */ +typedef struct mdb_copy { + MDB_env *mc_env; + MDB_txn *mc_txn; + pthread_mutex_t mc_mutex; + pthread_cond_t mc_cond; /**< Condition variable for #mc_new */ + char *mc_wbuf[2]; + char *mc_over[2]; + int mc_wlen[2]; + int mc_olen[2]; + pgno_t mc_next_pgno; + HANDLE mc_fd; + int mc_toggle; /**< Buffer number in provider */ + int mc_new; /**< (0-2 buffers to write) | (#MDB_EOF at end) */ + /** Error code. Never cleared if set. Both threads can set nonzero + * to fail the copy. Not mutex-protected, LMDB expects atomic int. + */ + volatile int mc_error; +} mdb_copy; + + /** Dedicated writer thread for compacting copy. */ +static THREAD_RET ESECT CALL_CONV +mdb_env_copythr(void *arg) +{ + mdb_copy *my = arg; + char *ptr; + int toggle = 0, wsize, rc; +#ifdef _WIN32 + DWORD len; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + int len; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#ifdef SIGPIPE + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0) + my->mc_error = rc; +#endif +#endif + + pthread_mutex_lock(&my->mc_mutex); + for(;;) { + while (!my->mc_new) + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + if (my->mc_new == 0 + MDB_EOF) /* 0 buffers, just EOF */ + break; + wsize = my->mc_wlen[toggle]; + ptr = my->mc_wbuf[toggle]; +again: + rc = MDB_SUCCESS; + while (wsize > 0 && !my->mc_error) { + DO_WRITE(rc, my->mc_fd, ptr, wsize, len); + if (!rc) { + rc = ErrCode(); +#if defined(SIGPIPE) && !defined(_WIN32) + if (rc == EPIPE) { + /* Collect the pending SIGPIPE, otherwise at least OS X + * gives it to the process on thread-exit (ITS#8504). + */ + int tmp; + sigwait(&set, &tmp); + } +#endif + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + if (rc) { + my->mc_error = rc; + } + /* If there's an overflow page tail, write it too */ + if (my->mc_olen[toggle]) { + wsize = my->mc_olen[toggle]; + ptr = my->mc_over[toggle]; + my->mc_olen[toggle] = 0; + goto again; + } + my->mc_wlen[toggle] = 0; + toggle ^= 1; + /* Return the empty buffer to provider */ + my->mc_new--; + pthread_cond_signal(&my->mc_cond); + } + pthread_mutex_unlock(&my->mc_mutex); + return (THREAD_RET)0; +#undef DO_WRITE +} + + /** Give buffer and/or #MDB_EOF to writer thread, await unused buffer. + * + * @param[in] my control structure. + * @param[in] adjust (1 to hand off 1 buffer) | (MDB_EOF when ending). + */ +static int ESECT +mdb_env_cthr_toggle(mdb_copy *my, int adjust) +{ + pthread_mutex_lock(&my->mc_mutex); + my->mc_new += adjust; + pthread_cond_signal(&my->mc_cond); + while (my->mc_new & 2) /* both buffers in use */ + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + pthread_mutex_unlock(&my->mc_mutex); + + my->mc_toggle ^= (adjust & 1); + /* Both threads reset mc_wlen, to be safe from threading errors */ + my->mc_wlen[my->mc_toggle] = 0; + return my->mc_error; +} + + /** Depth-first tree traversal for compacting copy. + * @param[in] my control structure. + * @param[in,out] pg database root. + * @param[in] flags includes #F_DUPDATA if it is a sorted-duplicate sub-DB. + */ +static int ESECT +mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags) +{ + MDB_cursor mc = {0}; + MDB_node *ni; + MDB_page *mo, *mp, *leaf; + char *buf, *ptr; + int rc, toggle; + unsigned int i; + + /* Empty DB, nothing to do */ + if (*pg == P_INVALID) + return MDB_SUCCESS; + + mc.mc_snum = 1; + mc.mc_txn = my->mc_txn; + + rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL); + if (rc) + return rc; + rc = mdb_page_search_root(&mc, NULL, MDB_PS_FIRST); + if (rc) + return rc; + + /* Make cursor pages writable */ + buf = ptr = malloc(my->mc_env->me_psize * mc.mc_snum); + if (buf == NULL) + return ENOMEM; + + for (i=0; i<mc.mc_top; i++) { + mdb_page_copy((MDB_page *)ptr, mc.mc_pg[i], my->mc_env->me_psize); + mc.mc_pg[i] = (MDB_page *)ptr; + ptr += my->mc_env->me_psize; + } + + /* This is writable space for a leaf page. Usually not needed. */ + leaf = (MDB_page *)ptr; + + toggle = my->mc_toggle; + while (mc.mc_snum > 0) { + unsigned n; + mp = mc.mc_pg[mc.mc_top]; + n = NUMKEYS(mp); + + if (IS_LEAF(mp)) { + if (!IS_LEAF2(mp) && !(flags & F_DUPDATA)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + memcpy(NODEDATA(ni), &my->mc_next_pgno, sizeof(pgno_t)); + rc = mdb_page_get(&mc, pg, &omp, NULL); + if (rc) + goto done; + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + memcpy(mo, omp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno; + my->mc_next_pgno += omp->mp_pages; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (omp->mp_pages > 1) { + my->mc_olen[toggle] = my->mc_env->me_psize * (omp->mp_pages - 1); + my->mc_over[toggle] = (char *)omp + my->mc_env->me_psize; + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + } else if (ni->mn_flags & F_SUBDATA) { + MDB_db db; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&db, NODEDATA(ni), sizeof(db)); + my->mc_toggle = toggle; + rc = mdb_env_cwalk(my, &db.md_root, ni->mn_flags & F_DUPDATA); + if (rc) + goto done; + toggle = my->mc_toggle; + memcpy(NODEDATA(ni), &db, sizeof(db)); + } + } + } + } else { + mc.mc_ki[mc.mc_top]++; + if (mc.mc_ki[mc.mc_top] < n) { + pgno_t pg; +again: + ni = NODEPTR(mp, mc.mc_ki[mc.mc_top]); + pg = NODEPGNO(ni); + rc = mdb_page_get(&mc, pg, &mp, NULL); + if (rc) + goto done; + mc.mc_top++; + mc.mc_snum++; + mc.mc_ki[mc.mc_top] = 0; + if (IS_BRANCH(mp)) { + /* Whenever we advance to a sibling branch page, + * we must proceed all the way down to its first leaf. + */ + mdb_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize); + goto again; + } else + mc.mc_pg[mc.mc_top] = mp; + continue; + } + } + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + mdb_page_copy(mo, mp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno++; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (mc.mc_top) { + /* Update parent if there is one */ + ni = NODEPTR(mc.mc_pg[mc.mc_top-1], mc.mc_ki[mc.mc_top-1]); + SETPGNO(ni, mo->mp_pgno); + mdb_cursor_pop(&mc); + } else { + /* Otherwise we're done */ + *pg = mo->mp_pgno; + break; + } + } +done: + free(buf); + return rc; +} + + /** Copy environment with compaction. */ +static int ESECT +mdb_env_copyfd1(MDB_env *env, HANDLE fd) +{ + MDB_meta *mm; + MDB_page *mp; + mdb_copy my = {0}; + MDB_txn *txn = NULL; + pthread_t thr; + pgno_t root, new_root; + int rc = MDB_SUCCESS; + +#ifdef _WIN32 + if (!(my.mc_mutex = CreateMutex(NULL, FALSE, NULL)) || + !(my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { + rc = ErrCode(); + goto done; + } + my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize); + if (my.mc_wbuf[0] == NULL) { + /* _aligned_malloc() sets errno, but we use Windows error codes */ + rc = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } +#else + if ((rc = pthread_mutex_init(&my.mc_mutex, NULL)) != 0) + return rc; + if ((rc = pthread_cond_init(&my.mc_cond, NULL)) != 0) + goto done2; +#ifdef HAVE_MEMALIGN + my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2); + if (my.mc_wbuf[0] == NULL) { + rc = errno; + goto done; + } +#else + { + void *p; + if ((rc = posix_memalign(&p, env->me_os_psize, MDB_WBUF*2)) != 0) + goto done; + my.mc_wbuf[0] = p; + } +#endif +#endif + memset(my.mc_wbuf[0], 0, MDB_WBUF*2); + my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF; + my.mc_next_pgno = NUM_METAS; + my.mc_env = env; + my.mc_fd = fd; + rc = THREAD_CREATE(thr, mdb_env_copythr, &my); + if (rc) + goto done; + + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + goto finish; + + mp = (MDB_page *)my.mc_wbuf[0]; + memset(mp, 0, NUM_METAS * env->me_psize); + mp->mp_pgno = 0; + mp->mp_flags = P_META; + mm = (MDB_meta *)METADATA(mp); + mdb_env_init_meta0(env, mm); + mm->mm_address = env->me_metas[0]->mm_address; + + mp = (MDB_page *)(my.mc_wbuf[0] + env->me_psize); + mp->mp_pgno = 1; + mp->mp_flags = P_META; + *(MDB_meta *)METADATA(mp) = *mm; + mm = (MDB_meta *)METADATA(mp); + + /* Set metapage 1 with current main DB */ + root = new_root = txn->mt_dbs[MAIN_DBI].md_root; + if (root != P_INVALID) { + /* Count free pages + freeDB pages. Subtract from last_pg + * to find the new last_pg, which also becomes the new root. + */ + MDB_ID freecount = 0; + MDB_cursor mc; + MDB_val key, data; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + if (rc != MDB_NOTFOUND) + goto finish; + freecount += txn->mt_dbs[FREE_DBI].md_branch_pages + + txn->mt_dbs[FREE_DBI].md_leaf_pages + + txn->mt_dbs[FREE_DBI].md_overflow_pages; + + new_root = txn->mt_next_pgno - 1 - freecount; + mm->mm_last_pg = new_root; + mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mm->mm_dbs[MAIN_DBI].md_root = new_root; + } else { + /* When the DB is empty, handle it specially to + * fix any breakage like page leaks from ITS#8174. + */ + mm->mm_dbs[MAIN_DBI].md_flags = txn->mt_dbs[MAIN_DBI].md_flags; + } + if (root != P_INVALID || mm->mm_dbs[MAIN_DBI].md_flags) { + mm->mm_txnid = 1; /* use metapage 1 */ + } + + my.mc_wlen[0] = env->me_psize * NUM_METAS; + my.mc_txn = txn; + rc = mdb_env_cwalk(&my, &root, 0); + if (rc == MDB_SUCCESS && root != new_root) { + rc = MDB_INCOMPATIBLE; /* page leak or corrupt DB */ + } + +finish: + if (rc) + my.mc_error = rc; + mdb_env_cthr_toggle(&my, 1 | MDB_EOF); + rc = THREAD_FINISH(thr); + mdb_txn_abort(txn); + +done: +#ifdef _WIN32 + if (my.mc_wbuf[0]) _aligned_free(my.mc_wbuf[0]); + if (my.mc_cond) CloseHandle(my.mc_cond); + if (my.mc_mutex) CloseHandle(my.mc_mutex); +#else + free(my.mc_wbuf[0]); + pthread_cond_destroy(&my.mc_cond); +done2: + pthread_mutex_destroy(&my.mc_mutex); +#endif + return rc ? rc : my.mc_error; +} + + /** Copy environment as-is. */ +static int ESECT +mdb_env_copyfd0(MDB_env *env, HANDLE fd) +{ + MDB_txn *txn = NULL; + mdb_mutexref_t wmutex = NULL; + int rc; + size_t wsize, w3; + char *ptr; +#ifdef _WIN32 + DWORD len, w2; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + ssize_t len; + size_t w2; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#endif + + /* Do the lock/unlock of the reader mutex before starting the + * write txn. Otherwise other read txns could block writers. + */ + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + return rc; + + if (env->me_txns) { + /* We must start the actual read txn after blocking writers */ + mdb_txn_end(txn, MDB_END_RESET_TMP); + + /* Temporarily block writers until we snapshot the meta pages */ + wmutex = env->me_wmutex; + if (LOCK_MUTEX(rc, env, wmutex)) + goto leave; + + rc = mdb_txn_renew0(txn); + if (rc) { + UNLOCK_MUTEX(wmutex); + goto leave; + } + } + + wsize = env->me_psize * NUM_METAS; + ptr = env->me_map; + w2 = wsize; + while (w2 > 0) { + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + w2 -= len; + continue; + } else { + /* Non-blocking or async handles are not supported */ + rc = EIO; + break; + } + } + if (wmutex) + UNLOCK_MUTEX(wmutex); + + if (rc) + goto leave; + + w3 = txn->mt_next_pgno * env->me_psize; + { + size_t fsize = 0; + if ((rc = mdb_fsize(env->me_fd, &fsize))) + goto leave; + if (w3 > fsize) + w3 = fsize; + } + wsize = w3 - wsize; + while (wsize > 0) { + if (wsize > MAX_WRITE) + w2 = MAX_WRITE; + else + w2 = wsize; + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + +leave: + mdb_txn_abort(txn); + return rc; +} + +int ESECT +mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags) +{ + if (flags & MDB_CP_COMPACT) + return mdb_env_copyfd1(env, fd); + else + return mdb_env_copyfd0(env, fd); +} + +int ESECT +mdb_env_copyfd(MDB_env *env, HANDLE fd) +{ + return mdb_env_copyfd2(env, fd, 0); +} + +int ESECT +mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) +{ + int rc; + MDB_name fname; + HANDLE newfd = INVALID_HANDLE_VALUE; + + rc = mdb_fname_init(path, env->me_flags | MDB_NOLOCK, &fname); + if (rc == MDB_SUCCESS) { + rc = mdb_fopen(env, &fname, MDB_O_COPY, 0666, &newfd); + mdb_fname_destroy(fname); + } + if (rc == MDB_SUCCESS) { + rc = mdb_env_copyfd2(env, newfd, flags); + if (close(newfd) < 0 && rc == MDB_SUCCESS) + rc = ErrCode(); + } + return rc; +} + +int ESECT +mdb_env_copy(MDB_env *env, const char *path) +{ + return mdb_env_copy2(env, path, 0); +} + +int ESECT +mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff) +{ + if (flag & ~CHANGEABLE) + return EINVAL; + if (onoff) + env->me_flags |= flag; + else + env->me_flags &= ~flag; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_flags(MDB_env *env, unsigned int *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_flags & (CHANGEABLE|CHANGELESS); + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_userctx(MDB_env *env, void *ctx) +{ + if (!env) + return EINVAL; + env->me_userctx = ctx; + return MDB_SUCCESS; +} + +void * ESECT +mdb_env_get_userctx(MDB_env *env) +{ + return env ? env->me_userctx : NULL; +} + +int ESECT +mdb_env_set_assert(MDB_env *env, MDB_assert_func *func) +{ + if (!env) + return EINVAL; +#ifndef NDEBUG + env->me_assert_func = func; +#endif + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_path(MDB_env *env, const char **arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_path; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_fd; + return MDB_SUCCESS; +} + +/** Common code for #mdb_stat() and #mdb_env_stat(). + * @param[in] env the environment to operate in. + * @param[in] db the #MDB_db record containing the stats to return. + * @param[out] arg the address of an #MDB_stat structure to receive the stats. + * @return 0, this function always succeeds. + */ +static int ESECT +mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg) +{ + arg->ms_psize = env->me_psize; + arg->ms_depth = db->md_depth; + arg->ms_branch_pages = db->md_branch_pages; + arg->ms_leaf_pages = db->md_leaf_pages; + arg->ms_overflow_pages = db->md_overflow_pages; + arg->ms_entries = db->md_entries; + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_stat(MDB_env *env, MDB_stat *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + + return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], arg); +} + +int ESECT +mdb_env_info(MDB_env *env, MDB_envinfo *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + arg->me_mapaddr = meta->mm_address; + arg->me_last_pgno = meta->mm_last_pg; + arg->me_last_txnid = meta->mm_txnid; + + arg->me_mapsize = env->me_mapsize; + arg->me_maxreaders = env->me_maxreaders; + arg->me_numreaders = env->me_txns ? env->me_txns->mti_numreaders : 0; + return MDB_SUCCESS; +} + +/** Set the default comparison functions for a database. + * Called immediately after a database is opened to set the defaults. + * The user can then override them with #mdb_set_compare() or + * #mdb_set_dupsort(). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +static void +mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi) +{ + uint16_t f = txn->mt_dbs[dbi].md_flags; + + txn->mt_dbxs[dbi].md_cmp = + (f & MDB_REVERSEKEY) ? mdb_cmp_memnr : + (f & MDB_INTEGERKEY) ? mdb_cmp_cint : mdb_cmp_memn; + + txn->mt_dbxs[dbi].md_dcmp = + !(f & MDB_DUPSORT) ? 0 : + ((f & MDB_INTEGERDUP) + ? ((f & MDB_DUPFIXED) ? mdb_cmp_int : mdb_cmp_cint) + : ((f & MDB_REVERSEDUP) ? mdb_cmp_memnr : mdb_cmp_memn)); +} + +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi) +{ + MDB_val key, data; + MDB_dbi i; + MDB_cursor mc; + MDB_db dummy; + int rc, dbflag, exact; + unsigned int unused = 0, seq; + char *namedup; + size_t len; + + if (flags & ~VALID_FLAGS) + return EINVAL; + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + /* main DB? */ + if (!name) { + *dbi = MAIN_DBI; + if (flags & PERSISTENT_FLAGS) { + uint16_t f2 = flags & PERSISTENT_FLAGS; + /* make sure flag changes get committed */ + if ((txn->mt_dbs[MAIN_DBI].md_flags | f2) != txn->mt_dbs[MAIN_DBI].md_flags) { + txn->mt_dbs[MAIN_DBI].md_flags |= f2; + txn->mt_flags |= MDB_TXN_DIRTY; + } + } + mdb_default_cmp(txn, MAIN_DBI); + return MDB_SUCCESS; + } + + if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) { + mdb_default_cmp(txn, MAIN_DBI); + } + + /* Is the DB already open? */ + len = strlen(name); + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + if (!txn->mt_dbxs[i].md_name.mv_size) { + /* Remember this free slot */ + if (!unused) unused = i; + continue; + } + if (len == txn->mt_dbxs[i].md_name.mv_size && + !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) { + *dbi = i; + return MDB_SUCCESS; + } + } + + /* If no free slot and max hit, fail */ + if (!unused && txn->mt_numdbs >= txn->mt_env->me_maxdbs) + return MDB_DBS_FULL; + + /* Cannot mix named databases with some mainDB flags */ + if (txn->mt_dbs[MAIN_DBI].md_flags & (MDB_DUPSORT|MDB_INTEGERKEY)) + return (flags & MDB_CREATE) ? MDB_INCOMPATIBLE : MDB_NOTFOUND; + + /* Find the DB info */ + dbflag = DB_NEW|DB_VALID|DB_USRVALID; + exact = 0; + key.mv_size = len; + key.mv_data = (void *)name; + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact); + if (rc == MDB_SUCCESS) { + /* make sure this is actually a DB */ + MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]); + if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; + } else { + if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE)) + return rc; + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + } + + /* Done here so we cannot fail after creating a new DB */ + if ((namedup = strdup(name)) == NULL) + return ENOMEM; + + if (rc) { + /* MDB_NOTFOUND and MDB_CREATE: Create new DB */ + data.mv_size = sizeof(MDB_db); + data.mv_data = &dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.md_root = P_INVALID; + dummy.md_flags = flags & PERSISTENT_FLAGS; + WITH_CURSOR_TRACKING(mc, + rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA)); + dbflag |= DB_DIRTY; + } + + if (rc) { + free(namedup); + } else { + /* Got info, register DBI in this txn */ + unsigned int slot = unused ? unused : txn->mt_numdbs; + txn->mt_dbxs[slot].md_name.mv_data = namedup; + txn->mt_dbxs[slot].md_name.mv_size = len; + txn->mt_dbxs[slot].md_rel = NULL; + txn->mt_dbflags[slot] = dbflag; + /* txn-> and env-> are the same in read txns, use + * tmp variable to avoid undefined assignment + */ + seq = ++txn->mt_env->me_dbiseqs[slot]; + txn->mt_dbiseqs[slot] = seq; + + memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db)); + *dbi = slot; + mdb_default_cmp(txn, slot); + if (!unused) { + txn->mt_numdbs++; + } + } + + return rc; +} + +int ESECT +mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg) +{ + if (!arg || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (txn->mt_dbflags[dbi] & DB_STALE) { + MDB_cursor mc; + MDB_xcursor mx; + /* Stale, must read the DB's root. cursor_init does it for us. */ + mdb_cursor_init(&mc, txn, dbi, &mx); + } + return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); +} + +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi) +{ + char *ptr; + if (dbi < CORE_DBS || dbi >= env->me_maxdbs) + return; + ptr = env->me_dbxs[dbi].md_name.mv_data; + /* If there was no name, this was already closed */ + if (ptr) { + env->me_dbxs[dbi].md_name.mv_data = NULL; + env->me_dbxs[dbi].md_name.mv_size = 0; + env->me_dbflags[dbi] = 0; + env->me_dbiseqs[dbi]++; + free(ptr); + } +} + +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags) +{ + /* We could return the flags for the FREE_DBI too but what's the point? */ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + *flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS; + return MDB_SUCCESS; +} + +/** Add all the DB's pages to the free list. + * @param[in] mc Cursor on the DB to free. + * @param[in] subs non-Zero to check for sub-DBs in this DB. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_drop0(MDB_cursor *mc, int subs) +{ + int rc; + + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc == MDB_SUCCESS) { + MDB_txn *txn = mc->mc_txn; + MDB_node *ni; + MDB_cursor mx; + unsigned int i; + + /* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves. + * This also avoids any P_LEAF2 pages, which have no nodes. + * Also if the DB doesn't have sub-DBs and has no overflow + * pages, omit scanning leaves. + */ + if ((mc->mc_flags & C_SUB) || + (!subs && !mc->mc_db->md_overflow_pages)) + mdb_cursor_pop(mc); + + mdb_cursor_copy(mc, &mx); + while (mc->mc_snum > 0) { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + unsigned n = NUMKEYS(mp); + if (IS_LEAF(mp)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + rc = mdb_page_get(mc, pg, &omp, NULL); + if (rc != 0) + goto done; + mdb_cassert(mc, IS_OVERFLOW(omp)); + rc = mdb_midl_append_range(&txn->mt_free_pgs, + pg, omp->mp_pages); + if (rc) + goto done; + mc->mc_db->md_overflow_pages -= omp->mp_pages; + if (!mc->mc_db->md_overflow_pages && !subs) + break; + } else if (subs && (ni->mn_flags & F_SUBDATA)) { + mdb_xcursor_init1(mc, ni); + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto done; + } + } + if (!subs && !mc->mc_db->md_overflow_pages) + goto pop; + } else { + if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0) + goto done; + for (i=0; i<n; i++) { + pgno_t pg; + ni = NODEPTR(mp, i); + pg = NODEPGNO(ni); + /* free it */ + mdb_midl_xappend(txn->mt_free_pgs, pg); + } + } + if (!mc->mc_top) + break; + mc->mc_ki[mc->mc_top] = i; + rc = mdb_cursor_sibling(mc, 1); + if (rc) { + if (rc != MDB_NOTFOUND) + goto done; + /* no more siblings, go back to beginning + * of previous level. + */ +pop: + mdb_cursor_pop(mc); + mc->mc_ki[0] = 0; + for (i=1; i<mc->mc_snum; i++) { + mc->mc_ki[i] = 0; + mc->mc_pg[i] = mx.mc_pg[i]; + } + } + } + /* free it */ + rc = mdb_midl_append(&txn->mt_free_pgs, mc->mc_db->md_root); +done: + if (rc) + txn->mt_flags |= MDB_TXN_ERROR; + } else if (rc == MDB_NOTFOUND) { + rc = MDB_SUCCESS; + } + mc->mc_flags &= ~C_INITIALIZED; + return rc; +} + +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del) +{ + MDB_cursor *mc, *m2; + int rc; + + if ((unsigned)del > 1 || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + + if (TXN_DBI_CHANGED(txn, dbi)) + return MDB_BAD_DBI; + + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) + return rc; + + rc = mdb_drop0(mc, mc->mc_db->md_flags & MDB_DUPSORT); + /* Invalidate the dropped DB's cursors */ + for (m2 = txn->mt_cursors[dbi]; m2; m2 = m2->mc_next) + m2->mc_flags &= ~(C_INITIALIZED|C_EOF); + if (rc) + goto leave; + + /* Can't delete the main DB */ + if (del && dbi >= CORE_DBS) { + rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA); + if (!rc) { + txn->mt_dbflags[dbi] = DB_STALE; + mdb_dbi_close(txn->mt_env, dbi); + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + } else { + /* reset the DB record, mark it dirty */ + txn->mt_dbflags[dbi] |= DB_DIRTY; + txn->mt_dbs[dbi].md_depth = 0; + txn->mt_dbs[dbi].md_branch_pages = 0; + txn->mt_dbs[dbi].md_leaf_pages = 0; + txn->mt_dbs[dbi].md_overflow_pages = 0; + txn->mt_dbs[dbi].md_entries = 0; + txn->mt_dbs[dbi].md_root = P_INVALID; + + txn->mt_flags |= MDB_TXN_DIRTY; + } +leave: + mdb_cursor_close(mc); + return rc; +} + +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_cmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_dcmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_rel = rel; + return MDB_SUCCESS; +} + +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_relctx = ctx; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxkeysize(MDB_env *env) +{ + return ENV_MAXKEY(env); +} + +int ESECT +mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx) +{ + unsigned int i, rdrs; + MDB_reader *mr; + char buf[64]; + int rc = 0, first = 1; + + if (!env || !func) + return -1; + if (!env->me_txns) { + return func("(no reader locks)\n", ctx); + } + rdrs = env->me_txns->mti_numreaders; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + if (mr[i].mr_pid) { + txnid_t txnid = mr[i].mr_txnid; + sprintf(buf, txnid == (txnid_t)-1 ? + "%10d %"Z"x -\n" : "%10d %"Z"x %"Z"u\n", + (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid); + if (first) { + first = 0; + rc = func(" pid thread txnid\n", ctx); + if (rc < 0) + break; + } + rc = func(buf, ctx); + if (rc < 0) + break; + } + } + if (first) { + rc = func("(no active readers)\n", ctx); + } + return rc; +} + +/** Insert pid into list if not already present. + * return -1 if already present. + */ +static int ESECT +mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid) +{ + /* binary search of pid in list */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = pid - ids[cursor]; + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + /* found, so it's a duplicate */ + return -1; + } + } + + if( val > 0 ) { + ++cursor; + } + ids[0]++; + for (n = ids[0]; n > cursor; n--) + ids[n] = ids[n-1]; + ids[n] = pid; + return 0; +} + +int ESECT +mdb_reader_check(MDB_env *env, int *dead) +{ + if (!env) + return EINVAL; + if (dead) + *dead = 0; + return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS; +} + +/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */ +static int ESECT +mdb_reader_check0(MDB_env *env, int rlocked, int *dead) +{ + mdb_mutexref_t rmutex = rlocked ? NULL : env->me_rmutex; + unsigned int i, j, rdrs; + MDB_reader *mr; + MDB_PID_T *pids, pid; + int rc = MDB_SUCCESS, count = 0; + + rdrs = env->me_txns->mti_numreaders; + pids = malloc((rdrs+1) * sizeof(MDB_PID_T)); + if (!pids) + return ENOMEM; + pids[0] = 0; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + pid = mr[i].mr_pid; + if (pid && pid != env->me_pid) { + if (mdb_pid_insert(pids, pid) == 0) { + if (!mdb_reader_pid(env, Pidcheck, pid)) { + /* Stale reader found */ + j = i; + if (rmutex) { + if ((rc = LOCK_MUTEX0(rmutex)) != 0) { + if ((rc = mdb_mutex_failed(env, rmutex, rc))) + break; + rdrs = 0; /* the above checked all readers */ + } else { + /* Recheck, a new process may have reused pid */ + if (mdb_reader_pid(env, Pidcheck, pid)) + j = rdrs; + } + } + for (; j<rdrs; j++) + if (mr[j].mr_pid == pid) { + DPRINTF(("clear stale reader pid %u txn %"Z"d", + (unsigned) pid, mr[j].mr_txnid)); + mr[j].mr_pid = 0; + count++; + } + if (rmutex) + UNLOCK_MUTEX(rmutex); + } + } + } + } + free(pids); + if (dead) + *dead = count; + return rc; +} + +#ifdef MDB_ROBUST_SUPPORTED +/** Handle #LOCK_MUTEX0() failure. + * Try to repair the lock file if the mutex owner died. + * @param[in] env the environment handle + * @param[in] mutex LOCK_MUTEX0() mutex + * @param[in] rc LOCK_MUTEX0() error (nonzero) + * @return 0 on success with the mutex locked, or an error code on failure. + */ +static int ESECT +mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc) +{ + int rlocked, rc2; + MDB_meta *meta; + + if (rc == MDB_OWNERDEAD) { + /* We own the mutex. Clean up after dead previous owner. */ + rc = MDB_SUCCESS; + rlocked = (mutex == env->me_rmutex); + if (!rlocked) { + /* Keep mti_txnid updated, otherwise next writer can + * overwrite data which latest meta page refers to. + */ + meta = mdb_env_pick_meta(env); + env->me_txns->mti_txnid = meta->mm_txnid; + /* env is hosed if the dead thread was ours */ + if (env->me_txn) { + env->me_flags |= MDB_FATAL_ERROR; + env->me_txn = NULL; + rc = MDB_PANIC; + } + } + DPRINTF(("%cmutex owner died, %s", (rlocked ? 'r' : 'w'), + (rc ? "this process' env is hosed" : "recovering"))); + rc2 = mdb_reader_check0(env, rlocked, NULL); + if (rc2 == 0) + rc2 = mdb_mutex_consistent(mutex); + if (rc || (rc = rc2)) { + DPRINTF(("LOCK_MUTEX recovery failed, %s", mdb_strerror(rc))); + UNLOCK_MUTEX(mutex); + } + } else { +#ifdef _WIN32 + rc = ErrCode(); +#endif + DPRINTF(("LOCK_MUTEX failed, %s", mdb_strerror(rc))); + } + + return rc; +} +#endif /* MDB_ROBUST_SUPPORTED */ + +#if defined(_WIN32) +/** Convert \b src to new wchar_t[] string with room for \b xtra extra chars */ +static int ESECT +utf8_to_utf16(const char *src, MDB_name *dst, int xtra) +{ + int rc, need = 0; + wchar_t *result = NULL; + for (;;) { /* malloc result, then fill it in */ + need = MultiByteToWideChar(CP_UTF8, 0, src, -1, result, need); + if (!need) { + rc = ErrCode(); + free(result); + return rc; + } + if (!result) { + result = malloc(sizeof(wchar_t) * (need + xtra)); + if (!result) + return ENOMEM; + continue; + } + dst->mn_alloced = 1; + dst->mn_len = need - 1; + dst->mn_val = result; + return MDB_SUCCESS; + } +} +#endif /* defined(_WIN32) */ +/** @} */ diff --git a/contrib/libs/openldap/libraries/liblmdb/midl.c b/contrib/libs/openldap/libraries/liblmdb/midl.c new file mode 100644 index 0000000000..1cbe879606 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/midl.c @@ -0,0 +1,359 @@ +/** @file midl.c + * @brief ldap bdb back-end ID List functions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2022 The OpenLDAP Foundation. + * Portions Copyright 2001-2021 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include "midl.h" + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup idls ID List Management + * @{ + */ +#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) ) + +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( ids[cursor], id ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +#if 0 /* superseded by append/sort */ +int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) +{ + unsigned x, i; + + x = mdb_midl_search( ids, id ); + assert( x > 0 ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0] && ids[x] == id ) { + /* duplicate */ + assert(0); + return -1; + } + + if ( ++ids[0] >= MDB_IDL_DB_MAX ) { + /* no room */ + --ids[0]; + return -2; + + } else { + /* insert id */ + for (i=ids[0]; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = id; + } + + return 0; +} +#endif + +MDB_IDL mdb_midl_alloc(int num) +{ + MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID)); + if (ids) { + *ids++ = num; + *ids = 0; + } + return ids; +} + +void mdb_midl_free(MDB_IDL ids) +{ + if (ids) + free(ids-1); +} + +void mdb_midl_shrink( MDB_IDL *idp ) +{ + MDB_IDL ids = *idp; + if (*(--ids) > MDB_IDL_UM_MAX && + (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID)))) + { + *ids++ = MDB_IDL_UM_MAX; + *idp = ids; + } +} + +static int mdb_midl_grow( MDB_IDL *idp, int num ) +{ + MDB_IDL idn = *idp-1; + /* grow it */ + idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)); + if (!idn) + return ENOMEM; + *idn++ += num; + *idp = idn; + return 0; +} + +int mdb_midl_need( MDB_IDL *idp, unsigned num ) +{ + MDB_IDL ids = *idp; + num += ids[0]; + if (num > ids[-1]) { + num = (num + num/4 + (256 + 2)) & -256; + if (!(ids = realloc(ids-1, num * sizeof(MDB_ID)))) + return ENOMEM; + *ids++ = num - 2; + *idp = ids; + } + return 0; +} + +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] >= ids[-1]) { + if (mdb_midl_grow(idp, MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0]++; + ids[ids[0]] = id; + return 0; +} + +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] + app[0] >= ids[-1]) { + if (mdb_midl_grow(idp, app[0])) + return ENOMEM; + ids = *idp; + } + memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID)); + ids[0] += app[0]; + return 0; +} + +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ) +{ + MDB_ID *ids = *idp, len = ids[0]; + /* Too big? */ + if (len + n > ids[-1]) { + if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0] = len + n; + ids += len; + while (n) + ids[n--] = id++; + return 0; +} + +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ) +{ + MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k; + idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ + old_id = idl[j]; + while (i) { + merge_id = merge[i--]; + for (; old_id < merge_id; old_id = idl[--j]) + idl[k--] = old_id; + idl[k--] = merge_id; + } + idl[0] = total; +} + +/* Quicksort + Insertion sort for small arrays */ + +#define SMALL 8 +#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; } + +void +mdb_midl_sort( MDB_IDL ids ) +{ + /* Max possible depth of int-indexed tree * 2 items/level */ + int istack[sizeof(int)*CHAR_BIT * 2]; + int i,j,k,l,ir,jstack; + MDB_ID a, itmp; + + ir = (int)ids[0]; + l = 1; + jstack = 0; + for(;;) { + if (ir - l < SMALL) { /* Insertion sort */ + for (j=l+1;j<=ir;j++) { + a = ids[j]; + for (i=j-1;i>=1;i--) { + if (ids[i] >= a) break; + ids[i+1] = ids[i]; + } + ids[i+1] = a; + } + if (jstack == 0) break; + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >> 1; /* Choose median of left, center, right */ + MIDL_SWAP(ids[k], ids[l+1]); + if (ids[l] < ids[ir]) { + MIDL_SWAP(ids[l], ids[ir]); + } + if (ids[l+1] < ids[ir]) { + MIDL_SWAP(ids[l+1], ids[ir]); + } + if (ids[l] < ids[l+1]) { + MIDL_SWAP(ids[l], ids[l+1]); + } + i = l+1; + j = ir; + a = ids[l+1]; + for(;;) { + do i++; while(ids[i] > a); + do j--; while(ids[j] < a); + if (j < i) break; + MIDL_SWAP(ids[i],ids[j]); + } + ids[l+1] = ids[j]; + ids[j] = a; + jstack += 2; + if (ir-i+1 >= j-l) { + istack[jstack] = ir; + istack[jstack-1] = i; + ir = j-1; + } else { + istack[jstack] = j-1; + istack[jstack-1] = l; + l = i; + } + } + } +} + +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = (unsigned)ids[0].mid; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( id, ids[cursor].mid ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ) +{ + unsigned x, i; + + x = mdb_mid2l_search( ids, id->mid ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0].mid && ids[x].mid == id->mid ) { + /* duplicate */ + return -1; + } + + if ( ids[0].mid >= MDB_IDL_UM_MAX ) { + /* too big */ + return -2; + + } else { + /* insert id */ + ids[0].mid++; + for (i=(unsigned)ids[0].mid; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = *id; + } + + return 0; +} + +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ) +{ + /* Too big? */ + if (ids[0].mid >= MDB_IDL_UM_MAX) { + return -2; + } + ids[0].mid++; + ids[ids[0].mid] = *id; + return 0; +} + +/** @} */ +/** @} */ diff --git a/contrib/libs/openldap/libraries/liblmdb/midl.h b/contrib/libs/openldap/libraries/liblmdb/midl.h new file mode 100644 index 0000000000..2075206bd5 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/midl.h @@ -0,0 +1,186 @@ +/** @file midl.h + * @brief LMDB ID List header file. + * + * This file was originally part of back-bdb but has been + * modified for use in libmdb. Most of the macros defined + * in this file are unused, just left over from the original. + * + * This file is only used internally in libmdb and its definitions + * are not exposed publicly. + */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2022 The OpenLDAP Foundation. + * Portions Copyright 2001-2021 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _MDB_MIDL_H_ +#define _MDB_MIDL_H_ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ + +/** @defgroup idls ID List Management + * @{ + */ + /** A generic unsigned ID number. These were entryIDs in back-bdb. + * Preferably it should have the same size as a pointer. + */ +typedef size_t MDB_ID; + + /** An IDL is an ID List, a sorted array of IDs. The first + * element of the array is a counter for how many actual + * IDs are in the list. In the original back-bdb code, IDLs are + * sorted in ascending order. For libmdb IDLs are sorted in + * descending order. + */ +typedef MDB_ID *MDB_IDL; + +/* IDL sizes - likely should be even bigger + * limiting factors: sizeof(ID), thread stack size + */ +#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */ +#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN) +#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1)) + +#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1) +#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1) + +#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID)) +#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 ) +#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) )) +#define MDB_IDL_FIRST( ids ) ( (ids)[1] ) +#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] ) + + /** Current max length of an #mdb_midl_alloc()ed IDL */ +#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] ) + + /** Append ID to IDL. The IDL must be big enough. */ +#define mdb_midl_xappend(idl, id) do { \ + MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \ + xidl[xlen] = (id); \ + } while (0) + + /** Search for an ID in an IDL. + * @param[in] ids The IDL to search. + * @param[in] id The ID to search for. + * @return The index of the first ID greater than or equal to \b id. + */ +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ); + + /** Allocate an IDL. + * Allocates memory for an IDL of the given size. + * @return IDL on success, NULL on failure. + */ +MDB_IDL mdb_midl_alloc(int num); + + /** Free an IDL. + * @param[in] ids The IDL to free. + */ +void mdb_midl_free(MDB_IDL ids); + + /** Shrink an IDL. + * Return the IDL to the default size if it has grown larger. + * @param[in,out] idp Address of the IDL to shrink. + */ +void mdb_midl_shrink(MDB_IDL *idp); + + /** Make room for num additional elements in an IDL. + * @param[in,out] idp Address of the IDL. + * @param[in] num Number of elements to make room for. + * @return 0 on success, ENOMEM on failure. + */ +int mdb_midl_need(MDB_IDL *idp, unsigned num); + + /** Append an ID onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The ID to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ); + + /** Append an IDL onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] app The IDL to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ); + + /** Append an ID range onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The lowest ID to append. + * @param[in] n Number of IDs to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ); + + /** Merge an IDL onto an IDL. The destination IDL must be big enough. + * @param[in] idl The IDL to merge into. + * @param[in] merge The IDL to merge. + */ +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ); + + /** Sort an IDL. + * @param[in,out] ids The IDL to sort. + */ +void mdb_midl_sort( MDB_IDL ids ); + + /** An ID2 is an ID/pointer pair. + */ +typedef struct MDB_ID2 { + MDB_ID mid; /**< The ID */ + void *mptr; /**< The pointer */ +} MDB_ID2; + + /** An ID2L is an ID2 List, a sorted array of ID2s. + * The first element's \b mid member is a count of how many actual + * elements are in the array. The \b mptr member of the first element is unused. + * The array is sorted in ascending order by \b mid. + */ +typedef MDB_ID2 *MDB_ID2L; + + /** Search for an ID in an ID2L. + * @param[in] ids The ID2L to search. + * @param[in] id The ID to search for. + * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id. + */ +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ); + + + /** Insert an ID2 into a ID2L. + * @param[in,out] ids The ID2L to insert into. + * @param[in] id The ID2 to insert. + * @return 0 on success, -1 if the ID was already present in the ID2L. + */ +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ); + + /** Append an ID2 into a ID2L. + * @param[in,out] ids The ID2L to append into. + * @param[in] id The ID2 to append. + * @return 0 on success, -2 if the ID2L is too big. + */ +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ); + +/** @} */ +/** @} */ +#ifdef __cplusplus +} +#endif +#endif /* _MDB_MIDL_H_ */ diff --git a/contrib/libs/openldap/libraries/liblmdb/ya.make b/contrib/libs/openldap/libraries/liblmdb/ya.make new file mode 100644 index 0000000000..f64e636adf --- /dev/null +++ b/contrib/libs/openldap/libraries/liblmdb/ya.make @@ -0,0 +1,32 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE( + ISC AND + OLDAP-2.8 AND + Public-Domain +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLDAPI_SOCK=\"/run/openldap/ldapi\" +) + +IF (OS_ANDROID) + CFLAGS( + -DMDB_USE_ROBUST=0 + ) +ENDIF() + +SRCS( + mdb.c + midl.c +) + +END() diff --git a/contrib/libs/openldap/libraries/liblunicode/ucdata/MUTTUCData.txt b/contrib/libs/openldap/libraries/liblunicode/ucdata/MUTTUCData.txt new file mode 100644 index 0000000000..82c4659411 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblunicode/ucdata/MUTTUCData.txt @@ -0,0 +1,303 @@ +# +# $Id: MUTTUCData.txt,v 1.3 1999/10/29 00:04:35 mleisher Exp $ +# +# Copyright 1999 Computing Research Labs, New Mexico State University +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +# THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# +# Implementation specific character properties. +# +# +# Space, other. +# +0009;;Ss;;;;;;;;;;;; +000A;;Ss;;;;;;;;;;;; +000B;;Ss;;;;;;;;;;;; +000C;;Ss;;;;;;;;;;;; +000D;;Ss;;;;;;;;;;;; +# +# Non-breaking. +# +00A0;;Nb;;;;;;;;;;;; +2007;;Nb;;;;;;;;;;;; +2011;;Nb;;;;;;;;;;;; +FEFF;;Nb;;;;;;;;;;;; +# +# Symmetric. +# +0028;;Sy;;;;;;;;;;;; +0029;;Sy;;;;;;;;;;;; +005B;;Sy;;;;;;;;;;;; +005D;;Sy;;;;;;;;;;;; +007B;;Sy;;;;;;;;;;;; +007D;;Sy;;;;;;;;;;;; +00AB;;Sy;;;;;;;;;;;; +00BB;;Sy;;;;;;;;;;;; +0F3A;;Sy;;;;;;;;;;;; +0F3B;;Sy;;;;;;;;;;;; +0F3C;;Sy;;;;;;;;;;;; +0F3D;;Sy;;;;;;;;;;;; +0F3E;;Sy;;;;;;;;;;;; +0F3F;;Sy;;;;;;;;;;;; +2018;;Sy;;;;;;;;;;;; +2019;;Sy;;;;;;;;;;;; +201A;;Sy;;;;;;;;;;;; +201B;;Sy;;;;;;;;;;;; +201C;;Sy;;;;;;;;;;;; +201D;;Sy;;;;;;;;;;;; +201E;;Sy;;;;;;;;;;;; +201F;;Sy;;;;;;;;;;;; +2039;;Sy;;;;;;;;;;;; +203A;;Sy;;;;;;;;;;;; +2045;;Sy;;;;;;;;;;;; +2046;;Sy;;;;;;;;;;;; +207D;;Sy;;;;;;;;;;;; +207E;;Sy;;;;;;;;;;;; +208D;;Sy;;;;;;;;;;;; +208E;;Sy;;;;;;;;;;;; +2329;;Sy;;;;;;;;;;;; +232A;;Sy;;;;;;;;;;;; +3008;;Sy;;;;;;;;;;;; +3009;;Sy;;;;;;;;;;;; +300A;;Sy;;;;;;;;;;;; +300B;;Sy;;;;;;;;;;;; +300C;;Sy;;;;;;;;;;;; +300D;;Sy;;;;;;;;;;;; +300E;;Sy;;;;;;;;;;;; +300F;;Sy;;;;;;;;;;;; +3010;;Sy;;;;;;;;;;;; +3011;;Sy;;;;;;;;;;;; +3014;;Sy;;;;;;;;;;;; +3015;;Sy;;;;;;;;;;;; +3016;;Sy;;;;;;;;;;;; +3017;;Sy;;;;;;;;;;;; +3018;;Sy;;;;;;;;;;;; +3019;;Sy;;;;;;;;;;;; +301A;;Sy;;;;;;;;;;;; +301B;;Sy;;;;;;;;;;;; +301D;;Sy;;;;;;;;;;;; +301E;;Sy;;;;;;;;;;;; +301F;;Sy;;;;;;;;;;;; +FD3E;;Sy;;;;;;;;;;;; +FD3F;;Sy;;;;;;;;;;;; +FE35;;Sy;;;;;;;;;;;; +FE36;;Sy;;;;;;;;;;;; +FE37;;Sy;;;;;;;;;;;; +FE38;;Sy;;;;;;;;;;;; +FE39;;Sy;;;;;;;;;;;; +FE3A;;Sy;;;;;;;;;;;; +FE3B;;Sy;;;;;;;;;;;; +FE3C;;Sy;;;;;;;;;;;; +FE3D;;Sy;;;;;;;;;;;; +FE3E;;Sy;;;;;;;;;;;; +FE3F;;Sy;;;;;;;;;;;; +FE40;;Sy;;;;;;;;;;;; +FE41;;Sy;;;;;;;;;;;; +FE42;;Sy;;;;;;;;;;;; +FE43;;Sy;;;;;;;;;;;; +FE44;;Sy;;;;;;;;;;;; +FE59;;Sy;;;;;;;;;;;; +FE5A;;Sy;;;;;;;;;;;; +FE5B;;Sy;;;;;;;;;;;; +FE5C;;Sy;;;;;;;;;;;; +FE5D;;Sy;;;;;;;;;;;; +FE5E;;Sy;;;;;;;;;;;; +FF08;;Sy;;;;;;;;;;;; +FF09;;Sy;;;;;;;;;;;; +FF3B;;Sy;;;;;;;;;;;; +FF3D;;Sy;;;;;;;;;;;; +FF5B;;Sy;;;;;;;;;;;; +FF5D;;Sy;;;;;;;;;;;; +FF62;;Sy;;;;;;;;;;;; +FF63;;Sy;;;;;;;;;;;; +# +# Hex digit. +# +0030;;Hd;;;;;;;;;;;; +0031;;Hd;;;;;;;;;;;; +0032;;Hd;;;;;;;;;;;; +0033;;Hd;;;;;;;;;;;; +0034;;Hd;;;;;;;;;;;; +0035;;Hd;;;;;;;;;;;; +0036;;Hd;;;;;;;;;;;; +0037;;Hd;;;;;;;;;;;; +0038;;Hd;;;;;;;;;;;; +0039;;Hd;;;;;;;;;;;; +0041;;Hd;;;;;;;;;;;; +0042;;Hd;;;;;;;;;;;; +0043;;Hd;;;;;;;;;;;; +0044;;Hd;;;;;;;;;;;; +0045;;Hd;;;;;;;;;;;; +0046;;Hd;;;;;;;;;;;; +0061;;Hd;;;;;;;;;;;; +0062;;Hd;;;;;;;;;;;; +0063;;Hd;;;;;;;;;;;; +0064;;Hd;;;;;;;;;;;; +0065;;Hd;;;;;;;;;;;; +0066;;Hd;;;;;;;;;;;; +FF10;;Hd;;;;;;;;;;;; +FF11;;Hd;;;;;;;;;;;; +FF12;;Hd;;;;;;;;;;;; +FF13;;Hd;;;;;;;;;;;; +FF14;;Hd;;;;;;;;;;;; +FF15;;Hd;;;;;;;;;;;; +FF16;;Hd;;;;;;;;;;;; +FF17;;Hd;;;;;;;;;;;; +FF18;;Hd;;;;;;;;;;;; +FF19;;Hd;;;;;;;;;;;; +FF21;;Hd;;;;;;;;;;;; +FF22;;Hd;;;;;;;;;;;; +FF23;;Hd;;;;;;;;;;;; +FF24;;Hd;;;;;;;;;;;; +FF25;;Hd;;;;;;;;;;;; +FF26;;Hd;;;;;;;;;;;; +FF41;;Hd;;;;;;;;;;;; +FF42;;Hd;;;;;;;;;;;; +FF43;;Hd;;;;;;;;;;;; +FF44;;Hd;;;;;;;;;;;; +FF45;;Hd;;;;;;;;;;;; +FF46;;Hd;;;;;;;;;;;; +# +# Quote marks. +# +0022;;Qm;;;;;;;;;;;; +0027;;Qm;;;;;;;;;;;; +00AB;;Qm;;;;;;;;;;;; +00BB;;Qm;;;;;;;;;;;; +2018;;Qm;;;;;;;;;;;; +2019;;Qm;;;;;;;;;;;; +201A;;Qm;;;;;;;;;;;; +201B;;Qm;;;;;;;;;;;; +201C;;Qm;;;;;;;;;;;; +201D;;Qm;;;;;;;;;;;; +201E;;Qm;;;;;;;;;;;; +201F;;Qm;;;;;;;;;;;; +2039;;Qm;;;;;;;;;;;; +203A;;Qm;;;;;;;;;;;; +300C;;Qm;;;;;;;;;;;; +300D;;Qm;;;;;;;;;;;; +300E;;Qm;;;;;;;;;;;; +300F;;Qm;;;;;;;;;;;; +301D;;Qm;;;;;;;;;;;; +301E;;Qm;;;;;;;;;;;; +301F;;Qm;;;;;;;;;;;; +FE41;;Qm;;;;;;;;;;;; +FE42;;Qm;;;;;;;;;;;; +FE43;;Qm;;;;;;;;;;;; +FE44;;Qm;;;;;;;;;;;; +FF02;;Qm;;;;;;;;;;;; +FF07;;Qm;;;;;;;;;;;; +FF62;;Qm;;;;;;;;;;;; +FF63;;Qm;;;;;;;;;;;; +# +# Special Devanagari forms +# +E900;DEVANAGARI KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;; +E901;DEVANAGARI GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;; +E902;DEVANAGARI TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;; +E903;DEVANAGARI TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;; +E904;DEVANAGARI SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;; +E905;DEVANAGARI SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;; +E906;DEVANAGARI SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;; +E907;DEVANAGARI KRA LIGATURE;Lo;0;L;;;;;N;;;;; +E908;DEVANAGARI JRA LIGATURE;Lo;0;L;;;;;N;;;;; +E909;DEVANAGARI ZRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90A;DEVANAGARI PHRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90B;DEVANAGARI FRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90C;DEVANAGARI PRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90D;DEVANAGARI SRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90E;DEVANAGARI RU LIGATURE;Lo;0;L;;;;;N;;;;; +E90F;DEVANAGARI RUU LIGATURE;Lo;0;L;;;;;N;;;;; +E915;DEVANAGARI HALF LETTER KA;Lo;0;L;;;;;N;;;;; +E916;DEVANAGARI HALF LETTER KHA;Lo;0;L;;;;;N;;;;; +E917;DEVANAGARI HALF LETTER GA;Lo;0;L;;;;;N;;;;; +E918;DEVANAGARI HALF LETTER GHA;Lo;0;L;;;;;N;;;;; +E919;DEVANAGARI HALF LETTER NGA;Lo;0;L;;;;;N;;;;; +E91A;DEVANAGARI HALF LETTER CA;Lo;0;L;;;;;N;;;;; +E91B;DEVANAGARI HALF LETTER CHA;Lo;0;L;;;;;N;;;;; +E91C;DEVANAGARI HALF LETTER JA;Lo;0;L;;;;;N;;;;; +E91D;DEVANAGARI HALF LETTER JHA;Lo;0;L;;;;;N;;;;; +E91E;DEVANAGARI HALF LETTER NYA;Lo;0;L;;;;;N;;;;; +E91F;DEVANAGARI HALF LETTER TTA;Lo;0;L;;;;;N;;;;; +E920;DEVANAGARI HALF LETTER TTHA;Lo;0;L;;;;;N;;;;; +E921;DEVANAGARI HALF LETTER DDA;Lo;0;L;;;;;N;;;;; +E922;DEVANAGARI HALF LETTER DDHA;Lo;0;L;;;;;N;;;;; +E923;DEVANAGARI HALF LETTER NNA;Lo;0;L;;;;;N;;;;; +E924;DEVANAGARI HALF LETTER TA;Lo;0;L;;;;;N;;;;; +E925;DEVANAGARI HALF LETTER THA;Lo;0;L;;;;;N;;;;; +E926;DEVANAGARI HALF LETTER DA;Lo;0;L;;;;;N;;;;; +E927;DEVANAGARI HALF LETTER DHA;Lo;0;L;;;;;N;;;;; +E928;DEVANAGARI HALF LETTER NA;Lo;0;L;;;;;N;;;;; +E929;DEVANAGARI HALF LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;; +E92A;DEVANAGARI HALF LETTER PA;Lo;0;L;;;;;N;;;;; +E92B;DEVANAGARI HALF LETTER PHA;Lo;0;L;;;;;N;;;;; +E92C;DEVANAGARI HALF LETTER BA;Lo;0;L;;;;;N;;;;; +E92D;DEVANAGARI HALF LETTER BHA;Lo;0;L;;;;;N;;;;; +E92E;DEVANAGARI HALF LETTER MA;Lo;0;L;;;;;N;;;;; +E92F;DEVANAGARI HALF LETTER YA;Lo;0;L;;;;;N;;;;; +E930;DEVANAGARI HALF LETTER RA;Lo;0;L;;;;;N;;;;; +E931;DEVANAGARI HALF LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;; +E932;DEVANAGARI HALF LETTER LA;Lo;0;L;;;;;N;;;;; +E933;DEVANAGARI HALF LETTER LLA;Lo;0;L;;;;;N;;;;; +E934;DEVANAGARI HALF LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;; +E935;DEVANAGARI HALF LETTER VA;Lo;0;L;;;;;N;;;;; +E936;DEVANAGARI HALF LETTER SHA;Lo;0;L;;;;;N;;;;; +E937;DEVANAGARI HALF LETTER SSA;Lo;0;L;;;;;N;;;;; +E938;DEVANAGARI HALF LETTER SA;Lo;0;L;;;;;N;;;;; +E939;DEVANAGARI HALF LETTER HA;Lo;0;L;;;;;N;;;;; +E940;DEVANAGARI KKA LIGATURE;Lo;0;L;0915 094D 0915;;;;N;;;;; +E941;DEVANAGARI KTA LIGATURE;Lo;0;L;0915 094D 0924;;;;N;;;;; +E942;DEVANAGARI NGKA LIGATURE;Lo;0;L;0919 094D 0915;;;;N;;;;; +E943;DEVANAGARI NGKHA LIGATURE;Lo;0;L;0919 094D 0916;;;;N;;;;; +E944;DEVANAGARI NGGA LIGATURE;Lo;0;L;0919 094D 0917;;;;N;;;;; +E945;DEVANAGARI NGGHA LIGATURE;Lo;0;L;0919 094D 0918;;;;N;;;;; +E946;DEVANAGARI NYJA LIGATURE;Lo;0;L;091E 094D 091C;;;;N;;;;; +E947;DEVANAGARI DGHA LIGATURE;Lo;0;L;0926 094D 0918;;;;N;;;;; +E948;DEVANAGARI DDA LIGATURE;Lo;0;L;0926 094D 0926;;;;N;;;;; +E949;DEVANAGARI DDHA LIGATURE;Lo;0;L;0926 094D 0927;;;;N;;;;; +E94A;DEVANAGARI DBA LIGATURE;Lo;0;L;0926 094D 092C;;;;N;;;;; +E94B;DEVANAGARI DBHA LIGATURE;Lo;0;L;0926 094D 092D;;;;N;;;;; +E94C;DEVANAGARI DMA LIGATURE;Lo;0;L;0926 094D 092E;;;;N;;;;; +E94D;DEVANAGARI DYA LIGATURE;Lo;0;L;0926 094D 092F;;;;N;;;;; +E94E;DEVANAGARI DVA LIGATURE;Lo;0;L;0926 094D 0935;;;;N;;;;; +E94F;DEVANAGARI TT-TTA LIGATURE;Lo;0;L;091F 094D 091F;;;;N;;;;; +E950;DEVANAGARI TT-TTHA LIGATURE;Lo;0;L;091F 094D 0920;;;;N;;;;; +E951;DEVANAGARI TTH-TTHA LIGATURE;Lo;0;L;0920 094D 0920;;;;N;;;;; +E952;DEVANAGARI DD-GA LIGATURE;Lo;0;L;0921 094D 0917;;;;N;;;;; +E953;DEVANAGARI DD-DDA LIGATURE;Lo;0;L;0921 094D 0921;;;;N;;;;; +E954;DEVANAGARI DD-DDHA LIGATURE;Lo;0;L;0921 094D 0922;;;;N;;;;; +E955;DEVANAGARI NNA LIGATURE;Lo;0;L;0928 094D 0928;;;;N;;;;; +E956;DEVANAGARI HMA LIGATURE;Lo;0;L;0939 094D 092E;;;;N;;;;; +E957;DEVANAGARI HYA LIGATURE;Lo;0;L;0939 094D 092F;;;;N;;;;; +E958;DEVANAGARI HLA LIGATURE;Lo;0;L;0939 094D 0932;;;;N;;;;; +E959;DEVANAGARI HVA LIGATURE;Lo;0;L;0939 094D 0935;;;;N;;;;; +E95A;DEVANAGARI STRA LIGATURE;Lo;0;L;0938 094D 0924 094D 0930;;;;N;;;;; +E970;DEVANAGARI HALF KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;; +E971;DEVANAGARI HALF GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;; +E972;DEVANAGARI HALF TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;; +E973;DEVANAGARI HALF TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;; +E974;DEVANAGARI HALF SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;; +E975;DEVANAGARI HALF SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;; +E976;DEVANAGARI HALF SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;; +E97B;DEVANAGARI SIGN RRA-REPHA;Mn;36;L;;;;;N;;;;; +E97C;DEVANAGARI HAR LIGATURE;Lo;0;L;0939 0943;;;;N;;;;; +E97D;DEVANAGARI SIGN EYELASH RA;Lo;0;L;;;;;N;;;;; +E97E;DEVANAGARI SIGN REPHA;Mn;36;L;;;;;N;;;;; +E97F;DEVANAGARI SIGN SUBJOINED RA;Mn;36;L;;;;;N;;;;; diff --git a/contrib/libs/openldap/libraries/liblunicode/ucdata/README b/contrib/libs/openldap/libraries/liblunicode/ucdata/README new file mode 100644 index 0000000000..6a02cc1857 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblunicode/ucdata/README @@ -0,0 +1,313 @@ +# +# $Id: README,v 1.33 2001/01/02 18:46:19 mleisher Exp $ +# + + MUTT UCData Package 2.5 + ----------------------- + +This is a package that supports ctype-like operations for Unicode UCS-2 text +(and surrogates), case mapping, decomposition lookup, and provides a +bidirectional reordering algorithm. To use it, you will need to get the +latest "UnicodeData-*.txt" (or later) file from the Unicode Web or FTP site. + +The character information portion of the package consists of three parts: + + 1. A program called "ucgendat" which generates five data files from the + UnicodeData-*.txt file. The files are: + + A. case.dat - the case mappings. + B. ctype.dat - the character property tables. + C. comp.dat - the character composition pairs. + D. decomp.dat - the character decompositions. + E. cmbcl.dat - the non-zero combining classes. + F. num.dat - the codes representing numbers. + + 2. The "ucdata.[ch]" files which implement the functions needed to + check to see if a character matches groups of properties, to map between + upper, lower, and title case, to look up the decomposition of a + character, look up the combining class of a character, and get the number + value of a character. + + 3. The UCData.java class which provides the same API (with minor changes for + the numbers) and loads the same binary data files as the C code. + +A short reference to the functions available is in the "api.txt" file. + +Techie Details +============== + +The "ucgendat" program parses files from the command line which are all in the +Unicode Character Database (UCDB) format. An additional properties file, +"MUTTUCData.txt", provides some extra properties for some characters. + +The program looks for the two character properties fields (2 and 4), the +combining class field (3), the decomposition field (5), the numeric value +field (8), and the case mapping fields (12, 13, and 14). The decompositions +are recursively expanded before being written out. + +The decomposition table contains all the canonical decompositions. This means +all decompositions that do not have tags such as "<compat>" or "<font>". + +The data is almost all stored as unsigned longs (32-bits assumed) and the +routines that load the data take care of endian swaps when necessary. This +also means that supplementary characters (>= 0x10000) can be placed in the +data files the "ucgendat" program parses. + +The data is written as external files and broken into six parts so it can be +selectively updated at runtime if necessary. + +The data files currently generated from the "ucgendat" program total about 56K +in size all together. + +The format of the binary data files is documented in the "format.txt" file. + +========================================================================== + + The "Pretty Good Bidi Algorithm" + -------------------------------- + +This routine provides an alternative to the Unicode Bidi algorithm. The +difference is that this version of the PGBA does not handle the explicit +directional codes (LRE, RLE, LRO, RLO, PDF). It should now produce the same +results as the Unicode BiDi algorithm for implicit reordering. Included are +functions for doing cursor motion in both logical and visual order. + +This implementation is provided to demonstrate an effective alternate method +for implicit reordering. To make this useful for an application, it probably +needs some changes to the memory allocation and deallocation, as well as data +structure additions for rendering. + +Mark Leisher <mleisher@crl.nmsu.edu> +19 November 1999 + +----------------------------------------------------------------------------- + +CHANGES +======= +Version 2.5 +----------- +1. Changed the number lookup to set the denominator to 1 in cases of digits. + This restores functional compatibility with John Cowan's UCType package. + +2. Added support for the AL property. + +3. Modified load and reload functions to return error codes. + +Version 2.4 +----------- +1. Improved some bidi algorithm documentation in the code. + +2. Fixed a code mixup that produced a non-working version. + +Version 2.3 +----------- +1. Fixed a misspelling in the ucpgba.h header file. + +2. Fixed a bug which caused trailing weak non-digit sequences to be left out of + the reordered string in the bidi algorithm. + +3. Fixed a problem with weak sequences containing non-spacing marks in the + bidi algorithm. + +4. Fixed a problem with text runs of the opposite direction of the string + surrounding a weak + neutral text run appearing in the wrong order in the + bidi algorithm. + +5. Added a default overall direction parameter to the reordering function for + cases of strings with no strong directional characters in the bidi + algorithm. + +6. The bidi API documentation was improved. + +7. Added a man page for the bidi API. + +Version 2.2 +----------- +1. Fixed a problem with the bidi algorithm locating directional section + boundaries. + +2. Fixed a problem with the bidi algorithm starting the reordering correctly. + +3. Fixed a problem with the bidi algorithm determining end boundaries for LTR + segments. + +4. Fixed a problem with the bidi algorithm reordering weak (digits and number + separators) segments. + +5. Added automatic switching of symmetrically paired characters when + reversing RTL segments. + +6. Added a missing symmetric character to the extra character properties in + MUTTUCData.txt. + +7. Added support for doing logical and visual cursor traversal. + +Version 2.1 +----------- +1. Updated the ucgendat program to handle the Unicode 3.0 character database + properties. The AL and BM bidi properties gets marked as strong RTL and + Other Neutral, the NSM, LRE, RLE, PDF, LRO, and RLO controls all get marked + as Other Neutral. + +2. Fixed some problems with testing against signed values in the UCData.java + code and some minor cleanup. + +3. Added the "Pretty Good Bidi Algorithm." + +Version 2.0 +----------- +1. Removed the old Java stuff for a new class that loads directly from the + same data files as the C code does. + +2. Fixed a problem with choosing the correct field when mapping case. + +3. Adjust some search routines to start their search in the correct position. + +4. Moved the copyright year to 1999. + +Version 1.9 +----------- +1. Fixed a problem with an incorrect amount of storage being allocated for the + combining class nodes. + +2. Fixed an invalid initialization in the number code. + +3. Changed the Java template file formatting a bit. + +4. Added tables and function for getting decompositions in the Java class. + +Version 1.8 +----------- +1. Fixed a problem with adding certain ranges. + +2. Added two more macros for testing for identifiers. + +3. Tested with the UnicodeData-2.1.5.txt file. + +Version 1.7 +----------- +1. Fixed a problem with looking up decompositions in "ucgendat." + +Version 1.6 +----------- +1. Added two new properties introduced with UnicodeData-2.1.4.txt. + +2. Changed the "ucgendat.c" program a little to automatically align the + property data on a 4-byte boundary when new properties are added. + +3. Changed the "ucgendat.c" programs to only generate canonical + decompositions. + +4. Added two new macros ucisinitialpunct() and ucisfinalpunct() to check for + initial and final punctuation characters. + +5. Minor additions and changes to the documentation. + +Version 1.5 +----------- +1. Changed all file open calls to include binary mode with "b" for DOS/WIN + platforms. + +2. Wrapped the unistd.h include so it won't be included when compiled under + Win32. + +3. Fixed a bad range check for hex digits in ucgendat.c. + +4. Fixed a bad endian swap for combining classes. + +5. Added code to make a number table and associated lookup functions. + Functions added are ucnumber(), ucdigit(), and ucgetnumber(). The last + function is to maintain compatibility with John Cowan's "uctype" package. + +Version 1.4 +----------- +1. Fixed a bug with adding a range. + +2. Fixed a bug with inserting a range in order. + +3. Fixed incorrectly specified ucisdefined() and ucisundefined() macros. + +4. Added the missing unload for the combining class data. + +5. Fixed a bad macro placement in ucisweak(). + +Version 1.3 +----------- +1. Bug with case mapping calculations fixed. + +2. Bug with empty character property entries fixed. + +3. Bug with incorrect type in the combining class lookup fixed. + +4. Some corrections done to api.txt. + +5. Bug in certain character property lookups fixed. + +6. Added a character property table that records the defined characters. + +7. Replaced ucisunknown() with ucisdefined() and ucisundefined(). + +Version 1.2 +----------- +1. Added code to ucgendat to generate a combining class table. + +2. Fixed an endian problem with the byte count of decompositions. + +3. Fixed some minor problems in the "format.txt" file. + +4. Removed some bogus "Ss" values from MUTTUCData.txt file. + +5. Added API function to get combining class. + +6. Changed the open mode to "rb" so binary data files will be opened correctly + on DOS/WIN as well as other platforms. + +7. Added the "api.txt" file. + +Version 1.1 +----------- +1. Added ucisxdigit() which I overlooked. + +2. Added UC_LT to the ucisalpha() macro which I overlooked. + +3. Change uciscntrl() to include UC_CF. + +4. Added ucisocntrl() and ucfntcntrl() macros. + +5. Added a ucisblank() which I overlooked. + +6. Added missing properties to ucissymbol() and ucisnumber(). + +7. Added ucisgraph() and ucisprint(). + +8. Changed the "Mr" property to "Sy" to mark this subset of mirroring + characters as symmetric to avoid trampling the Unicode/ISO10646 sense of + mirroring. + +9. Added another property called "Ss" which includes control characters + traditionally seen as spaces in the isspace() macro. + +10. Added a bunch of macros to be API compatible with John Cowan's package. + +ACKNOWLEDGEMENTS +================ + +Thanks go to John Cowan <cowan@locke.ccil.org> for pointing out lots of +missing things and giving me stuff, particularly a bunch of new macros. + +Thanks go to Bob Verbrugge <bob_verbrugge@nl.compuware.com> for pointing out +various bugs. + +Thanks go to Christophe Pierret <cpierret@businessobjects.com> for pointing +out that file modes need to have "b" for DOS/WIN machines, pointing out +unistd.h is not a Win 32 header, and pointing out a problem with ucisalnum(). + +Thanks go to Kent Johnson <kent@pondview.mv.com> for finding a bug that caused +incomplete decompositions to be generated by the "ucgendat" program. + +Thanks go to Valeriy E. Ushakov <uwe@ptc.spbu.ru> for spotting an allocation +error and an initialization error. + +Thanks go to Stig Venaas <Stig.Venaas@uninett.no> for providing a patch to +support return types on load and reload, and for major updates to handle +canonical composition and decomposition. diff --git a/contrib/libs/openldap/libraries/liblunicode/ucdata/ucdata.h b/contrib/libs/openldap/libraries/liblunicode/ucdata/ucdata.h new file mode 100644 index 0000000000..30674631f7 --- /dev/null +++ b/contrib/libs/openldap/libraries/liblunicode/ucdata/ucdata.h @@ -0,0 +1,364 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 2001 Computing Research Labs, New Mexico State University + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* $Id: ucdata.h,v 1.6 2001/01/02 18:46:20 mleisher Exp $ */ + +#ifndef _h_ucdata +#define _h_ucdata + +LDAP_BEGIN_DECL + +#define UCDATA_VERSION "2.4" + +/************************************************************************** + * + * Masks and macros for character properties. + * + **************************************************************************/ + +/* + * Values that can appear in the `mask1' parameter of the ucisprop() + * function. + */ +#define UC_MN 0x00000001 /* Mark, Non-Spacing */ +#define UC_MC 0x00000002 /* Mark, Spacing Combining */ +#define UC_ME 0x00000004 /* Mark, Enclosing */ +#define UC_ND 0x00000008 /* Number, Decimal Digit */ +#define UC_NL 0x00000010 /* Number, Letter */ +#define UC_NO 0x00000020 /* Number, Other */ +#define UC_ZS 0x00000040 /* Separator, Space */ +#define UC_ZL 0x00000080 /* Separator, Line */ +#define UC_ZP 0x00000100 /* Separator, Paragraph */ +#define UC_CC 0x00000200 /* Other, Control */ +#define UC_CF 0x00000400 /* Other, Format */ +#define UC_OS 0x00000800 /* Other, Surrogate */ +#define UC_CO 0x00001000 /* Other, Private Use */ +#define UC_CN 0x00002000 /* Other, Not Assigned */ +#define UC_LU 0x00004000 /* Letter, Uppercase */ +#define UC_LL 0x00008000 /* Letter, Lowercase */ +#define UC_LT 0x00010000 /* Letter, Titlecase */ +#define UC_LM 0x00020000 /* Letter, Modifier */ +#define UC_LO 0x00040000 /* Letter, Other */ +#define UC_PC 0x00080000 /* Punctuation, Connector */ +#define UC_PD 0x00100000 /* Punctuation, Dash */ +#define UC_PS 0x00200000 /* Punctuation, Open */ +#define UC_PE 0x00400000 /* Punctuation, Close */ +#define UC_PO 0x00800000 /* Punctuation, Other */ +#define UC_SM 0x01000000 /* Symbol, Math */ +#define UC_SC 0x02000000 /* Symbol, Currency */ +#define UC_SK 0x04000000 /* Symbol, Modifier */ +#define UC_SO 0x08000000 /* Symbol, Other */ +#define UC_L 0x10000000 /* Left-To-Right */ +#define UC_R 0x20000000 /* Right-To-Left */ +#define UC_EN 0x40000000 /* European Number */ +#define UC_ES 0x80000000 /* European Number Separator */ + +/* + * Values that can appear in the `mask2' parameter of the ucisprop() + * function. + */ +#define UC_ET 0x00000001 /* European Number Terminator */ +#define UC_AN 0x00000002 /* Arabic Number */ +#define UC_CS 0x00000004 /* Common Number Separator */ +#define UC_B 0x00000008 /* Block Separator */ +#define UC_S 0x00000010 /* Segment Separator */ +#define UC_WS 0x00000020 /* Whitespace */ +#define UC_ON 0x00000040 /* Other Neutrals */ +/* + * Implementation specific character properties. + */ +#define UC_CM 0x00000080 /* Composite */ +#define UC_NB 0x00000100 /* Non-Breaking */ +#define UC_SY 0x00000200 /* Symmetric */ +#define UC_HD 0x00000400 /* Hex Digit */ +#define UC_QM 0x00000800 /* Quote Mark */ +#define UC_MR 0x00001000 /* Mirroring */ +#define UC_SS 0x00002000 /* Space, other */ + +#define UC_CP 0x00004000 /* Defined */ + +/* + * Added for UnicodeData-2.1.3. + */ +#define UC_PI 0x00008000 /* Punctuation, Initial */ +#define UC_PF 0x00010000 /* Punctuation, Final */ + +/* + * This is the primary function for testing to see if a character has some set + * of properties. The macros that test for various character properties all + * call this function with some set of masks. + */ +LDAP_LUNICODE_F (int) +ucisprop LDAP_P((ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2)); + +#define ucisalpha(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT, 0) +#define ucisdigit(cc) ucisprop(cc, UC_ND, 0) +#define ucisalnum(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT|UC_ND, 0) +#define uciscntrl(cc) ucisprop(cc, UC_CC|UC_CF, 0) +#define ucisspace(cc) ucisprop(cc, UC_ZS|UC_SS, 0) +#define ucisblank(cc) ucisprop(cc, UC_ZS, 0) +#define ucispunct(cc) ucisprop(cc, UC_PD|UC_PS|UC_PE|UC_PO, UC_PI|UC_PF) +#define ucisgraph(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\ + UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\ + UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\ + UC_SO, UC_PI|UC_PF) +#define ucisprint(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\ + UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\ + UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\ + UC_SO|UC_ZS, UC_PI|UC_PF) +#define ucisupper(cc) ucisprop(cc, UC_LU, 0) +#define ucislower(cc) ucisprop(cc, UC_LL, 0) +#define ucistitle(cc) ucisprop(cc, UC_LT, 0) +#define ucisxdigit(cc) ucisprop(cc, 0, UC_HD) + +#define ucisisocntrl(cc) ucisprop(cc, UC_CC, 0) +#define ucisfmtcntrl(cc) ucisprop(cc, UC_CF, 0) + +#define ucissymbol(cc) ucisprop(cc, UC_SM|UC_SC|UC_SO|UC_SK, 0) +#define ucisnumber(cc) ucisprop(cc, UC_ND|UC_NO|UC_NL, 0) +#define ucisnonspacing(cc) ucisprop(cc, UC_MN, 0) +#define ucisopenpunct(cc) ucisprop(cc, UC_PS, 0) +#define ucisclosepunct(cc) ucisprop(cc, UC_PE, 0) +#define ucisinitialpunct(cc) ucisprop(cc, 0, UC_PI) +#define ucisfinalpunct(cc) ucisprop(cc, 0, UC_PF) + +#define uciscomposite(cc) ucisprop(cc, 0, UC_CM) +#define ucishex(cc) ucisprop(cc, 0, UC_HD) +#define ucisquote(cc) ucisprop(cc, 0, UC_QM) +#define ucissymmetric(cc) ucisprop(cc, 0, UC_SY) +#define ucismirroring(cc) ucisprop(cc, 0, UC_MR) +#define ucisnonbreaking(cc) ucisprop(cc, 0, UC_NB) + +/* + * Directionality macros. + */ +#define ucisrtl(cc) ucisprop(cc, UC_R, 0) +#define ucisltr(cc) ucisprop(cc, UC_L, 0) +#define ucisstrong(cc) ucisprop(cc, UC_L|UC_R, 0) +#define ucisweak(cc) ucisprop(cc, UC_EN|UC_ES, UC_ET|UC_AN|UC_CS) +#define ucisneutral(cc) ucisprop(cc, 0, UC_B|UC_S|UC_WS|UC_ON) +#define ucisseparator(cc) ucisprop(cc, 0, UC_B|UC_S) + +/* + * Other macros inspired by John Cowan. + */ +#define ucismark(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME, 0) +#define ucismodif(cc) ucisprop(cc, UC_LM, 0) +#define ucisletnum(cc) ucisprop(cc, UC_NL, 0) +#define ucisconnect(cc) ucisprop(cc, UC_PC, 0) +#define ucisdash(cc) ucisprop(cc, UC_PD, 0) +#define ucismath(cc) ucisprop(cc, UC_SM, 0) +#define uciscurrency(cc) ucisprop(cc, UC_SC, 0) +#define ucismodifsymbol(cc) ucisprop(cc, UC_SK, 0) +#define ucisnsmark(cc) ucisprop(cc, UC_MN, 0) +#define ucisspmark(cc) ucisprop(cc, UC_MC, 0) +#define ucisenclosing(cc) ucisprop(cc, UC_ME, 0) +#define ucisprivate(cc) ucisprop(cc, UC_CO, 0) +#define ucissurrogate(cc) ucisprop(cc, UC_OS, 0) +#define ucislsep(cc) ucisprop(cc, UC_ZL, 0) +#define ucispsep(cc) ucisprop(cc, UC_ZP, 0) + +#define ucisidentstart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL, 0) +#define ucisidentpart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL|\ + UC_MN|UC_MC|UC_ND|UC_PC|UC_CF, 0) + +#define ucisdefined(cc) ucisprop(cc, 0, UC_CP) +#define ucisundefined(cc) !ucisprop(cc, 0, UC_CP) + +/* + * Other miscellaneous character property macros. + */ +#define ucishan(cc) (((cc) >= 0x4e00 && (cc) <= 0x9fff) ||\ + ((cc) >= 0xf900 && (cc) <= 0xfaff)) +#define ucishangul(cc) ((cc) >= 0xac00 && (cc) <= 0xd7ff) + +/************************************************************************** + * + * Functions for case conversion. + * + **************************************************************************/ + +LDAP_LUNICODE_F (ac_uint4) uctoupper LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (ac_uint4) uctolower LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (ac_uint4) uctotitle LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions for getting compositions. + * + **************************************************************************/ + +/* + * This routine determines if there exists a composition of node1 and node2. + * If it returns 0, there is no composition. Any other value indicates a + * composition was returned in comp. + */ +LDAP_LUNICODE_F (int) uccomp LDAP_P((ac_uint4 node1, ac_uint4 node2, + ac_uint4 *comp)); + +/* + * Does Hangul composition on the string str with length len, and returns + * the length of the composed string. + */ +LDAP_LUNICODE_F (int) uccomp_hangul LDAP_P((ac_uint4 *str, int len)); + +/* + * Does canonical composition on the string str with length len, and returns + * the length of the composed string. + */ +LDAP_LUNICODE_F (int) uccanoncomp LDAP_P((ac_uint4 *str, int len)); + +/************************************************************************** + * + * Functions for getting decompositions. + * + **************************************************************************/ + +/* + * This routine determines if the code has a decomposition. If it returns 0, + * there is no decomposition. Any other value indicates a decomposition was + * returned. + */ +LDAP_LUNICODE_F (int) +ucdecomp LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 **decomp)); + +/* + * Equivalent to ucdecomp() except that it includes compatibility + * decompositions. + */ +LDAP_LUNICODE_F (int) +uckdecomp LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 **decomp)); + +/* + * If the code is a Hangul syllable, this routine decomposes it into the array + * passed. The array size should be at least 3. + */ +LDAP_LUNICODE_F (int) +ucdecomp_hangul LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 decomp[])); + +/* + * This routine does canonical decomposition of the string in of length + * inlen, and returns the decomposed string in out with length outlen. + * The memory for out is allocated by this routine. It returns the length + * of the decomposed string if okay, and -1 on error. + */ +LDAP_LUNICODE_F (int) +uccanondecomp LDAP_P((const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx)); + +/* + * Equivalent to uccanondecomp() except that it includes compatibility + * decompositions. + */ +LDAP_LUNICODE_F (int) +uccompatdecomp LDAP_P((const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx)); + +/************************************************************************** + * + * Functions for getting combining classes. + * + **************************************************************************/ + +/* + * This will return the combining class for a character to be used with the + * Canonical Ordering algorithm. + */ +LDAP_LUNICODE_F (ac_uint4) uccombining_class LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions for getting numbers and digits. + * + **************************************************************************/ + +struct ucnumber { + int numerator; + int denominator; +}; + +LDAP_LUNICODE_F (int) +ucnumber_lookup LDAP_P((ac_uint4 code, struct ucnumber *num)); + +LDAP_LUNICODE_F (int) +ucdigit_lookup LDAP_P((ac_uint4 code, int *digit)); + +/* + * For compatibility with John Cowan's "uctype" package. + */ +LDAP_LUNICODE_F (struct ucnumber) ucgetnumber LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (int) ucgetdigit LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions library initialization and cleanup. + * + **************************************************************************/ + +/* + * Macros for specifying the data tables to be loaded, unloaded, or reloaded + * by the ucdata_load(), ucdata_unload(), and ucdata_reload() routines. + */ +#define UCDATA_CASE 0x01 +#define UCDATA_CTYPE 0x02 +#define UCDATA_DECOMP 0x04 +#define UCDATA_CMBCL 0x08 +#define UCDATA_NUM 0x10 +#define UCDATA_COMP 0x20 +#define UCDATA_KDECOMP 0x40 + +#define UCDATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\ + UCDATA_CMBCL|UCDATA_NUM|UCDATA_COMP|UCDATA_KDECOMP) + +/* + * Functions to load, unload, and reload specific data files. + */ +LDAP_LUNICODE_F (int) ucdata_load LDAP_P((char *paths, int mask)); +LDAP_LUNICODE_F (void) ucdata_unload LDAP_P((int mask)); +LDAP_LUNICODE_F (int) ucdata_reload LDAP_P((char *paths, int mask)); + +#ifdef UCDATA_DEPRECATED +/* + * Deprecated functions, now just compatibility macros. + */ +#define ucdata_setup(p) ucdata_load(p, UCDATA_ALL) +#define ucdata_cleanup() ucdata_unload(UCDATA_ALL) +#endif + +LDAP_END_DECL + +#endif /* _h_ucdata */ diff --git a/contrib/libs/openldap/libraries/ya.make b/contrib/libs/openldap/libraries/ya.make new file mode 100644 index 0000000000..1a85ec0f99 --- /dev/null +++ b/contrib/libs/openldap/libraries/ya.make @@ -0,0 +1,6 @@ +# Generated by devtools/yamaker. + +RECURSE( + liblber + liblmdb +) diff --git a/contrib/libs/openldap/ya.make b/contrib/libs/openldap/ya.make new file mode 100644 index 0000000000..11e8379f48 --- /dev/null +++ b/contrib/libs/openldap/ya.make @@ -0,0 +1,126 @@ +# Generated by devtools/yamaker from nixpkgs 22.11. + +LIBRARY() + +LICENSE( + BSD-3-Clause AND + BSD-4-Clause-UC AND + Bsd-Original-Uc-1986 AND + Custom-openldap AND + MIT AND + OLDAP-2.8 AND + Unicode-Mappings +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +VERSION(2.6.6) + +ORIGINAL_SOURCE(https://gitlab.com/api/v4/projects/openldap%2Fopenldap/repository/archive.tar.gz?sha=OPENLDAP_REL_ENG_2_6_6) + +PEERDIR( + contrib/libs/openldap/libraries/liblber + contrib/libs/openssl + contrib/libs/sasl +) + +ADDINCL( + GLOBAL contrib/libs/openldap/include + contrib/libs/sasl/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLDAPI_SOCK=\"/run/openldap/ldapi\" + -DLDAP_LIBRARY +) + +SRCS( + libraries/libldap/abandon.c + libraries/libldap/account_usability.c + libraries/libldap/add.c + libraries/libldap/addentry.c + libraries/libldap/assertion.c + libraries/libldap/avl.c + libraries/libldap/bind.c + libraries/libldap/cancel.c + libraries/libldap/charray.c + libraries/libldap/compare.c + libraries/libldap/controls.c + libraries/libldap/cyrus.c + libraries/libldap/dds.c + libraries/libldap/delete.c + libraries/libldap/deref.c + libraries/libldap/dnssrv.c + libraries/libldap/error.c + libraries/libldap/extended.c + libraries/libldap/fetch.c + libraries/libldap/filter.c + libraries/libldap/free.c + libraries/libldap/getattr.c + libraries/libldap/getdn.c + libraries/libldap/getentry.c + libraries/libldap/getvalues.c + libraries/libldap/init.c + libraries/libldap/lbase64.c + libraries/libldap/ldap_sync.c + libraries/libldap/ldif.c + libraries/libldap/ldifutil.c + libraries/libldap/messages.c + libraries/libldap/modify.c + libraries/libldap/modrdn.c + libraries/libldap/msctrl.c + libraries/libldap/open.c + libraries/libldap/options.c + libraries/libldap/os-ip.c + libraries/libldap/os-local.c + libraries/libldap/pagectrl.c + libraries/libldap/passwd.c + libraries/libldap/ppolicy.c + libraries/libldap/print.c + libraries/libldap/psearchctrl.c + libraries/libldap/rdwr.c + libraries/libldap/references.c + libraries/libldap/request.c + libraries/libldap/result.c + libraries/libldap/rq.c + libraries/libldap/sasl.c + libraries/libldap/sbind.c + libraries/libldap/schema.c + libraries/libldap/search.c + libraries/libldap/sort.c + libraries/libldap/sortctrl.c + libraries/libldap/stctrl.c + libraries/libldap/string.c + libraries/libldap/tavl.c + libraries/libldap/thr_debug.c + libraries/libldap/thr_nt.c + libraries/libldap/thr_posix.c + libraries/libldap/thr_pth.c + libraries/libldap/thr_thr.c + libraries/libldap/threads.c + libraries/libldap/tls2.c + libraries/libldap/tls_g.c + libraries/libldap/tls_o.c + libraries/libldap/tpool.c + libraries/libldap/turn.c + libraries/libldap/txn.c + libraries/libldap/unbind.c + libraries/libldap/url.c + libraries/libldap/utf-8-conv.c + libraries/libldap/utf-8.c + libraries/libldap/util-int.c + libraries/libldap/vc.c + libraries/libldap/version.c + libraries/libldap/vlvctrl.c + libraries/libldap/whoami.c +) + +END() + +RECURSE( + libraries +) |