Connect to multiple queue managers in different servers - c++

I am trying to connect a C++ application (using MQCONNX) based on a PaaS IBM MQ client to two different queue managers, each one based on a different server (one in a PaaS server and the other one in a Unix server). Unfortunately I am not able to do it as I am getting a message when I try to connect to the second server saying that it is not possible as it is connected to the first queue manager. I am using two different MQHCONN connections, one for each queue manager, but the problem is still there.
I have taken a look into this link, but I still have some doubts, as for example, from which server should I copy the CCDT to the client?
https://www.ibm.com/support/pages/connecting-mq-clients-multiple-queue-managers-client-channel-definition-table-ccdt
Any help would be much appreciated, or even a quick sample of how to use CCDT, as right now I am completely stuck.
Many thanks in advance for any help.

Assuming Queue Manager 1 is called MQG1 and Queue Manager 2 is called MQG2 and these can be found using connection names of machine1.com(1701) and machine2.com(1702) respectively, and using channel names MQG1.SVRCONN and MQG2.SVRCONN respectively, you can create your CCDT, on your client application machine, thus:-
runmqsc -n
issue these commands into runmqsc:-
DEFINE CHANNEL(MQG1.SVRCONN) CHLTYPE(CLNTCONN) CONNAME('machine1.com(1701)') QMNAME(MQG1)
DEFINE CHANNEL(MQG2.SVRCONN) CHLTYPE(CLNTCONN) CONNAME('machine2.com(1702)') QMNAME(MQG2)
Then you can code your 2 x MQCONN (or MQCONNX if you need to specify any additional things on the connection) thus:-
#include <cmqc.h> /* Includes for MQI constants */
#include <cmqstrc.h> /* Convert MQRC into string */
MQHCONN hConn1 = MQHC_UNUSABLE_HCONN;
MQHCONN hConn2 = MQHC_UNUSABLE_HCONN;
MQCHAR QMName1[MQ_Q_MGR_NAME_LENGTH] = "MQG1";
MQCHAR QMName2[MQ_Q_MGR_NAME_LENGTH] = "MQG2";
MQLONG CompCode, Reason;
MQCONN(QMName1,
&hConn1,
&CompCode,
&Reason);
if (CompCode)
{
printf("MQCONN to %s failed with reason code %s (%d)\n", QMName1, MQRC_STR(Reason), Reason);
}
MQCONN(QMName2,
&hConn2,
&CompCode,
&Reason);
if (CompCode)
{
printf("MQCONN to %s failed with reason code %s (%d)\n", QMName2, MQRC_STR(Reason), Reason);
}
Take care with how you are linking your program. If you try to make two local connections, you will get a return code of MQRC_ANOTHER_Q_MGR_CONNECTED. Ensure you either link with the client library, set connection option MQCNO_CLIENT (which means you must use MQCONNX) or set the environment variable MQ_CONNECT_TYPE=CLIENT.
You might find the following blog post useful additional reading:-
IBM MQ Little Gem #30: MQ_CONNECT_TYPE

Related

get the binary data transferred from grpc client

I am new to gRPC framework, and I have created a sample client-server on my PC (referring to this).
In my client-server application I have implemented a simple RPC
service NameStudent {
rpc GetRoll(RollNo) returns (Details) {}
}
The client sends a RollNo and receives his/her details which are name, age, gender, parent name, and roll no.
message RollNo{
int32 roll = 1;
}
message Details {
string name = 1;
string gender = 2;
int32 age = 3;
string parent = 4;
RollNo rollid = 5;
}
The actual server and client codes are adaptation of the sample code explained here
Now my server is able to listen to "0.0.0.0:50051(address:port)" and client is able to send the roll no on "localhost:50051" and receive the details.
I want to see the actual binary data that is transferred between client and server. i have tried using Wireshark, but I don't understand what I am seeing here.
Here is the screenshot of wireshark capture
And here are the details of highlighted entry from above screenshot.
Need help in understanding wireshark here, Or any other way that can be used to see the binary data.
Wireshark uses the port to determine how to decode the communication, and it doesn't know any protocol associated with 50051. So you need to configure it to treat this as HTTP.
Right click on a row and select "Decode As..." in the context menu.
Then set "Current" to "HTTP" or "HTTP2" (HTTP will generally auto-detect HTTP2) and hit "OK".
Then the HTTP/2 frames should be decoded. And if using a recent version of Wireshark, you may also see the gRPC frames decoded.
The whole idea of grpc is to HIDE that. Let's say we ignore that and you know what you're doing.
Look at https://en.wikipedia.org/wiki/Protocol_Buffers. gRPC uses Protocol Buffers for it's data representation. You might get a hint at the data you're seeing.
Two good starting points for a reverse engineer exercise are:
Start simple: compile a program that sends an integer. Understand it. Sniff it. Then compile a program that sends a string. Try several values. Once you understand it, pass to tacke the problem of understanding how's google sending your structure.
Use known data and do small variations: knowing what 505249... means is easier if you start knowing the data you're sending (as an example, send "Hello world" string; then change it to "Hella world"; see what changes on the coded sniff; also check that sending several times the same data produces the same sniffed output). Apply prior point: start simple, first empty string, then " ", then "a", then "b", etc. and then pass to complex and larger strings. Don't be affraid to start simple.

