├── .gitignore ├── log.h ├── README.md ├── Makefile ├── udpmask.h ├── transform.c ├── log.c ├── transform.h ├── tests └── test_transform.c └── udpmask.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | udpmask 3 | tests/test_transform 4 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | #ifndef _incl_LOG_H 2 | #define _incl_LOG_H 3 | 4 | #include 5 | 6 | extern int use_syslog; 7 | 8 | void startlog(const char *ident); 9 | void mylog(int priority, const char *message, ...); 10 | void endlog(void); 11 | 12 | #define log_err(msg...) mylog(LOG_ERR, msg) 13 | #define log_warn(msg...) mylog(LOG_WARNING, msg) 14 | #define log_info(msg...) mylog(LOG_INFO, msg) 15 | #define log_debug(msg...) mylog(LOG_DEBUG, msg) 16 | 17 | #endif /* _incl_LOG_H */ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # udpmask 2 | 3 | Minimal UDP packet obfuscation tool for bypassing deep packet inspection. 4 | No external dependencies (including pthread), can be compiled to run on 5 | embedded devices. 6 | 7 | ## Usage 8 | 9 | Server side 10 | 11 | udpmask -m server -c host-to-transfer-traffic-to -o port-to-transfer-traffic-to -p 51194 12 | 13 | Client side 14 | 15 | udpmask -m client -c udpmask-server-host -o 51194 -p 61194 16 | 17 | On client side, configure the software you wants to obfuscate traffic for to 18 | connect to localhost:61194. 19 | 20 | ## Use case 21 | 22 | * Obfuscate OpenVPN UDP traffic 23 | * Obfuscate WireGuard traffic 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS := $(CFLAGS) -std=gnu99 -O2 -Wall 3 | OBJS = udpmask.o log.o transform.o 4 | TESTS = tests/test_transform 5 | EXEC = udpmask 6 | PREFIX = /usr/local 7 | 8 | all: $(EXEC) 9 | 10 | $(EXEC): $(OBJS) 11 | $(CC) $(CFLAGS) -o $@ $^ 12 | 13 | %.o: %.c 14 | $(CC) $(CFLAGS) -o $@ -c $< 15 | 16 | tests/test_%: tests/test_%.c %.o log.o 17 | $(CC) $(CFLAGS) -I. -o $@ $^ 18 | 19 | test: $(TESTS) 20 | $(foreach test_cmd,$(TESTS),$(test_cmd)) 21 | 22 | install: $(EXEC) 23 | install -d $(DESTDIR)$(PREFIX)/bin 24 | install -m 0755 $(EXEC) $(DESTDIR)$(PREFIX)/bin 25 | 26 | clean: 27 | rm -f $(EXEC) $(TESTS) *.o 28 | 29 | .PHONY: all install clean test 30 | -------------------------------------------------------------------------------- /udpmask.h: -------------------------------------------------------------------------------- 1 | #ifndef _incl_UDPMASK_H 2 | #define _incl_UDPMASK_H 3 | 4 | #include 5 | #include 6 | 7 | #define UM_SERVER_PORT 51194 8 | #define UM_CLIENT_PORT 61194 9 | #define UM_MAX_CLIENT 16 10 | #define UM_BUFFER 65507 11 | #define UM_TIMEOUT 300 // socket clean up timeout 12 | #define UM_HOST_TIMEOUT 60 // dns lookup cache timeout 13 | 14 | #define TIME_INVALID (time_t) -1 15 | 16 | #define ARRAY_SIZE(a) (int) (sizeof(a) / sizeof(a[0])) 17 | #define NEW_SOCK() socket(AF_INET, SOCK_DGRAM, 0) 18 | 19 | enum um_mode { 20 | UM_MODE_NONE = -1, 21 | UM_MODE_SERVER, 22 | UM_MODE_CLIENT, 23 | UM_MODE_PASSTHROU 24 | }; 25 | 26 | struct um_sockmap { 27 | int in_use; 28 | int sock; 29 | time_t last_use; 30 | struct sockaddr_in from; 31 | }; 32 | 33 | #endif /* _incl_UDPMASK_H */ 34 | -------------------------------------------------------------------------------- /transform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "log.h" 9 | #include "transform.h" 10 | 11 | void check_gen_mask(struct um_transform *ctx) 12 | { 13 | log_debug("mask used %d times", ctx->mask_ct); 14 | 15 | if (ctx->mask_ct++ < MASK_MAXCT) { 16 | return; 17 | } 18 | 19 | log_debug("mask used more than %u times ago, update", MASK_MAXCT); 20 | 21 | genmask(ctx->mask, MASK_LEN); 22 | ctx->mask_ct = 0; 23 | } 24 | 25 | size_t maskbuf(struct um_transform *ctx, unsigned char *buf, size_t buflen) { 26 | check_gen_mask(ctx); 27 | 28 | transform(buf, buflen, ctx->mask); 29 | memcpy(buf + buflen, ctx->mask, MASK_LEN); 30 | 31 | return buflen + MASK_LEN; 32 | } 33 | 34 | size_t unmaskbuf(struct um_transform *ctx, unsigned char *buf, size_t buflen) { 35 | unsigned char rcv_mask[MASK_LEN]; 36 | size_t len = buflen - MASK_LEN; 37 | 38 | memcpy(rcv_mask, buf + len, MASK_LEN); 39 | transform(buf, len, rcv_mask); 40 | 41 | return len; 42 | } 43 | 44 | size_t masknoop(struct um_transform * ctx, unsigned char *buf, size_t buflen) { 45 | return buflen; 46 | } 47 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "log.h" 9 | 10 | const static char *logname; 11 | static int loglevel = LOG_INFO; 12 | 13 | int use_syslog = 0; 14 | 15 | void startlog(const char *ident) 16 | { 17 | if (use_syslog) { 18 | openlog(ident, LOG_PID, LOG_USER); 19 | setlogmask(LOG_UPTO(loglevel)); 20 | } else { 21 | logname = ident; 22 | } 23 | } 24 | 25 | void mylog(int priority, const char *message, ...) 26 | { 27 | va_list ap; 28 | 29 | if (use_syslog) { 30 | va_start(ap, message); 31 | vsyslog(priority, message, ap); 32 | va_end(ap); 33 | } else { 34 | if (priority > loglevel) { 35 | return; 36 | } 37 | 38 | time_t t = time(NULL); 39 | char tmp[256]; 40 | memset((void *) tmp, 0, sizeof(tmp)); 41 | 42 | char *loglevel; 43 | switch (priority) { 44 | case LOG_ERR: 45 | loglevel = "ERROR"; 46 | break; 47 | 48 | case LOG_WARNING: 49 | loglevel = "WARNING"; 50 | break; 51 | 52 | case LOG_INFO: 53 | loglevel = "INFO"; 54 | break; 55 | 56 | case LOG_DEBUG: 57 | default: 58 | loglevel = "DEBUG"; 59 | break; 60 | } 61 | 62 | sprintf(tmp, "[%lu] %s[%d]: %s: ", t, logname, getpid(), loglevel); 63 | 64 | char out[strlen(tmp) + strlen(message) + 1]; 65 | strcpy(out, tmp); 66 | strcat(out, message); 67 | strcat(out, "\n"); 68 | 69 | va_start(ap, message); 70 | vfprintf(stderr, out, ap); 71 | va_end(ap); 72 | } 73 | } 74 | 75 | void endlog(void) 76 | { 77 | if (use_syslog) { 78 | closelog(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /transform.h: -------------------------------------------------------------------------------- 1 | #ifndef _incl_TRANSFORM_H 2 | #define _incl_TRANSFORM_H 3 | 4 | #include 5 | #include 6 | 7 | #define MASK_UNIT uint32_t 8 | #define MASK_LEN ((int) sizeof(MASK_UNIT)) 9 | #define MASK_MAXCT ((unsigned int) 100000) 10 | 11 | struct um_transform { 12 | unsigned char mask[MASK_LEN]; 13 | unsigned int mask_ct; 14 | }; 15 | 16 | #define transform(buf, buflen, m) \ 17 | do { \ 18 | size_t c = buflen / MASK_LEN; \ 19 | for (size_t i = 0; i < c; i++) { \ 20 | ((MASK_UNIT *) buf)[i] ^= *((MASK_UNIT *) m); \ 21 | } \ 22 | for (size_t i = c * MASK_LEN; i < buflen; i++) { \ 23 | buf[i] ^= m[i % MASK_LEN]; \ 24 | } \ 25 | } while (0) \ 26 | 27 | 28 | #define genmask(mask, n) \ 29 | do { \ 30 | for (size_t i = 0; i < n; i++) { \ 31 | for (int j = 0; j < 10; j++) { \ 32 | mask[i] = (unsigned char) (rand() % 256); \ 33 | if (mask[i] != 0) break; \ 34 | } \ 35 | } \ 36 | } while (0) \ 37 | 38 | void check_gen_mask(struct um_transform *); 39 | size_t maskbuf(struct um_transform *, unsigned char *, size_t); 40 | size_t unmaskbuf(struct um_transform *, unsigned char *, size_t); 41 | size_t masknoop(struct um_transform *, unsigned char *, size_t); 42 | 43 | typedef size_t (*buf_func)(struct um_transform *, unsigned char *, size_t); 44 | 45 | #endif /* _incl_TRANSFORM_H */ 46 | -------------------------------------------------------------------------------- /tests/test_transform.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "transform.h" 6 | #include "udpmask.h" 7 | 8 | void print_mask(struct um_transform *ctx) { 9 | printf("mask:"); 10 | for (int i = 0; i < MASK_LEN; i++) { 11 | printf(" 0x%02X", ctx->mask[i]); 12 | } 13 | printf("\n"); 14 | } 15 | 16 | int main(void) 17 | { 18 | printf("MASK_LEN: %d\n", MASK_LEN); 19 | 20 | struct um_transform tran; 21 | memset(&tran, 0, sizeof(tran)); 22 | 23 | int iter = 10000; 24 | struct timeval t_start, t_end; 25 | double t_diff; 26 | 27 | gettimeofday(&t_start, NULL); 28 | for (int i = 0; i < iter; i++) { 29 | check_gen_mask(&tran); 30 | tran.mask_ct = MASK_MAXCT; 31 | } 32 | gettimeofday(&t_end, NULL); 33 | t_diff = (double) ((t_end.tv_sec - t_start.tv_sec) * 1e6 + (t_end.tv_usec - t_start.tv_usec)); 34 | printf("Time for check_gen_mask, %d iterations: %f us\n", iter, t_diff); 35 | printf("Time for check_gen_mask, 1 iterations: %f us\n", t_diff / iter); 36 | 37 | check_gen_mask(&tran); 38 | print_mask(&tran); 39 | 40 | unsigned char buf[1024] = { 41 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 42 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 43 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 44 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 45 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 46 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 47 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 48 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 49 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 50 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 51 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 52 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 53 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 54 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 55 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 56 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 57 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 58 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 59 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 60 | 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 61 | }; 62 | size_t buflen = strlen((const char *) buf); 63 | 64 | printf("buf:"); 65 | for (size_t i = 0; i < buflen; i++) { 66 | printf(" 0x%02X", buf[i]); 67 | } 68 | printf("\n"); 69 | 70 | gettimeofday(&t_start, NULL); 71 | for (int i = 0; i < iter; i++) { 72 | maskbuf(&tran, buf, buflen); 73 | } 74 | gettimeofday(&t_end, NULL); 75 | t_diff = (double) ((t_end.tv_sec - t_start.tv_sec) * 1e6 + (t_end.tv_usec - t_start.tv_usec)); 76 | printf("Time for maskbuf, %d iterations: %f us\n", iter, t_diff); 77 | printf("Time for maskbuf, 1 iterations: %f us\n", t_diff / iter); 78 | 79 | buflen = maskbuf(&tran, buf, buflen); 80 | 81 | printf("maskbuf:"); 82 | for (size_t i = 0; i < buflen; i++) { 83 | printf(" 0x%02X", buf[i]); 84 | } 85 | printf("\n"); 86 | 87 | gettimeofday(&t_start, NULL); 88 | for (int i = 0; i < iter; i++) { 89 | unmaskbuf(&tran, buf, buflen); 90 | } 91 | gettimeofday(&t_end, NULL); 92 | t_diff = (double) ((t_end.tv_sec - t_start.tv_sec) * 1e6 + (t_end.tv_usec - t_start.tv_usec)); 93 | printf("Time for unmaskbuf, %d iterations: %f us\n", iter, t_diff); 94 | printf("Time for unmaskbuf, 1 iterations: %f us\n", t_diff / iter); 95 | 96 | buflen = unmaskbuf(&tran, buf, buflen); 97 | 98 | printf("buf:"); 99 | for (size_t i = 0; i < buflen; i++) { 100 | printf(" 0x%02X", buf[i]); 101 | } 102 | printf("\n"); 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /udpmask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "log.h" 18 | #include "transform.h" 19 | #include "udpmask.h" 20 | 21 | static int bind_sock = -1; 22 | 23 | static char host_conn[256]; 24 | static uint16_t port_conn = 0; 25 | 26 | static int timeout = UM_TIMEOUT; 27 | static struct um_sockmap map[UM_MAX_CLIENT]; 28 | 29 | static volatile sig_atomic_t signal_term = 0; 30 | 31 | static int usage(void) 32 | { 33 | const char ubuf[] = 34 | "Usage: udpmask -m mode\n" 35 | " -c remote -o remote_port\n" 36 | " [-l listen] [-p listen_port]\n" 37 | " [-t timeout] [-d] [-P pidfile]\n" 38 | " [-h]\n"; 39 | fprintf(stderr, ubuf); 40 | return 1; 41 | } 42 | 43 | ///////////////////////////////////////////////////////////////////// 44 | // sock_fd_max 45 | ///////////////////////////////////////////////////////////////////// 46 | 47 | static int sock_fd_max = -1; 48 | 49 | static inline void update_sock_fd_max(void) 50 | { 51 | sock_fd_max = bind_sock; 52 | for (int i = 0; i < ARRAY_SIZE(map); i++) { 53 | if (map[i].in_use && map[i].sock > sock_fd_max) { 54 | sock_fd_max = map[i].sock; 55 | } 56 | } 57 | } 58 | 59 | #define UPDATE_SOCK_FD_MAX_ADD(sock) \ 60 | do { \ 61 | if (sock > sock_fd_max) { \ 62 | sock_fd_max = sock; \ 63 | } \ 64 | } while (0) \ 65 | 66 | #define UPDATE_SOCK_FD_MAX_RM(sock) \ 67 | do { \ 68 | if (sock >= sock_fd_max) { \ 69 | update_sock_fd_max(); \ 70 | } \ 71 | } while (0) \ 72 | 73 | ///////////////////////////////////////////////////////////////////// 74 | 75 | ///////////////////////////////////////////////////////////////////// 76 | // um_sockmap 77 | ///////////////////////////////////////////////////////////////////// 78 | 79 | #define UPDATE_LAST_USE(idx, time_val) \ 80 | do { \ 81 | if (time_val != TIME_INVALID) { \ 82 | map[idx].last_use = time_val; \ 83 | } \ 84 | } while (0) \ 85 | 86 | static inline int um_sockmap_ins(int sock, struct sockaddr_in *addr) 87 | { 88 | int i = 0; 89 | 90 | for (i = 0; i < ARRAY_SIZE(map); i++) { 91 | if (!map[i].in_use) { 92 | map[i].in_use = 1; 93 | map[i].sock = sock; 94 | map[i].last_use = TIME_INVALID; 95 | map[i].from = *addr; 96 | break; 97 | } 98 | } 99 | 100 | if (i >= ARRAY_SIZE(map)) { 101 | return -1; 102 | } 103 | 104 | return i; 105 | } 106 | 107 | static inline int um_sockmap_clean(fd_set *active_set, time_t time_val) 108 | { 109 | int purged = 0; 110 | 111 | if (timeout <= 0) { 112 | return purged; 113 | } 114 | 115 | for (int i = 0; i < ARRAY_SIZE(map); i++) { 116 | if (map[i].in_use && (map[i].last_use == TIME_INVALID || 117 | time_val - map[i].last_use >= timeout)) { 118 | map[i].in_use = 0; 119 | close(map[i].sock); 120 | FD_CLR(map[i].sock, active_set); 121 | 122 | UPDATE_SOCK_FD_MAX_RM(map[i].sock); 123 | 124 | log_info("Purged connection from [%s:%hu]", 125 | inet_ntoa(map[i].from.sin_addr), 126 | ntohs(map[i].from.sin_port)); 127 | 128 | if (!purged) { 129 | purged = 1; 130 | } 131 | } 132 | } 133 | 134 | return purged; 135 | } 136 | 137 | ///////////////////////////////////////////////////////////////////// 138 | 139 | static inline int sockaddr_in_cmp(const struct sockaddr_in *a, 140 | const struct sockaddr_in *b) 141 | { 142 | int af_cmp = a->sin_family - b->sin_family; 143 | if (af_cmp != 0) { 144 | return af_cmp; 145 | } 146 | 147 | int port_cmp = a->sin_port - b->sin_port; 148 | if (port_cmp != 0) { 149 | return port_cmp; 150 | } 151 | 152 | long in_addr_cmp = a->sin_addr.s_addr - b->sin_addr.s_addr; 153 | if (in_addr_cmp != 0) { 154 | return (int) in_addr_cmp; 155 | } 156 | 157 | return 0; 158 | } 159 | 160 | static void sighanlder(int signum) 161 | { 162 | if (signum == SIGHUP || signum == SIGINT || signum == SIGTERM) { 163 | signal_term = 1; 164 | } 165 | } 166 | 167 | // Main loop 168 | int start(enum um_mode mode) 169 | { 170 | struct um_transform tran; 171 | 172 | memset(&tran, 0, sizeof(tran)); 173 | genmask(tran.mask, MASK_LEN); 174 | 175 | ssize_t ret; 176 | int select_ret; 177 | int sock_idx; 178 | int tmp_sock = -1; 179 | 180 | struct hostent *rh; 181 | struct in_addr conn_addr_in = { 182 | .s_addr = 0, 183 | }; 184 | struct sockaddr_in conn_addr = { 185 | .sin_family = AF_INET, 186 | .sin_port = htons(port_conn), 187 | .sin_addr = { 188 | .s_addr = 0, 189 | }, 190 | }; 191 | time_t time_conn_addr = 0; 192 | 193 | struct sockaddr_in recv_addr; 194 | socklen_t recv_addr_len = sizeof(recv_addr); 195 | 196 | unsigned char buf[UM_BUFFER]; 197 | size_t buflen; 198 | 199 | buf_func snd_buf_func; 200 | buf_func rcv_buf_func; 201 | 202 | switch (mode) { 203 | case UM_MODE_SERVER: 204 | snd_buf_func = &unmaskbuf; 205 | rcv_buf_func = &maskbuf; 206 | break; 207 | case UM_MODE_CLIENT: 208 | snd_buf_func = &maskbuf; 209 | rcv_buf_func = &unmaskbuf; 210 | break; 211 | case UM_MODE_PASSTHROU: 212 | snd_buf_func = &masknoop; 213 | rcv_buf_func = &masknoop; 214 | break; 215 | default: 216 | log_err("Unknown mode"); 217 | return 1; 218 | } 219 | 220 | fd_set active_fd_set, read_fd_set; 221 | FD_ZERO(&active_fd_set); 222 | FD_SET(bind_sock, &active_fd_set); 223 | 224 | log_info("Connection timeout %ds", timeout); 225 | 226 | int clean_up_trigged; 227 | time_t time_val; 228 | 229 | update_sock_fd_max(); 230 | 231 | while (!signal_term) { 232 | read_fd_set = active_fd_set; 233 | 234 | sock_idx = -1; 235 | clean_up_trigged = 0; 236 | 237 | select_ret = select(sock_fd_max + 1, &read_fd_set, NULL, NULL, NULL); 238 | if (select_ret <= 0) { 239 | log_debug("select() returns %d", select_ret); 240 | continue; 241 | } 242 | 243 | time_val = time(NULL); 244 | 245 | if (FD_ISSET(bind_sock, &read_fd_set)) { 246 | // Deal with packets from "listening" socket 247 | ret = recvfrom(bind_sock, (void *) buf, UM_BUFFER, 0, 248 | (struct sockaddr *) &recv_addr, &recv_addr_len); 249 | 250 | if (ret > 0) { 251 | buflen = (size_t) ret; 252 | 253 | // Try to locate existing connection from map 254 | for (int i = 0; i < ARRAY_SIZE(map); i++) { 255 | if (map[i].in_use && 256 | sockaddr_in_cmp(&recv_addr, &(map[i].from)) == 0) { 257 | sock_idx = i; 258 | break; 259 | } 260 | } 261 | 262 | if (sock_idx < 0) { 263 | log_info("New connection from [%s:%hu]", 264 | inet_ntoa(recv_addr.sin_addr), 265 | ntohs(recv_addr.sin_port)); 266 | 267 | tmp_sock = NEW_SOCK(); 268 | if (tmp_sock < 0) { 269 | log_err("socket(): %s", strerror(errno)); 270 | } else { 271 | um_sockmap_clean(&active_fd_set, time_val); 272 | clean_up_trigged = 1; 273 | 274 | sock_idx = um_sockmap_ins(tmp_sock, &recv_addr); 275 | if (sock_idx >= 0) { 276 | // Inserted newly created socket into sockmap 277 | FD_SET(tmp_sock, &active_fd_set); 278 | UPDATE_SOCK_FD_MAX_ADD(tmp_sock); 279 | } else { 280 | // Failed to insert newly created socket into sockmap 281 | log_warn("Max clients reached. " 282 | "Dropping new connection [%s:%hu]", 283 | inet_ntoa(recv_addr.sin_addr), 284 | ntohs(recv_addr.sin_port)); 285 | } 286 | } 287 | } 288 | 289 | // Check sock_idx again to deal with new connection 290 | if (sock_idx >= 0) { 291 | if (time_val - time_conn_addr >= UM_HOST_TIMEOUT || 292 | conn_addr.sin_addr.s_addr == 0) { 293 | rh = gethostbyname2(host_conn, AF_INET); 294 | if (!rh) { 295 | herror("gethostbyname2()"); 296 | } else { 297 | memcpy(&conn_addr_in, rh->h_addr_list[0], 298 | rh->h_length); 299 | conn_addr.sin_addr = conn_addr_in; 300 | time_conn_addr = time_val; 301 | } 302 | } 303 | 304 | buflen = (*snd_buf_func)(&tran, buf, buflen); 305 | sendto(map[sock_idx].sock, (void *) buf, buflen, 0, 306 | (struct sockaddr *) &conn_addr, 307 | sizeof(conn_addr)); 308 | UPDATE_LAST_USE(sock_idx, time_val); 309 | } 310 | } 311 | } 312 | 313 | for (int i = 0; i < ARRAY_SIZE(map); i++) { 314 | if (map[i].in_use && FD_ISSET(map[i].sock, &read_fd_set)) { 315 | ret = recv(map[i].sock, (void *) buf, UM_BUFFER, 0); 316 | 317 | if (ret > 0) { 318 | UPDATE_LAST_USE(i, time_val); 319 | 320 | buflen = (size_t) ret; 321 | 322 | buflen = (*rcv_buf_func)(&tran, buf, buflen); 323 | sendto(bind_sock, (void *) buf, buflen, 0, 324 | (struct sockaddr *) &map[i].from, 325 | sizeof(map[i].from)); 326 | } 327 | } 328 | } 329 | 330 | if (!clean_up_trigged) { 331 | um_sockmap_clean(&active_fd_set, time_val); 332 | } 333 | } 334 | 335 | // Clean up 336 | for (int i = 0; i < ARRAY_SIZE(map); i++) { 337 | if (map[i].in_use) { 338 | map[i].in_use = 0; 339 | close(map[i].sock); 340 | } 341 | } 342 | 343 | return 0; 344 | } 345 | 346 | int main(int argc, char **argv) 347 | { 348 | srand(time(0)); 349 | 350 | int ret = 0; 351 | 352 | memset((void *) host_conn, '\0', sizeof(host_conn)); 353 | memset((void *) map, 0, sizeof(map)); 354 | 355 | enum um_mode mode = UM_MODE_NONE; 356 | struct in_addr addr = { .s_addr = INADDR_ANY }; 357 | uint16_t port = 0; 358 | 359 | struct sockaddr_in bind_addr; 360 | memset((void *) &bind_addr, 0, sizeof(bind_addr)); 361 | 362 | const char *pidfile = 0; 363 | int show_usage = 0, daemonize = 0; 364 | int c; 365 | int r; 366 | 367 | while ((c = getopt(argc, argv, "m:p:l:s:c:o:t:dP:L:h")) != -1) { 368 | switch (c) { 369 | case 'm': 370 | if (strcmp(optarg, "server") == 0) { 371 | mode = UM_MODE_SERVER; 372 | } else if (strcmp(optarg, "client") == 0) { 373 | mode = UM_MODE_CLIENT; 374 | } else if (strcmp(optarg, "passthrough") == 0) { 375 | mode = UM_MODE_PASSTHROU; 376 | } else { 377 | show_usage = 1; 378 | } 379 | break; 380 | 381 | case 'l': 382 | r = inet_pton(AF_INET, optarg, (void *) &addr); 383 | if (r == 0) { 384 | show_usage = 1; 385 | } else if (r < 0) { 386 | perror("inet_pton()"); 387 | ret = 1; 388 | goto exit; 389 | } 390 | break; 391 | 392 | case 'p': 393 | port = (uint16_t) atoi(optarg); 394 | break; 395 | 396 | case 'c': 397 | strncpy(host_conn, optarg, strlen(optarg)); 398 | break; 399 | 400 | case 'o': 401 | port_conn = (uint16_t) atoi(optarg); 402 | break; 403 | 404 | case 't': 405 | r = atoi(optarg); 406 | if (r >= 0) { 407 | timeout = r; 408 | } 409 | break; 410 | 411 | case 'd': 412 | daemonize = 1; 413 | break; 414 | 415 | case 'P': 416 | pidfile = optarg; 417 | break; 418 | 419 | case 'h': 420 | case '?': 421 | default: 422 | show_usage = 1; 423 | break; 424 | } 425 | } 426 | 427 | if (port_conn == 0 || strlen(host_conn) == 0) { 428 | show_usage = 1; 429 | } 430 | 431 | bind_sock = NEW_SOCK(); 432 | if (bind_sock < 0) { 433 | perror("socket()"); 434 | ret = 1; 435 | goto exit; 436 | } 437 | 438 | switch (mode) { 439 | case UM_MODE_SERVER: 440 | if (port == 0) { 441 | port = UM_SERVER_PORT; 442 | } 443 | break; 444 | 445 | case UM_MODE_CLIENT: 446 | if (port == 0) { 447 | port = UM_CLIENT_PORT; 448 | } 449 | break; 450 | 451 | case UM_MODE_PASSTHROU: 452 | if (port == 0) { 453 | port = UM_SERVER_PORT; 454 | } 455 | break; 456 | 457 | default: 458 | show_usage = 1; 459 | break; 460 | } 461 | 462 | if (show_usage) { 463 | ret = usage(); 464 | goto exit; 465 | } 466 | 467 | signal(SIGHUP, sighanlder); 468 | signal(SIGINT, sighanlder); 469 | signal(SIGTERM, sighanlder); 470 | 471 | if (daemonize) { 472 | use_syslog = 1; 473 | 474 | pid_t pid, sid; 475 | pid = fork(); 476 | if (pid < 0) { 477 | perror("fork()"); 478 | ret = 1; 479 | goto exit; 480 | } else if (pid > 0) { 481 | goto exit; 482 | } 483 | 484 | umask(022); 485 | 486 | sid = setsid(); 487 | if (sid < 0) { 488 | perror("setsid()"); 489 | ret = 1; 490 | goto exit; 491 | } 492 | 493 | if (chdir("/") < 0) { 494 | perror("chdir()"); 495 | ret = 1; 496 | goto exit; 497 | } 498 | 499 | close(STDIN_FILENO); 500 | close(STDOUT_FILENO); 501 | close(STDERR_FILENO); 502 | 503 | if (!pidfile) { 504 | pidfile = "/var/run/udpmask.pid"; 505 | } 506 | 507 | FILE *fp = fopen(pidfile, "w"); 508 | if (fp) { 509 | fprintf(fp, "%i\n", getpid()); 510 | fclose(fp); 511 | } 512 | } 513 | 514 | startlog(basename(argv[0])); 515 | 516 | bind_addr.sin_family = AF_INET; 517 | bind_addr.sin_addr = addr; 518 | bind_addr.sin_port = htons(port); 519 | 520 | log_info("Bind to [%s:%hu]", inet_ntoa(addr), port); 521 | 522 | r = bind(bind_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)); 523 | if (r != 0) { 524 | log_err("bind(): %s", strerror(errno)); 525 | ret = 1; 526 | goto exit; 527 | } 528 | 529 | log_info("Remote address [%s:%hu]", host_conn, port_conn); 530 | 531 | ret = start(mode); 532 | 533 | exit: 534 | close(bind_sock); 535 | endlog(); 536 | return ret; 537 | } 538 | --------------------------------------------------------------------------------