How to do password authentication for a user using LDAP? - c++

I am writing a client app (using OpenLDAP libraries) for which the users gets authenticated via LDAP server.
Here is the sample, hard coded, program that fails to compare userPassword for a user.
#include <stdio.h>
#include <ldap.h>
#define LDAP_SERVER "ldap://192.168.1.95:389"
int main( int argc, char **argv ){
LDAP *ld;
int rc;
char bind_dn[100];
LDAPMessage *result, *e;
char *dn;
int has_value;
sprintf( bind_dn, "cn=%s,dc=ashwin,dc=com", "manager" );
printf( "Connecting as %s...\n", bind_dn );
if( ldap_initialize( &ld, LDAP_SERVER ) )
{
perror( "ldap_initialize" );
return( 1 );
}
rc = ldap_simple_bind_s( ld, bind_dn, "ashwin" );
if( rc != LDAP_SUCCESS )
{
fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(rc) );
return( 1 );
}
printf( "Successful authentication\n" );
rc = ldap_search_ext_s(ld, "dc=ashwin,dc=com", LDAP_SCOPE_SUBTREE, "sn=ashwin kumar", NULL, 0, NULL, NULL, NULL, 0, &result);
if ( rc != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_search_ext_s: %s\n", ldap_err2string(rc));
}
for ( e = ldap_first_entry( ld, result ); e != NULL; e = ldap_next_entry( ld, e ) ) {
if ( (dn = ldap_get_dn( ld, e )) != NULL ) {
printf( "dn: %s\n", dn );
has_value = ldap_compare_s( ld, dn, "userPassword", "secret" );
switch ( has_value ) {
case LDAP_COMPARE_TRUE:
printf( "Works.\n");
break;
case LDAP_COMPARE_FALSE:
printf( "Failed.\n");
break;
default:
ldap_perror( ld, "ldap_compare_s" );
return( 1 );
}
ldap_memfree( dn );
}
}
ldap_msgfree( result );
ldap_unbind( ld );
return( 0 );
}
userPassword if it is plain in LDAP server, it works.
the same password if it is MD5 encrypted, ldap_compare_s fails. And that's because I am passing the cleartext password to compare.
How do I get this sample program working?
Am I doing this right? Is it correct to use ldap_compare_s to authenticate user via LDAP?
P.S: This is the first time I am working on LDAP.

This is not really the right way to perform a password check on LDAP, what you should do is attempt to bind using the dn obtained from the first search and the password supplied.
i.e. you perform a second bind to verify the password. If the bind fails then the password is incorrect.
Something akin to:
if ( (dn = ldap_get_dn( ld, e )) != NULL ) {
printf( "dn: %s\n", dn );
/* rebind */
ldap_initialize(&ld2, LDAP_SERVER);
rc = ldap_simple_bind_s(ld2, dn, "secret");
printf("%d\n", rc);
if (rc != 0) {
printf("Failed.\n");
} else {
printf("Works.\n");
ldap_unbind(ld2);
}
ldap_memfree( dn );
}
For security reasons indicating that the username is incorrect (i.e. the search for the user account fails) is generally considered excessive disclosure, and should be avoided.

Related

EAGAIN in ZMQ extended request reply

