
Accesso nativo in C++ ad Active Directory tramite ADWS, senza .NET, senza WCF, senza stack HTTP.
Accesso C++ nativo ad Active Directory via ADWS, senza .NET, senza WCF, senza stack HTTP.
BridgeHead è una libreria statica C++20 che implementa l'intero stack del protocollo Active Directory Web Services (ADWS) direttamente su TCP. Chiamata così dal server bridgehead AD, il gateway attraverso cui fluisce il traffico delle directory, offre al tuo codice C++ lo stesso accesso di basso livello alla porta 9389 che Get-ADUser e Get-ADComputer di PowerShell utilizzano internamente.
I layer di trasporto avvolgono quello sottostante tramite l'interfaccia comune bridgehead::transport::IByteStream. NbfseCodec è un'utilità di codec richiamata da AdwsClient per codificare/decodificare i messaggi SOAP prima e dopo il framing:```
AdwsClient WS-Enumeration + WS-Transfer [MS-ADDM]
├── NbfseCodec .NET Binary Format for SOAP [MC-NBFSE] (encode/decode)
└── NmfFramer .NET Message Framing [MC-NMF] (send/receive frames)
└── NnsSession .NET NegotiateStream [MS-NNS]
└── TcpSocket raw TCP/IP
Il punto di ingresso per i consumatori è `bridgehead::adws::AdwsClient`.
---
## Avvio rapido
### Query, enumerare tutti gli utenti```cpp
#include "bridgehead/adws/AdwsClient.hpp"
// NTLM (works on any host)
auto client = bridgehead::adws::AdwsClient::EnumerationClient(
"192.168.1.10", // DC IP or hostname
"DC01.corp.local", // DC FQDN, used in NMF Via header and Kerberos SPN
"CORP", // NetBIOS domain name
"Administrator", // username
"Passw0rd" // password
);
// Kerberos (username hidden on the wire; requires DC reachable on port 88)
auto client = bridgehead::adws::AdwsClient::EnumerationClient(
"192.168.1.10", "DC01.corp.local", "CORP",
"Administrator", "Passw0rd",
bridgehead::adws::AuthPackage::Kerberos
);
auto users = client.Query(
"(objectClass=user)",
{"sAMAccountName", "distinguishedName", "memberOf"}
);
for (auto& obj : users)
std::cout << obj.FirstValue("sAMAccountName") << '\n';
client.Enumerate( "(objectClass=computer)", {"dNSHostName", "operatingSystem"}, "", // empty = domain root base DN [](const bridgehead::adws::LdapObject& obj) { std::cout << obj.FirstValue("dNSHostName") << '\n'; return true; // return false to stop early (sends wsen:Release) } );
### Ambito e paginazione```cpp
// OneLevel scope, 50 objects per Pull round-trip
client.Query(
"(objectClass=user)",
{"sAMAccountName"},
"OU=Admins,DC=corp,DC=local",
50,
bridgehead::adws::SearchScope::OneLevel
);
auto objs = client.Query("(objectClass=user)", {"objectGUID", "objectSid"}); for (auto& obj : objs) { if (auto* b = obj.FirstBytes("objectGUID")) std::cout << bridgehead::adws::ParseGuid(b) << '\n'; // {XXXXXXXX-...} if (auto b = obj.FirstBytes("objectSid")) std::cout << bridgehead::adws::ParseSid(*b) << '\n'; // S-1-5-... }
### Decodifica del descrittore di sicurezza```cpp
#include "bridgehead/adws/SecurityDescriptor.hpp"
auto objs = client.Query("(objectClass=user)", {"nTSecurityDescriptor"});
if (auto* raw = objs[0].FirstBytes("nTSecurityDescriptor")) {
auto sd = bridgehead::adws::ParseSecurityDescriptor(*raw);
std::cout << "Owner: " << sd.ownerSid << '\n';
for (auto& ace : sd.dacl.aces)
std::cout << " type=" << (int)ace.type
<< " mask=0x" << std::hex << ace.mask
<< " sid=" << ace.sid << '\n';
}
auto rc = bridgehead::adws::AdwsClient::ResourceClient( "192.168.1.10", "DC01.corp.local", "CORP", "Administrator", "Passw0rd");
// Modify attributes rc.Put("CN=Alice,OU=Users,DC=corp,DC=local", { {"description", {"managed by bridgehead"}}, {"telephoneNumber", {"555-1234"}}, });
// Clear an attribute (both values and bytes empty = delete) rc.Put("CN=Alice,OU=Users,DC=corp,DC=local", { {"telephoneNumber", {}}, });
// Read back auto obj = rc.Get("CN=Alice,OU=Users,DC=corp,DC=local", {"description", "telephoneNumber"});
// Delete object rc.Delete("CN=TempUser,OU=Users,DC=corp,DC=local");
### Tipi di modifica LDAP, Add / Replace / Delete
`Put` di default usa `Replace` (sovrascrive tutti i valori esistenti). Usa `ModifyOperation` per un controllo granulare sugli attributi multivalore:```cpp
using bridgehead::adws::LdapModification;
using bridgehead::adws::ModifyOperation;
rc.Put("CN=Alice,OU=Users,DC=corp,DC=local", {
// Append a value to an existing multi-valued attribute
LdapModification{"otherTelephone", {"555-9999"}, {}, ModifyOperation::Add},
// Remove one specific value (leave others intact)
LdapModification{"otherTelephone", {"555-0000"}, {}, ModifyOperation::Delete},
// Replace is the default, explicit here for clarity
LdapModification{"description", {"updated"}, {}, ModifyOperation::Replace},
});
// Move to a different OU rc.Move( "CN=Alice,OU=OldOU,DC=corp,DC=local", // current DN "CN=Alice,OU=NewOU,DC=corp,DC=local" // new DN );
// Rename in place (same parent, new CN) rc.Move( "CN=Alice,OU=Users,DC=corp,DC=local", "CN=AliceSmith,OU=Users,DC=corp,DC=local" );
### Scrivere attributi binari
Fornisci valori binari nel campo `bytes` di `LdapModification`. Vengono codificati automaticamente in base64 sul filo:```cpp
std::vector<uint8_t> thumbnail = loadFile("photo.jpg");
rc.Put("CN=Alice,OU=Users,DC=corp,DC=local", {
LdapModification{"thumbnailPhoto", {}, {thumbnail}},
});
auto rf = bridgehead::adws::AdwsClient::ResourceFactoryClient( "192.168.1.10", "DC01.corp.local", "CORP", "Administrator", "Passw0rd");
rf.Create( "CN=NewUser,OU=Users,DC=corp,DC=local", "user", {{"sAMAccountName", {"newuser"}}, {"userAccountControl", {"512"}}} );
### Operazioni asincrone```cpp
#include "bridgehead/adws/AdwsClientAsync.hpp"
auto ac = bridgehead::adws::AdwsClientAsync::EnumerationClient(
"192.168.1.10", "DC01.corp.local", "CORP", "Administrator", "Passw0rd");
auto future = ac.QueryAsync("(objectClass=user)", {"sAMAccountName"});
// ... do other work while the query runs ...
auto users = future.get(); // blocks until complete; re-throws any exception
Timeout su un DC lento:```cpp if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { // query is still running }
All operations have async variants: `QueryAsync`, `EnumerateAsync`, `GetAsync`, `PutAsync`, `DeleteAsync`, `CreateAsync`, `MoveAsync`.
### Pool di connessione (carichi di lavoro ad alta frequenza / multi-thread)```cpp
#include "bridgehead/adws/AdwsClientPool.hpp"
// Enumeration pool, Query / Enumerate
bridgehead::adws::AdwsClientPool pool({
.host = "192.168.1.10", .fqdn = "DC01.corp.local",
.domain = "CORP", .username = "Administrator", .password = "Passw0rd",
.maxSize = 4, // up to 4 concurrent authenticated sessions
});
auto users = pool.Query("(objectClass=user)", {"sAMAccountName"});