How do I call a web service from iSeries COBOL? - c

We need to be able to call an internal web service from COBOL code running on an iSeries LPAR (V6R1). I worked through the one complete example that I was able to find online here. So, my next step was to try to repeat the process and call one of our existing web services.
I used the WSDL2WS command from QSH to generate the C client stubs. I modified the example COBOL client program and attempted a call to my web service. The problem that I'm running into seems to be related to the fact that the example C client methods are returning pointers and the COBOL code is assigning them to pointers. I suspect that an error in my COBOL code is the root of my problem, because the C method that that WSDL2WS created for my web service method returns a simple xsdc_string not a pointer to a custom result type:
xsdc__string TestUnsuccessfulMessage(AXISCHANDLE stub)
{
AXISCHANDLE call = axiscStubGetCall(stub);
xsdc__string Ret = NULL;
axiscCallSetSoapFaultNamespace(call, "http://myserver/PSItemMaintenance/ItemMaintenanceService.svc");
// ======================================================================
// Initialize client engine, set SOAP version, SOAPAction, operation, etc.
// ======================================================================
if (AXISC_SUCCESS != axiscCallInitialize(call, C_DOC_PROVIDER ))
return Ret;
if (NULL==axiscCallGetTransportProperty(call,"SOAPAction",0))
axiscCallSetTransportProperty(call,AXISC_SOAPACTION_HEADER , "http://tempuri.org/IItemMaintenanceService/TestUnsuccessfulMessage");
axiscCallSetSOAPVersion(call, SOAP_VER_1_1);
axiscCallSetOperation(call, "TestUnsuccessfulMessage", "http://tempuri.org/");
// ======================================================================
// Apply SSL configuration properties and user-set SOAP headers.
// ======================================================================
axiscStubIncludeSecure(stub);
axiscStubApplyUserPreferences(stub);
// ======================================================================
// Invoke web service, send/receive operation. Handle output parameters, if any.
// ======================================================================
if (AXISC_SUCCESS == axiscCallSendAndReceive(call))
{
if(AXISC_SUCCESS == axiscCallValidateMessage(call, "TestUnsuccessfulMessageResponse", "http://tempuri.org/", true_))
{
Ret = axiscCallGetElementAsString(call, "TestUnsuccessfulMessageResult", 0);
}
axiscStubCheckForExtraneousElements(stub);
}
axiscCallUnInitialize(call);
return Ret;
}
I can setup the web service in debug mode and can see the call from the iSeries when I run the program that calls the COBOL and C modules. I can also see that I'm returning back a simple string value.
The problem comes when the COBOL program tries to take the return value and use it. I believe that the relevant bits of the COBOL code are:
LINKAGE PROCEDURE FOR "TestSuccessfulMessage"
USING ALL DESCRIBED
LINKAGE SECTION.
01 LookupResult PIC X(1000).
CALL PROCEDURE "TestSuccessfulMessage"
USING BY VALUE STUB
RETURNING LookupResult.
I get a MCH3601 when it's coded this way. If I return into a pointer and then set the address to LookupResult, I end up with a null value.
I hope that I'm just missing a small detail somewhere. I have very little COBOL experience. I'm just trying to create a reference app as a proof of concept for another team within our company. Any help, or suggestions of what to try would be appreciated. I can supply more code.
Update: I tried just moving the LookupResult declaration into working storage. While that eliminated the MCH3601 error, I just got a bunch of garbage data in my display. I can see pieces of info about my iSeries session (i.e. deviceID, etc...) within the data.
I also tried leaving the LookupResult in the linkage section and creating a pointer in working storage. Then I added a "SET Address of LookupResult TO ResultPointer". Again, the call ended without error, but when I display LookupResult I get garbage data. However, it's different than the data that comes back if I return straight into LookupResult. I can see pieces of the SOAP envelope in this data.
Final:
All of the changes that I had to make were in the COBOL code. Here are the relevant pieces:
WORKING-STORAGE SECTION.
01 Endpoint PIC X(100).
01 STUB USAGE POINTER.
01 ResultPointer USAGE POINTER.
LINKAGE SECTION.
01 pszEndpoint PIC X(100).
01 LookupResult PIC X(7).
CALL PROCEDURE "TestSuccessfulMessage"
USING BY VALUE STUB
RETURNING INTO ResultPointer.
SET Address of LookupResult TO ResultPointer.

