OPAL (Open Phone Abstraction Library) Transport not terminated when reattaching thread? - multithreading

I am currently in the process of writing an application using OPAL that makes H323 calls. I have looked online for a number of working examples and have managed to put something together that resembles what it should look like, at present I am able to call an external ip via my application but when I accept the call it craps out and dies.
Leaving me with:
0:12.949 SetUpCall:8752 as
sert.cxx(259) Assertion fail: Transport not terminated when reattaching thread
, file d:\voip\software\opal\src\opal\transports.cxx, line 1021
PAssertFunc(0xde3f88, 0xffffffffd228226f, 0, 0) + 0x82
PAssertFunc(0x10d6e5f8, 0x3fd, 0, 0x10d6eb98) + 0x15b
OpalTransport::AttachThread(0xde5870, 0xffffffffd22fc76b, 0, 0) + 0x96
H323Connection::SetUpConnection(0xffffffffd22fc713, 0, 0, 0xde6ea8) + 0x196
AsynchCallSetUp(0x10d3572c, 0, 0, 0xdd0108) + 0x7c
PThread1Arg<PSafePtr<OpalConnection,PSafePtrBase> >::Main(0xffffffffd2282faf
From what I have deduced, perhaps incorrectly if that there is a thread lock issue caused (possibly arising from the fact my application is sending my expected H323 call, but it is also throwing in a call from sip:fred straight after, I have no idea why).
My current enpoint is set up as follows:
bool OpalManagerLite::Init()
{
h323EP = new MyH323EndPoint(*this);
SetAudioJitterDelay(20, 1000);
DisableDetectInBandDTMF(true);
if(!InitEndpoint(h323EP))
{
return false;
}
if (h323EP != NULL) {
AddRouteEntry("pc:.* = h323:<da>");
AddRouteEntry("h323:.* = pc:<da>");
AddRouteEntry("pots:.*\\*.*\\*.* = h323:<dn2ip>");
AddRouteEntry("h323:.*\\*.*\\*.* = pots:<dn2ip>");
AddRouteEntry("h323:.* = pots:<da>");
AddRouteEntry("pots:.* = h323:<da>");
}
PString defaultSrcEP = "pc:*";
srcEP = defaultSrcEP;
return true;
}
bool OpalManagerLite::InitEndpoint(MyH323EndPoint * h323EP)
{
h323EP->DisableFastStart(true);
h323EP->DisableH245Tunneling(true);
h323EP->ForceSymmetricTCS(true);
h323EP->SetLocalUserName("Ronnie");
h323EP->SetDefaultLocalPartyName("Ronnie");
PStringArray listeners; // Default listeners opened
OpalListenerTCP *toListen = new OpalListenerTCP(*h323EP,
PIPSocket::GetDefaultIpAny(), NULL, NULL);
if (!h323EP->StartListeners(listeners))
{
return false;
}
return true;
}
My call is as you would expect when making a call e.g. SetUpCall(srcEP, ip, callToken);
Any help would be greatly appreciated as to why this is occurring, I know I must of tripped up somewhere I just cannot see where.

Related

Errors creating a multithreaded named pipe server with Administrator only access on windows c++

Im trying to create a multithreaded namedpipe server as outlined in the msdn sample here https://learn.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server but Im trying to restrict the namedpipe to access by adminstrators group members only.
The example works correctly when no SECURITY_ATTRIBUTES structure is specified but when an SA is specified the first call is successful, but following calls to CreateNamedPipe fail as long as the first pipe is listening or communicating with a client. The create call fails, usually with ACCESS_DENIED, but sometimes with error 1305 The revision level is unknown. When the first pipe closes due to client disconnecting the following call will be successful for the next createnamedpipe call but will in turn fail once that pipe has a client.
I have tried multiple values for the grfInheritance field with no avail. This is my first adventure into explicitly specifying SECURITY so forgive me if I have missed something obvious. Note that in the Function that calls createnamedpipe I create a new SA structure with each create attempt but I have also tried creating one and sharing it outside the create loop.
Relevant code follows:
function that creates the pipe:
HRESULT DapiSettingsSvr::DapiSettingsListener()
{
while(m_run)
{
//find an unused control array member. If they are all used we have max connection so dont create a pipe.
UINT connectId = 0;
for (connectId; connectId < MAX_CONNECTIONS; connectId++)
{
if (m_controlArray[connectId].inuse == false)
break;
}
SECURITY_ATTRIBUTES sa;
HRESULT hr = InitializeSecurity(&sa);
if (FAILED(hr))
{
return hr;
}
if (connectId < MAX_CONNECTIONS)
{
HANDLE hpipe;
hpipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_BYTE | // byte bipe
PIPE_READMODE_BYTE | // read as bytes
PIPE_WAIT | // do not return until data is recieved
PIPE_REJECT_REMOTE_CLIENTS, // no remote connections
MAX_CONNECTIONS, // max. instances
OUTPUT_BUFFER_SIZE, // output buffer size
INPUT_BUFFER_SIZE, // input buffer size
0, // client time-out
&sa); // default security attribute
// CleanUpSecurityResources();
if (hpipe == INVALID_HANDLE_VALUE)
{
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"CreateNamedPipe failed, GLE=%d.\n", GetLastError());
DapiSettingLogger(logbuffer);
}
else
{
m_controlArray[connectId].inuse = true;
m_controlArray[connectId].pThis = this;
m_controlArray[connectId].connectId = connectId;
m_controlArray[connectId].pipehandle = hpipe;
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename);
DapiSettingLogger(logbuffer);
// block until a client tries to connect.success is non zero. However a client can connect between the create call and ConnectNamedPipe call.
// In this case ConnectNamedPipe returns zero but GLE = ERROR_PIPE_CONNECTED and a valid connection exists. Check for this case.
fConnected = ConnectNamedPipe(hpipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
// Create a thread for this client.
m_controlArray[connectId].threadHandle = CreateThread(
NULL, // no security attribute
0, // default stack size
WorkerInstance, // thread proc
(LPVOID)&m_controlArray[connectId], // thread parameter
0, // not suspended
&m_controlArray[connectId].threadId); // returns thread ID
if (m_controlArray[connectId].threadHandle == NULL)
{
swprintf_s(logbuffer, ARRAYSIZE(logbuffer), L"CreateThread failed, GLE=%d.\n", GetLastError());
DapiSettingLogger(logbuffer);
CloseHandle(m_controlArray[connectId].pipehandle);
ZeroMemory(&m_controlArray[connectId], sizeof(WORKER_INFO));
}
}
else
{
// The client could not connect, so close the pipe.
CloseHandle(m_controlArray[connectId].pipehandle);
ZeroMemory(&m_controlArray[connectId], sizeof(WORKER_INFO));
}
} //else valid connection
}
else
{
DapiSettingLogger((LPWSTR) L"Max Connections reached\n");
}
}
return S_OK;
}
Function that creates the SA
HRESULT DapiSettingsSvr::InitializeSecurity(SECURITY_ATTRIBUTES* psa)
{
HRESULT result = S_OK;
DWORD res, error;
EXPLICIT_ACCESS ea[1];
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
// Create a SID for the BUILTIN\Administrators group.
if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&m_pAdminSID))
{
error = GetLastError();
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"AllocateAndInitializeSid Error %u\n", error);
DapiSettingLogger(logbuffer);
result = HRESULT_FROM_WIN32(error);
goto Cleanup;
}
ea[0].grfAccessPermissions = GENERIC_ALL;
ea[0].grfAccessMode = GRANT_ACCESS;
ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; //changing
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR)m_pAdminSID;
// Create a new ACL that contains the new ACE.
res = SetEntriesInAcl(1, ea, NULL, &m_pACL);
if (ERROR_SUCCESS != res)
{
swprintf(logbuffer, ARRAYSIZE(logbuffer),L"SetEntriesInAcl Error %u\n", res);
DapiSettingLogger(logbuffer);
result = HRESULT_FROM_WIN32(res);
goto Cleanup;
}
// Initialize a descriptor Use localalloc as it allows memory moving without changing handle value
m_pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == m_pSD)
{
error = GetLastError();
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"LocalAlloc Error %u\n", error);
result = HRESULT_FROM_WIN32(error);
goto Cleanup;
}
if (!InitializeSecurityDescriptor(m_pSD,
SECURITY_DESCRIPTOR_REVISION))
{
error = GetLastError();
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"InitializeSecurityDescriptor Error %u\n", error);
result = HRESULT_FROM_WIN32(error);
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(m_pSD,
TRUE, // bDaclPresent flag
m_pACL,
FALSE)) // not a default DACL
{
error = GetLastError();
swprintf(logbuffer, ARRAYSIZE(logbuffer), L"SetSecurityDescriptorDacl Error %u\n", error);
result = HRESULT_FROM_WIN32(error);
goto Cleanup;
}
Cleanup:
if (FAILED(result))
{
CleanUpSecurityResources();
}
else
{
// Initialize a security attributes structure.
psa->nLength = sizeof(SECURITY_ATTRIBUTES);
psa->lpSecurityDescriptor = m_pSD;
psa->bInheritHandle = TRUE; /// NOTE I have toyed with this value also
}
return result;
}
Any input on what Im doing incorrectly would be greatly appriciated!!
Thanks!
According to Named Pipe Security and Access Rights,
In addition to the requested access rights, the DACL must allow the
calling thread FILE_CREATE_PIPE_INSTANCE access to the named pipe.
Ok, I figured this out. Im going to mark YangXiaoPo's answer as correct as this pointed me in the right direction but for clarification GENERIC_ALL already includes the right to FILE_CREATE_PIPE_INSTANCE or at least thats what my testing indcates. So setting the EXPICIT_ACCESS structure field to ea[0].grfAccessPermissions = GENERIC_ALL | FILE_CREATE_PIPE_INSTANCE; does not resolve this issue.
The answer lies in the fact that I was running the PipeServer program from within visual studio ( debug ) and thus as a generic user. So the first time through the loop a pipe gets created and the SA with the local administrators group ACE is then applied to the pipe.
So we get a pipe created in the listening state. As soon as a client connects the working thread is created and then the the while(m_run) loop does another iteration and tries to create a new pipe instance. This attempt fails ( actually a looping fail ) because the security attribute with the administrators only ACL is now looked at and the program is not running as an administrator. As soon as the first client disconnects the working thread closes the pipe handle ( effectively destroying the pipe ) and then in the next iteration a pipe is again created.
Running the program as Administrator ( or starting Visual studio as Admin and then debugging ) resolves the issue, though I think a fully correct solution would be to create a second ACE that specified Creator Owner in addition to Admin for the SA DACL.
Thanks!!