Connecting IBM MQ queue using F5 virtual ip and C++

I am trying to connect to some IBM MQ queues using C++. These queues have been defined under different manager queues in different servers. The idea is to connect to a VIP that will balance the workload pointing to each server.
The problem I have is I am using cmqc.h libraries, and in order to connect I have to make use of MQCONN or MQCONNX, for which I need the queue manager name, something that I cannot know as at the moment of the connection I donĀ“t know which one will be used due to F5 balancer.
The code I am currently using as example is the following:
#include <cmqc.h>
#include <cmqxc.h>
#include <string.h>
#include <stdio.h>
#include <sstream>
#include <stdlib.h>
int main()
{
MQHCONN connectionHandle;
MQHOBJ m_SourceQueue;
MQLONG completionCode = 0;
MQLONG reasonCode = 0;
setenv("MQSERVER","SYSTEM.DEF.SVRCONN/TCP/<SERVER_IP_ADDRESS>(56245)",1);
MQCONN(<QUEUE_MANAGER_NAME>, &connectionHandle, &completionCode, &reasonCode);
if(MQCC_OK != completionCode)
{
printf ("%s \n", "Error");
printf ("%s %d \n", "Completion Code", completionCode);
printf ("%s %d \n", "Reason Code", reasonCode);
}
MQDISC(&connectionHandle, &completionCode, &reasonCode);
}
Does somebody have any idea how to connect to the queue when queue manager name is not available?
Based on the code you provide you can use NULL, or blanks, or even a * instead of a queue manager name.
For example:
MQCONN("", &connectionHandle, &completionCode, &reasonCode);
MQCONN(" ", &connectionHandle, &completionCode, &reasonCode);
MQCONN("*", &connectionHandle, &completionCode, &reasonCode);
Any of the above will connect to the queue manager listening on the host and port you specify in the MQSERVER environment variable.
MQCONN is documented in the IBM MQ Knowledge Center page MQCONN - Connect queue manager. Quoting a few things related to the queue manager name from this page:
If the name consists entirely of blanks, the name of the default queue
manager is used.
In the case of MQSERVER the default queue manager is the one listening on host and port connected to.
The page also has the following in the context of a CCDT but it works the same for MQSERVER:
If an all-blank name is specified, each client-connection channel with
an all-blank queue-manager name is tried until one is successful; in
this case there is no check against the actual name of the queue
manager.
Prefixing an asterisk to the connection name implies that the
application does not depend on connecting to a particular queue
manager in the group. Suitable applications are:
Applications that put messages but do not get messages.
Applications that put request messages and then get the reply messages from a temporary dynamic queue.
Unsuitable applications are ones that need to get messages from a
particular queue at a particular queue manager; such applications must
not prefix the name with an asterisk.
I would suggest that you instead use CCDT (Client Channel Definition Table) as it provides much more flexibility. MQSERVER can only provide the host, port, and channel name. A CCDT will allow you to configure many more options, for example TLS, security exits, max message length to name a few.

SSH local port forwarding using libssh