If I return into a pointer and then set the address to LookupResult, I
end up with a null value.
I am a COBOL person, but not on iSeries. Caveat emptor.
If xsdc__string resolves to a pointer, then your COBOL code should have a variable of type POINTER for the RETURNING portion of the CALL. Perhaps the C function is in fact returning NULL, the code certainly allows for it - maybe axiscCallInitialize isn't returning AXISC_SUCCESS.
At least on z/OS, you would want the COBOL pointer variable to be in either Working-Storage or Local Storage, and then you would SET ADDRESS OF LookupResult TO that pointer variable. I'm presuming that doesn't change just because you're on a different machine architecture.
...FWIW

Related

BlueNRG Bluetooth: read central device name

I'm using the STM BlueNRG-MS chip on my peripheral device and after connection I'd like to read the name of the connected central device (android phone).
I thought I can do this directly in my user_notify routine which is registered as hci callback
/* Initialize the Host-Controller Interface */
hci_init(user_notify, NULL);
So on the EVT_LE_CONN_COMPLETE event, I take the provided handle for the central device and I use aci_gatt_read_using_charac_uuid() to read what I thought is the characteristic with the device name (uuid 0x2a00).
case EVT_LE_META_EVENT:
{
evt_le_meta_event *evt = (void *)event_pckt->data;
switch(evt->subevent){
case EVT_LE_CONN_COMPLETE:
{
evt_le_connection_complete *cc = (void *)evt->data;
GAP_ConnectionComplete_CB(cc->peer_bdaddr, cc->handle);
uint16_t uuid = 0x2a00;
resp = aci_gatt_read_using_charac_uuid(cc->handle, 0, 1, UUID_TYPE_16, (uint8_t*)&uuid);
LOG("GATT read status: %d", resp);
enqueEvent(EVENT_BLE_CONNECTED);
}
break;
}
}
Long story short, it doesn't work. First thing I'm not sure about is, what is the start_handle and end_handle parameter of aci_gatt_read_using_charac_uuid(), it returns ERR_INVALID_HCI_CMD_PARAMS.
Can someone shed some light here?
update
What also puzzles me is that the function aci_gatt_read_using_charac_uuid() is nowehere referenced in the BlueNRG-MS Programming Guidelines.
update2
I changed the function call to aci_gatt_read_using_charac_uuid(cc->handle, 0x0001, 0xffff, UUID_TYPE_16, (uint8_t*)&uuid); but I still get the ERR_INVALID_HCI_CMD_PARAMS. What which paramter could even be invalid? The uuid exists, I can read the device name if I use the BlueNRG GUI with a bluetooth dongle.
update3
Has anyone ever used this function or somehow managed to read a characteristic from a central device? I'd highly appreciate any help or hint.
Here you go, The BlueNRG-MS Bluetooth® LE stack application command interface (ACI) - User manual
page 75 - 4.6.25 Aci_Gatt_Read_Charac_Using_UUID()
and make sure you have called Aci_Gatt_Init()
The user manual is last revised July 2019, the document you link to is from 2018, don't know if this is why ?
The start_handle and end_handle is the range of handles in your service as pictured here -
Here is a discussion to the closest thing I could find to match your question.
As it turned out there are two bugs in the BlueNRG API.
In bluenrg_aci_const.h file the OCF code OCF_GATT_READ_USING_CHARAC_UUID shall be 0x119 instead of 0x109.
And in the implementation of the aci_gatt_read_using_charac_uuid() function, there is a missing setting on event:
rq.event = EVT_CMD_STATUS;
Patching them fixed the issue.

Bluetooth gatt connection global variable not changing