How can I check if I can write to a file without elevated token?

I know that this question was asked by many other people, but my case has one important difference. I can't just open file handle with write option because I want to check if I can write in this file without elevated token.
For example, user can run my installer as administrator so I can write in almost all of folders, but after installation my program won't work.
I thought that I can just get the token of my process, disable all privileges and apply it to new thread. I did it but it doesn't work.
I don't want to include code that I wrote because there are a lot of insignificant stuff. Instead, I'll just describe order of functions that I use.
GetCurrentProcess
OpenProcessToken
DuplicateTokenEx - somebody told me that it removes the elevation
AdjustTokenPrivileges - I call it with DisableAllPriveleges = true
CreateThread - Suspend = true
SetThreadToken
CreateFile
I can still write to all folders. What am I doing wrong?
for create not elevated token from your existing token we can use CreateRestrictedToken function with LUA_TOKEN flag. this by fact what is UAC doing when create restricted version of an existing access token on interactive logon. and then we can do access via this token. also note that we not need use new thread - we can temporary impersonate current thread with this lua token and then revert back.
so code can look like:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG CheckFileWriteAccess(PCWSTR FileName, ULONG& dwFileError)
{
HANDLE hToken, hLuaToken;
ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken));
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(CreateRestrictedToken(hToken, LUA_TOKEN, 0, 0, 0, 0, 0, 0, &hLuaToken));
CloseHandle(hToken);
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(DuplicateToken(hLuaToken, ::SecurityImpersonation, &hToken));
CloseHandle(hLuaToken);
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(SetThreadToken(0, hToken));
CloseHandle(hToken);
if (dwError == NOERROR)
{
HANDLE hFile = CreateFileW(FileName, FILE_GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
dwFileError = NOERROR;
}
else
{
dwFileError = GetLastError();
NTSTATUS status = RtlGetLastNtStatus();
if (RtlNtStatusToDosError(status) == dwFileError)
{
dwFileError = HRESULT_FROM_NT(status);
}
}
SetThreadToken(0, 0);
}
}
}
}
return dwError;
}
note also snippet
dwFileError = GetLastError();
NTSTATUS status = RtlGetLastNtStatus();
if (RtlNtStatusToDosError(status) == dwFileError)
{
dwFileError = HRESULT_FROM_NT(status);
}
win32 error returned from CreateFileW api - frequently confused, because many different (by sense) NTSTATUS errors mapped to single win32 error. so always better check RtlGetLastNtStatus() instead GetLastError(). even more better of course use NtOpenFile (it documented, user mode, supported, will be not altered or removed) which direct return actual NTSTATUS.
note also that potential you can got error STATUS_SHARING_VIOLATION too. more releable open file with READ_CONTROL only access (this never give sharing violation) query it security descriptor and than use AccessCheck with LUA token, but this require more complex code

