I am trying to write a simple FTP-like client-server network program on macOS using sendfile() function. After reading Apple's developer Manual on this topic, sadly I am still having trouble using it.
Code
// creation of fd
int fd = open("file_path", O_RDONLY);
off_t len = 0;
// the creation of sockets used in sendfile
getaddrinfo(NULL, port, &hints, &servinfo);
// p is iterating through servinfo (p=p->ai_next)
sockfd= socket(p->ai_family, p->ai_socktype, p->ai_protocol);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
bind(sockfd, p->ai_addr, p->ai_addrlen);
listen(sockfd, BACKLOG); //BACKLOG is a macro configuring pending connections
new_sockfd = accept(sockfd, (struct sockaddr*)&clients_addr, &sin_size);
// sendfile
if(sendfile(new_sockfd, fd, 0, &len, NULL, 0)==-1){
fprintf(stderr, "server sendfile errno: %d", errno);
// sorry I know this is not the best way to interpret the errno
}
The client got a message of "connection closed by peer" while the errno code has been set to 45;
I have checked the file descriptor fd using read() and printed it, it works fine;
You have got the arguments backwards. According to the documentation, error code ENOTSUP (which I assume is "operation not supported" - thanks to Jeremy Friesner for looking it up - you should print strerror(errno) as it's useful) means that fd is not a regular file.
fd is the first argument.
int sendfile(int fd, int s, off_t offset, off_t *len, struct sf_hdtr *hdtr, int flags);
The sendfile() system call sends a regular file specified by descriptor fd out a stream socket specified by descriptor s.
So your code isn't trying to send the file through the socket. It's trying to send the socket through the file, which doesn't make sense. Change the order.
Related
Getting address family can be done with getsockopt() with option SO_DOMAIN (see socket options) on linux.
(read Get address family from socket. Linux)
How do I do the same thing on OS X ?
When using the code that worked on linux on OS X I get
error: use of undeclared identifier 'SO_DOMAIN'
int err = getsockopt(sock, SOL_SOCKET, SO_DOMAIN, &addr_family, &len);
^
so not SO_DOMAIN ?
You can find this out by calling proc_pidfdinfo() on your own PID, and requesting PROC_PIDFDSOCKETINFO:
#include <libproc.h>
#include <stdbool.h>
#include <unistd.h>
bool socketDomainGet(int socket, int *domain) {
struct socket_fdinfo info;
int rc = proc_pidfdinfo(getpid(), socket, PROC_PIDFDSOCKETINFO, &info, sizeof info);
if (rc <= 0) return false;
*domain = info.psi.soi_family;
return true;
}
Unlike getsockname(), this works even if the socket hasn't been bound yet. Note as well as the domain (psi.soi_family), it also can tell you the type (psi.soi_type) and the protocol (psi.soi_protocol).
Keep in mind though this warning Apple gives in the libproc.h header:
/*
* This header file contains private interfaces to obtain process information.
* These interfaces are subject to change in future releases.
*/
They probably aren't going to break this, but the risk is higher than for more official APIs.
use SO_TYPE instead of SO_DOMAIN
in OS X man GETSOCKOPT(2) says
SO_TYPE get the type of the socket (get only)
What is the meaning of F_GETFD in fcntl() function in unix ?, From what I understand it should return -1 if there is no file descriptor in the position specified.. If that's true, when would it happen ? when doing close to a file descriptor in that posstion, F_GETFD doesn't return -1 either..
This is a part of a program using F_GETFD and it will not return -1 if I close the x fd (thid fd entered 0 in the fd table since we closed 0 beforehand and did dup(x)):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
void try(){
printf("got_sig\n");
}
int main(int argc, char *argv[]){
int x, stdout=1;
signal(SIGUSR1,&try);
x = open("t1.txt",O_RDWR|O_CREAT, 0666);
close(0);
dup(x);
close(x);
if (fcntl(0,F_GETFD)==-1)
printf("false\n");
kill(getpid(),SIGUSR1);
//close(x);
write(stdout,"BYE\n",4);
exit(0);
}
When will F_GETFD return -1 ?
From the man page of fcntl():
File descriptor flags
The following commands manipulate the flags associated with a file descriptor. Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. If the FD_CLOEXEC bit
is set, the file descriptor will automatically be closed during a successful execve(2).
F_GETFD (void)
Return (as the function result) the file descriptor flags; arg
is ignored.
(Or see the POSIX standard text for fctnl(), if you care.)
So, fnctl(fd, F_GETFD) tells you if the file descriptor stays open over an execve() or not.
The usual error cases for fcntl() apply, so it would e.g. return -1 and set errno to EBADF if the fd you gave wasn't open. In your code, you called fcntl() on fd 0, which holds the dup'ed copy of x, and so is open. fcntl() would return -1 for the fd x, though, that's the one you explicitly closed.
Inspecting the implementation of do_fcntl in the Linux kernel, we can see that:
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
struct file *filp)
{
long err = -EINVAL;
switch (cmd) {
...
case F_GETFD:
err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;
break;
...
}
return err;
}
do_fcntl(F_GETFD) may return only FD_CLOEXEC or 0.
The syscall definition from here can return -EBADF in case of a bad file descriptor or the file descriptor was opened with O_PATH.
Judging from that, the fcntl(F_GETFD) will return only -1 with errno set to -9 (-EBADF), or return 0 or 1 (FD_CLOEXEC) on Linux 4.20. So the fcntl(F_GETFD) will return -1 in case of a bad file descriptor or file descriptor opened with O_PATH. Note that for other kernels it may be different and note that it can change over time.
here is a test program I wrote
int main( int argc, const char* argv[] )
{
const char name[1024] = "/dev/shm/test_file";
off_t len = atol(argv[argc - 1]);
char buf[1024];
FILE * f = fopen(name, "w");
for (int i = 0; i < len; i++) {
int ret = fwrite(buf, 1024, 1, f);
if (ret != 1) {
printf("disk full\n");
}
}
if ( fclose(f) != 0)
printf("failed to close\n");
return 0;
}
I tried to fill the /dev/shm to almost full
tmpfs 36G 36G 92K 100% /dev/shm
and ran
$ ./a.out 93
failed to close
my glibc
$ /lib/libc.so.6
GNU C Library stable release version 2.12, by Roland McGrath et al.
the kernel version is 2.6.32-642.13.1.el6.x86_64
I understand that this behavior is caused by fwrite try to cache the data in memory. (I tried setvbuf(NULL...) and fwrite immediately return failure). But this seems a little different from the definition
The fwrite() function shall return the number of elements successfully
written, which may be less than nitems if a write error is
encountered. If size or nitems is 0, fwrite() shall return 0 and the
state of the stream remains unchanged. Otherwise, if a write error
occurs, the error indicator for the stream shall be set, [CX] [Option
Start] and errno shall be set to indicate the error. [Option End]
The data was not successfully written to disk however its return value is 1. no errno set.
In this test case, the fclose catch the failure. But it could be caught by even a ftell function which is quite confusing.
I am wondering if this happens to all versions of glibc and would this be consider a bug.
The data was not successfully written to disk
The standard doesn't talk about the disk. It talks about data being successfully written to the stream (which it has been).
I am wondering if this happens to all versions of glibc
Most likely.
and would this be consider a bug.
It's a bug in your interpretation of the requirements on fwrite.
I'm using WPE PRO, and I can capture packets and send it back. I tried do it using WinSock 2(The same lib which WPE PRO use), but I don't know how to send packet to a existent TCP connection like WPE PRO does.
http://wpepro.net/index.php?categoryid=2
How can I do it ?
Are you asking how to make someone else's program send data over its existing Winsock connection?
I've done exactly this but unfortunately do not have the code on-hand at the moment. If you give me an hour or two I can put up a working example using C; if you need one let me know and I will.
Edit: sample DLL to test at the bottom of the page if you or anyone else wants to; I can't. All I know is that it compiles. You just need to download (or write!) a freeware DLL injector program to test it; there are tons out there.
In the meantime, what you need to research is:
The very basics of how EXEs are executed.
DLL injection
API hooking
Windows Sockets API
1. The very basics of how EXEs are executed:
The whole entire process of what I'm about to explain to you boils down to this very principal. When you double-click an executable, Windows parses it and loads its code, etc. into memory. This is the key. The compiled code is all being put into RAM. What does this imply? Well, if the application's code is all in RAM, can we change the application's code while it's running by just changing some of its memory? After all, it's just a bunch of instructions.
The answer is yes and will provide us the means of messing with another application - in this case, telling it to send some data over its open socket.
(This principal is also the reason you have to be careful writing programs in low-level languages like C since if you put bad stuff in bad parts of RAM, it can crash the program or open you up to shell code exploits).
2. DLL injection:
The problem is, how do we know which memory to overwrite? Do we have access to that program's memory, especially the parts containing the instructions we want to change? You can write to another process' memory but it's more complicated. The easiest way to change their memory (again, when I say memory, we're talking about the machine code instructions being executed) is by having a DLL loaded and running within that process. Think of your DLL as a .c file you can add to another program and write your own code: you can access the program's variables, call its functions, anything; because it's running within the process.
DLL injection can be done through numerous methods. The usual is by calling the CreateRemoteThread() API function. Do a Google search on that.
3. API Hooking
What is API hooking? To put it more generally, it's "function hooking", we just happen to be interested in hooking API calls; in this case, the ones used for Sockets (socket(), send(), etc.).
Let's use an example. A target application written in C using Winsock. Let's see what they are doing and then show an example of what we WANT to make it do:
Their original source code creating a socket:
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Now, that's the original program's source code. Our DLL won't have access to that because it's loaded within an EXE and an EXE is compiled (duh!). So let's say their call to socket() looked something like this after being compiled to machine code (assembly). I don't know assembly at all but this is just for illustration:
The assembly/machine code:
PUSH 06 ; IPPROTO_TCP
PUSH 01 ; SOCK_STREAM
PUSH 02 ; AF_INET
CALL WS2_32.socket ; This is one of the parts our DLL will need to intercept ("hook").
In order for us to make that program send data (using our DLL), we need to know the socket's handle. So we need to intercept their call to the socket function. Here are some considerations:
The last instruction there would need to be changed to: CALL OurOwnDLL.socket. That CALL instruction is just a value in memory somewhere (remember?) so we can do that with WriteProcessMemory. We'll get to that.
We want to take control of the target program, not crash it or make it behave strangely. So our code needs to be transparent. Our DLL which we will inject needs to have a socket function identical to the original, return the same value, etc. The only difference is, we will be logging the return value (SocketHandle) so that we can use it later when we want to send data.
We also need to know if/when the socket connects since we can't send data unless it is (assuming we're using TCP like most applications do). This means we need to also hook the Winsock connect API function and also duplicate that in our DLL.
DLL to inject and monitor the socket and connect functions (untested):
This C DLL will have everything in place to hook and unhook functions. I can't test it at the moment and I'm not even much of a C programmer so let me know if you come across any problems.
Compile this as a Windows DLL not using Unicode and inject it into a process that you know uses WS2_32's socket() and connect() functions and let me know if it works. I have no means to test, sorry. If you need further help or fixes, let me know.
/*
SocketHookDLL.c
Author: Daniel Elkins
License: Public Domain
Version: 1.0.0
Created: May 14th, 2014 at 12:23 AM
Updated: [Never]
Summary:
1. Link to the Winsock library so we can use its functions.
2. Export our own `socket` and `connect` functions so that
they can be called by the target application instead of
the original ones from WS2_32.
3. "Hook" the socket APIs by writing over the target's memory,
causing `CALL WS2_32.socket` to `CALL SocketHookDLL.socket`, using
WriteProcessMemory.
4. Make sure to keep a copy of the original memory for when we no
no longer want to hook those socket functions (i.e. DLL detaching).
*/
#pragma comment(lib, "WS2_32.lib")
#include <WinSock2.h>
/* These functions hook and un-hook an API function. */
unsigned long hookFunction (const char * dllModule, const char * apiFunction, unsigned char * memoryBackup);
unsigned int unHookFunction (const char * dllModule, const char * apiFunction, unsigned char * memoryBackup);
/*
These functions (the ones we want to hook) are copies of the original Winsock's functions from Winsock2.h.
1. Calls OurDLL.hooked_socket() (unknowingly).
2. OurDLL.hooked_socket() calls the original Winsock.socket() function.
3. We take note of the returned SOCKET handle so we can use it later to send data.
4. OurDLL.hooked_socket() returns the SOCKET back to the target app so everthing works as it should (hopefully!).
Note: You can change return values, parameters (like data being sent/received like WPE does), just be aware it will
also (hopefully, intendingly) change the behavior of the target application.
*/
SOCKET WSAAPI hooked_socket (int af, int type, int protocol);
int WSAAPI hooked_connect (SOCKET s, const struct sockaddr FAR * name, int namelen);
/* Backups of the original memory; need one for each API function you hook (if you want to unhook it later). */
unsigned char backupSocket[6];
unsigned char backupConnect[6];
/* Our SOCKET handle used by the target application. */
SOCKET targetsSocket = INVALID_SOCKET;
/* This is the very first code that gets executed once our DLL is injected: */
BOOL APIENTRY DllMain (HMODULE moduleHandle, DWORD reason, LPVOID reserved)
{
/*
We will hook the desired Socket APIs when attaching
to target EXE and UN-hook them when being detached.
*/
switch (reason)
{
case DLL_PROCESS_ATTACH:
/* Here goes nothing! */
hookFunction ("WS2_32.DLL", "socket", backupSocket);
hookFunction ("WS2_32.DLL", "connect", backupConnect);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_PROCESS_DETACH:
unHookFunction ("WS2_32.DLL", "socket", backupSocket);
unHookFunction ("WS2_32.DLL", "connect", backupConnect);
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
unsigned long hookFunction (const char * dllModule, const char * apiFunction, unsigned char * memoryBackup)
{
/*
Hook an API function:
=====================
1. Build the necessary assembly (machine code) opcodes to get our DLL called!
2. Get a handle to the API we're hooking.
3. Use ReadProcessMemory() to backup the original memory to un-hook the function later.
4. Use WriteProcessMemory to make changes to the instructions in memory.
*/
HANDLE thisTargetProcess;
HMODULE dllModuleHandle;
unsigned long apiAddress;
unsigned long memoryWritePosition;
unsigned char newOpcodes[6] = {
0xE9, 0x00, 0x00, 0x00, 0x00, 0xC3 // Step #1.
};
thisTargetProcess = GetCurrentProcess ();
// Step #2.
dllModuleHandle = GetModuleHandle (dllModule);
if (!dllModuleHandle)
return 0;
apiAddress = (unsigned long) GetProcAddress (dllModuleHandle, apiFunction);
if (!apiAddress)
return 0;
// Step #3.
ReadProcessMemory (thisTargetProcess, (void *) apiAddress, memoryBackup, 6, 0);
memoryWritePosition = ((unsigned long) apiFunction - apiAddress - 5);
memcpy (&newOpcodes[1], &apiAddress, 4);
// Step #4.
WriteProcessMemory (thisTargetProcess, (void *) apiAddress, newOpcodes, 6, 0);
return apiAddress;
}
unsigned int unHookFunction (const char * dllModule, const char * apiFunction, unsigned char * memoryBackup)
{
HANDLE thisTargetProcess;
HMODULE dllModuleHandle;
unsigned long apiAddress;
unsigned long memoryWritePosition;
thisTargetProcess = GetCurrentProcess ();
dllModuleHandle = GetModuleHandleA (dllModule);
if (!dllModuleHandle)
return 0;
apiAddress = (unsigned long) GetProcAddress (dllModuleHandle, apiFunction);
if (!apiAddress)
return 0;
if (WriteProcessMemory (thisTargetProcess, (void *) apiAddress, memoryBackup, 6, 0))
return 1;
return 0;
}
/* You may want to use a log file instead of a MessageBox due to time-outs, etc. */
SOCKET WSAAPI hooked_socket (int af, int type, int protocol)
{
targetsSocket = socket (af, type, protocol);
MessageBox (NULL, "(Close this quickly)\r\n\r\nThe target's socket was hooked successfully!", "Hooked SOCKET", MB_OK);
return targetsSocket;
}
int WSAAPI hooked_connect (SOCKET s, const struct sockaddr FAR * name, int namelen)
{
MessageBox (NULL, "(Close this quickly)\r\n\r\nThe target just connected to a remote address.", "Target Connected", MB_OK);
return connect (s, name, namelen);
}
I'm writing a ping clone to practice socket programming. I'm able to send ICMP packets, but the destination address is 0.0.0.0 regardless of what I pass to sendto. Here is the most pertinent snip of code:
struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
if (0 == inet_aton(argv[1], &dest.sin_addr)) {
puts("could not parse address\n");
exit(EXIT_FAILURE);
}
ssize_t nbytes = sendto(sock, &hdr, ICMP_MINLEN, /*flags=*/0,
(struct sockaddr*)&dest, sizeof(dest));
The full program is on GitHub. I'm compiling on and for Mac OS X 10.6.8, and I'm using Wireshark 1.8.4 to verify the packets.
I have verified the value of dest.sin_addr.s_addr with a call to printf("%X"), and inet_aton is working as expected. I have even deliberately set it to 0x08080808 (Google's name server), and it still doesn't work.
What might I be doing wrong here?
There's one (copy-paste) bug in your full program on github:
struct sockaddr_in src;
memset(&dest, 0, sizeof(dest));
After declaring src, you memset dest again. This means that dest's sin_addr member is now 0 so you're requesting a destination address of 0.0.0.0.
As an aside, src's sin_port is left uninitialised so you may sometimes get other errors if you end up requesting a local port that is already in use.