swap back socket address when reply by sendmsg
[hband-ld-preload-libs.git] / src / proxy-socket-address-info.c
blobb5e6ffaa33948c85be176e53ea88518a45583435
2 /*
3 What is this library?
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.
28 #include <dlfcn.h>
29 #include <errno.h>
30 #include <err.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <sys/types.h>
37 #include <arpa/inet.h>
40 #ifdef DEBUG
41 #undef DEBUG
42 #define DEBUG(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__)
43 #else
44 #define DEBUG(...) 0
45 #endif
47 typedef int bool_t;
48 #define TRUE 1
49 #define FALSE 0
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")))
62 close(int fd);
63 int __attribute__((visibility("default")))
64 getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
67 void _init(void)
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);
89 abort();
91 return path;
94 bool_t
95 extract_ip_port(struct sockaddr * socket_addr, char * out_ipaddr, uint16_t * out_port)
97 in_port_t * port_p;
98 void * addr_p;
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;
110 else return FALSE;
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;
115 return TRUE;
118 bool_t
119 update_socket_addr(struct sockaddr * socket_addr, char * ipaddr_str, char * portnum_str)
121 int ret;
122 in_port_t * port_p;
123 void * addr_p;
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);
135 else return FALSE;
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;
142 bool_t
143 save_socket_address(struct sockaddr * socket_addr, char * dest_str)
145 uint16_t port;
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);
150 return TRUE;
153 bool_t
154 change_socket_address(struct sockaddr * socket_addr)
156 uint16_t orig_port;
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];
160 ssize_t nbytes;
161 char * portnum_str;
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);
173 return FALSE;
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);
184 bool_t
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];
189 int symlink_ret;
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;
201 void
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);
210 ssize_t
211 recvmsg(int sockfd, struct msghdr *msg, int flags)
213 ssize_t recvmsg_ret;
214 char orig_sock_addr_str[SOCKET_ADDR_MAX_LEN+1];
216 recvmsg_ret = sys_recvmsg(sockfd, msg, flags);
217 if(msg)
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);
223 return recvmsg_ret;
226 ssize_t
227 sendmsg(int sockfd, const struct msghdr *msg, int flags)
229 ssize_t sendmsg_ret;
230 sendmsg_ret = sys_sendmsg(sockfd, msg, flags);
231 if(msg)
233 change_socket_address(msg->msg_name);
235 return sendmsg_ret;