5 This is an LD_PRELOAD-able lib, of which purpose is to fake the source IP addresses and port numbers
6 of the internet traffic received by the target programm.
8 IP and port are changed in the msghdr structure returned by recvmsg(2).
9 That to which IP and port should be changed is determined by symlinks found in the directory
10 pointed by PSAI_SHM_PATH environment, /run/shm/psai by default.
11 Original IP address IP1 and port number PORT1 pair is searched on the path "PSAI_SHM_PATH/IP1,PORT1",
12 then if found, take the symlink target in format IP2,PORT2 and get the fake IP and port from it.
14 This is useful in situations where you put a reverse proxy in front of your network application (server),
15 which proxy terminates the remote client's network connection, thus hiding the real client's IP from the server.
16 Use this lib on the proxy to manage a PSAI (proxy socket address info) table (ie. symlinks in SHM)
17 for the traffic sent to the server;
18 and use it on the server program to make it know the real client IPs from the PSAI table.
20 It's a good idea to point PSAI_SHM_PATH to a memory-backed filesystem to prevent high I/O,
21 but I don't know hard reason against putting it on a network-shared filesystem - this way the
22 proxy and the server probably don't even need to be on the same machine.
24 IP address and port numbers are separated not by the well-established colon ":" char, but by comma ","
25 in order not to confuse IPv6 addresses.
35 #include <sys/socket.h>
36 #include <sys/types.h>
37 #include <arpa/inet.h>
42 #define DEBUG(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
52 static ssize_t (*sys_recvmsg
)(int sockfd
, struct msghdr
*msg
, int flags
);
53 static ssize_t (*sys_sendmsg
)(int sockfd
, const struct msghdr
*msg
, int flags
);
54 static int (*sys_close
)(int fd
);
55 static int (*sys_getpeername
)(int sockfd
, struct sockaddr
*addr
, socklen_t
*addrlen
);
57 ssize_t
__attribute__((visibility("default")))
58 recvmsg(int sockfd
, struct msghdr
*msg
, int flags
);
59 ssize_t
__attribute__((visibility("default")))
60 sendmsg(int sockfd
, const struct msghdr
*msg
, int flags
);
61 int __attribute__((visibility("default")))
63 int __attribute__((visibility("default")))
64 getpeername(int sockfd
, struct sockaddr
*addr
, socklen_t
*addrlen
);
69 sys_recvmsg
= dlsym(RTLD_NEXT
, "recvmsg");
70 sys_sendmsg
= dlsym(RTLD_NEXT
, "sendmsg");
71 sys_close
= dlsym(RTLD_NEXT
, "close");
72 sys_getpeername
= dlsym(RTLD_NEXT
, "getpeername");
75 #define DFLT_SHM_PATH "/run/shm/psai"
76 #define SHM_PATH_MAX_LEN 64
77 #define IPADDR_MAX_LEN INET6_ADDRSTRLEN
78 #define PORTNUM_MAX_LEN 5
79 #define SOCKET_ADDR_MAX_LEN (IPADDR_MAX_LEN + 1 + PORTNUM_MAX_LEN)
80 #define PATH_MAX_LEN (SHM_PATH_MAX_LEN + 1 + SOCKET_ADDR_MAX_LEN)
82 static char * shm_path()
84 char * path
= getenv("PSAI_SHM_PATH");
85 if(path
== NULL
) return DFLT_SHM_PATH
;
86 if(strlen(path
) > SHM_PATH_MAX_LEN
)
88 warnx("PSAI_SHM_PATH length > %u", SHM_PATH_MAX_LEN
);
95 extract_ip_port(struct sockaddr
* socket_addr
, char * out_ipaddr
, uint16_t * out_port
)
100 if(socket_addr
->sa_family
== AF_INET
)
102 port_p
= &((struct sockaddr_in
*)socket_addr
)->sin_port
;
103 addr_p
= &((struct sockaddr_in
*)socket_addr
)->sin_addr
;
105 else if(socket_addr
->sa_family
== AF_INET6
)
107 port_p
= &((struct sockaddr_in6
*)socket_addr
)->sin6_port
;
108 addr_p
= &((struct sockaddr_in6
*)socket_addr
)->sin6_addr
;
112 if(out_port
) *out_port
= ntohs(*port_p
);
113 if(!out_ipaddr
) return TRUE
;
114 if(inet_ntop(socket_addr
->sa_family
, addr_p
, out_ipaddr
, IPADDR_MAX_LEN
) == NULL
) return FALSE
;
119 update_socket_addr(struct sockaddr
* socket_addr
, char * ipaddr_str
, char * portnum_str
)
125 if(socket_addr
->sa_family
== AF_INET
)
127 port_p
= &(((struct sockaddr_in
*)socket_addr
)->sin_port
);
128 addr_p
= &(((struct sockaddr_in
*)socket_addr
)->sin_addr
);
130 else if(socket_addr
->sa_family
== AF_INET6
)
132 port_p
= &(((struct sockaddr_in6
*)socket_addr
)->sin6_port
);
133 addr_p
= &(((struct sockaddr_in6
*)socket_addr
)->sin6_addr
);
137 if(portnum_str
) *port_p
= htons(atoi(portnum_str
));
138 ret
= inet_pton(socket_addr
->sa_family
, ipaddr_str
, addr_p
);
139 return ret
== 1 ? TRUE
: FALSE
;
143 save_socket_address(struct sockaddr
* socket_addr
, char * dest_str
)
146 char ipaddr
[IPADDR_MAX_LEN
+1];
148 if(! extract_ip_port(socket_addr
, (char*)&ipaddr
, &port
)) return FALSE
;
149 snprintf(dest_str
, SOCKET_ADDR_MAX_LEN
+1, "%s,%u", ipaddr
, port
);
154 change_socket_address(struct sockaddr
* socket_addr
)
157 char orig_ip_port
[SOCKET_ADDR_MAX_LEN
+1];
158 char linkname_buf
[PATH_MAX_LEN
+1];
159 char linktarg_buf
[SOCKET_ADDR_MAX_LEN
+1];
163 if(! save_socket_address(socket_addr
, orig_ip_port
)) return FALSE
;
164 snprintf(linkname_buf
, PATH_MAX_LEN
+1, "%s/%s", shm_path(), orig_ip_port
);
165 DEBUG("socket address lookup: %s", linkname_buf
);
167 nbytes
= readlink(linkname_buf
, linktarg_buf
, SOCKET_ADDR_MAX_LEN
+1);
168 if(nbytes
<= 0) return FALSE
;
169 if(nbytes
> SOCKET_ADDR_MAX_LEN
)
171 linktarg_buf
[SOCKET_ADDR_MAX_LEN
] = '\0';
172 DEBUG("symlink target too long: %s -> %s...", linkname_buf
, linktarg_buf
);
175 linktarg_buf
[nbytes
] = '\0';
176 DEBUG("socket address mapping: %s -> %s", linkname_buf
, linktarg_buf
);
178 portnum_str
= strrchr(linktarg_buf
, ',');
179 if(portnum_str
) { portnum_str
[0] = '\0'; portnum_str
++; }
181 return update_socket_addr(socket_addr
, linktarg_buf
, portnum_str
);
185 set_proxy_address_info(struct sockaddr
* socket_addr
, char * target_str
)
187 char sock_addr_str
[SOCKET_ADDR_MAX_LEN
+1];
188 char linkname_buf
[PATH_MAX_LEN
+1];
191 if(! save_socket_address(socket_addr
, sock_addr_str
)) return FALSE
;
192 snprintf(linkname_buf
, PATH_MAX_LEN
+1, "%s/%s", shm_path(), sock_addr_str
);
194 unlink(linkname_buf
);
195 symlink_ret
= symlink(target_str
, linkname_buf
);
196 if(symlink_ret
== -1) warn("symlink: %s", linkname_buf
);
198 return symlink_ret
== 0 ? TRUE
: FALSE
;
202 unset_proxy_address_info(char * ip_port
)
204 char linkname_buf
[PATH_MAX_LEN
+1];
205 snprintf(linkname_buf
, PATH_MAX_LEN
+1, "%s/%s", shm_path(), ip_port
);
206 unlink(linkname_buf
);
211 recvmsg(int sockfd
, struct msghdr
*msg
, int flags
)
214 char orig_sock_addr_str
[SOCKET_ADDR_MAX_LEN
+1];
216 recvmsg_ret
= sys_recvmsg(sockfd
, msg
, flags
);
219 save_socket_address(msg
->msg_name
, orig_sock_addr_str
);
220 change_socket_address(msg
->msg_name
);
221 set_proxy_address_info(msg
->msg_name
, orig_sock_addr_str
);
227 sendmsg(int sockfd
, const struct msghdr
*msg
, int flags
)
230 sendmsg_ret
= sys_sendmsg(sockfd
, msg
, flags
);
233 change_socket_address(msg
->msg_name
);