2

I'm trying to do basic operations like copy and equality comparison with a SOCKADDR C Union.

#ifdef AF_INET6

#define SOCKADDR        union { \
                            struct sockaddr_in him4; \
                            struct sockaddr_in6 him6; \
                        }

#define SOCKADDR_LEN    (ipv6_available() ? sizeof(SOCKADDR) : \
                         sizeof(struct sockaddr_in))

#else

#define SOCKADDR        union { struct sockaddr_in him4; }
#define SOCKADDR_LEN    sizeof(SOCKADDR)

#endif

Let's assume I have a SOCKADDR sa on my stack, which is created with the UDP recvfrom method below:

void *buf = (void *)jlong_to_ptr(address);
SOCKADDR sa;
socklen_t sa_len = SOCKADDR_LEN;
jint n = recvfrom(fd, buf, len, 0, (struct sockaddr *)&sa, &sa_len);

Great, so far so good!

  1. If I want to duplicate my sa, can I just do SOCKADDR newSA = sa?

  2. Is this true for all UNIONs, in other words, I can just assign to copy?

  3. Why do I have to cast my union to a (struct sockaddr*) and how is this cast even possible since I don't see sockaddr in the union declaration?

  4. How about equality, can I just do sa1 == sa2 for logical equivalence?

Jongware
  • 22,200
  • 8
  • 54
  • 100
LatencyFighter
  • 351
  • 2
  • 11
  • 1
    My suggestion for you is to read [Difference between Deep copy and shallow copy](https://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy) to get clarity on your first two points. – Gaurav Pathak Jan 12 '18 at 06:19
  • @Gaurav: do you have a similar link at hand to explain the difference between C and C++? – Jongware Jan 13 '18 at 02:07

2 Answers2

3

If I want to duplicate my sa, can I just do SOCKADDR newSA = sa?

Yes you can.

Is this true for all UNIONs, in other words, I can just assign to copy?

if union contain pointers then you need to be a bit careful. 'Assign to copy' will create two copies of union with separate pointers pointing to common memory.

Why do I have to cast my union to a (struct sockaddr*)

You must cast to match it with 'recvfrom' function signature.

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
             struct sockaddr *src_addr, socklen_t *addrlen);

how is this cast even possible since I don't see sockaddr in the union declaration?

include files required for 'recvfrom' also contain the definition for 'struct sockaddr'

How about equality, can I just do sa1 == sa2 for logical equivalence?

No in 'C'(Possible in C++ with operator overloading). This operation doesn't make sense in 'C'. even though contents of sa1 and sa2 could be same but they are two independent structures at separate memory locations and '==' operator in 'C' only understands built-in types (int, char etc). In C++ it's a different story because operators can be overloaded and you can define your criteria for comparison.

Nitin
  • 145
  • 9
  • Thanks! Is there an easy approach to implement equality for this union? – LatencyFighter Jan 12 '18 at 07:20
  • You need to define a function e.g. sockcompare(sa1, sa2) . compare each element of struct sockaddr individually and return true if all match. I see, @rdalmeida posted a function below. Please mark my response 'answered' if your questions were answered. – Nitin Jan 12 '18 at 08:20
  • On `==`: Can't I say "*If you can assign the types using `=`, you can also compare the types using `==`*"? I mean, when I can pass structures and can assign them, why shouldn't I be able to compare them? – U. Windl Sep 06 '22 at 12:03
2

Complementing Nitin's answer, comparing SOCKADDR is not trivial because of IPv6. Below an example:

/* sock_addr_cmp_addr - compare addresses for equality */

int     sock_addr_cmp_addr(const struct sockaddr * sa,
                       const struct sockaddr * sb)
{
    if (sa->sa_family != sb->sa_family)
    return (sa->sa_family - sb->sa_family);

    /*
     * With IPv6 address structures, assume a non-hostile implementation that
     * stores the address as a contiguous sequence of bits. Any holes in the
     * sequence would invalidate the use of memcmp().
     */
    if (sa->sa_family == AF_INET) {
    return (SOCK_ADDR_IN_ADDR(sa).s_addr - SOCK_ADDR_IN_ADDR(sb).s_addr);
#ifdef HAS_IPV6
    } else if (sa->sa_family == AF_INET6) {
    return (memcmp((char *) &(SOCK_ADDR_IN6_ADDR(sa)),
               (char *) &(SOCK_ADDR_IN6_ADDR(sb)),
               sizeof(SOCK_ADDR_IN6_ADDR(sa))));
#endif
    } else {
    msg_panic("sock_addr_cmp_addr: unsupported address family %d",
          sa->sa_family);
    }
}

/* sock_addr_cmp_port - compare ports for equality */

int     sock_addr_cmp_port(const struct sockaddr * sa,
                       const struct sockaddr * sb)
{
    if (sa->sa_family != sb->sa_family)
    return (sa->sa_family - sb->sa_family);

    if (sa->sa_family == AF_INET) {
    return (SOCK_ADDR_IN_PORT(sa) - SOCK_ADDR_IN_PORT(sb));
#ifdef HAS_IPV6
    } else if (sa->sa_family == AF_INET6) {
    return (SOCK_ADDR_IN6_PORT(sa) - SOCK_ADDR_IN6_PORT(sb));
#endif
    } else {
    msg_panic("sock_addr_cmp_port: unsupported address family %d",
          sa->sa_family);
    }
}

Source: https://opensource.apple.com/source/postfix/postfix-197/postfix/src/util/sock_addr.c

rdalmeida
  • 1,806
  • 12
  • 16