WTSQuerySessionInformation on VPN - c++

We are trying to write a C++ DLL that would run on VMWare server and would return the client (terminal user) IP Address and name.
I am using WTSQuerySessionInformation to fetch the IP Address. The problem is when I am running from within the company's network, the DLL returns the exact IP Address which maps to an appropriate HostName.
But when I login from home to the company's VPN and try the same, it gives me a different IP which doesn't have any DNS Name.
LPTSTR ppBuffer = NULL;
DWORD pBytesReturned = 0;
PWTS_CLIENT_ADDRESS pWTSCA = NULL;
WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSClientAddress, &ppBuffer, &pBytesReturned);
pWTSCA = (PWTS_CLIENT_ADDRESS)ppBuffer;
String^ addrStr = String::Empty;
for (int i = 2; i < 6; i++)
{
addrStr += Convert::ToString(pWTSCA->Address[i]);
if (i != 5)
addrStr += ".";
}
Is there a way to work around this problem? Am I following the right approach, or there is a different way of doing this?
Edit:
If I use WTSClientName, it return the IP address separate by hyphen (like W-X-Y-Z). Could you please help me understand if I have done anything wrong here? Here is the code:
LPTSTR szClientName = NULL;
DWORD dwSize = 0;
String^ cliName = String::Empty;
if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, TSClientName, &szClientName, &dwSize))
{
cliName = gcnew String(szClientName, 0, dwSize);
}
return cliName;

Related

C++ LDAP Checking if a user is a member of a specific group