Server crashes after closesocket

I have multithreading application, it's periodically polling a few hundred devices.
Each thread serves one device, its socket and other descriptors are encapsulated at individual object, so no shared descriptors.
Occasionally application crashes after closesocket(fSock), when I try set descriptor fSock to 0.
I assume, I should not set fSock = 0, if closesocket(fSock) returns SOCKET_ERROR.
Or is there any other reason?
My code:
bool _EthDev::Connect()
{
int sockErr, ret, i, j;
int szOut = sizeof(sockaddr_in);
// create socket
if ((fSock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
sockErr = GetLastError();
Log("Invalid socket err %d", sockErr);
fSock = 0;
return false;
}
// set fast closing socket (by RST)
linger sLinger;
sLinger.l_onoff = 1;
sLinger.l_linger = 0;
if (sockErr = setsockopt(fSock, SOL_SOCKET, SO_LINGER, (const char FAR*)&sLinger, sizeof(linger)))
{
sockErr = WSAGetLastError();
Log("Setsockopt err %d", sockErr);
closesocket(fSock);
fSock = 0; // here crashes
return false;
}
// connect to device
fSockaddr.sin_port = htons((u_short)(baseport));
if (connect(fSock, (struct sockaddr*)&fSockaddr, szOut))
{
closesocket(fSock);
fSock = 0;
return false;
}
...
return true;
}
I have multithreading application, ... [it] occasionally crashes
A multithreading application that occasionally crashes is a classic symptom of a race condition. I think to prevent the crashes you need to figure out what the race condition is in your code, and fix that.
I assume, I should not set fSock = 0, if closesocket(fSock) returns
SOCKET_ERROR. Or is there any other reason?
I doubt the problem is actually related to closesocket() or to setting fSock to 0. Keep in mind that sockets are really just integers, and setting an integer to 0 isn't likely to cause a crash on its own. What could cause a crash is a write to invalid memory -- and fSock = 0 does write to the memory location where the member variable fSock is (or was) located at.
Therefore, a more likely hypothesis is that the _EthDev object got deleted by thread B while thread A was still in the middle of calling Connect() on it. This would be most likely happen while the connect() call was executing, because a blocking connect() call can take a relatively long time to return. So if there was another thread out there that rudely deleted the _EthDev object during the connect() call, then as soon as connect() returned, the next line of code that would write to the location where the (now deleted) _EthDev object used to be would be the "fSock = 0;" line, and that could cause a crash.
I suggest you review your code that deletes _EthDev objects, and if it isn't careful to first shut down any thread(s) using those objects (and also to wait for the threads to exit!) before deleting the _EthDev objects, you should rewrite it so that it does so reliably. Deleting an object while another thread might still be using it is asking for trouble.

C++ usrsctp callback parameters null

I'm currently creating a network application that uses the usrsctp library on windows and I'm having an odd problem with parameters appearing as null when they shouldn't be on a callback function. I'm not sure if this is a specific usrsctp issue or something I'm doing wrong so I wanted to check here first.
When creating a new sctp socket you pass a function as one of the parameters that you want to be called when data is received as shown in the code below
static int receive_cb(struct socket *sock, union sctp_sockstore addr, void *data,
size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info)
{
if (data == NULL) {
printf("receive_cb - Data NULL, closing socket...\n");
done = 1;
usrsctp_close(sock);
}
else {
_write(_fileno(stdout), data, datalen);
free(data);
}
return (1);
}
...
//Create SCTP socket
if ((sctpsock = usrsctp_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP, receive_cb, NULL, 0, NULL)) == NULL) {
perror("usrsctp_socket");
return -1;
}
Tracing through the library I can see that before the call back is called all the parameters are correct
As soon as I step into it they become null
I've no idea what would cause this, the callback function was taken straight from the official examples so nothing should be wrong there.
Ok, worked out the issue, it seems that the parameter before 'union sctp_sockstore addr' was causing the stack to be pushed by 0x1c and moving the rest of the parameters away from where they should be. I've never come across this issue before but changing the parameter to a pointer fixed it.
I had the same Issue, in my case the reason was a missing define for INET.
Since the size of 'union sctp_sockstore' depends on this define.
So you have to ensure, that you use the same defines as you used when compiling the library.

