aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/openldap
diff options
context:
space:
mode:
authormolotkov-and <molotkov-and@ydb.tech>2023-08-18 17:20:47 +0300
committermolotkov-and <molotkov-and@ydb.tech>2023-08-18 19:42:07 +0300
commit73215359bc33e76f5b94d1832a377072bf245cfc (patch)
tree9cb8ad61d8c3cd107353d42951560ff3cf1b966d /contrib/libs/openldap
parent1cbfd34a55732f7b1d407986b45e40853f01f2c2 (diff)
downloadydb-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')
-rw-r--r--contrib/libs/openldap/CHANGES223
-rw-r--r--contrib/libs/openldap/CMakeLists.darwin-x86_64.txt108
-rw-r--r--contrib/libs/openldap/CMakeLists.linux-aarch64.txt109
-rw-r--r--contrib/libs/openldap/CMakeLists.linux-x86_64.txt109
-rw-r--r--contrib/libs/openldap/CMakeLists.txt15
-rw-r--r--contrib/libs/openldap/COPYRIGHT64
-rw-r--r--contrib/libs/openldap/INSTALL109
-rw-r--r--contrib/libs/openldap/LICENSE47
-rw-r--r--contrib/libs/openldap/README92
-rw-r--r--contrib/libs/openldap/include/ac/assert.h57
-rw-r--r--contrib/libs/openldap/include/ac/bytes.h78
-rw-r--r--contrib/libs/openldap/include/ac/ctype.h33
-rw-r--r--contrib/libs/openldap/include/ac/dirent.h54
-rw-r--r--contrib/libs/openldap/include/ac/errno.h32
-rw-r--r--contrib/libs/openldap/include/ac/fdset.h42
-rw-r--r--contrib/libs/openldap/include/ac/localize.h44
-rw-r--r--contrib/libs/openldap/include/ac/param.h39
-rw-r--r--contrib/libs/openldap/include/ac/signal.h80
-rw-r--r--contrib/libs/openldap/include/ac/socket.h264
-rw-r--r--contrib/libs/openldap/include/ac/stdarg.h28
-rw-r--r--contrib/libs/openldap/include/ac/stdlib.h48
-rw-r--r--contrib/libs/openldap/include/ac/string.h118
-rw-r--r--contrib/libs/openldap/include/ac/syslog.h38
-rw-r--r--contrib/libs/openldap/include/ac/time.h35
-rw-r--r--contrib/libs/openldap/include/ac/unistd.h72
-rw-r--r--contrib/libs/openldap/include/lber.h691
-rw-r--r--contrib/libs/openldap/include/lber_pvt.h223
-rw-r--r--contrib/libs/openldap/include/lber_types.h63
-rw-r--r--contrib/libs/openldap/include/ldap.h2817
-rw-r--r--contrib/libs/openldap/include/ldap_avl.h165
-rw-r--r--contrib/libs/openldap/include/ldap_cdefs.h248
-rw-r--r--contrib/libs/openldap/include/ldap_config.h74
-rw-r--r--contrib/libs/openldap/include/ldap_defaults.h78
-rw-r--r--contrib/libs/openldap/include/ldap_features.h56
-rw-r--r--contrib/libs/openldap/include/ldap_int_thread.h290
-rw-r--r--contrib/libs/openldap/include/ldap_log.h211
-rw-r--r--contrib/libs/openldap/include/ldap_pvt.h588
-rw-r--r--contrib/libs/openldap/include/ldap_pvt_thread.h347
-rw-r--r--contrib/libs/openldap/include/ldap_pvt_uc.h163
-rw-r--r--contrib/libs/openldap/include/ldap_queue.h593
-rw-r--r--contrib/libs/openldap/include/ldap_rq.h102
-rw-r--r--contrib/libs/openldap/include/ldap_schema.h360
-rw-r--r--contrib/libs/openldap/include/ldap_utf8.h106
-rw-r--r--contrib/libs/openldap/include/ldif.h171
-rw-r--r--contrib/libs/openldap/include/lutil.h375
-rw-r--r--contrib/libs/openldap/include/lutil_lockf.h34
-rw-r--r--contrib/libs/openldap/include/openldap.h39
-rw-r--r--contrib/libs/openldap/include/portable-linux.h1191
-rw-r--r--contrib/libs/openldap/include/portable-musl.h6
-rw-r--r--contrib/libs/openldap/include/portable.h7
-rw-r--r--contrib/libs/openldap/libraries/CMakeLists.darwin-x86_64.txt9
-rw-r--r--contrib/libs/openldap/libraries/CMakeLists.linux-aarch64.txt9
-rw-r--r--contrib/libs/openldap/libraries/CMakeLists.linux-x86_64.txt9
-rw-r--r--contrib/libs/openldap/libraries/CMakeLists.txt15
-rw-r--r--contrib/libs/openldap/libraries/liblber/CMakeLists.darwin-x86_64.txt31
-rw-r--r--contrib/libs/openldap/libraries/liblber/CMakeLists.linux-aarch64.txt34
-rw-r--r--contrib/libs/openldap/libraries/liblber/CMakeLists.linux-x86_64.txt34
-rw-r--r--contrib/libs/openldap/libraries/liblber/CMakeLists.txt15
-rw-r--r--contrib/libs/openldap/libraries/liblber/assert.c40
-rw-r--r--contrib/libs/openldap/libraries/liblber/bprint.c296
-rw-r--r--contrib/libs/openldap/libraries/liblber/debug.c73
-rw-r--r--contrib/libs/openldap/libraries/liblber/decode.c1026
-rw-r--r--contrib/libs/openldap/libraries/liblber/encode.c651
-rw-r--r--contrib/libs/openldap/libraries/liblber/io.c725
-rw-r--r--contrib/libs/openldap/libraries/liblber/lber-int.h225
-rw-r--r--contrib/libs/openldap/libraries/liblber/memory.c825
-rw-r--r--contrib/libs/openldap/libraries/liblber/options.c237
-rw-r--r--contrib/libs/openldap/libraries/liblber/sockbuf.c988
-rw-r--r--contrib/libs/openldap/libraries/liblber/stdio.c244
-rw-r--r--contrib/libs/openldap/libraries/liblber/version.c22
-rw-r--r--contrib/libs/openldap/libraries/liblber/ya.make39
-rw-r--r--contrib/libs/openldap/libraries/libldap/abandon.c458
-rw-r--r--contrib/libs/openldap/libraries/libldap/account_usability.c128
-rw-r--r--contrib/libs/openldap/libraries/libldap/add.c263
-rw-r--r--contrib/libs/openldap/libraries/libldap/addentry.c72
-rw-r--r--contrib/libs/openldap/libraries/libldap/assertion.c100
-rw-r--r--contrib/libs/openldap/libraries/libldap/avl.c671
-rw-r--r--contrib/libs/openldap/libraries/libldap/bind.c117
-rw-r--r--contrib/libs/openldap/libraries/libldap/cancel.c76
-rw-r--r--contrib/libs/openldap/libraries/libldap/charray.c275
-rw-r--r--contrib/libs/openldap/libraries/libldap/compare.c197
-rw-r--r--contrib/libs/openldap/libraries/libldap/controls.c552
-rw-r--r--contrib/libs/openldap/libraries/libldap/cyrus.c1335
-rw-r--r--contrib/libs/openldap/libraries/libldap/dds.c156
-rw-r--r--contrib/libs/openldap/libraries/libldap/delete.c174
-rw-r--r--contrib/libs/openldap/libraries/libldap/deref.c289
-rw-r--r--contrib/libs/openldap/libraries/libldap/dnssrv.c422
-rw-r--r--contrib/libs/openldap/libraries/libldap/error.c395
-rw-r--r--contrib/libs/openldap/libraries/libldap/extended.c419
-rw-r--r--contrib/libs/openldap/libraries/libldap/fetch.c148
-rw-r--r--contrib/libs/openldap/libraries/libldap/filter.c1117
-rw-r--r--contrib/libs/openldap/libraries/libldap/free.c107
-rw-r--r--contrib/libs/openldap/libraries/libldap/getattr.c157
-rw-r--r--contrib/libs/openldap/libraries/libldap/getdn.c3334
-rw-r--r--contrib/libs/openldap/libraries/libldap/getentry.c124
-rw-r--r--contrib/libs/openldap/libraries/libldap/getvalues.c211
-rw-r--r--contrib/libs/openldap/libraries/libldap/init.c782
-rw-r--r--contrib/libs/openldap/libraries/libldap/lbase64.c108
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldap-int.h925
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldap-tls.h91
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldap_sync.c928
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldap_thr_debug.h197
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldif.c919
-rw-r--r--contrib/libs/openldap/libraries/libldap/ldifutil.c724
-rw-r--r--contrib/libs/openldap/libraries/libldap/messages.c68
-rw-r--r--contrib/libs/openldap/libraries/libldap/modify.c233
-rw-r--r--contrib/libs/openldap/libraries/libldap/modrdn.c273
-rw-r--r--contrib/libs/openldap/libraries/libldap/msctrl.c280
-rw-r--r--contrib/libs/openldap/libraries/libldap/open.c678
-rw-r--r--contrib/libs/openldap/libraries/libldap/options.c1012
-rw-r--r--contrib/libs/openldap/libraries/libldap/os-ip.c1265
-rw-r--r--contrib/libs/openldap/libraries/libldap/os-local.c351
-rw-r--r--contrib/libs/openldap/libraries/libldap/pagectrl.c271
-rw-r--r--contrib/libs/openldap/libraries/libldap/passwd.c170
-rw-r--r--contrib/libs/openldap/libraries/libldap/ppolicy.c257
-rw-r--r--contrib/libs/openldap/libraries/libldap/print.c62
-rw-r--r--contrib/libs/openldap/libraries/libldap/psearchctrl.c348
-rw-r--r--contrib/libs/openldap/libraries/libldap/rdwr.c463
-rw-r--r--contrib/libs/openldap/libraries/libldap/references.c147
-rw-r--r--contrib/libs/openldap/libraries/libldap/request.c1714
-rw-r--r--contrib/libs/openldap/libraries/libldap/result.c1401
-rw-r--r--contrib/libs/openldap/libraries/libldap/rq.c225
-rw-r--r--contrib/libs/openldap/libraries/libldap/sasl.c867
-rw-r--r--contrib/libs/openldap/libraries/libldap/sbind.c115
-rw-r--r--contrib/libs/openldap/libraries/libldap/schema.c3400
-rw-r--r--contrib/libs/openldap/libraries/libldap/search.c545
-rw-r--r--contrib/libs/openldap/libraries/libldap/sort.c183
-rw-r--r--contrib/libs/openldap/libraries/libldap/sortctrl.c552
-rw-r--r--contrib/libs/openldap/libraries/libldap/stctrl.c302
-rw-r--r--contrib/libs/openldap/libraries/libldap/string.c177
-rw-r--r--contrib/libs/openldap/libraries/libldap/tavl.c523
-rw-r--r--contrib/libs/openldap/libraries/libldap/thr_debug.c1338
-rw-r--r--contrib/libs/openldap/libraries/libldap/thr_nt.c252
-rw-r--r--contrib/libs/openldap/libraries/libldap/thr_posix.c411
-rw-r--r--contrib/libs/openldap/libraries/libldap/thr_pth.c238
-rw-r--r--contrib/libs/openldap/libraries/libldap/thr_thr.c192
-rw-r--r--contrib/libs/openldap/libraries/libldap/threads.c111
-rw-r--r--contrib/libs/openldap/libraries/libldap/tls2.c1690
-rw-r--r--contrib/libs/openldap/libraries/libldap/tls_g.c1278
-rw-r--r--contrib/libs/openldap/libraries/libldap/tls_o.c1728
-rw-r--r--contrib/libs/openldap/libraries/libldap/tpool.c1495
-rw-r--r--contrib/libs/openldap/libraries/libldap/turn.c96
-rw-r--r--contrib/libs/openldap/libraries/libldap/txn.c153
-rw-r--r--contrib/libs/openldap/libraries/libldap/unbind.c319
-rw-r--r--contrib/libs/openldap/libraries/libldap/url.c1653
-rw-r--r--contrib/libs/openldap/libraries/libldap/utf-8-conv.c485
-rw-r--r--contrib/libs/openldap/libraries/libldap/utf-8.c562
-rw-r--r--contrib/libs/openldap/libraries/libldap/util-int.c1026
-rw-r--r--contrib/libs/openldap/libraries/libldap/vc.c367
-rw-r--r--contrib/libs/openldap/libraries/libldap/version.c22
-rw-r--r--contrib/libs/openldap/libraries/libldap/vlvctrl.c361
-rw-r--r--contrib/libs/openldap/libraries/libldap/whoami.c102
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/CHANGES276
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/COPYRIGHT20
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/LICENSE47
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/lmdb.h1608
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/mdb.c10354
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/midl.c359
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/midl.h186
-rw-r--r--contrib/libs/openldap/libraries/liblmdb/ya.make32
-rw-r--r--contrib/libs/openldap/libraries/liblunicode/ucdata/MUTTUCData.txt303
-rw-r--r--contrib/libs/openldap/libraries/liblunicode/ucdata/README313
-rw-r--r--contrib/libs/openldap/libraries/liblunicode/ucdata/ucdata.h364
-rw-r--r--contrib/libs/openldap/libraries/ya.make6
-rw-r--r--contrib/libs/openldap/ya.make126
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, &lts, 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 ? &ltp_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 = &empty;
+
+ 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
+)