I have an issue of an if statement not passing whilst my system gatt connection is not made.
Context
I have a BLE system using a NRF52840-dk board programmed in C. I also have a mobile application which, communicates with this board via a Gatt connection. I have a single service with a single characteristic. I write to this characteristic from my mobile application and, from this do some processing. At the moment I can send over a timestamp and begin storing data. However, I need to then send data back to my mobile device by this connection.
So what I have is a command to be sent from the phone to ask for some data. This should then send data back to the phone by changing the characteristic value.
Before I can change the value I need to see if the command has been issued. However, due to the priorities and constraints of the device I need to do this processing in the main function not in the BLE interrupt that I have done my time stamping in. This is due to the data I will be transmitting eventually will be large.
My issue however is, I receive the command to send some data back to the phone and update a global int value (changed from 0 to 1). Then in my main loop test this value and, if it is 1 write to the terminal and change the value back. I would then use this point of the code to run a function to send the data.
But this statement does not pass.
This is my main loop code
if(GATT_CONNECTED == false)//This works!
{
//Do some functions here
}
else if (GATT_CONNECTED == true)// GATT_CONNECTED = true
{
NRF_LOG_INFO("Test1 passed");//Testing variable this does not print
if(main_test == 1)
{
NRF_LOG_INFO("Test2 passed");//This does not print either irrelevant of value
main_test = 0;//False
}
idle_state_handle();
}
I don't know if the issue is the way I have defined my variable or due to interrupt priorities or something like that. But, when my Gatt connection is made the loop of (GATT_CONNECTED == true) does not seem to process.
My variable is defined in another file where my GATT connection is handled. The GATT connected variable is handled in main. my main_test variable is defined in another c file as int main_test = 0;. In the header declared as extern int main_test;.
I know the GATT_CONNECTED variable works as I have code in it that only runs when my gatt is not connected. I have omitted it for simplicity.
Any ideas,
Thanks
Ps Hope you are all keeping well and, safe
Edit
Added code for simplicity
main.c
bool GATT_CONNECTED = false;
int main(void)
{
Init_Routine();
while(1)
{
Process_data();//This runs if the gatt is not connected if statement inside
if(GATT_CONNECTED == true)//This does not run true when the gatt is connected
{
NRF_LOG_INFO("check gatt connectedpassed");//Testing variable.
nrf_gpio_pin_set(LED_4);//Turn an LED on once led 4 does not work
}
idle_state_handle();
}
}

send_v3trap context value

I am developing shared library and subagent for net-snmp. I need to send v3 traps for specific hardware events. I would like to know what value need to be filled in the context value of send_v3trap API.
void send_v3trap(netsnmp_variable_list * vars,const char * context )
Is this context value, same as user defined engine id ? i.e., the one which needs to be configured in snmptrapd.conf as below ?
createUser -e ENGINEID myuser SHA "my authentication pass" AES "my encryption pass"
More on configuring in this link
There is an example source code available for sending
v2traps
By looking at the net-snmp source code, send_v3trap calls internally send_v2trap and eventually,
/* A context name was provided, so copy it and its length to the v2 pdu
* template. */
if (context != NULL)
{
template_v2pdu->contextName = strdup(context);
template_v2pdu->contextNameLen = strlen(context);
}
Answering my own question.
"context" value can be filled with
value returned by "snmpv3_get_engineID"
NULL
As long as, configurations are proper in terms of v3 i.e., trapsess -v3 specified in the host and on the target, engineid of net-snmp is specified, then everything works fine.
Only unclear part still is, if someone is able to send v3 traps without specifying "context", in which scenario would it be useful really !

(Why) Does Windows "Calc.exe" lack a WndProc?