Been trying this for a while so far with no success, so hoping someone can help out (and that I'm not far off!). I just want to return whether a user is a member of a specific group through LDAP. So far I have the below code;
int authMethod = LDAP_AUTH_SIMPLE;
LDAP* pLdapConnection = NULL;
ULONG version = LDAP_VERSION3;
ULONG getOptSuccess = 0;
ULONG connectSuccess = 0;
INT returnCode = 0;
int retSearch = 0;
LDAPMessage *res;
int num_entries = 0, num_refs = 0;
pLdapConnection = ldap_init((char*)m_Hostname.GetString(), LDAP_PORT);
returnCode = ldap_set_option(pLdapConnection,
LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
// Connect to the server.
connectSuccess = ldap_connect(pLdapConnection, NULL);
// Bind
returnCode = ldap_bind_s(pLdapConnection, (char*)m_Username.GetString(), (char*)m_Password.GetString(), authMethod);
// Attempt to search for user
retSearch = ldap_search_s(pLdapConnection, "dc=as,dc=local", LDAP_SCOPE_SUBTREE, "(&(sAMAccountName = examplename))", NULL, NULL, &res);
All of this works so far, up until the searching part, for example - I want to search for a user "username" in group "Technical". I've tried things like the below;
retSearch = ldap_search_s(pLdapConnection, "dc=as,dc=local", LDAP_SCOPE_SUBTREE, "(&(sAMAccountName=username)(memberof=CN=Technical))",
nullptr, 0, &pSearchResult);
That does not return anything, so I've tried searching more and the only thing similar I've found is - LDAP Finding Members of a group PHP but it's in PHP and I cannot seem to transfer that over to C++ so far.
Any help in the right direction would be helpful as I cannot work it out. :-)
Your filter should be something like:
(&(objectClass=user)(sAMAccountName=yourUserName)
(memberOf=CN=YourGroup,OU=Users,DC=YourDomain,DC=com))
To include membership due to group nesting:
(&(objectClass=user)(sAMAccountName=yourUserName)
(memberOf:1.2.840.113556.1.4.1941:=cn=YourGroup,ou=Users,dc=YourDomain,dc=com))
The numbers 1.2.840.113556.1.4.1941 are an extended match.

Obtaining wi-fi connection status from Connection Manager

I have some code which successfully iterates over a list of wi-fi networks, and provides feedback about available networks. The essential calls shown here...
WlanOpenHandle(WLAN_API_VERSION, NULL, &dwVersion, &hSession);
PWLAN_INTERFACE_INFO_LIST pInterfaceInfoList = NULL;
WlanEnumInterfaces(hSession, NULL, &pInterfaceInfoList);
for(int i ...)
{
PWLAN_AVAILABLE_NETWORK_LIST pAvailableNetworkList = NULL;
WlanGetAvailableNetworkList(hSession, &interfaceGUID,
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES |
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,
NULL, &pAvailableNetworkList);
for(int j ...)
{
WLAN_AVAILABLE_NETWORK network = pAvailableNetworkList->Network[j];
:
}
}
This all works fine, and inside the inner loop I'm able to access all of the attributes that I need, such as signal strength, security flags, etc via the network data structure.
One thing that I am not able to obtain is information regarding connection status, such as AUTHENTICATING or AUTHENTICATION_FAILED, etc, so I have tried to introduce another call inside the loop as follows...
CM_CONNECTION_DETAILS connectionDetails;
memset(&connectionDetails, 0, sizeof(CM_CONNECTION_DETAILS));
connectionDetails.Version = CM_CURRENT_VERSION;
const char* ccp = reinterpret_cast<const char*>(network.dot11Ssid.ucSSID);
mbstowcs(connectionDetails.szName, &ccp[0], network.dot11Ssid.uSSIDLength);
DWORD dwCount = sizeof(CM_CONNECTION_DETAILS);
CM_RESULT cmr = CmGetConnectionDetailsByName(connectionDetails.szName,
&connectionDetails, &dwCount);
if (cmr == CMRE_SUCCESS)
{
:
}
Upon calling the CmGetConnectionDetailsByName() function, the details inside the CM_CONNECTION_DETAILS structure look correct (name and version), but the function returns with CMRE_INVALID_CONNECTION and the structure is not populated.
I haven't been able to find any examples of this call being successful (only a couple of references to the call returning the same CMRE_INVALID_CONNECTION code).
Does anyone have any experience of using the call successfully, or alternatively suggest a better way to find out the connection status of a network (ie if AUTHENTICATION is in progress or if AUTHENTICATION failed, etc)?
[I'm using Visual Studio 2013 C++ (native Windows app, not MFC), the target is 32-bit and Unicode, running on Windows Compact 2013]
The function below doesn't quite give me the fine control that I was looking for, but it does at least give me the opportunity to find out the state a particular interface. This means that I can find out if the interface is currently in the process or authenticating, and depending whether the final state is connected or disconnected, I can find out if authentication was successful or not.
WLAN_INTERFACE_STATE getNetworkState(HANDLE hSession, GUID* pGUID, std::wstring& wsState, bool bReportState=true)
{
WLAN_INTERFACE_STATE result = wlan_interface_state_not_ready;
DWORD dwDataSize;
void* pData;
DWORD dwErrorCode = WlanQueryInterface(hSession, pGUID, wlan_intf_opcode_interface_state, NULL, &dwDataSize, &pData, NULL);
if (dwErrorCode == ERROR_SUCCESS && pData != NULL)
{
WLAN_INTERFACE_STATE* pState = reinterpret_cast<WLAN_INTERFACE_STATE*>(pData);
if (pState != NULL)
{
switch (*pState)
{
case wlan_interface_state_not_ready: wsState = L"NOT_READY"; break;
case wlan_interface_state_connected: wsState = L"CONNECTED"; break;
case wlan_interface_state_ad_hoc_network_formed: wsState = L"AD_HOC_NETWORK_FORMED"; break;
case wlan_interface_state_disconnecting: wsState = L"DISCONNECTING"; break;
case wlan_interface_state_disconnected: wsState = L"DISCONNECTED"; break;
case wlan_interface_state_associating: wsState = L"ASSOCIATING"; break;
case wlan_interface_state_discovering: wsState = L"DISCOVERING"; break;
case wlan_interface_state_authenticating: wsState = L"AUTHENTICATING"; break;
}
result = *pState;
}
WlanFreeMemory(pData);
}
return result;
}
A limitation of this check, is that it doesn't readily support multiple connections on the same interface, This query doesn't allow us to query to which of the connections the status refers.
If I arrive at a better solution, I will report it here.

How to change the Network name shown in Windows's ncpa.cpl using C++?

Windows' "Control Panel\Network and Internet\Network Connections" dialog (also by running ncpa.cpl) is used to show the Networks. A user can rename the name of Network by right-clicking and choosing "rename". This name is different with the adapter name, for instance, my ethernet card has the Network name being "Ethernet", but with the adapter name called "Qualcomm Atheros AR8171/8175 PCI-E Gigabit Ethernet Controller (NDIS 6.30)". I know I can change the latter one using NetCfg API, but I don't know how to change the former one, the "Ethernet".
I know that netsh command seems to do this work. But I want to implement this renaming using C++. Is there a way?
SOLUTION:
I'm so happy that got problem SOLVED using NetShareManager API! NETCON_PROPERTIES structure saves the GUID of the device, used to search the device, INetConnection::Rename can be used to rename the Network Connection name!
Here's the code:
BOOL DoTheWork(INetSharingManager *pNSM, wchar_t strDeviceName[])
{ // add a port mapping to every firewalled or shared connection
BOOL bFound = FALSE;
INetSharingEveryConnectionCollection * pNSECC = NULL;
HRESULT hr = pNSM->get_EnumEveryConnection (&pNSECC);
if (!pNSECC)
wprintf (L"failed to get EveryConnectionCollection!\r\n");
else {
// enumerate connections
IEnumVARIANT * pEV = NULL;
IUnknown * pUnk = NULL;
hr = pNSECC->get__NewEnum (&pUnk);
if (pUnk) {
hr = pUnk->QueryInterface (__uuidof(IEnumVARIANT),
(void**)&pEV);
pUnk->Release();
}
if (pEV) {
VARIANT v;
VariantInit (&v);
while ((S_OK == pEV->Next (1, &v, NULL)) && (bFound == FALSE)) {
if (V_VT (&v) == VT_UNKNOWN) {
INetConnection * pNC = NULL;
V_UNKNOWN (&v)->QueryInterface (__uuidof(INetConnection),
(void**)&pNC);
if (pNC) {
NETCON_PROPERTIES *pNETCON_PROPERTIES;
pNC->GetProperties(&pNETCON_PROPERTIES);
wchar_t currentGUID[BUF_SIZE];
GUID guid = pNETCON_PROPERTIES->guidId;
wsprintf(currentGUID, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
if (wcscmp(currentGUID, strDeviceName) == 0)
{
pNC->Rename(NPCAP_LOOPBACK_INTERFACE_NAME);
bFound = TRUE;
}
pNC->Release();
}
}
VariantClear(&v);
}
pEV->Release();
}
pNSECC->Release();
}
return bFound;
}
From a cursory look at the code for
netsh interface set interface name "oldname" newname="newname"
It looks to be using INetworkConnection::Rename. In order to get the connection you want, I believe you would have to use the INetSharingManager to enumerate all the connections available. Then you would have to iterate through them to find the one you want and then call the Rename function.
All that being said, as #Alexander Balabin said, the NLM API would probably be easier, but I don't know for sure if it does what you want. I can just tell you what netsh does.
I have not tried this, but NLM API's INetwork has a SetName method which looks like it should do what you want.

Launch default email client to open a "send email" window with a pre-selected file attachment

I need to add a "Create and email" feauture to our app. Our program creates an output file, and I must then launch the default email client to open a "write email" window, and with the output file preselected as an attachment.
I've seen other programs do it, even if the default client is Thunderbird instead of Outlook.
I ended up using MAPI to achieve it. I used LoadLibrary and GetProcAddress to get the needed functions.
The code I used is this:
bool MailSender::Send(HWND hWndParent, LPCTSTR szSubject)
{
if (!m_hLib)
return false;
LPMAPISENDMAIL SendMail;
SendMail = (LPMAPISENDMAIL) GetProcAddress(m_hLib, "MAPISendMail");
if (!SendMail)
return false;
vector<MapiFileDesc> filedesc;
for (std::vector<attachment>::const_iterator ii = m_Files.begin(); ii!=m_Files.end(); ii++)
{
MapiFileDesc fileDesc;
ZeroMemory(&fileDesc, sizeof(fileDesc));
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = (LPSTR) ii->path.c_str();
fileDesc.lpszFileName = (LPSTR) ii->name.c_str();
filedesc.push_back(fileDesc);
}
std::string subject;
if (szSubject)
subject = utf16to8(szSubject).c_str();
else
{
for (std::vector<attachment>::const_iterator ii = m_Files.begin(); ii!=m_Files.end(); ii++)
{
subject += ii->name.c_str();
if (ii+1 != m_Files.end())
subject += ", ";
}
}
MapiMessage message;
ZeroMemory(&message, sizeof(message));
message.lpszSubject = (LPSTR) subject.c_str();
message.nFileCount = filedesc.size();
message.lpFiles = &filedesc[0];
int nError = SendMail(0, (ULONG)hWndParent, &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);
if (nError != SUCCESS_SUCCESS && nError != MAPI_USER_ABORT && nError != MAPI_E_LOGIN_FAILURE)
return false;
return true;
}
Using the mailto scheme may be a solution but it's going to be tricky due to restrictions on what fields are considered safe (see the RFC 2368 and 6067 for the full details if you want to go that route).
Another solution would be to figure out what email client is installed and - wherever possible - launch it and specify all you need via command line. See here for Thunderbird & here for Outlook.
You can use the following command to start the start the default client app with attachment
"Path to default mail client.exe" -mail -compose subject='Subject',attachment='File path',body='body'"
Path to default mail client-> can be taken from registry path
HKEY_LM\SOFTWARE\Clients\Mail\Email Client Name\shell\open\command
Mail Client Name -> can be taken from
HKEY_LM\Software\Clients\Mail

WMI Remote connection

I have an issue regarding WMI connection through asp.net from Computer A (windows 2003 server) to Computer B (Windows XP)..
The error is as follows:
RPC server is unavailable..
There are a few steps that you must take in order to successfully leverage WMI connectivity. The basics are you must allow remote management on the target box of course. If you can’t RDP into it, chances are, you can’t remote manage anything else. This can also include Windows firewall issues too. Make sure your request can even get in at all.
Next, start simple. Can you even poll for the running processes on that box? Try to output all the running processes on the target box with System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetProcesses("machine-name"). If you can at least get some information on the box then the RPC message you are getting has to do with incorrect arguments being passed in, perhaps?
Anyways, I recently wrote a web application that allowed the users to find a server on the LAN and kill a target process there or start a new one. I did it in C# so the code snippet below is just what I used. It's not the best but its working in production right now:
public static class RemoteProcessAccess
{
public static void KillProcessByProcessID(string NameOfServer, string DomainName, string LogIn, string Password, int processID)
{
//#1 The vars for this static method
#region /// <variables> ...
string userName;
string password;
string machineName;
string myDomain;
Hashtable hs = new Hashtable();
ManagementScope mScope;
ConnectionOptions cnOptions;
ManagementObjectSearcher objSearcher;
ManagementOperationObserver opsObserver;
ManagementClass manageClass;
DirectoryEntry entry;
DirectorySearcher searcher;
DirectorySearcher userSearcher;
#endregion
//#2 Set the basics sent into the method
machineName = NameOfServer;
myDomain = DomainName;
userName = LogIn;
password = Password;
cnOptions = new ConnectionOptions();
cnOptions.Impersonation = ImpersonationLevel.Impersonate;
cnOptions.EnablePrivileges = true;
cnOptions.Username = myDomain + "\\" + userName;
cnOptions.Password = password;
mScope = new ManagementScope(#"\\" + machineName + #"\ROOT\CIMV2", cnOptions);
//#3 Begin Connection to Remote Box
mScope.Connect();
objSearcher = new ManagementObjectSearcher(String.Format("Select * from Win32_Process Where ProcessID = {0}", processID));
opsObserver = new ManagementOperationObserver();
objSearcher.Scope = mScope;
string[] sep = { "\n", "\t" };
//#4 Loop through
foreach (ManagementObject obj in objSearcher.Get())
{
string caption = obj.GetText(TextFormat.Mof);
string[] split = caption.Split(sep, StringSplitOptions.RemoveEmptyEntries);
// Iterate through the splitter
for (int i = 0; i < split.Length; i++)
{
if (split[i].Split('=').Length > 1)
{
string[] procDetails = split[i].Split('=');
procDetails[1] = procDetails[1].Replace(#"""", "");
procDetails[1] = procDetails[1].Replace(';', ' ');
switch (procDetails[0].Trim().ToLower())
{
//You could look for any of the properties here and do something else,
case "processid":
int tmpProc = Convert.ToInt32(procDetails[1].ToString());
//if the process id equals the one passed in....
//(this is redundant since we should have limited the return
//by the query where above, but we're paranoid here
if (tmpProc.Equals(processID))
{
obj.InvokeMethod(opsObserver, "Terminate", null);
}
break;
}//end process ID switch...
}//end our if statement...
}//end our for loop...
}//end our for each loop...
}//end static method
}
Look at KB875605 ("How to troubleshoot WMI-related issues in Windows XP SP2")
You could enable the RPC server on any target machine by running this on the target's command prompt:
[/code]
netsh firewall set service RemoteAdmin
[/code]
Worked for me at least. :)
Try to use wmic command line to get information from the remote computer, also you can install the code of Services+ and try to connect and debug your connection to the server, most likely it is firewall problem or RPC services is down or disabled.