Converting an AF_INET socket to use AF_UNIX via LD_PRELOAD - c

I'm wondering how feasible it is to be able to convert an AF_INET socket to use an AF_UNIX instead. The reason for this is that I've got a program which will open a TCP socket, but which we cannot change. To reduce overhead we would therefore like to instead tie this socket to use an AF_UNIX one for its communication instead.
So far, my idea has been to use LD_PRELOAD to achieve this---intercepting bind() and accept(), however it is not clear how best to achieve this, or even if this is the best approach.
So far, bind in bind(), if the socket type is AF_INET and its IP/port is the socket I wish to convert to AF_UNIX, I then close the sockd here, and open an AF_UNIX one. However, this seems to be causing problems further on in accept() -- because I am unsure what to do when the sockfd in accept() matches the one I wish to tell to use an AF_UNIX socket.
Any help kindly appreciated.
Jason

Your idea sounds perfectly feasible. In fact, I think it sounds like the best way to achieve what you want. I wouldn't expect very different, or even measurably different, overhead/performance though.
Of course you'd also have to intercept socket() in addition to bind() and accept(). In bind(), you could, for example, converted the requested TCP port to a fixed pathname /tmp/converted_socket.<port-number> or something like that.

I had a similar problem and came up with unsock, a shim library that does what you describe, and more.
unsock supports other address types like AF_VSOCK and AF_TIPC, and the Firecracker VM multiplexing proxy as well.
There are three key insights I want to share:
Since sockets are created for a particular address family using socket(2), and then later connected or bound using connect(2)/bind(2), you may be tempted to simply intercept socket and fix the address there.
One problem is that you may want to selectively intercept certain addresses only, which you don't know at the time of the call.
The other problem is that file descriptors may be passed upon you from another process (e.g., via AF_UNIX auxiliary mes, so you may not be able to intercept socket(2) in the first place.
In other words, you need to intercept connect(2), bind(2), and sendto(2).
When you intercept connect(2), bind(2), and sendto(2), you need to retroactively change the address family for socket(2). Thankfully, you can just create a new socket and use dup3(2) to reassign the new socket to the existing file descriptor. This saves a lot of housekeeping!
accept(2) and recvfrom(2) also need to be intercepted, and the returned addresses converted back to something the caller understands. This will inevitably break certain assumptions unless you do maintain a mapping back to the actual, non-AF_INET address.

Related

Purpose of sendto address for C raw socket?

I'm sending some ping packets via a raw socket in C, on my linux machine.
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
This means that I specify the IP packet header when I write to the socket (IP_HDRINCL is implied).
Writing to the socket with send fails, telling me I need to specify an address.
If I use sendto then it works. For sendto I must specify a sockaddr_in struct to use, which includes the fields sin_family, sin_port and sin_addr.
However, I have noticed a few things:
The sin_family is AF_INET - which was already specified when the socket was created.
The sin_port is naturally unused (ports are not a concept for IP).
It doesn't matter what address I use, so long as it is an external address (the IP packet specifies 8.8.8.8 and the sin_addr specifies 1.1.1.1).
It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?
Writing to the socket with send fails, telling me I need to specify an address.
It fails, because the send() function can only be used on connected sockets (as stated here). Usually you would use send() for TCP communication (connection-oriented) and sendto() can be used to send UDP datagrams (connectionless).
Since you want to send "ping" packets, or more correctly ICMP datagrams, which are clearly connectionless, you have to use the sendto() function.
It seems none of the extra fields in sendto are actually used to great
extent. So, is there a technical reason why I have to use sendto
instead of send or is it just an oversight in the API?
Short answer:
When you are not allowed to use send(), then there is only one option left, called sendto().
Long answer:
It is not just an oversight in the API. If you want to send a UDP datagram by using an ordinary socket (e.g. SOCK_DGRAM), sendto() needs the information about the destination address and port, which you provided in the struct sockaddr_in, right? The kernel will insert that information into the resulting IP header, since the struct sockaddr_in is the only place where you specified who the receiver will be. Or in other words: in this case the kernel has to take the destination info from your struct as you don't provide an additional IP header.
Because sendto() is not only used for UDP but also raw sockets, it has to be a more or less "generic" function which can cover all the different use cases, even when some parameters like the port number are not relevant/used in the end.
For instance, by using IPPROTO_RAW (which automatically implies IP_HDRINCL), you show your intention that you want to create the IP header on your own. Thus the last two arguments of sendto() are actually redundant information, because they're already included in the data buffer you pass to sendto() as the second argument. Note that, even when you use IP_HDRINCL with your raw socket, the kernel will fill in the source address and checksum of your IP datagram if you set the corresponding fields to 0.
If you want to write your own ping program, you could also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create the IP header for you, so you have one thing less to worry about. Now you can easily see how the two sendto()-parameters *dest_addr and addrlen become significant again because it's the only place where you provide a destination address.
The language and APIs are very old and have grown over time. Some APIs can look weird from todays perspective but you can't change the old interfaces without breaking a huge amount of existing code. Sometimes you just have to get used to things that were defined/designed many years or decades ago.
Hope that answers your question.
The send() call is used when the sockets are in a TCP SOCK_STREAM connected state.
From the man page:
the send() call may be used only when the socket is in a connected
state (so that the intended recipient is known).
Since your application obviously does not connect with any other socket, we cannot expect send() to work.
In addition to InvertedHeli's answer, the dest_addr passed in sendto() will be used by kernel to determine which network interface to used.
For example, if dest_addr has ip 127.0.0.1 and the raw packet has dest address 8.8.8.8, your packet will still be routed to the lo interface.

C `sendto` versus `write`

Correct me if I'm wrong, but my understanding of sending a raw packet inevitably is defined as buffering an array of bytes in an array, and writing it to a socket. However, most example code I've seen so far tend towards sendto, rarely is send used, and I've never seen code other than my own use write. Am I missing something? What is with this apparent preoccupation with complicating code like this?
Why use send and sendto when write seems to me to be the obvious choice when dealing with raw sockets?
sendto is typically used with unconnected UDP sockets or raw sockets. It takes a parameter specifying the destination address/port of the packet. send and write don't have this parameter, so there's no way to tell the data where to go.
send is used with TCP sockets and connected UDP sockets. Since a connection has been established, a destination does not need to be specified, and in fact this function doesn't have a parameter for one.
While the write function can be used in places where send can be used, it lacks the flags parameter which can enable certain behaviors on TCP sockets. It also doesn't return the same set of error codes as send, so if things go wrong you might not get a meaningful error code. In theory you could also use write on a raw socket if the IP_HDRINCL socket option is set, but again it's not preferable since it doesn't support the same error codes as send.

Why does POSIX not define a mid-layer socket API?

I am looking at socket programming again. I get the details (well, I can copy them from various websites, and I know the code is enabling the Unix low-level procedures), but I don't get the POSIX logic and thinking in its API.
Why have they not defined a slightly higher-level interface built on these lower-level socket functions?
Presumably, such code could factor out code that is repeated often (and error-prone) into more convenient FILE like interfaces. Factoring would seem even more appropriate than just convenient when the lower level use is the same in > 90% of its use. Almost all sockets use that I see in application programs open a socket, read and write to it and close the socket. Also, why does one need to bind, when this is really something that the open call always does?
What cases does the current interface even cover that could not easily be covered by an interface that would look almost like the FILE interface?
One explanation is that there are uses where one would not bind to a socket, for example, or where fgets/fputs/fprintf/fscanf like functionality would need something extra (time-outs)?
There must be a reason that I am missing. Otherwise, 20 years later, there would already be one or more standard libraries that facilitate this and that would be in wide use. I couldn't find one on google that mimics all the FILE routines.
The point is strikingly simple:
Because sockets are not files.
Let me elaborate: recv/send works quite like read/write, if you limit yourself to linearly reading a file from the beginning, and to appending at its end.
However, you'll say, send doesn't let me write arbitrary lengths of data trough! If I try to send more data than fits into a protocol's packet buffer, it will throw an error!
And that's actually the beauty of sockets: you actually send the data away. you can't keep it; it's gone once it's sent, and it's not stored once it's received. Sockets give you a whole different set of abilities (like sending smaller packets than the maximum packet size of the network, for example), which on the other hand demand you take some control yourself.
EDIT: send will not "throw" an error. "throwing" is not a C/Posix way of handling errors. Instead it will return an error (from man 2 send):
If the message is too long to pass atomically through the underlying protocol, the error EMSGSIZE is returned, and the message is not transmitted.
The C programming language is and will likely always be a lightweight one. You need to understand that C runs basically anywhere and some things need a long research and work to get standardized.
Also, I have seen that new libraries are added because C++ went ahead and made them standard so it's a kind of C sharing.
Please do note that you can "bind" a socket to a file through fdopen(3) and consider it as a binary file. Of course you will still need to bind it, make it listen, accept and all the actions you can do on a socket that won't work for a file.
Indeed, despite the similar interface, a socket acts only partially as a UNIX file: there's even an errno value, ENOTSOCK which indicates a socket specific operation on a non-socket file descriptor.
Furthermore, consider buffering. You do want a file write to be done in large chunks, thus a bigger buffering, to make it faster; this won't work for a socket as you need to send data immediately, that is, undelayed.
Consider this example:
char one = '1', two = '2', three = '3';
fwrite(&one, 1, 1, socket_file);
fprintf(socket_file, "%c\n", two);
send(fd, &three, 1, 0);
where fd is a connected socket(AF_INET, SOCK_STREAM, 0) and socket_file = fdopen(fd, "w+"). The receiver will read 312 because there's no flush except upon process termination at the FILE layer, unlike with send where three is sent immediately.

UDP based chat in C

I'm supposed to make a communicator in C, based on dgrams. I don't know what arguments should I pass to bind() function. I skimmed through most UDP-chat questions & codes here on StackOverflow but I still can't find any specific information on the issue.
What type of address structure should I use?
What port can I use? Any number bigger than 1024 ?
What IP adress do I bind my socket with? (most of people put INADDR_ANY but isn't it for receiving only?)
Also, do I need multiple sockets? One for receiving & another for sending messages.
What type of address structure should I use?
If you are using IPv4, use a sockaddr_in. If you want to use IPv6 instead, use a sockaddr_in6.
What port can I use? Any number bigger than 1024 ?
Yes, assuming no other program is already using that port number for its own UDP socket. (If another program is using the port number you chose, it will cause bind() to fail with errno EADDRINUSE)
What IP adress do I bind my socket with? (most of people put
INADDR_ANY but isn't it for receiving only?)
INADDR_ANY is what you generally want to use. It tells the OS that you want to receive incoming UDP packets on any of the computers network interfaces. (If you only wanted to receive UDP packets from a particular network interface, OTOH, e.g. only on WiFi, you could specify that network interface's IP address instead)
Also, do I need multiple sockets? One for receiving & another for
sending messages.
You can have multiple sockets if you want, but it's not necessary to do it that way. You can instead use a single socket for both sending and receiving UDP packets. One common pattern is to use a single socket, set to non-blocking mode, and something like select() or poll() to multiplex the input and output needs of your program. An alternative pattern would be to use two threads (one for sending and one for receiving), blocking I/O, and either one or two sockets (depending on whether you prefer to have the two threads share a socket, or give each thread its own socket). I prefer the single-threaded/single-socket/select() solution myself, as I think it is the least error-prone approach.

Data transfer over TCP-IPv6 connection

I am working on a client-server application in C and on Linux platform. What I am trying to achieve is to change the socket id over a TCP connection on both client and server without data loss where in the client sends the data from a file to the server in the main thread. The application is multithreaded where the other threads change the socket id based on some global flags set.
Problem: The application has two TCP socket connections established, over both IPv4 and IPv6 paths. I am transferring a file over the TCP-IPv4 connection first in the main thread. The other thread is checking on some global flags and has access to/share the socket IDs created for each protocol in the main thread. The send and recv use a pointer variable in its call to point to the socket ID to be used for the data transfer. The data is transferred initially over TCP-Ipv4. Once the global flags are set and few other checks are made the other thread changes the socket ID used in send call to point to IPv6 socket. This thread also takes care of communicating the change between the two hosts.I am getting all the data over IPv4 sent completely before switching. Also I am getting data sent over Ipv6 after the socket ID is just switched. But down the transfer there is loss of data over IPv6 connection.(I am using a pointer variable in send function on server side send(*p_dataSocket.socket_id,sentence,p_size,0); to change the pointer to IPv6 socket ID on the fly)
The error after recv and send call on both side respectively is says ESPIPE:Illegal seek, but this error exists even before switching. So I am pretty much sure this is nothing to do with the data loss
I am using pselect() to check for the available data for each socket. I can somehow understand the data loss while switching(if not properly handled) but I am not able to figure out why the data loss is occurring down the transfer after switching. I hope I am clear on what the issue is. I have also checked to send the data individually over each protocol without switching and there is no data loss.It I initially transfer the data over Ipv6 and then switch to IPv4, there is no data loss. Also would really appreciate to know to how to investigate in this issue apart from using errno or netstat.
When you are using TCP to send data you just can't loose a part of the information in between. You either receive the byte stream the way it was sent or receive nothing at all - provided that you are using the socket-related functions correctly.
There are several points you may want to investigate.
First of all you must make sure that you are really sending the data which is lost. Add some logging on the server side application: dump anything that you transmit witn send() into some file. Include some extra info as well, like:
Data packet no.==1234, *p_dataSocket.socket_id==11, Data=="data_contents_here", 22 bytes total; send() return==22
The important thing here is to watch the contents of *p_dataSocket.socket_id. Make sure that you are using mutex or something like that cause you have a thread which regularly reads socket_id contents and another thread which occasionally changes it. You are not guranteed against the getting of a wrong value from that address unless your threads have monopoly access to it while reading/writing. It is important both for normal program operation and for the debugging information generation.
Another possible problem here is the logic which selects sentence to send. Corruption of this variable may be hard to track in multithreaded program. The logging of transmitted information will help you here too.
Use any TCP sniffer to check what TCP stack really transmits. Are there packets with lost data? If there are no those packets, try to find out which send() call was responsible for sending that data. If those packets exist, check the receiving side for bugs.
errno value should not be used alone. Its value has meaning only when you get an erroneous return from a function. Try to find out when exactly errno becomes ESPIPE That may happen when any of API functions return something like -1 (depends on function). When you find out where it happens you should find out what is wrong in that particular piece of code (debugger is your friend). Have in mind that errno behavior in multithreaded environment depends on your system implementation. Make sure that you use -pthread option (gcc) or at least compile with -D_REENTRANT to minimize the risks.
Check this question for some info about the possible cause of your situation with errno==ESPIPE. Try some debuggin techniques, as suggested there. Errno value of ESPIPE gives a hint that you are using file descriptors incorrectly somewhere in your program. Maybe somewhere you are using a socket fd as regular file or something like that. This may be caused by some race condition (simultaneous access to one object from several threads).

Resources