Problem
I try to do local port forwarding using libssh with the libssh-C++-wrapper. My intention is to forward port localhost:3306 on a server to localhost:3307 on my machine via SSH to connect via MySQL to localhost:3307.
void ssh_session::forward(){
ssh::Channel channel(this->session);
//remotehost, remoteport, localhost, localport
channel.openForward("localhost",3306,"localhost",3307);
std::cout<< "Channel is " << (channel.isOpen()?"open!":"closed!") << std::endl;
}
with session in the constructor of ssh::Channel being of type ssh::Session.
The code above prints Channel is open!. If I try to connect to localhost:3307 using the MySQL Connector/C++ I get
ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (61)
Observations
If I use the shell command $ ssh -L 3307:localhost:3306 me#myserver.com everything works fine and I can connect.
If I use ssh::Session session used in the constructor or ssh::Channel channel to execute remote shell commands everything works therefore the session is fine!
The documentation of libssh (which is total crap for the C++ wrapper libsshpp.hpp since a lot of public member functions are not documented and you have to look into the source code) shows that ssh::Channel::openForward() is a wrapper for the C function ssh_channel_open_forward()
The documentation of ssh_channel_open_forward() states
Warning
This function does not bind the local port and does not automatically forward the content of a socket to the channel. You still have to use channel_read and channel_write for this.
I think that could cause the problem. I have no problem by reading and writing in to the ssh:Channel but thats not how the MySQL Connector/C++ works.
Question
How can I achieve the same behaviour produced by the common shell command
$ ssh -L 3307:localhost:3306 me#myserver.com
using libssh?
Warning
This function does not bind the local port and does not automatically forward the content of a socket to the channel. You
still have to use channel_read and channel_write for this.
This is telling you that you need to write your own local socket code. Unfortunately, it doesn't do it for you.
The simplest implementation would be to bind a local socket, and use ssh_select to listen for events (e.g. new connection to accept, socket or channel events). You can keep your socket fds aand ssh_channels in a vector for easy management.
When you get any event, just loop over all the operations in a non-blocking way, i.e.
try to accept a new connection, and append the fd, and a new ssh_channel (created as in your question) to your vectors.
try to read all the socket fds, and forward anything to the corresponding ssh channel using ssh_channel_write (make sure to setsockopt SO_RCVTIMEO to 0)
try to read all the channels, using ssh_channel_read_nonblocking, and forward to the socket fd using write.
You also need to handle errors everywhere, and close the corresponding fd and ssh_channel.
Overall it's probably going to be too much code for a StackOverflow answer, but I may come back and add it in if I get time.
The tempting alternative to all that would be to just run ssh -L ... as a subprocess using fork & exec, avoiding all that boilerplate socket code, and benefitting from an efficient, bug-free implementation.

C++: One client communicating with multiple server

I was wondering, if it is possible to let one client communicate with multiple server at the same time. As far as I know, common browsers like for example firefox are doing exactly this.
The problem I have now is, that the client has to listen and wait for data from the server, rather then requesting it itself. It has to listen to multiple server at once. Is this even possible? What happens if the client is listening to server 1 and server 2 sends something? Is the package lost or will it be resend until the client communicates a successful receival? The protocol used is TCP.
edit: platform is Windows. Thanks for pointing this out Arunmu.
This is nothing different from regular socket programming using select/poll/epoll OR using thread-pool OR using process-per-connection OR whatever model that you know.
I can show you a rough pseudo-code on how to do it with epoll.
NOTE: None of my functions exist in C++, its just for explanation purpose. ANd I am ALSO assuming that you are on Linux, since you have mentioned nothing about the platform.
socket sd = connect("server.com", 8080);
sd.set_nonblocking(1);
epoll_event event;
event.data.fd = sd
epoll_ctl(ADD, event);
...
...
while (True) {
auto n = epoll_wait(events, 1);
for (int i : 1...n) {
if (events[i].data.fd == sd) // The socket added in epoll_ctl
{
std::thread(&Session::read_handler, rd_hndler_, sd); // Call the read in another thread or same thread
}
}
}
I hope you got the gist. In essence, think of server like a client and client like a server and you have your problem solved (kind of). Check out below link to know more about epoll
https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
To see an fully functional server design using epoll, checkout:
https://github.com/arun11299/cpp-reactor-server/blob/master/epoll/reactor.cc

Libnodave - daveStart() Error using TCP Connection

I have established connection to a Siemens S7-300 PLC (simulated via PlcSIM) using the libnodave library. There are no issues connecting and writing data to the PLC. However, I am unable to change the status of the PLC from Start/Stop. I am attempting to use the following libnodave methods for such actions:
int daveStatus = daveStart(dc);
int daveStatus = daveStop(dc);
Both function calls return the same Error: 33794
nodave.c Cites the error as the following:
case 0x8402: return "CPU already in RUN or already in STOP ?";
The use of the daveStart() and daveStop() functions can be viewed in the example testS7online.c:
if(doStop) {
daveStop(dc);
}
if(doRun) {
daveStart(dc);
}
In the examples the start/stop functions are only called when MPI connections to the PLC are made. Does anyone know if the start/stop functions are supported for use with TCP connections? If so, any suggestions as to what may be causing my error?
I have just tried dc.start() and dc.stop() using libnodave 8.4 and NetToPlcSim tool. It worked perfectly. Possibly you don't use NetToPlcSim tool that makes connection to PLCSim via TCP/IP (that is 127.0.0.1 port 102 obviously) hence dc can't even connect. So if your lines don't work, then u must be doing something wrong.