I'm trying to create a REQ <--> Router <--> Dealer <--> REP communication in C++. The child process binds the router and dealer, proxies between router and dealer, connects the REP to the dealer and waits for a message with zmq_recv.
The parent process connects a REQ to the router and tries to send a message, however I'm getting a zmq_send error in parent: Resource temporarily unavailable (which is EAGAIN). According to zmq_send docs, EAGAIN means:
Non-blocking mode was requested and the message cannot be sent at the moment.
However the message does get sent since it is received in the child process. Why does it return that errno?
Here is the MCVE:
#include <zmq.h>
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <thread>
#include <stdio.h>
int main() {
char connect_path[35];
int rc;
int msg;
pid_t child_pid = fork();
if (child_pid == 0) {
// Child
void* child_context = zmq_ctx_new ();
if (child_context == NULL) {
std::cerr << "\nChild context error\n";
}
void* router = zmq_socket(child_context, ZMQ_ROUTER);
if (router == NULL) {
perror("zmq_socket of type router error");
}
char bind_path[35];
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-router", getpid());
rc = zmq_bind(router, bind_path);
assert (rc == 0);
void* dealer = zmq_socket(child_context, ZMQ_DEALER);
if (dealer == NULL) {
perror("zmq_socket of type dealer error");
}
snprintf(bind_path, sizeof(bind_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_bind(dealer, bind_path);
assert (rc == 0);
std::thread z_proxy (zmq_proxy, router, dealer, nullptr);
z_proxy.detach();
void* rep_socket = zmq_socket (child_context, ZMQ_REP);
if (rep_socket == NULL) {
perror("zmq_socket of type rep error");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-dealer", getpid());
rc = zmq_connect(rep_socket, connect_path);
assert (rc == 0);
while(1) {
if (zmq_recv (rep_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_recv error");
}
printf("\nReceived msg %d in process %d\n", msg, getpid());
break;
}
if (zmq_close(rep_socket) != 0) {
perror("zmq_close of rep_socket in child error");
}
if (zmq_ctx_term(child_context) != 0) {
perror("zmq_ctx_term of child_context error");
}
} else {
// Parent
sleep(1);
void* parent_context = zmq_ctx_new ();
if (parent_context == NULL) {
std::cerr << "\nParent ctx error\n";
}
void* req_socket = zmq_socket (parent_context, ZMQ_REQ);
if (req_socket == NULL) {
perror("zmq_socket of type req error in parent");
}
snprintf(connect_path, sizeof(connect_path), "ipc:///tmp/zmqtest%d-router", child_pid);
rc = zmq_connect(req_socket, connect_path);
assert (rc == 0);
msg = 30;
if (zmq_send (req_socket, &msg, sizeof(msg), 0) != 0) {
perror("zmq_send error in parent");
}
if (zmq_close(req_socket) != 0) {
perror("zmq_close of req_socket in parent error");
}
if (zmq_ctx_term(parent_context) != 0) {
perror("zmq_ctx_term of parent_context error");
}
}
}
Step 1: Make a trivial test:
Well, as a minimum point, there ought be this sort of test-en-Queueing first:
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d\nZMQ: zmq_errno ~ %s\n",
rc,
zmq_strerror ( zmq_errno() )
);
.
Step 2: post the printed outputs
Next, if there are any "missed" shots, the error-analysis may advise on potential reason(s)
( if and only if the parent_ctx indeed rejected to even accept the data from a simplest ever zmq_send() call into it's internal queueing facility with an explicit reason for having done so ).
Otherwise we know nothing ( and the ZMQ_DONTWAIT flag is not the reason here ).
As the test was run, it yielded:
INF: zmq_send ( req_socket, 'A_TEST_BLOCK', 12, ZMQ_DONTWAIT )
ZMQ: returned rc == 12
ZMQ: zmq_errno ~ Resource temporarily unavailable
Step 3:
The test has confirmed, as per documentation:
The zmq_send() function shall return number of bytes in the message if successful.
So, let's dig a step deeper:
int major, minor, patch;
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch,
zmq_strerror ( zmq_errno() )
);
Step 4:
In case the bleeding-edge API-updates do not conform to the published API-specification, document the incident:
printf ( "EXPECT( NO ERROR, ON START ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( <major>.<minor>.<patch> ): zmq_version ~\n" );
int major, minor, patch
zmq_version ( &major, &minor, &patch );
printf ( "INF: current ØMQ version is %d.%d.%d\nZMQ: zmq_errno ~ %s\n",
major, minor, patch
)
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
printf ( "EXPECT( NO ERROR ): zmq_send() ~ %s\n" );
rc = zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT );
printf ( "INF: zmq_send ( req_socket, "A_TEST_BLOCK", 12, ZMQ_DONTWAIT )\nZMQ: returned rc == %d which ouhgt be == 12, is it?\n",
rc
);
printf ( "EXPECT( NO ERROR ): zmq_errno ~ %s\n",
zmq_strerror ( zmq_errno() )
);
and feel free to file an issue, if unexpected results appear.

How to AES CBC encryption Using cryptoAPI

I want to encrypt a file with AES CBC mode encryption, Using cryptoAPI functions and set my own key from the command-line (It my change in the code)
I imagine that the key (after change) will be 1a1dc91c907325c6 and tried in this form:
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwBlobLen;
PBYTE pbKeyBlob = NULL;
pbKeyBlob = (PBYTE)"1a1dc91c907325c6";
if(!CryptAcquireContext(&hProv, NULL,NULL, PROV_RSA_AES,CRYPT_VERIFYCONTEXT))
{
printf(" Error in AcquireContext 0x%08x \n",GetLastError());
}
if (!CryptImportKey(hProv,pbKeyBlob,sizeof(pbKeyBlob),0,CRYPT_EXPORTABLE,&hKey ))
{
printf("Error 0x%08x in importing the Des key \n",GetLastError());
}
but CryptImportKey failed
I don't know how to use cryptoAPI functions and It's parameters
I tested some other codes and change the parameters or function's call's order for about 2 weeks but I wasn't able to do this
Please help me [a big help :)]
Thank you
You should do it like this:
if( ::CryptAcquireContext( &m_hCryptoProvider, NULL, NULL/*Default*/, PROV_RSA_AES, CRYPT_VERIFYCONTEXT ) )
{
//Hash Password
// CALG_SHA1 OK
// CALG_AES_128 / CALG_AES_256 => error
if( ::CryptCreateHash( m_hCryptoProvider, CALG_SHA1, 0, 0, &m_hHashPassword ) )
{
// Hash for the password.
if( ::CryptHashData( m_hHashPassword, (BYTE *)password, (DWORD) _tcslen(password) * sizeof(TCHAR), 0 ) )
{
// Session key from the hash
if( ::CryptDeriveKey( m_hCryptoProvider, CALG_AES_256, m_hHashPassword, CRYPT_CREATE_SALT, &m_hCryptKey ) )
{
TRACE( TEXT("Crypto-API OK\n") );
return ERROR_SUCCESS;
}
else
{
TRACE( TEXT("Error in CryptDeriveKey\n") );
}
}
else
{
TRACE( TEXT("Error in CryptHashData\n") );
}
}
else
{
TRACE( TEXT("Error in CryptCreateHash\n") );
}
}
else
{
TRACE( TEXT("Error in CryptAcquireContext\n") );
}
After that you need to use CryptEncrypt/CryptDecrypt to do encode/decode data.

C++ copy sqlite blob from one database to another

I am trying to copy some blob data from one sqlite table to another in C++. However, once the data has been copied over to the new table, it seems to be getting corrupted. The said data contains some jpeg images. The code I am using to copy from TABLE1 to TABLE2 is shown below:
// Read the blob from the database
int64_t rowID = 0;
sscanf( id.c_str(), "%llu", &rowID );
sqlite3_blob* blobHandle = NULL;
if( sqlite3_blob_open( m_dbHandle_temp, "main", m_currentTileTable.c_str(), "tile_data", rowID, 0, &blobHandle ) != SQLITE_OK )
{
sqlite3_blob_close( blobHandle ); // An SQLite blob will be initialised regardless of the success of 'sqlite3_blob_open'
return false;
}
tiles_insert_statement.append( ")" );
// Copy blob to database
sqlite3_stmt *stmt = 0;
const char* tail;
sqlite3_prepare_v2( m_dbHandle, tiles_insert_statement.c_str(), strlen( tiles_insert_statement.c_str() )+1, &stmt, &tail );
int bindSuccess = sqlite3_bind_blob( stmt, 1, blobHandle, sqlite3_blob_bytes( blobHandle ), SQLITE_TRANSIENT );
if( sqlite3_step( stmt ) != SQLITE_DONE )
printf( "Error message: %s\n", sqlite3_errmsg( m_dbHandle ) );
sqlite3_finalize( stmt );
// close handles
sqlite3_blob_close( blobHandle );
Is there anything I am doing wrong in the above code, the reason I say that it is getting corrupted is because, I am reading the blobs on an android device to be displayed in an image viewer. The blobs in the TABLE 1 can be read and displayed fine, however the ones in TABLE 2 do not display anything. Any help is greatly appreaciated.
SOLUTION:
// Read the blob from the database
int64_t rowID = 0;
sscanf( id.c_str(), "%llu", &rowID );
sqlite3_blob* blobHandle = NULL;
if( sqlite3_blob_open( m_dbHandle_temp, "main", m_currentTileTable.c_str(), "tile_data", rowID, 0, &blobHandle ) != SQLITE_OK )
{
sqlite3_blob_close( blobHandle ); // An SQLite blob will be initialised regardless of the success of 'sqlite3_blob_open'
return false;
}
unsigned int length = sqlite3_blob_bytes( blobHandle );
// TODO - instances of this class OWN the buffer.
// Delete the buffer in the destructor ;)
unsigned char* buffer = new unsigned char[ length ];
if( sqlite3_blob_read( blobHandle, buffer, length, 0 ) != SQLITE_OK )
{
return false;
}
tiles_insert_statement.append( ")" );
sqlite3_stmt *stmt = 0;
const char* tail;
sqlite3_prepare_v2( m_dbHandle, tiles_insert_statement.c_str(), strlen( tiles_insert_statement.c_str() )+1, &stmt, &tail );
int bindSuccess = sqlite3_bind_blob( stmt, 1, buffer, length, SQLITE_TRANSIENT );
if( sqlite3_step( stmt ) != SQLITE_DONE )
printf( "Error message: %s\n", sqlite3_errmsg( m_dbHandle ) );
sqlite3_finalize( stmt );
// close handles
sqlite3_blob_close( blobHandle );
sqlite3_bind_blob expects a pointer to the actual blob data; it is not possible to use a blob handle for that.
To get the blob data as a memory chunk, execute a query like SELECT tile_data FROM MyTable WHERE ... and read the value with sqlite3_column_blob.

How to impersonate a user from a service correctly?

I'm working a service, which should impersonate the logged on user.
My code so far, with basic error handling:
// get the active console session ID of the logged on user
if ( !WTSQueryUserToken( WTSGetActiveConsoleSessionId(), &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
HANDLE hDuplicated;
// duplicate the token
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "DuplicateToken succeeded.", 0, true );
}
// impersonate the logged on user
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError(), true );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( !GetRoamingProfilePath( DC, CurrentUser, RoamingProfilePath ) )
{
ShowErrorText( "Failed to retrieve roaming profile path.", GetLastError(), true );
}
}
if ( RevertToSelf( ) )
{
ShowErrorText( "Impersonation ended successfully.", 0, true );
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
}
else
{
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
}
//do some stuff
if ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError( ), true );
return;
}
According to MSDN:
When a user logs on interactively, the system automatically loads the user's profile. If a service or an application impersonates a user, the system does not load the user's profile. Therefore, the service or application should load the user's profile with LoadUserProfile.
Services and applications that call LoadUserProfile should check to see if the user has a roaming profile. If the user has a roaming profile, specify its path as the lpProfilePath member of PROFILEINFO. To retrieve the user's roaming profile path, you can call the NetUserGetInfo function, specifying information level 3 or 4.
Upon successful return, the hProfile member of PROFILEINFO is a registry key handle opened to the root of the user's hive. It has been opened with full access (KEY_ALL_ACCESS). If a service that is impersonating a user needs to read or write to the user's registry file, use this handle instead of HKEY_CURRENT_USER. Do not close the hProfile handle. Instead, pass it to the UnloadUserProfile function.
If i use my code as it is now, then it works. However is it a little strange, because first i have to impersonate the logged on user, and then end the impersonation, to Load the users profile. If i don't end the impersonation then LoadUserProfile will fail with error 5 ( Access denied ). And after LoadUserProfile succeeded i should impersonate the user again?
So my question is, this meant to be done this way, or i am doing something wrong?
Another question is, that if LoadUserProfile succeeded i could use hProfile as a Handle to the logged on users registry. Question is how? Because to use RegOpenKeyEy and RegSetValueEx i need to pass a HKEY, not a HANDLE. So how can i use this Handle?
Thank!
You don't need to call ImpersonateLoggedOnUser() since you are passing the user's token to LoadUserProfile(). Call ImpersonateLoggedOnUser() only if you need to call APIs that do not allow you to pass a user token to them.
If you read the rest of the LoadUserProfile() documentation, it says:
The calling process must have the SE_RESTORE_NAME and SE_BACKUP_NAME privileges.
By impersonating the user you are trying to load a profile for, you are likely losing those privileges. So don't impersonate the user.
Update: Try something like this:
// get the active console session ID of the logged on user
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if ( dwSessionID == 0xFFFFFFFF )
{
ShowErrorText( "WTSGetActiveConsoleSessionId failed.", GetLastError( ), true );
return;
}
if ( !WTSQueryUserToken( dwSessionID, &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
// duplicate the token
HANDLE hDuplicated = NULL;
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
CloseHandle( hToken );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
USER_INFO_4 *UserInfo = NULL;
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( NetUserGetInfo( DC, CurrentUser, 4, (LPBYTE*)&UserInfo) != NERR_Success )
{
ShowErrorText( "NetUserGetInfo failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
lpProfileInfo.lpProfilePath = UserInfo->usri3_profile;
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
if ( UserInfo )
NetApiBufferFree(UserInfo);
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
if ( UserInfo )
NetApiBufferFree(UserInfo);
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
//do some stuff
if ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
CloseHandle( hDuplicated );
CloseHandle( hToken );
As for the Registry, the hProfile handle is the opened HKEY for the user's HKEY_CURRENT_USER tree. Simpy type-cast it from HANDLE to HKEY when passing it to Registry API functions. It is already opened, so you do not need to call RegOpenKeyEx() to open that same key again, but you can use it as the root key when creating/opening subkeys, or reading/writing values in the root key.

How do I find the port name for a bluetooth device with a specific device name?

How do I find the port name for a bluetooth device with a specific device name?
I have this code, which enumerates all bluetooth devices, but doesn't give me their port name:
HBLUETOOTH_DEVICE_FIND founded_device;
BLUETOOTH_DEVICE_INFO device_info;
device_info.dwSize = sizeof(device_info);
BLUETOOTH_DEVICE_SEARCH_PARAMS search_criteria;
search_criteria.dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
search_criteria.fReturnAuthenticated = TRUE;
search_criteria.fReturnRemembered = FALSE;
search_criteria.fReturnConnected = FALSE;
search_criteria.fReturnUnknown = FALSE;
search_criteria.fIssueInquiry = FALSE;
search_criteria.cTimeoutMultiplier = 0;
founded_device = BluetoothFindFirstDevice(&search_criteria, &device_info);
if(founded_device == NULL)
return -1;
do {
wstring ws = device_info.szName;
cout << string(ws.begin(), ws.end()) << endl;
} while (BluetoothFindNextDevice(founded_device, &device_info));
And then I have this code, which enumerates all port names but doesn't give me the device name:
DWORD bytesNeeded = 0;
DWORD portCount = 0;
BOOL ret = EnumPorts(nullptr, 2, nullptr, 0, &bytesNeeded, &portCount);
BYTE *ports = new BYTE[bytesNeeded];
if(EnumPorts(nullptr, 2, (LPBYTE)ports, bytesNeeded, &bytesNeeded, &portCount))
{
PORT_INFO_2 *portInfo = (PORT_INFO_2*)ports;
for(DWORD i = 0; i < portCount; ++i)
cout << portInfo[i].pPortName << endl;
}
delete [] ports;
I need to automatically connect to a specific device when my app is started, so I need to either get the port name for the bluetooth device in the first piece of code so I can connect to it, or check each portname in the second piece of code to make sure it's the right device before connecting to it.
How do I do it?
I remember struggling with this in the past.
the only solution i found was to use sockets for communicating with the Bluetooth device using its address, then use the send() and recv() methods for communicating with the device.
// assuming you have the BT device address in blueToothDeviceAddr;
char blueToothDeviceAddr[18];
SOCKET sock;
SOCKADDR_BTH sa = { 0,0,0,0 };
int sa_len = sizeof(sa);
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 )
{
ExitProcess(100);
}
// parse the specified Bluetooth address
if( SOCKET_ERROR == WSAStringToAddress( blueToothDeviceAddr, AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) )
{
ExitProcess(101);
}
// query it for the right port
// create the socket
sock = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if( SOCKET_ERROR == sock )
{
ExitProcess(102);
}
// fill in the rest of the SOCKADDR_BTH struct
GUID pService = (GUID)SerialPortServiceClass_UUID;
SOCKADDR_BTH outSA;
sa.port = SDPGetPort(blueToothDeviceAddr, (LPGUID) &pService,&outSA);
if( sa.port == 0 )
{
ExitProcess(103);
}
// in case you have a pass code you need to register for authetication callback
// look the web for this part
// connect to the device
if( SOCKET_ERROR == connect( sock, (LPSOCKADDR) &outSA, sa_len ) )
{
int lastError = GetLastError();
ExitProcess(105);
}
Under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
you can find a subkey which has a list of keys containing the device address.
Under this last key, you can find a subkey named Device Parameters which finally has the PortName value.
The code is written in C++ with MFC libraries and is tested under Windows XP, 7 and 10. I hope it helps you !
// Returns the outgoing COM port of a bluetooth device given by address
int GetBluetoothCOM( CString sAddr )
{
int iPort = 0;
HKEY hKey_1;
DWORD KeyNdx_1 = 0;
DWORD MaxKeyLen_1;
char KeyNam_1[ MAX_PATH + 1 ];
LONG RetVal_1;
sAddr.MakeUpper();
sAddr.Replace( ":", "" );
sAddr.Replace( " ", "" );
// Enumerate keys under: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\BTHENUM
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM", NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_1 );
while( true )
{
MaxKeyLen_1 = MAX_PATH;
RetVal_1 = RegEnumKeyEx( hKey_1, KeyNdx_1, KeyNam_1, &MaxKeyLen_1, NULL, NULL, NULL, NULL );
if( RetVal_1 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_1 == ERROR_SUCCESS )
{
HKEY hKey_2;
DWORD KeyNdx_2 = 0;
DWORD MaxKeyLen_2;
char KeyNam_2[ MAX_PATH + 1 ];
LONG RetVal_2;
// Enumerate subkeys
RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ), NULL, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hKey_2 );
while( true )
{
MaxKeyLen_2 = MAX_PATH;
RetVal_2 = RegEnumKeyEx( hKey_2, KeyNdx_2, KeyNam_2, &MaxKeyLen_2, NULL, NULL, NULL, NULL );
if( RetVal_2 == ERROR_NO_MORE_ITEMS )
{
break;
}
if( RetVal_2 == ERROR_SUCCESS )
{
// Find out if the key name contains &ADDRESS_
CString sKey = "SYSTEM\\CurrentControlSet\\Enum\\BTHENUM\\" + CString( KeyNam_1 ) + "\\" + CString( KeyNam_2 );
sKey.MakeUpper();
if( sKey.Find( "&" + sAddr + "_" ) != -1 )
{
HKEY hKey;
char szPort[ 100 + 1 ];
DWORD dwLen = 100;
// I find out the device
RegOpenKeyEx( HKEY_LOCAL_MACHINE, sKey + "\\Device Parameters", 0, KEY_READ, &hKey );
if( RegQueryValueEx( hKey, "PortName", NULL, NULL, ( LPBYTE ) &szPort, &dwLen ) == ERROR_SUCCESS )
{
szPort[ dwLen ] = 0;
CString sPort = CString( szPort );
sPort.MakeUpper();
if( sPort.Find( "COM" ) == -1 )
{
RegCloseKey( hKey );
continue;
}
sPort.Replace( "COM", "" );
sPort.Trim();
iPort = atoi( sPort.GetBuffer() );
if( iPort != 0 )
{
RegCloseKey( hKey );
break;
}
}
RegCloseKey( hKey );
}
}
++KeyNdx_2;
}
RegCloseKey( hKey_2 );
if( iPort != 0 )
{
break;
}
}
++KeyNdx_1;
};
RegCloseKey( hKey_1 );
return iPort;
}