1
votes

I have a application which acts as client and server both. As a server it accepts SOAP requests on port xxxx[Agent URL] and send notifications to the sender on port yyyy [notification URL].

So basically it acts as a server on port xxxx and client on port yyyy. My service has a dedicated IP either IPv6 or IPv4.

We are using GSOAP for communication and overriding GSOAP function tcp_connect() for client side binding.

Currently I am facing issues with transition of service to IPv6. Use case: when I listening on IPv6 address and my notification URL is IPv4...

From the GSOAP implementation a socket is created from the Notification URL.

sk = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

Now we try to bind to this socket accordingly(either IPv4 or IPv6):

    struct addrinfo hints, *res, *p;
    int status;
    const char* client_ip = AGENT_CLIENT_IP;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if( (status=getaddrinfo(client_ip, NULL, &hints, &res))!=0 )
    {
            sprintf(err_msg_buf,"bind failed in tcp_connect()");
            soap->fclosesocket(soap, sk);
            return error;
    }

    for( p=res; p!=NULL; p=p->ai_next){
        if(p->ai_family == AF_INET) 
        {
            struct sockaddr_in * ipv4 = (struct sockaddr_in *)p->ai_addr;
                    status = bind(sk, ipv4, (int)sizeof(struct sockaddr_in));
        }
        else if(p->ai_family == AF_INET6)
        {
            struct sockaddr_in6 * ipv6 = (struct sockaddr_in6 *)p->ai_addr;
                    status = bind(sk, ipv6, (int)sizeof(struct sockaddr_in6));
        }
        else
        {
                sprintf(err_msg_buf,"tcp_connect() error. IP Address neither IPv6 nor IPv4 ");
                soap->fclosesocket(soap, sk);
                return error;
        }

        break;
    }

            if(-1 == status)
            {
                    sprintf(err_msg_buf," Binding to client host ip failed in tcp_connect()");
                    return error;
    }

Since the socket is already created(according to the type of Notification URL), bind fails if the socket is of type mismatch is there.

How can I make my client side binding work when socket family and agent ip address are of different family ?

1
I know that this is not a solution, but my first thought was "split the application into separate client and server parts which each can independently be IPv4 or IPv6, and use some sort of IPC to link them"Peter M
It seems like you must create two separate sockets. Is that not possible?John Zwinck

1 Answers

2
votes

Maybe I am not getting what you are trying or you have some misunderstanding of how TCP/IP and RPC normally works.

Let me paraphrase your set up and then show what I think is odd about it.

You have a server and one or multiple clients. The server accepts IPv4 and IPV6 connections on a fixed port, let us say 1337. To answer the request you open a new TCP stream (or maybe SOAP) on a different fixed port, say 1338. You are now wondering why, when a second client connects the bind to 1338 fails?

The short answer is: "The port is in use, duh, us a different port!"

But that misses the point that the setup, to say the least ODD. Although I have never used GSOAP, I have used SOAP and other RPC frameworks and what you outline is weird, unless I am missing something you did not outline.

The first thing that is odd, if you need an answer to a SOAP request, why do you simply formulate one with a return value? Call the SOAP function and the client will block until it gets an answer. If you don't want the call to block for the relatively long duration of the call do the entire thing asynchronously.

So you want to pass data to the client back later? Here you have two solutions, either the client polls the server or you open a new SOAP connection to the client. The first solution is basically desirable, because in most cases the client can connect to the server but not the other way around. For example the client can be behind a NAT, what do you do now? The second solution works well when you know that the client will always be reachable.

It seems to me that you are trying to do the second "return channel" solution. In this case why are you binding to a port? The client side of any IP connection does not need to bound to a port. The OS will automatically assign an available port. What you need to do is then bind the port on the client to a well known IP. You then use this well known client port and use it in connect on the server (or not, since you are using SOAP).

Since this is all confusing let me illustrate this with a small diagram:

                   Client                   Server
                   ------                   ------
Request Channel    <random port>            1337
Back Channel       1338                     <random port>

To sum up:

So either you are reimplementing something that works in SOAP and should stop doing that or if you absolutely need a back channel, simply don't call bind on a client socket.