|
@@ -0,0 +1,449 @@
|
|
1
|
+From 709646d59d96cb73a7e70347f37de9823e4e5f14 Mon Sep 17 00:00:00 2001
|
|
2
|
+From: Leonid Evdokimov <leon@darkk.net.ru>
|
|
3
|
+Date: Fri, 13 Apr 2012 01:57:23 +0400
|
|
4
|
+Subject: [PATCH 03/12] Initial support for UDP + TPROXY redirection. No more
|
|
5
|
+ dest_ip in redudp.
|
|
6
|
+
|
|
7
|
+ * TPROXY requires Linux 2.6.29+ (see man 7 ip[1]).
|
|
8
|
+ * all redsocks code is running as root to bind to arbitrary port.
|
|
9
|
+ * Non-Linux and old-Linux builds are broken at the moment.
|
|
10
|
+
|
|
11
|
+[1] http://www.kernel.org/doc/man-pages/online/pages/man7/ip.7.html
|
|
12
|
+---
|
|
13
|
+ dnstc.c | 2 +-
|
|
14
|
+ redudp.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
|
|
15
|
+ redudp.h | 2 +
|
|
16
|
+ utils.c | 43 +++++++++++++-
|
|
17
|
+ utils.h | 2 +-
|
|
18
|
+ 5 files changed, 227 insertions(+), 19 deletions(-)
|
|
19
|
+
|
|
20
|
+diff --git a/dnstc.c b/dnstc.c
|
|
21
|
+index 43881d8..5f9fedd 100644
|
|
22
|
+--- a/dnstc.c
|
|
23
|
++++ b/dnstc.c
|
|
24
|
+@@ -68,7 +68,7 @@ static void dnstc_pkt_from_client(int fd, short what, void *_arg)
|
|
25
|
+ ssize_t pktlen, outgoing;
|
|
26
|
+
|
|
27
|
+ assert(fd == EVENT_FD(&self->listener));
|
|
28
|
+- pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr);
|
|
29
|
++ pktlen = red_recv_udp_pkt(fd, buf.raw, sizeof(buf), &clientaddr, NULL);
|
|
30
|
+ if (pktlen == -1)
|
|
31
|
+ return;
|
|
32
|
+
|
|
33
|
+diff --git a/redudp.c b/redudp.c
|
|
34
|
+index 9516a50..262af3e 100644
|
|
35
|
+--- a/redudp.c
|
|
36
|
++++ b/redudp.c
|
|
37
|
+@@ -15,6 +15,7 @@
|
|
38
|
+ */
|
|
39
|
+
|
|
40
|
+ #include <stdlib.h>
|
|
41
|
++#include <search.h>
|
|
42
|
+ #include <string.h>
|
|
43
|
+ #include <sys/types.h>
|
|
44
|
+ #include <sys/uio.h>
|
|
45
|
+@@ -33,30 +34,157 @@
|
|
46
|
+ #include "redudp.h"
|
|
47
|
+
|
|
48
|
+ #define redudp_log_error(client, prio, msg...) \
|
|
49
|
+- redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg)
|
|
50
|
++ redsocks_log_write_plain(__FILE__, __LINE__, __func__, 0, &(client)->clientaddr, get_destaddr(client), prio, ## msg)
|
|
51
|
+ #define redudp_log_errno(client, prio, msg...) \
|
|
52
|
+- redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, &(client)->instance->config.destaddr, prio, ## msg)
|
|
53
|
++ redsocks_log_write_plain(__FILE__, __LINE__, __func__, 1, &(client)->clientaddr, get_destaddr(client), prio, ## msg)
|
|
54
|
+
|
|
55
|
+ static void redudp_pkt_from_socks(int fd, short what, void *_arg);
|
|
56
|
+ static void redudp_drop_client(redudp_client *client);
|
|
57
|
+ static void redudp_fini_instance(redudp_instance *instance);
|
|
58
|
+ static int redudp_fini();
|
|
59
|
++static int redudp_transparent(int fd);
|
|
60
|
+
|
|
61
|
+ typedef struct redudp_expected_assoc_reply_t {
|
|
62
|
+ socks5_reply h;
|
|
63
|
+ socks5_addr_ipv4 ip;
|
|
64
|
+ } PACKED redudp_expected_assoc_reply;
|
|
65
|
+
|
|
66
|
++struct bound_udp4_key {
|
|
67
|
++ struct in_addr sin_addr;
|
|
68
|
++ uint16_t sin_port;
|
|
69
|
++};
|
|
70
|
++
|
|
71
|
++struct bound_udp4 {
|
|
72
|
++ struct bound_udp4_key key;
|
|
73
|
++ int ref;
|
|
74
|
++ int fd;
|
|
75
|
++};
|
|
76
|
++
|
|
77
|
+ /***********************************************************************
|
|
78
|
+ * Helpers
|
|
79
|
+ */
|
|
80
|
++// TODO: separate binding to privileged process (this operation requires uid-0)
|
|
81
|
++static void* root_bound_udp4 = NULL; // to avoid two binds to same IP:port
|
|
82
|
++
|
|
83
|
++static int bound_udp4_cmp(const void *a, const void *b)
|
|
84
|
++{
|
|
85
|
++ return memcmp(a, b, sizeof(struct bound_udp4_key));
|
|
86
|
++}
|
|
87
|
++
|
|
88
|
++static void bound_udp4_mkkey(struct bound_udp4_key *key, const struct sockaddr_in *addr)
|
|
89
|
++{
|
|
90
|
++ memset(key, 0, sizeof(*key));
|
|
91
|
++ key->sin_addr = addr->sin_addr;
|
|
92
|
++ key->sin_port = addr->sin_port;
|
|
93
|
++}
|
|
94
|
++
|
|
95
|
++static int bound_udp4_get(const struct sockaddr_in *addr)
|
|
96
|
++{
|
|
97
|
++ struct bound_udp4_key key;
|
|
98
|
++ struct bound_udp4 *node, **pnode;
|
|
99
|
++
|
|
100
|
++ bound_udp4_mkkey(&key, addr);
|
|
101
|
++ // I assume, that memory allocation for lookup is awful, so I use
|
|
102
|
++ // tfind/tsearch pair instead of tsearch/check-result.
|
|
103
|
++ pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp);
|
|
104
|
++ if (pnode) {
|
|
105
|
++ assert((*pnode)->ref > 0);
|
|
106
|
++ (*pnode)->ref++;
|
|
107
|
++ return (*pnode)->fd;
|
|
108
|
++ }
|
|
109
|
++
|
|
110
|
++ node = calloc(1, sizeof(*node));
|
|
111
|
++ if (!node) {
|
|
112
|
++ log_errno(LOG_ERR, "calloc");
|
|
113
|
++ goto fail;
|
|
114
|
++ }
|
|
115
|
++
|
|
116
|
++ node->key = key;
|
|
117
|
++ node->ref = 1;
|
|
118
|
++ node->fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
119
|
++ if (node->fd == -1) {
|
|
120
|
++ log_errno(LOG_ERR, "socket");
|
|
121
|
++ goto fail;
|
|
122
|
++ }
|
|
123
|
++
|
|
124
|
++ if (0 != redudp_transparent(node->fd))
|
|
125
|
++ goto fail;
|
|
126
|
++
|
|
127
|
++ if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) {
|
|
128
|
++ log_errno(LOG_ERR, "bind");
|
|
129
|
++ goto fail;
|
|
130
|
++ }
|
|
131
|
++
|
|
132
|
++ pnode = tsearch(node, &root_bound_udp4, bound_udp4_cmp);
|
|
133
|
++ if (!pnode) {
|
|
134
|
++ log_errno(LOG_ERR, "tsearch(%p) == %p", node, pnode);
|
|
135
|
++ goto fail;
|
|
136
|
++ }
|
|
137
|
++ assert(node == *pnode);
|
|
138
|
++
|
|
139
|
++ return node->fd;
|
|
140
|
++
|
|
141
|
++fail:
|
|
142
|
++ if (node) {
|
|
143
|
++ if (node->fd != -1)
|
|
144
|
++ redsocks_close(node->fd);
|
|
145
|
++ free(node);
|
|
146
|
++ }
|
|
147
|
++ return -1;
|
|
148
|
++}
|
|
149
|
++
|
|
150
|
++static void bound_udp4_put(const struct sockaddr_in *addr)
|
|
151
|
++{
|
|
152
|
++ struct bound_udp4_key key;
|
|
153
|
++ struct bound_udp4 **pnode, *node;
|
|
154
|
++ void *parent;
|
|
155
|
++
|
|
156
|
++ bound_udp4_mkkey(&key, addr);
|
|
157
|
++ pnode = tfind(&key, &root_bound_udp4, bound_udp4_cmp);
|
|
158
|
++ assert(pnode && (*pnode)->ref > 0);
|
|
159
|
++
|
|
160
|
++ node = *pnode;
|
|
161
|
++
|
|
162
|
++ node->ref--;
|
|
163
|
++ if (node->ref)
|
|
164
|
++ return;
|
|
165
|
++
|
|
166
|
++ parent = tdelete(node, &root_bound_udp4, bound_udp4_cmp);
|
|
167
|
++ assert(parent);
|
|
168
|
++
|
|
169
|
++ redsocks_close(node->fd); // expanding `pnode` to avoid use after free
|
|
170
|
++ free(node);
|
|
171
|
++}
|
|
172
|
++
|
|
173
|
++static int redudp_transparent(int fd)
|
|
174
|
++{
|
|
175
|
++ int on = 1;
|
|
176
|
++ int error = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on));
|
|
177
|
++ if (error)
|
|
178
|
++ log_errno(LOG_ERR, "setsockopt(..., SOL_IP, IP_TRANSPARENT)");
|
|
179
|
++ return error;
|
|
180
|
++}
|
|
181
|
++
|
|
182
|
++static int do_tproxy(redudp_instance* instance)
|
|
183
|
++{
|
|
184
|
++ return instance->config.destaddr.sin_addr.s_addr == 0;
|
|
185
|
++}
|
|
186
|
++
|
|
187
|
++static struct sockaddr_in* get_destaddr(redudp_client *client)
|
|
188
|
++{
|
|
189
|
++ if (do_tproxy(client->instance))
|
|
190
|
++ return &client->destaddr;
|
|
191
|
++ else
|
|
192
|
++ return &client->instance->config.destaddr;
|
|
193
|
++}
|
|
194
|
++
|
|
195
|
+ static void redudp_fill_preamble(socks5_udp_preabmle *preamble, redudp_client *client)
|
|
196
|
+ {
|
|
197
|
+ preamble->reserved = 0;
|
|
198
|
+ preamble->frag_no = 0; /* fragmentation is not supported */
|
|
199
|
+ preamble->addrtype = socks5_addrtype_ipv4;
|
|
200
|
+- preamble->ip.addr = client->instance->config.destaddr.sin_addr.s_addr;
|
|
201
|
+- preamble->ip.port = client->instance->config.destaddr.sin_port;
|
|
202
|
++ preamble->ip.addr = get_destaddr(client)->sin_addr.s_addr;
|
|
203
|
++ preamble->ip.port = get_destaddr(client)->sin_port;
|
|
204
|
+ }
|
|
205
|
+
|
|
206
|
+ static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p)
|
|
207
|
+@@ -104,6 +232,8 @@ static void redudp_drop_client(redudp_client *client)
|
|
208
|
+ redudp_log_errno(client, LOG_ERR, "event_del");
|
|
209
|
+ redsocks_close(fd);
|
|
210
|
+ }
|
|
211
|
++ if (client->sender_fd != -1)
|
|
212
|
++ bound_udp4_put(&client->destaddr);
|
|
213
|
+ list_for_each_entry_safe(q, tmp, &client->queue, list) {
|
|
214
|
+ list_del(&q->list);
|
|
215
|
+ free(q);
|
|
216
|
+@@ -344,7 +474,8 @@ static void redudp_relay_connected(struct bufferevent *buffev, void *_arg)
|
|
217
|
+ redudp_client *client = _arg;
|
|
218
|
+ int do_password = socks5_is_valid_cred(client->instance->config.login, client->instance->config.password);
|
|
219
|
+ int error;
|
|
220
|
+- redudp_log_error(client, LOG_DEBUG, "<trace>");
|
|
221
|
++ char relayaddr_str[RED_INET_ADDRSTRLEN];
|
|
222
|
++ redudp_log_error(client, LOG_DEBUG, "via %s", red_inet_ntop(&client->instance->config.relayaddr, relayaddr_str, sizeof(relayaddr_str)));
|
|
223
|
+
|
|
224
|
+ if (!red_is_socket_connected_ok(buffev)) {
|
|
225
|
+ redudp_log_errno(client, LOG_NOTICE, "red_is_socket_connected_ok");
|
|
226
|
+@@ -382,7 +513,7 @@ static void redudp_timeout(int fd, short what, void *_arg)
|
|
227
|
+ redudp_drop_client(client);
|
|
228
|
+ }
|
|
229
|
+
|
|
230
|
+-static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, char *buf, size_t pktlen)
|
|
231
|
++static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_in *clientaddr, struct sockaddr_in *destaddr, char *buf, size_t pktlen)
|
|
232
|
+ {
|
|
233
|
+ redudp_client *client = calloc(1, sizeof(*client));
|
|
234
|
+
|
|
235
|
+@@ -395,9 +526,13 @@ static void redudp_first_pkt_from_client(redudp_instance *self, struct sockaddr_
|
|
236
|
+ INIT_LIST_HEAD(&client->queue);
|
|
237
|
+ client->instance = self;
|
|
238
|
+ memcpy(&client->clientaddr, clientaddr, sizeof(*clientaddr));
|
|
239
|
++ if (destaddr)
|
|
240
|
++ memcpy(&client->destaddr, destaddr, sizeof(client->destaddr));
|
|
241
|
+ evtimer_set(&client->timeout, redudp_timeout, client);
|
|
242
|
+ // XXX: self->relay_ss->init(client);
|
|
243
|
+
|
|
244
|
++ client->sender_fd = -1; // it's postponed until socks-server replies to avoid trivial DoS
|
|
245
|
++
|
|
246
|
+ client->relay = red_connect_relay(&client->instance->config.relayaddr,
|
|
247
|
+ redudp_relay_connected, redudp_relay_error, client);
|
|
248
|
+ if (!client->relay)
|
|
249
|
+@@ -431,7 +566,7 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg)
|
|
250
|
+
|
|
251
|
+ assert(fd == EVENT_FD(&client->udprelay));
|
|
252
|
+
|
|
253
|
+- pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr);
|
|
254
|
++ pktlen = red_recv_udp_pkt(fd, pkt.buf, sizeof(pkt.buf), &udprelayaddr, NULL);
|
|
255
|
+ if (pktlen == -1)
|
|
256
|
+ return;
|
|
257
|
+
|
|
258
|
+@@ -455,8 +590,8 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg)
|
|
259
|
+ return;
|
|
260
|
+ }
|
|
261
|
+
|
|
262
|
+- if (pkt.header.ip.port != client->instance->config.destaddr.sin_port ||
|
|
263
|
+- pkt.header.ip.addr != client->instance->config.destaddr.sin_addr.s_addr)
|
|
264
|
++ if (pkt.header.ip.port != get_destaddr(client)->sin_port ||
|
|
265
|
++ pkt.header.ip.addr != get_destaddr(client)->sin_addr.s_addr)
|
|
266
|
+ {
|
|
267
|
+ char buf[RED_INET_ADDRSTRLEN];
|
|
268
|
+ struct sockaddr_in pktaddr = {
|
|
269
|
+@@ -472,8 +607,18 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg)
|
|
270
|
+ redsocks_time(&client->last_relay_event);
|
|
271
|
+ redudp_bump_timeout(client);
|
|
272
|
+
|
|
273
|
++ if (do_tproxy(client->instance) && client->sender_fd == -1) {
|
|
274
|
++ client->sender_fd = bound_udp4_get(&client->destaddr);
|
|
275
|
++ if (client->sender_fd == -1) {
|
|
276
|
++ redudp_log_error(client, LOG_WARNING, "bound_udp4_get failure");
|
|
277
|
++ return;
|
|
278
|
++ }
|
|
279
|
++ }
|
|
280
|
++
|
|
281
|
+ fwdlen = pktlen - sizeof(pkt.header);
|
|
282
|
+- outgoing = sendto(EVENT_FD(&client->instance->listener),
|
|
283
|
++ outgoing = sendto(do_tproxy(client->instance)
|
|
284
|
++ ? client->sender_fd
|
|
285
|
++ : EVENT_FD(&client->instance->listener),
|
|
286
|
+ pkt.buf + sizeof(pkt.header), fwdlen, 0,
|
|
287
|
+ (struct sockaddr*)&client->clientaddr, sizeof(client->clientaddr));
|
|
288
|
+ if (outgoing != fwdlen) {
|
|
289
|
+@@ -486,18 +631,21 @@ static void redudp_pkt_from_socks(int fd, short what, void *_arg)
|
|
290
|
+ static void redudp_pkt_from_client(int fd, short what, void *_arg)
|
|
291
|
+ {
|
|
292
|
+ redudp_instance *self = _arg;
|
|
293
|
+- struct sockaddr_in clientaddr;
|
|
294
|
++ struct sockaddr_in clientaddr, destaddr, *pdestaddr;
|
|
295
|
+ char buf[0xFFFF]; // UDP packet can't be larger then that
|
|
296
|
+ ssize_t pktlen;
|
|
297
|
+ redudp_client *tmp, *client = NULL;
|
|
298
|
+
|
|
299
|
++ pdestaddr = do_tproxy(self) ? &destaddr : NULL;
|
|
300
|
++
|
|
301
|
+ assert(fd == EVENT_FD(&self->listener));
|
|
302
|
+- pktlen = red_recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr);
|
|
303
|
++ pktlen = red_recv_udp_pkt(fd, buf, sizeof(buf), &clientaddr, pdestaddr);
|
|
304
|
+ if (pktlen == -1)
|
|
305
|
+ return;
|
|
306
|
+
|
|
307
|
+ // TODO: this lookup may be SLOOOOOW.
|
|
308
|
+ list_for_each_entry(tmp, &self->clients, list) {
|
|
309
|
++ // TODO: check destaddr
|
|
310
|
+ if (0 == memcmp(&clientaddr, &tmp->clientaddr, sizeof(clientaddr))) {
|
|
311
|
+ client = tmp;
|
|
312
|
+ break;
|
|
313
|
+@@ -515,7 +663,7 @@ static void redudp_pkt_from_client(int fd, short what, void *_arg)
|
|
314
|
+ }
|
|
315
|
+ }
|
|
316
|
+ else {
|
|
317
|
+- redudp_first_pkt_from_client(self, &clientaddr, buf, pktlen);
|
|
318
|
++ redudp_first_pkt_from_client(self, &clientaddr, pdestaddr, buf, pktlen);
|
|
319
|
+ }
|
|
320
|
+ }
|
|
321
|
+
|
|
322
|
+@@ -554,7 +702,6 @@ static int redudp_onenter(parser_section *section)
|
|
323
|
+ instance->config.relayaddr.sin_family = AF_INET;
|
|
324
|
+ instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
325
|
+ instance->config.destaddr.sin_family = AF_INET;
|
|
326
|
+- instance->config.destaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
327
|
+ instance->config.max_pktqueue = 5;
|
|
328
|
+ instance->config.udp_timeout = 30;
|
|
329
|
+ instance->config.udp_timeout_stream = 180;
|
|
330
|
+@@ -614,6 +761,28 @@ static int redudp_init_instance(redudp_instance *instance)
|
|
331
|
+ goto fail;
|
|
332
|
+ }
|
|
333
|
+
|
|
334
|
++ if (do_tproxy(instance)) {
|
|
335
|
++ int on = 1;
|
|
336
|
++ char buf[RED_INET_ADDRSTRLEN];
|
|
337
|
++ // iptables TPROXY target does not send packets to non-transparent sockets
|
|
338
|
++ if (0 != redudp_transparent(fd))
|
|
339
|
++ goto fail;
|
|
340
|
++
|
|
341
|
++ error = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &on, sizeof(on));
|
|
342
|
++ if (error) {
|
|
343
|
++ log_errno(LOG_ERR, "setsockopt(listener, SOL_IP, IP_RECVORIGDSTADDR)");
|
|
344
|
++ goto fail;
|
|
345
|
++ }
|
|
346
|
++
|
|
347
|
++ log_error(LOG_DEBUG, "redudp @ %s: TPROXY", red_inet_ntop(&instance->config.bindaddr, buf, sizeof(buf)));
|
|
348
|
++ }
|
|
349
|
++ else {
|
|
350
|
++ char buf1[RED_INET_ADDRSTRLEN], buf2[RED_INET_ADDRSTRLEN];
|
|
351
|
++ log_error(LOG_DEBUG, "redudp @ %s: destaddr=%s",
|
|
352
|
++ red_inet_ntop(&instance->config.bindaddr, buf1, sizeof(buf1)),
|
|
353
|
++ red_inet_ntop(&instance->config.destaddr, buf2, sizeof(buf2)));
|
|
354
|
++ }
|
|
355
|
++
|
|
356
|
+ error = bind(fd, (struct sockaddr*)&instance->config.bindaddr, sizeof(instance->config.bindaddr));
|
|
357
|
+ if (error) {
|
|
358
|
+ log_errno(LOG_ERR, "bind");
|
|
359
|
+diff --git a/redudp.h b/redudp.h
|
|
360
|
+index 308bd33..3f1d9d1 100644
|
|
361
|
+--- a/redudp.h
|
|
362
|
++++ b/redudp.h
|
|
363
|
+@@ -24,6 +24,8 @@ typedef struct redudp_client_t {
|
|
364
|
+ list_head list;
|
|
365
|
+ redudp_instance *instance;
|
|
366
|
+ struct sockaddr_in clientaddr;
|
|
367
|
++ struct sockaddr_in destaddr;
|
|
368
|
++ int sender_fd; // shared between several clients socket (bound to `destaddr`)
|
|
369
|
+ struct event timeout;
|
|
370
|
+ struct bufferevent *relay;
|
|
371
|
+ struct event udprelay;
|
|
372
|
+diff --git a/utils.c b/utils.c
|
|
373
|
+index 6e1f3af..afdeea8 100644
|
|
374
|
+--- a/utils.c
|
|
375
|
++++ b/utils.c
|
|
376
|
+@@ -26,17 +26,54 @@
|
|
377
|
+ #include "utils.h"
|
|
378
|
+ #include "redsocks.h" // for redsocks_close
|
|
379
|
+
|
|
380
|
+-int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr)
|
|
381
|
++int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr, struct sockaddr_in *toaddr)
|
|
382
|
+ {
|
|
383
|
+ socklen_t addrlen = sizeof(*inaddr);
|
|
384
|
+ ssize_t pktlen;
|
|
385
|
+-
|
|
386
|
+- pktlen = recvfrom(fd, buf, buflen, 0, (struct sockaddr*)inaddr, &addrlen);
|
|
387
|
++ struct msghdr msg;
|
|
388
|
++ struct iovec io;
|
|
389
|
++ char control[1024];
|
|
390
|
++
|
|
391
|
++ memset(&msg, 0, sizeof(msg));
|
|
392
|
++ msg.msg_name = inaddr;
|
|
393
|
++ msg.msg_namelen = sizeof(*inaddr);
|
|
394
|
++ msg.msg_iov = &io;
|
|
395
|
++ msg.msg_iovlen = 1;
|
|
396
|
++ msg.msg_control = control;
|
|
397
|
++ msg.msg_controllen = sizeof(control);
|
|
398
|
++ io.iov_base = buf;
|
|
399
|
++ io.iov_len = buflen;
|
|
400
|
++
|
|
401
|
++ pktlen = recvmsg(fd, &msg, 0);
|
|
402
|
+ if (pktlen == -1) {
|
|
403
|
+ log_errno(LOG_WARNING, "recvfrom");
|
|
404
|
+ return -1;
|
|
405
|
+ }
|
|
406
|
+
|
|
407
|
++ if (toaddr) {
|
|
408
|
++ memset(toaddr, 0, sizeof(*toaddr));
|
|
409
|
++ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|
410
|
++ if (
|
|
411
|
++ cmsg->cmsg_level == SOL_IP &&
|
|
412
|
++ cmsg->cmsg_type == IP_ORIGDSTADDR &&
|
|
413
|
++ cmsg->cmsg_len >= CMSG_LEN(sizeof(*toaddr))
|
|
414
|
++ ) {
|
|
415
|
++ struct sockaddr_in* cmsgaddr = (struct sockaddr_in*)CMSG_DATA(cmsg);
|
|
416
|
++ char buf[RED_INET_ADDRSTRLEN];
|
|
417
|
++ log_error(LOG_DEBUG, "IP_ORIGDSTADDR: %s", red_inet_ntop(cmsgaddr, buf, sizeof(buf)));
|
|
418
|
++ memcpy(toaddr, cmsgaddr, sizeof(*toaddr));
|
|
419
|
++ }
|
|
420
|
++ else {
|
|
421
|
++ log_error(LOG_WARNING, "unexepcted cmsg (level,type) = (%d,%d)",
|
|
422
|
++ cmsg->cmsg_level, cmsg->cmsg_type);
|
|
423
|
++ }
|
|
424
|
++ }
|
|
425
|
++ if (toaddr->sin_family != AF_INET) {
|
|
426
|
++ log_error(LOG_WARNING, "(SOL_IP, IP_ORIGDSTADDR) not found");
|
|
427
|
++ return -1;
|
|
428
|
++ }
|
|
429
|
++ }
|
|
430
|
++
|
|
431
|
+ if (addrlen != sizeof(*inaddr)) {
|
|
432
|
+ log_error(LOG_WARNING, "unexpected address length %u instead of %zu", addrlen, sizeof(*inaddr));
|
|
433
|
+ return -1;
|
|
434
|
+diff --git a/utils.h b/utils.h
|
|
435
|
+index d3af00f..c2277e9 100644
|
|
436
|
+--- a/utils.h
|
|
437
|
++++ b/utils.h
|
|
438
|
+@@ -44,7 +44,7 @@ char *redsocks_evbuffer_readline(struct evbuffer *buf);
|
|
439
|
+ struct bufferevent* red_connect_relay(struct sockaddr_in *addr, evbuffercb writecb, everrorcb errorcb, void *cbarg);
|
|
440
|
+ int red_socket_geterrno(struct bufferevent *buffev);
|
|
441
|
+ int red_is_socket_connected_ok(struct bufferevent *buffev);
|
|
442
|
+-int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *inaddr);
|
|
443
|
++int red_recv_udp_pkt(int fd, char *buf, size_t buflen, struct sockaddr_in *fromaddr, struct sockaddr_in *toaddr);
|
|
444
|
+
|
|
445
|
+ int fcntl_nonblock(int fd);
|
|
446
|
+
|
|
447
|
+--
|
|
448
|
+1.9.1
|
|
449
|
+
|