Skip to content

SocketAddr

Description

Wraps struct sockaddr_storage. raw[0..length] carries the actual address bytes; family records the address family so callers do not have to peek into raw. The byte layout of sockaddr_in / sockaddr_in6 is identical on Linux, macOS, and Windows, so SocketAddr is platform-portable as a value.

Usage example (Cross-references)

Usage examples (Cross-references)
        u32          length;
        SocketFamily family;
    } SocketAddr;
    
    ///
        SockFd     fd;
        SocketKind kind;
        SocketAddr bound;
    } Listener;
        SockFd     fd;
        SocketKind kind;
        SocketAddr peer;
    } Socket;
    /// TAGS: Socket, Address
    ///
    bool socket_addr_parse_zstr(SocketAddr *out, Zstr spec, SocketKind kind);
    
    ///
    /// TAGS: Socket, Parse, Address
    ///
    bool socket_addr_parse_str(SocketAddr *out, const Str *spec, SocketKind kind);
    #define SocketAddrParse(out, spec, kind)                                                                                                                      \
        _Generic(                                                                                                                                                 \
    /// TAGS: Socket, Address, Format
    ///
    Str socket_addr_format(const SocketAddr *addr, Allocator *alloc);
    #define SocketAddrFormat(...)           OVERLOAD(SocketAddrFormat, __VA_ARGS__)
    #define SocketAddrFormat_1(addr)        socket_addr_format((addr), MisraScope)
    /// TAGS: Socket, Listener, Bind
    ///
    bool ListenerOpen(Listener *out, SocketKind kind, const SocketAddr *addr, i32 backlog);
    
    ///
    /// TAGS: Socket, Listener, Address
    ///
    bool ListenerLocalAddr(const Listener *self, SocketAddr *out);
    
    ///
    /// TAGS: Socket, Connect
    ///
    bool SocketConnect(Socket *out, SocketKind kind, const SocketAddr *target);
    
    ///
    
        typedef Vec(HostsEntry) HostsTable;
        typedef Vec(SocketAddr) DnsAddrs;
    
        ///
        /// TAGS: Dns, Resolve, API
        ///
        bool dns_resolve_4_one_zstr(DnsResolver *self, Zstr spec, SocketKind kind, SocketAddr *out);
        bool dns_resolve_4_one_str(DnsResolver *self, const Str *spec, SocketKind kind, SocketAddr *out);
        ///
        bool dns_resolve_4_one_zstr(DnsResolver *self, Zstr spec, SocketKind kind, SocketAddr *out);
        bool dns_resolve_4_one_str(DnsResolver *self, const Str *spec, SocketKind kind, SocketAddr *out);
    
        ///
                char *: dns_resolve_4_vec_zstr((self), (Zstr)(spec), (kind), (DnsAddrs *)(out))                            \
            ),                                                                                                             \
            SocketAddr *: _Generic(                                                                                        \
                (spec),                                                                                                    \
                Str *: dns_resolve_4_one_str((self), (const Str *)(spec), (kind), (SocketAddr *)(out)),                    \
            SocketAddr *: _Generic(                                                                                        \
                (spec),                                                                                                    \
                Str *: dns_resolve_4_one_str((self), (const Str *)(spec), (kind), (SocketAddr *)(out)),                    \
                Zstr: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out)),                           \
                char *: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out))                          \
                (spec),                                                                                                    \
                Str *: dns_resolve_4_one_str((self), (const Str *)(spec), (kind), (SocketAddr *)(out)),                    \
                Zstr: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out)),                           \
                char *: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out))                          \
            )                                                                                                              \
                Str *: dns_resolve_4_one_str((self), (const Str *)(spec), (kind), (SocketAddr *)(out)),                    \
                Zstr: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out)),                           \
                char *: dns_resolve_4_one_zstr((self), (Zstr)(spec), (kind), (SocketAddr *)(out))                          \
            )                                                                                                              \
        )
    // ---------------------------------------------------------------------------
    
    static SocketAddr sockaddr_v4(const u8 ip[4], u16 port) {
        SocketAddr          a  = {0};
        struct sockaddr_in *sa = (struct sockaddr_in *)a.raw;
    
    static SocketAddr sockaddr_v4(const u8 ip[4], u16 port) {
        SocketAddr          a  = {0};
        struct sockaddr_in *sa = (struct sockaddr_in *)a.raw;
        sa->sin_family         = AF_INET;
    }
    
    static SocketAddr sockaddr_v6(const u8 ip[16], u16 port) {
        SocketAddr           a  = {0};
        struct sockaddr_in6 *sa = (struct sockaddr_in6 *)a.raw;
    
    static SocketAddr sockaddr_v6(const u8 ip[16], u16 port) {
        SocketAddr           a  = {0};
        struct sockaddr_in6 *sa = (struct sockaddr_in6 *)a.raw;
        sa->sin6_family         = AF_INET6;
                        u8 v6[16] = {0};
                        if (parse_ipv4(StrBegin(&ip_buf), v4)) {
                            SocketAddr a = sockaddr_v4(v4, 53);
                            VecPushBackR(out, a);
                        } else if (parse_ipv6(StrBegin(&ip_buf), v6)) {
                            VecPushBackR(out, a);
                        } else if (parse_ipv6(StrBegin(&ip_buf), v6)) {
                            SocketAddr a = sockaddr_v6(v6, 53);
                            VecPushBackR(out, a);
                        }
    // land bytes in `resp_buf[0 .. resp_cap)`. Returns the response length
    // on success or -1 on failure.
    static i64 udp_round_trip(const SocketAddr *ns, const u8 *query, u64 qlen, u8 *resp_buf, u64 resp_cap, u32 timeout_ms) {
        Socket sock = {0};
        if (!SocketConnect(&sock, SOCKET_KIND_UDP, ns)) {
    // response's id is checked to match before the records are extracted.
    static bool
        try_one_query(DnsResolver *self, const SocketAddr *ns, Zstr hostname, DnsType qtype, u16 port, DnsAddrs *out) {
        // Per-call scratch: one DNS query buffer (<= 1232 B) plus the parsed
        // response with its records vector. Everything is dropped at function
        VecForeachPtr(&resp.answers, rec) {
            if (rec->type == qtype) {
                SocketAddr a = qtype == DNS_TYPE_A ? sockaddr_v4(rec->ipv4, port) : sockaddr_v6(rec->ipv6, port);
                VecPushBackR(out, a);
                found = true;
            VecForeachPtr(&self->hosts, e) {
                if (StrLen(&e->name) > 0 && ZstrCompare(StrBegin(&e->name), nq) == 0) {
                    SocketAddr a = e->is_ipv6 ? sockaddr_v6(e->ip, port) : sockaddr_v4(e->ip, port);
                    VecPushBackR(out, a);
                    found = true;
        // setting up a DnsResolver still expect numeric addresses to skip
        // the network.
        SocketAddr direct;
        if (SocketAddrParse(&direct, spec, kind)) {
            VecPushBackR(out, direct);
    }
    
    bool dns_resolve_4_one_zstr(DnsResolver *self, Zstr spec, SocketKind kind, SocketAddr *out) {
        if (!self || !spec || !out) {
            return false;
    }
    
    bool dns_resolve_4_one_str(DnsResolver *self, const Str *spec, SocketKind kind, SocketAddr *out) {
        if (!self || !spec || !out) {
            return false;
    // Reading through `struct sockaddr->sa_family` lets the compiler pick
    // the right field for each platform.
    static void fill_socket_addr_from_sockaddr(SocketAddr *out, const struct sockaddr *sa, u32 len) {
        MemSet(out, 0, sizeof(*out));
        if (len > (u32)SOCKET_ADDR_MAX_SIZE) {
    // ---------------------------------------------------------------------------
    
    bool socket_addr_parse_zstr(SocketAddr *out, Zstr spec, SocketKind kind) {
        if (!out) {
            LOG_FATAL("SocketAddrParse: out is NULL");
    }
    
    bool socket_addr_parse_str(SocketAddr *out, const Str *spec, SocketKind kind) {
        if (!out) {
            LOG_FATAL("SocketAddrParse: out is NULL");
    }
    
    Str socket_addr_format(const SocketAddr *addr, Allocator *alloc) {
        Str out = StrInit(alloc);
        if (!addr || addr->length == 0) {
    // ---------------------------------------------------------------------------
    
    bool ListenerOpen(Listener *out, SocketKind kind, const SocketAddr *addr, i32 backlog) {
        if (!out || !addr) {
            LOG_FATAL("ListenerOpen: NULL argument");
    }
    
    bool ListenerLocalAddr(const Listener *self, SocketAddr *out) {
        if (!self || !out) {
            LOG_FATAL("ListenerLocalAddr: NULL argument");
    // ---------------------------------------------------------------------------
    
    bool SocketConnect(Socket *out, SocketKind kind, const SocketAddr *target) {
        if (!out || !target) {
            LOG_FATAL("SocketConnect: NULL argument");
    }
    
    static void handle_connection(Allocator *alloc, Socket *client, const SocketAddr *upstream_addr) {
        Str peer_str = SocketAddrFormat(&client->peer, alloc);
            }
    
            SocketAddr listen_addr;
            if (!DnsResolve(&resolver, listen_spec, SOCKET_KIND_TCP, &listen_addr)) {
                LOG_ERROR("invalid --listen address: {}", listen_spec);
            }
    
            SocketAddr upstream_addr;
            if (!DnsResolve(&resolver, upstream_spec, SOCKET_KIND_TCP, &upstream_addr)) {
                LOG_ERROR("invalid --upstream address: {}", upstream_spec);
    // Compare a resolved IPv4 SocketAddr against expected dotted-decimal
    // + port via SocketAddrFormat (exact-byte check without poking raw[]).
    static bool v4_is(const SocketAddr *ad, Allocator *a, Zstr expect) {
        if (ad->family != SOCKET_FAMILY_INET) {
            return false;
    }
    
    static bool v6_is(const SocketAddr *ad, Allocator *a, Zstr expect) {
        if (ad->family != SOCKET_FAMILY_INET6) {
            return false;
        resolver_init_crafted(&r, a);
    
        SocketAddr one;
        bool       got = dns_resolve_4_one_zstr(&r, "single:80", SOCKET_KIND_TCP, &one);
        resolver_init_crafted(&r, a);
    
        SocketAddr one;
        bool       got = dns_resolve_4_one_zstr(&r, "multi:80", SOCKET_KIND_TCP, &one);
        resolver_init_crafted(&r, a);
    
        SocketAddr one;
        bool       got = dns_resolve_4_one_zstr(&r, "absent:80", SOCKET_KIND_TCP, &one);
    
        Str        spec = StrInitFromZstr("multi:80", a);
        SocketAddr one;
        bool       got = dns_resolve_4_one_str(&r, &spec, SOCKET_KIND_TCP, &one);
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, spec, SOCKET_KIND_TCP);
        if (ok) {
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP)) {
            DefaultAllocatorDeinit(&alloc);
        }
    
        SocketAddr local;
        bool       ok = ListenerLocalAddr(&listener, &local);
        ok            = ok && local.family == SOCKET_FAMILY_INET;
        DnsResolverInit(&r, a);
    
        SocketAddr one;
        bool       got = DnsResolve(&r, "127.0.0.1:80", SOCKET_KIND_TCP, &one);
        Socket   server = {0};
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP)) {
            DefaultAllocatorDeinit(&alloc);
        // host:port string and re-parse so the test only ever talks to the
        // public API.
        SocketAddr local;
        if (!ListenerLocalAddr(&listener, &local)) {
            ListenerClose(&listener);
        }
        Str        local_str = SocketAddrFormat(&local, a);
        SocketAddr connect_addr;
        bool       parsed = SocketAddrParse(&connect_addr, (Zstr)StrBegin(&local_str), SOCKET_KIND_TCP);
        StrDeinit(&local_str);
    
        {
            SocketAddr addr;
            if (!SocketAddrParse(&addr, "127.0.0.1:8080", SOCKET_KIND_TCP)) {
                DefaultAllocatorDeinit(&alloc);
    
        {
            SocketAddr addr;
            if (!SocketAddrParse(&addr, "[::1]:8080", SOCKET_KIND_TCP)) {
                DefaultAllocatorDeinit(&alloc);
        Pair p = {0};
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP))
            return p;
            return p;
    
        SocketAddr local;
        if (!ListenerLocalAddr(&p.listener, &local)) {
            ListenerClose(&p.listener);
    
        Str        local_str = SocketAddrFormat(&local, a);
        SocketAddr connect_addr;
        bool       parsed = SocketAddrParse(&connect_addr, (Zstr)StrBegin(&local_str), SOCKET_KIND_TCP);
        StrDeinit(&local_str);
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, spec, SOCKET_KIND_TCP);
        if (ok) {
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, "1.2.3.4:80", SOCKET_KIND_TCP);
        ok            = ok && addr.family == SOCKET_FAMILY_INET;
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, "10.0.0.1:4660", SOCKET_KIND_TCP);
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, "[2001:db8::1]:443", SOCKET_KIND_TCP);
        ok            = ok && addr.family == SOCKET_FAMILY_INET6;
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr addr;
        bool       ok = SocketAddrParse(&addr, "[::1]:8080", SOCKET_KIND_TCP);
        ok            = ok && addr.family == SOCKET_FAMILY_INET6;
    // mutation would return true here.
    bool test_sk3_parse_no_port_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "127.0.0.1", SOCKET_KIND_TCP);
        return parsed == false;
    // Port overflow ">65535" rejected.
    bool test_sk3_parse_port_overflow_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "1.2.3.4:99999", SOCKET_KIND_TCP);
        return parsed == false;
    // Empty port "1.2.3.4:" rejected (parse_port empty -> false).
    bool test_sk3_parse_empty_port_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "1.2.3.4:", SOCKET_KIND_TCP);
        return parsed == false;
    // Unmatched '[' bracket "[::1" rejected (split_host_port: no ']' -> false).
    bool test_sk3_parse_unmatched_bracket_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "[::1", SOCKET_KIND_TCP);
        return parsed == false;
    // Empty spec "" rejected.
    bool test_sk3_parse_empty_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "", SOCKET_KIND_TCP);
        return parsed == false;
    // close[1] != ':' branch in split_host_port.
    bool test_sk3_parse_bracket_missing_colon_rejected(void) {
        SocketAddr addr;
        bool       parsed = SocketAddrParse(&addr, "[::1]8080", SOCKET_KIND_TCP);
        return parsed == false;
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:4660", SOCKET_KIND_TCP)) {
            DefaultAllocatorDeinit(&alloc);
        }
    
        SocketAddr local;
        bool       ok = ListenerLocalAddr(&listener, &local);
        // family + length come straight from fill_socket_addr_from_sockaddr.
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP)) {
            DefaultAllocatorDeinit(&alloc);
        }
    
        SocketAddr local;
        bool       ok = ListenerLocalAddr(&listener, &local);
        ok            = ok && local.family == SOCKET_FAMILY_INET;
        Allocator       *a     = ALLOCATOR_OF(&alloc);
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP)) {
            DefaultAllocatorDeinit(&alloc);
        }
    
        SocketAddr local;
        if (!ListenerLocalAddr(&listener, &local)) {
            ListenerClose(&listener);
        }
        Str        local_str = SocketAddrFormat(&local, a);
        SocketAddr connect_addr;
        bool       parsed = SocketAddrParse(&connect_addr, (Zstr)StrBegin(&local_str), SOCKET_KIND_TCP);
        StrDeinit(&local_str);
    // Guards the af == AF_UNSPEC rejection at ListenerOpen:751.
    bool test_sk3_listener_unspec_family_fails(void) {
        SocketAddr bad;
        MemSet(&bad, 0, sizeof(bad)); // family = SOCKET_FAMILY_UNSPEC
    // flipped success test would make the call return false.
    bool test_sk3_nonblocking_set_true_returns_true(void) {
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP)) {
            return false;
    // failure everything opened so far is torn down and false is returned.
    // ---------------------------------------------------------------------------
    static bool sk4_make_pair(Allocator *a, Listener *listener, Socket *client, Socket *server, SocketAddr *local_out) {
        *client = (Socket) {0};
        *server = (Socket) {0};
        *server = (Socket) {0};
    
        SocketAddr bind_addr;
        if (!SocketAddrParse(&bind_addr, "127.0.0.1:0", SOCKET_KIND_TCP))
            return false;
            return false;
    
        SocketAddr local;
        if (!ListenerLocalAddr(listener, &local)) {
            ListenerClose(listener);
    
        Str        local_str = SocketAddrFormat(&local, a);
        SocketAddr connect_addr;
        bool       parsed = SocketAddrParse(&connect_addr, (Zstr)StrBegin(&local_str), SOCKET_KIND_TCP);
        StrDeinit(&local_str);
        Socket     client;
        Socket     server;
        SocketAddr local;
        if (!sk4_make_pair(a, &listener, &client, &server, &local)) {
            DefaultAllocatorDeinit(&alloc);
        // 65535 accepted and round-trips to the exact value.
        {
            SocketAddr addr;
            bool       p = SocketAddrParse(&addr, "127.0.0.1:65535", SOCKET_KIND_TCP);
            ok           = ok && p;
        // 0 accepted and round-trips.
        {
            SocketAddr addr;
            bool       p = SocketAddrParse(&addr, "127.0.0.1:0", SOCKET_KIND_TCP);
            ok           = ok && p;
        // 4660 (0x1234) accepted -- non-boundary middle value, exact port.
        {
            SocketAddr addr;
            bool       p = SocketAddrParse(&addr, "1.2.3.4:4660", SOCKET_KIND_TCP);
            ok           = ok && p;
        // Overflow / junk -> rejected.
        {
            SocketAddr addr;
            ok = ok && !SocketAddrParse(&addr, "127.0.0.1:65536", SOCKET_KIND_TCP);
            ok = ok && !SocketAddrParse(&addr, "127.0.0.1:99999", SOCKET_KIND_TCP);
        StrAppendFmt(&spec, "{}", (Zstr) "1.2.3.4:4660");
    
        SocketAddr addr;
        bool       p = socket_addr_parse_str(&addr, &spec, SOCKET_KIND_TCP);
        ok           = ok && p;
    
        // NULL spec -> rejected, out zeroed.
        SocketAddr addr2;
        ok = ok && !socket_addr_parse_str(&addr2, NULL, SOCKET_KIND_TCP);
        // Empty address -> empty string.
        {
            SocketAddr empty = {0};
            Str        r     = SocketAddrFormat(&empty, a);
            ok               = ok && StrLen(&r) == 0;
        // IPv6 round-trip with brackets + exact port.
        {
            SocketAddr addr;
            bool       p = SocketAddrParse(&addr, "[::1]:4660", SOCKET_KIND_TCP);
            ok           = ok && p;
    static bool test_sd3_sockaddr_v4_fields(void) {
        const u8   ip[4] = {1, 2, 3, 4};
        SocketAddr a     = sockaddr_v4(ip, 0x1234);
    
        // Wrapper-level invariants (line 64/65 -> a.length, a.family).
    static bool test_sd3_sockaddr_v4_port_swap(void) {
        const u8   ip[4] = {10, 0, 0, 1};
        SocketAddr a     = sockaddr_v4(ip, 0xABCD);
    
        const struct sockaddr_in *sa     = (const struct sockaddr_in *)a.raw;
    static bool test_sd3_sockaddr_v6_fields(void) {
        const u8   ip[16] = {0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x34};
        SocketAddr a      = sockaddr_v6(ip, 0x1234);
    
        // Wrapper-level invariants (line 75/76).
    static bool test_sd3_sockaddr_v6_port_swap(void) {
        const u8   ip[16] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
        SocketAddr a      = sockaddr_v6(ip, 0xABCD);
    
        const struct sockaddr_in6 *sa     = (const struct sockaddr_in6 *)a.raw;
    }
    
    static bool v4_is(const SocketAddr *ad, Allocator *a, Zstr expect) {
        if (ad->family != SOCKET_FAMILY_INET) {
            return false;
        hosts_push_v4(&r, "pick", 9, 8, 7, 6);
    
        SocketAddr one;
        bool       got = dns_resolve_4_one_zstr(&r, "pick:443", SOCKET_KIND_TCP, &one);
        bool       ok  = got && v4_is(&one, a, "9.8.7.6:443");
        hosts_push_v4(&r, "pick", 9, 8, 7, 6);
    
        SocketAddr one;
        bool       got = dns_resolve_4_one_zstr(&r, "absent:443", SOCKET_KIND_TCP, &one);
Last updated on