Network Endpoints

Join provides endpoint classes that encapsulate network addresses. Endpoints combine an address with protocol-specific information (port, device, PID, etc.).

Endpoint features:

  • protocol-aware — each protocol has its endpoint type
  • type-safe — compile-time endpoint validation
  • TLS-ready — optional host name for SNI and certificate verification
  • comparison — sortable for use in maps/sets

Endpoint types

Internet endpoints

IP address + port:

Tcp::Endpoint tcpEndpoint("192.168.1.1", 8080);
Udp::Endpoint udpEndpoint("2001:db8::1", 53);

Unix endpoints

Filesystem path:

UnixStream::Endpoint unixEndpoint("/tmp/socket");
UnixDgram::Endpoint dgramEndpoint("/var/run/app.sock");

Process ID + multicast groups:

Netlink::Endpoint nlEndpoint(getpid(), RTMGRP_LINK);

Network interface:

Raw::Endpoint rawEndpoint("eth0");

Creating internet endpoints

From IP and port

IpAddress ip("10.0.0.1");
Tcp::Endpoint endpoint1(ip, 8080);

// Or directly
Tcp::Endpoint endpoint2("10.0.0.1", 8080);

From protocol and port

// Wildcard address for listening
Tcp::Endpoint listen(Tcp::v6(), 8080);  // [::]:8080

Internet endpoint properties

Tcp::Endpoint endpoint("192.168.1.100", 8080);

// IP address
IpAddress ip = endpoint.ip();
endpoint.ip(IpAddress("10.0.0.1"));

// Port
uint16_t port = endpoint.port();
endpoint.port(9090);

// Hostname (used by TLS for SNI and certificate verification)
std::string host = endpoint.hostname();
endpoint.hostname("example.com");

// Protocol
Tcp protocol = endpoint.protocol();  // v4() or v6()

// Device (IPv6 only, for link-local)
endpoint.device("eth0");
std::string dev = endpoint.device();

Unix endpoints

Filesystem paths for local IPC:

UnixStream::Endpoint server("/tmp/server.sock");
UnixDgram::Endpoint client("/tmp/client.sock");

// Get/set path
std::string path = server.device();
server.device("/var/run/new.sock");

// Protocol and length
UnixStream protocol = server.protocol();
socklen_t len = server.length();

Kernel communication endpoints:

// Process ID and multicast groups
Netlink::Endpoint route(Netlink::rt(), RTMGRP_LINK | RTMGRP_IPV4_IFADDR);

// Get/set properties
uint32_t pid = route.pid();
route.pid(getpid());

uint32_t groups = route.groups();
route.groups(RTMGRP_LINK);

// Protocol
Netlink protocol = route.protocol();  // rt() or nf()

Network interface access:

Raw::Endpoint eth("eth0");

// Get/set interface
std::string iface = eth.device();
eth.device("wlan0");

// Protocol
Raw protocol = eth.protocol();
socklen_t len = eth.length();

Comparison

Endpoints are comparable and sortable:

Tcp::Endpoint a("10.0.0.1", 80);
Tcp::Endpoint b("10.0.0.1", 443);
Tcp::Endpoint c("10.0.0.2", 80);

bool equal = (a == b);     // false (different ports)
bool less = (a < b);       // true (lower port)
bool less2 = (a < c);      // true (lower IP)

// Use in sorted containers
std::set<Tcp::Endpoint> endpoints;
std::map<Tcp::Endpoint, Connection> connections;

Stream output

Tcp::Endpoint endpoint("192.168.1.1", 8080);
std::cout << endpoint << "\n";  // "192.168.1.1:8080"

Tcp::Endpoint ipv6("2001:db8::1", 443);
std::cout << ipv6 << "\n";      // "[2001:db8::1]:443"

UnixStream::Endpoint unix("/tmp/socket");
std::cout << unix << "\n";      // "/tmp/socket"

Netlink::Endpoint nl(1234, RTMGRP_LINK);
std::cout << nl << "\n";        // "pid=1234,groups=1"

Usage patterns

Server binding

// Listen on a specific address
Tcp::Endpoint specific("192.168.1.10", 8080);
Tcp::Acceptor acceptor;
acceptor.create(specific);

// Listen on all interfaces (wildcard)
Tcp::Endpoint wildcard(Tcp::v4(), 8080);
Tcp::Acceptor wildcardAcceptor;
wildcardAcceptor.create(wildcard);

Client connection

Tcp::Socket socket;
Tcp::Endpoint endpoint("93.184.216.34", 80);
socket.connect(endpoint);

Connecting to a host name

Endpoints are built from an IP address, not from a host name. Resolve the name first with Dns::Resolver from the fabric module:

// libjoin.
#include <join/resolver.hpp>

using namespace join;

IpAddress ip = Dns::Resolver::lookupAddress("example.com");
if (ip.isWildcard())
{
    // resolution failed
}

Tcp::Endpoint endpoint(ip, 80);

For TLS, also set the host name: it is sent to the server (SNI) and used to verify its certificate.

IpAddress ip = Dns::Resolver::lookupAddress("example.com");
if (ip.isWildcard())
{
    // resolution failed
}

Tls::Endpoint endpoint(ip, 443);
endpoint.hostname("example.com");

UDP writeTo/readFrom

Udp::Socket socket(Udp::Socket::Mode::Blocking);
Udp::Endpoint remote("8.8.8.8", 53);

// Send to endpoint
const char query[] = "DNS query";
socket.writeTo(query, sizeof(query) - 1, remote);

// Receive from endpoint
char buffer[512];
Udp::Endpoint sender;
int n = socket.readFrom(buffer, sizeof(buffer), &sender);
std::cout << "Received " << n << " bytes from " << sender << "\n";
// Link-local addresses require interface specification
IpAddress linkLocal("fe80::1");
Tcp::Endpoint endpoint(linkLocal, 8080);
endpoint.device("eth0");  // Specify interface

Low-level access

Get underlying sockaddr:

Tcp::Endpoint endpoint("10.0.0.1", 8080);

// Mutable access
struct sockaddr* addr = endpoint.addr();
socklen_t len = endpoint.length();

// Const access
const struct sockaddr* caddr = endpoint.addr();

// Use with system calls
::connect(sockfd, endpoint.addr(), endpoint.length());

Best practices

  • Resolve host names first — endpoints take IP addresses, use Dns::Resolver::lookupAddress()
  • Wildcard for servers — listen on all interfaces with Tcp::Endpoint(Tcp::v4(), port) or Tcp::v6()
  • Check resolved IPs — lookupAddress() returns a wildcard address on failure
  • Specify device — for link-local IPv6 addresses
  • Store in containers — endpoints are comparable and copyable

Summary

Endpoint TypeAddress TypeAdditional Data
BasicUnixEndpointFilesystem path-
BasicNetlinkEndpointPID + GroupsProtocol type
BasicLinkLayerEndpointInterface-
BasicInternetEndpointIP + PortHostname, device

All endpoints provide:

  • protocol() — get associated protocol
  • length() — get sockaddr size
  • addr() — get underlying sockaddr
  • Comparison operators
  • Stream insertion