I am fiddling with wndprocs and WinSpy++ and i stumbled upon a strange thing with calc.exe.
It appears to lack a WndProc.
Here is my screenshot: a test program I made, the WinSpy++ window,, showing N/A, and the culprit.
Maybe the tool is a bit outdated, but the empirical evidence proves no WndProc is there.
I don't know if this is by design(this would be strange), or if I am missing something...
Here is referenced code:
Function FindWindow(title As String) As IntPtr
Return AutoIt.AutoItX.WinGetHandle(title)
End Function
Function GetWindowProc(handle As IntPtr) As IntPtr
Return GetWindowLong(handle, WindowLongFlags.GWL_WNDPROC)
End Function
In short (about your code): GetWindowLong() fails because you're trying to read an address in target process address space.
EXPLANATION
When GetWindowLong() returns 0 it means there is an error, from MSDN:
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Check Marshal.GetLastWin32Error() and you probably see error code is ERROR_ACCESS_DENIED (numeric value is 0x5).
Why? Because GetWindowLong() is trying to get address (or handle) of window procedure (not in your code, but in target process, in theory it may even be default window procedure but I never saw an application main window that doesn't hanle at least few messages). You may use this trick (but I never tried!) to see if a window is using default procedure (you have an address or not), I don't know...someone should try.
Now think what WNDPROC is:
LRESULT (CALLBACK* WNDPROC) (HWND, UINT, WPARAM, LPARAM);
An address (valid in process A) is not callable in process B (where it makes no sense at all). Windows DLLs code segments are shared across processes (I assume, I didn't check but it's reasonable in the game between safety and performance).
Moreover CallWindowProc(NULL, ...) will understand that NULL as a special value to invoke window procedure for that window class (on HWND owner). From MSDN:
...If this value is obtained by calling the GetWindowLong function ...the address of a window or dialog box procedure, or a special internal value meaningful only to CallWindowProc.
How Microsoft Spy++ does it (and maybe WinSpy++ does not)? Hard to say without WinSpy++ source code. For sure it's not such easy like GetWindowLong() and right way should involve CreateRemoteThread() and to do LoadLibrary() from that but both Microsoft Spy++ and WinSpy++ source code aren't available (AFAIK) for further inspection...
UPDATE
WinSpy++ inspection/debugging is pretty off-topic with the question (you should post a ticket to developers, your source code may fail for what I explained above, you should - always - check error codes) but we may take a look for fun.
In InjectThread.c we see it uses WriteProcessMemory + CreateRemoteThread then ReadProcessMemory to read data back (not relevant code omitted):
// Write a copy of our injection thread into the remote process
WriteProcessMemory(hProcess, pdwRemoteCode, lpCode, cbCodeSize, &dwWritten);
// Write a copy of the INJTHREAD to the remote process. This structure
// MUST start on a 32bit boundary
pRemoteData = (void *)((BYTE *)pdwRemoteCode + ((cbCodeSize + 4) & ~ 3));
// Put DATA in the remote thread's memory block
WriteProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwWritten);
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pdwRemoteCode, pRemoteData, 0, &dwRemoteThreadId);
// Wait for the thread to terminate
WaitForSingleObject(hRemoteThread, INFINITE);
// Read the user-structure back again
if(!ReadProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwRead))
{
//an error occurred
}
Window procedure in "General" tab and in "Class" tab differs (in "Class" tab it correctly display a value). From DisplayClassInfo.c:
//window procedure
if(spy_WndProc == 0)
{
wsprintf(ach, _T("N/A"));
}
else
{
wsprintf(ach, szHexFmt, spy_WndProc);
if(spy_WndProc != spy_WndClassEx.lpfnWndProc)
lstrcat(ach, _T(" (Subclassed)"));
}
//class window procedure
if(spy_WndClassEx.lpfnWndProc == 0)
wsprintf(ach, _T("N/A"));
else
wsprintf(ach, szHexFmt, spy_WndClassEx.lpfnWndProc);
As you see they're different values (obtained in different ways). Code to fill spy_WndProc is in WinSpy.c and GetRemoteWindowInfo.c. Extracted code from GetRemoteInfo() in WinSpy.c:
GetClassInfoEx(0, spy_szClassName, &spy_WndClassEx);
GetRemoteWindowInfo(hwnd, &spy_WndClassEx, &spy_WndProc, spy_szPassword, 200);
Now in GetRemoteWindowInfo() we see a call to GetClassInfoExProc (injected in the other process):
pInjData->wndproc = (WNDPROC)pInjData->fnGetWindowLong(pInjData->hwnd, GWL_WNDPROC);
pInjData->fnGetClassInfoEx(pInjData->hInst,
(LPTSTR)pInjData->szClassName, &pInjData->wcOutput);
As you can see (please follow using source code) wcOutput is what is displayed in "Class" tab and wndproc what is displayed in "General" tab. Simply GetWindowLong() fails but GetClassInfoEx does not (but they do not necessarily retrieve same value because (if I'm not wrong) what you have in WNDCLASSEX is what you registered with RegisterClassEx but what you get with GetWindowLong() is what you hooked with SetWindowLong().
You are right. It does not have a WndProc(...) function. It is just simply using a DlgProc to process the dialog events. I now this as I have written 'server/thin client' code in C/C++ to capture direct calls into windows API functions like WndProc(...). Any Windows GUI function really - BeginPaint(...) as an example. I used CALC.EXE as a test and executable runs on server while GUI calls are relayed/returned to/from the thin client. Have only tested calc.exe versions thru Vista. There is a chance the newer versions have been 'programmed' differently - meaning not using Win32 SDK. But, even MFC is just a shell to the Win32 SDK,

Sharing a DNSServiceRef using kDNSServiceFlagsShareConnection stalls my program

I'm building a client using dns-sd api from Bonjour. I notice that there is a flag called kDNSServiceFlagsShareConnection that it is used to share the connection of one DNSServiceRef.
Apple site says
For efficiency, clients that perform many concurrent operations may want to use a single Unix Domain Socket connection with the background daemon, instead of having a separate connection for each independent operation. To use this mode, clients first call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef. For each subsequent operation that is to share that same connection, the client copies the MainRef, and then passes the address of that copy, setting the ShareConnection flag to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef; it's a copy of an existing DNSServiceRef whose connection information should be reused.
There is even an example that shows how to use the flag. The problem i'm having is when I run the program it stays like waiting for something whenever I call a function with the flag. Here is the code:
DNSServiceErrorType error;
DNSServiceRef MainRef, BrowseRef;
error = DNSServiceCreateConnection(&MainRef);
BrowseRef = MainRef;
//I'm omitting when I check for errors
error = DNSServiceBrowse(&MainRef, kDNSServiceFlagsShareConnection, 0, "_http._tcp", "local", browse_reply, NULL);
// After this call the program stays waiting for I don't know what
//I'm omitting when I check for errors
error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, 0, "_http._tcp", "local", browse_reply, NULL);
//I'm omitting when i check for errors
DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation
DNSServiceRefDeallocate(MainRef); // Terminate the shared connection
Any ideas? thoughts? suggestion?
Since there are conflicting answers, I dug up the source - annotations by me.
// If sharing...
if (flags & kDNSServiceFlagsShareConnection)
{
// There must be something to share (can't use this on the first call)
if (!*ref)
{
return kDNSServiceErr_BadParam;
}
// Ref must look valid (specifically, ref->fd)
if (!DNSServiceRefValid(*ref) ||
// Most operations cannot be shared.
((*ref)->op != connection_request &&
(*ref)->op != connection_delegate_request) ||
// When sharing, pass the ref from the original call.
(*ref)->primary)
{
return kDNSServiceErr_BadReference;
}
The primary fiels is explained elsewhere:
// When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates
// For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on.
// For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary
The problem with the question is that DNSServiceBrowse maps to ref->op==browse_request which causes a kDNSServiceErr_BadReference.
It looks like kDNSServiceFlagsShareConnection is half-implemented, because I've also seen cases in which it works - this source was found by tracing back when it didn't work.
Service referenses for browsing and resolving may unfortunately not be shared. See the comments in the Bonjour documentation for the kDNSServiceFlagsShareConnection-flag. Since you only browse twice I would just let them have separate service-refs instead.
So both DNSServiceBrowse() and DNSServiceResolve() require an unallocated service-ref as first parameter.
I can't explain why your program chokes though. The first DNSServiceBrowse() call in your example should return immediately with an error code.
Although an old question, but it should help people looking around for answers now.
The answer by vidtige is incorrect, the may be shared for any operation, provided you pass the 'kDNSServiceFlagsShareConnection' flag along with the arguments. Sample below -
m_dnsrefsearch = m_dnsservice;
DNSServiceErrorType mdnserr = DNSServiceBrowse(&m_dnsrefsearch,kDNSServiceFlagsShareConnection,0,
"_workstation._tcp",NULL,
DNSServiceBrowseReplyCallback,NULL);
Reference - http://osxr.org/android/source/external/mdnsresponder/mDNSShared/dns_sd.h#0267

Resources