0

I'm trying to resolve the IP addresses of some docker compose services from another container within the docker compose network running a C/C++ socket program.

Here is my docker-compose.yml

version: "3.9"
services:
    grad_calc_1:
        image: worker:1
        ports: 
            - "8080:8080"
        environment:
            - seed=10
    grad_calc_2:
        image: worker:1
        ports: 
            - "8081:8080"
        environment:
            - seed=20
    optimizer:
        image: optimizer:1
        depends_on: 
            - grad_calc_1
            - grad_calc_2

Here is my code resolving the hostnames from the optimizer service

char* resolve_host(const char* host_name) {
    struct hostent *host_entry;
    char *IPbuffer;

    host_entry = gethostbyname(host_name);
    IPbuffer = inet_ntoa(*((struct in_addr*)host_entry->h_addr_list[0]));    
    return IPbuffer;
}

int main() {
    const char* hostname_1 = "grad_calc_1";
    const char* hostname_2 = "grad_calc_2";

    char* ip_1 = resolve_host(hostname_1);
    char* ip_2 = resolve_host(hostname_2);
    cout << "grad_calc_1 IP: " << ip_1 << endl; 
    cout << "grad_calc_2 IP: " << ip_2 << endl;
}

The output is 172.19.0.2 for both hostnames. I'm not sure what I'm doing wrong

gary69
  • 3,620
  • 6
  • 36
  • 50
  • 1
    `inet_ntoa` returns a statically allocated string, see [docs](https://linux.die.net/man/3/inet_ntoa) – KamilCuk Apr 26 '21 at 23:24
  • so that string isn't being updated by the second function invocation? – gary69 Apr 26 '21 at 23:26
  • The same buffer is overwritten by that second invocation. So you print the same value out twice. – Paul Sanders Apr 26 '21 at 23:32
  • You should probably use `inet_ntop()` instead, which understands IPv6 addresses and requires you to supply your own buffer (and therefore won't have this problem). – David Maze Apr 27 '21 at 00:03

2 Answers2

1

Building on @KamilCuk's comment, here's one possible fix:

#include <string>

std::string resolve_host(const char* host_name) {
    struct hostent *host_entry;
    const char *IPbuffer;

    host_entry = gethostbyname(host_name);
    IPbuffer = inet_ntoa(*((struct in_addr*)host_entry->h_addr_list[0]));    
    return IPbuffer;
}

int main() {
    const char* hostname_1 = "grad_calc_1";
    const char* hostname_2 = "grad_calc_2";

    std::string ip_1 = resolve_host(hostname_1);
    std::string ip_2 = resolve_host(hostname_2);
    cout << "grad_calc_1 IP: " << ip_1 << endl; 
    cout << "grad_calc_2 IP: " << ip_2 << endl;
}

The point here is that you copy the static buffer returned by inet_ntoa before the next call overwrites it.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
1

inet_ntoa returns a pointer to statically allocated string. Both pointers ip_1 and ip_2 point to the same memory. Either copy the memory or use one at a time. The behavior is documented.

char* ip_1 = resolve_host(hostname_1);
cout << "grad_calc_1 IP: " << ip_1 << endl; 
char* ip_2 = resolve_host(hostname_2);
cout << "grad_calc_2 IP: " << ip_2 << endl;

or like

std::string ip_1{resolve_host(hostname_1)};
char* ip_2 = resolve_host(hostname_2);
cout << "grad_calc_1 IP: " << ip_1 << endl; 
cout << "grad_calc_2 IP: " << ip_2 << endl;
KamilCuk
  • 120,984
  • 8
  • 59
  • 111