Embedded Linux poll() returns constantly

I have a particular problem. Poll keeps returning when I know there is nothing to read.
So the setup it as follows, I have 2 File Descriptors which form part of a fd set that poll watches. One is for a Pin high to low change (GPIO). The other is for a proxy input. The problem occurs with the Proxy Input.
The order of processing is: start main functions; it will then poll; write data to proxy; poll will break; accept the data; send the data over SPI; receiving slave device, signals that it wants to send ack, by Dropping GPIO low; poll() senses this drop and reacts;
Infinite POLLINs :(
IF I have no timeout on the Poll function, the program works perfectly. The moment I include a timeout on the Poll. The Poll returns continuously. Not sure what I am doing wrong here.
while(1)
{
memset((void*)fdset, 0, sizeof(fdset));
fdset[0].fd = gpio_fd;
fdset[0].events = POLLPRI; // POLLPRI - There is urgent data to read
fdset[1].fd = proxy_rx;
fdset[1].events = POLLIN; // POLLIN - There is data to read
rc = poll(fdset, nfds, 1000);//POLL_TIMEOUT);
if (rc < 0) // Error
{
printf("\npoll() failed/Interrupted!\n");
}
else if (rc == 0) // Timeout occurred
{
printf(" poll() timeout\n");
}
else
{
if (fdset[1].revents & POLLIN)
{
printf("fdset[1].revents & POLLIN\n");
if ((resultR =read(fdset[1].fd,command_buf,10))<0)
{
printf("Failed to read Data\n");
}
if (fdset[0].revents & POLLPRI)
//if( (gpio_fd != -1) && (FD_ISSET(gpio_fd, &err)))
{
lseek(fdset[0].fd, 0, SEEK_SET); // Read from the start of the file
len = read(fdset[0].fd, reader, 64);
}
}
}
}
So that is the gist of my code.
I have also used GDB and while debugging, I found that the GPIO descriptor was set with revents = 0x10, which means that an error occurred and that POLLPRI also occurred.
In this question, something similar was addressed. But I do read all the time whenever I get POLLIN. It is a bit amazing, that this problem only occurs when I include the timeout, if I replace the poll timeout with -1, it works perfectly.
When poll fails (returning -1) you should do something with errno, perhaps thru perror; and your nfds (the second argument to poll) is not set, but it should be the constant 2.
Probably the GCC compiler would have given a warning, at least with all warnings enabled (-Wall), about nfds not being set.
(I'm guessing that nfds being uninitialized might be some "random" large value.... So the kernel might be polling other "random" file descriptors, those in your fdset after index 2...)
BTW, you could strace your program. And using the fdset name is a bit confusing (it could refer to select(2)).
Assuming I fixed your formatting properly in your question, it looks like you have a missing } after the POLLIN block and the next if() that checks the POLLPRI. It would possibly work better this way:
if (fdset[1].revents & POLLIN)
{
printf("fdset[1].revents & POLLIN\n");
if ((resultR =read(fdset[1].fd,command_buf,10))<0)
{
printf("Failed to read Data\n");
}
}
if (fdset[0].revents & POLLPRI)
//if( (gpio_fd != -1) && (FD_ISSET(gpio_fd, &err)))
{
lseek(fdset[0].fd, 0, SEEK_SET); // Read from the start of the file
len = read(fdset[0].fd, reader, 64);
}
Although you can do whatever you want with indentation in C/C++/Java/JavaScript, not doing it right can bite you really hard. Hopefully, I'm wrong and your original code was correct.
Another one I often see: People not using the { ... } at all and end up writing code like:
if(expr) do_a; do_b;
and of course, do_b; will be executed all the time, whether expr is true or false... and although you could fix the above with a comma like so:
if(expr) do_a, do_b;
the only safe way to do it right is to use the brackets:
if(expr)
{
do_a;
do_b;
}
Always make sure your indentation is perfect and write small functions so you can see that it is indeed perfect.

Resources