-1

I have a Java client and a C server that communicate over a unix socket. I want to send an ip address from the client to the server. On the server side when i try to interprete the bytes i receive from the client i get [ 01 01 01 01 ] which is correct, but then when i print the ip_address to video i get 0.0.0.0. What am i doing wrong? I think i'm casting the address in a wrong manner somehow. I need to implement IPv4-Mapped IPv6 Addresses that's why im using sockaddr_storage which is the most general case.

Here is my some code extracts:

Java client does:

OutputStream out;    
byte[] ipAddress = Inet6Address.getByName("1.1.1.1").getAddress();
out.write(ipAddress);

C server does:

    struct sockaddr_storage * ip_address;
    n = read(rcv_sock, msg, PSIZE);
    printf("I recevied n bytes: %d\n", n);// i get 4 bytes
    print_bytes(msg, n);
    ip_address = (union sockaddr_storage *)msg;
    char str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(ip_address->__ss_padding), str, INET_ADDRSTRLEN);
    printf("ip_address = %s\n", str); //i get 0.0.0.0

In particular the server expects a message containing a 28 byte long header + a socket address structure. I've managed to decode the header properly but not the socket address structure.

Pheonix7
  • 2,131
  • 5
  • 21
  • 38
  • 1
    Why would you assume the byte representation of a Java Inet6Address is a C `struct sockaddr_storage`? – Andrew Henle Jan 21 '16 at 11:36
  • 1
    If your 01010101 data is what you intended to receive, then there is no need to wrap that into sockaddr of any sort nor take any offsets. Pass msg to inet_ntop as-is. – user3125367 Jan 21 '16 at 11:37
  • as per the documentation of the c server code. It expects a particular header + a socket address structure. I manage to make the header but i cant seem to correctly send it an address structure. – Pheonix7 Jan 21 '16 at 11:40
  • @user can u please show me what u mean – Pheonix7 Jan 21 '16 at 11:41
  • 1
    @Pheonix7 Can you link us or provide the documentation of the C server? Because as is, it seems the C server probably expects a `sockaddr_in` or similar to be passed. Which --in my book-- falls under *bad idea leading to non-portable and undefined behaviour on the fast lane*. In any case, the "header" is the `ss_family` field from `sockaddr`. – dhke Jan 21 '16 at 11:42
  • in fact it was made to work on freebsd and it used a sockunion. I need to make it work on linux and i would like to make it properly. so i figured a struct sockaddr_storage would be the best way – Pheonix7 Jan 21 '16 at 11:45
  • @Pheonix7 inet_ntop takes specific *address* as 2nd arg, not *sockaddr*. msg is already proper address from AF_INET family. inet_ntop was never intended to accept (sockaddr *) and your old code is probably wrong. – user3125367 Jan 21 '16 at 11:49
  • Plus, you're feeding an IPv4 address to an IPv6 class. – chrylis -cautiouslyoptimistic- Jan 21 '16 at 11:51
  • @Pheonix7 The socket address structure is probably a memory image of one of `struct sockaddr_in` or `struct sockaddr_in6`. Since these are not packed and subject to the usual alignment rules, sending those over to another process is unportable. But if you have no other choice, you can decode the structures well enough, they are fully documented. – dhke Jan 21 '16 at 11:56
  • Please clarify if the problem is on the C or the Java side and remove th unrelated tag! If it is on both sides, you do something terribly wrong and did not include all required information. – too honest for this site Jan 21 '16 at 12:29
  • it's C related mainly. is it better to use sockunion structure as its even more generic? – Pheonix7 Jan 21 '16 at 12:31
  • The only portable way to pass full socket address (not just ipv4/6 address) across network is to serialize appropriate fields from sockaddr structure into ascii. For example: "INET6 ::1 80", "INET 0.0.0.0 0", "INET 127.0.0.1 443". You *may* send raw struct data over network, but can't expect its layout to be the same on receiver. There is no "more generic" thing. And you're sending *raw ipv6 address* in your example, but treating it as *sockaddr* on the other side. Too many issues here, please carefully read socket/network tutorial and all related c/java manual pages at least. – user3125367 Jan 21 '16 at 12:55
  • ok i definitly will do more reading, but i just wanted to specify that im not really sending data over a network. Its all local on my machine. the communication is only over a unix socket. – Pheonix7 Jan 21 '16 at 13:06
  • @Pheonix7 Where is `sockunion` defined? FreeBSD only has `sockaddr_union` in its IPSec and pf headers. In any case, writing the memory image of a sockaddr is only portable between processes of the same "type", i.e. bit width, CPU type and so on. Hence, it is probably only okay to do this when used as an internal format. But you are accessing the server from Java. – dhke Jan 21 '16 at 13:34
  • @dhke yes, my first step was to change sockunion and use any linux supported sockaddr type. So i was thinking to use sockaddr_storage. Secondly, i am willing to change completely the type of packet the the server expects (suppose a packet consisting of 1 byte to identify msg type + socket address struct with ipAddress). What i need to do though is that the client is Java and this client has to send this new packet to the server. – Pheonix7 Jan 21 '16 at 13:42

1 Answers1

0

A TCPv4 (TCP/IPv4) socket is identified by a 32-bit address and a 16-bit port number. A TCPv6 (TCP/IPv6) socket is identified by a 128-bit address and a 16-bit port number. The sockaddr_ structures have much more internal information than that, used (and required) internally by the C library. You cannot transfer these structures from one system to another, and expect them to work. (Or make any sort of sense on the other end, really.)

IPv4-mapped IPv6 addresses are IPv6 addresses, with the first 80 bits zero, followed by 16 one bits, followed by the 32-bit IPv4 address. They're typically written in IPv6 format as ::ffff:a.b.c.d, where a.b.c.d is the IPv4 address mapped to IPv6.

Instead of having a client send a "sockaddr structure" of any sort, it definitely should send a 128-bit IPv6 address, and possibly the 16-bit port number. If you send these in binary format, it is common practice to transmit these in network byte order, i.e. most significant byte first.

However, I would posit it is much simpler and much more robust to send these as strings instead, for example using format IPv6 \0 port \0 (i.e. appending a zero byte after the address, and another after the port). This way you only need a pointer to the address (node) and a pointer to the port (serv), and you can reconstruct the needed sockaddr_in6 structure using getaddrinfo().

If you have to send the address and port in binary (for example, to ensure a fixed-size initial packet), it is theoretically possible for you to construct the sockaddr_in6 structure by hand. However, that is architecture-specific (byte-order sensitive), and fragile. It is much simpler to convert the IPv6 address to a string (either ::ffff:a.b.c.d if IPv4-mapped, or aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh in hex notation), and the port to a decimal number, and again use getaddrinfo() to get a suitable socket structure out of it.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • i see that the getaddrinfo() is the best way to send ip info but from the documentation it seems to be specific for network pk and not for local unix sockets. how would i set the parameters to do local communicationsending my specific header + this addrinfo type? – Pheonix7 Jan 24 '16 at 16:01
  • Your question states that you wish to transfer an IPv4/IPv6 address (and possibly a port number). The method you use to transfer that information does not matter. If your client and server use an [UNIX domain socket](http://man7.org/linux/man-pages/man7/unix.7.html) to communicate, they can transfer the socket descriptor as an ancillary message. This is very easy in C, but I don't think Java supports it (unless you have a native library to do it with). – Nominal Animal Jan 24 '16 at 18:16
  • i do. Im using JUDS, but it seems that JUDS does not support serialization – Pheonix7 Jan 24 '16 at 18:26
  • Assuming you refer to this [JUDS](https://github.com/mcfunley/juds), its [source](https://github.com/mcfunley/juds/blob/master/com/etsy/net/UnixDomainSocket.c) shows it does not have support for passing descriptors (`SCM_RIGHTS` ancillary messages). – Nominal Animal Jan 24 '16 at 19:51
  • oh no. the only other alternative i found to JUDS is junixsocket. Do you know if it supports passing descriptors? could it be work in my care? thanks for the help – Pheonix7 Jan 24 '16 at 20:45
  • [This fork of junixsocket](https://github.com/AgNO3/junixsocket) indicates that Java code using it can pass a socket descriptor to other code; but not receive. Perhaps you could contact me [directly](http://www.nominal-animal.net/), and explain exactly what you are trying to accomplish; I may be able to suggest better solutions for the underlying problem. – Nominal Animal Jan 24 '16 at 21:48