source: trunk/libtransmission/tr-udp.c @ 12544

Last change on this file since 12544 was 12544, checked in by jordan, 10 years ago

(trunk libT) #4016 "blocklists loaded in Transmission should be used to filter DHT communication." -- fixed.

File size: 9.5 KB
Line 
1/*
2Copyright (c) 2010 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21
22*/
23
24#include <assert.h>
25#include <string.h> /* memcmp(), memcpy(), memset() */
26#include <stdlib.h> /* malloc(), free() */
27
28#include <unistd.h> /* close() */
29
30#include <event2/event.h>
31
32#include "transmission.h"
33#include "net.h"
34#include "session.h"
35#include "tr-dht.h"
36#include "tr-utp.h"
37#include "tr-udp.h"
38
39/* Since we use a single UDP socket in order to implement multiple
40   uTP sockets, try to set up huge buffers. */
41
42#define RECV_BUFFER_SIZE (4 * 1024 * 1024)
43#define SEND_BUFFER_SIZE (1 * 1024 * 1024)
44#define SMALL_BUFFER_SIZE (32 * 1024)
45
46static void
47set_socket_buffers(int fd, int large)
48{
49    int size, rbuf, sbuf, rc;
50    socklen_t rbuf_len = sizeof(rbuf), sbuf_len = sizeof(sbuf);
51
52    size = large ? RECV_BUFFER_SIZE : SMALL_BUFFER_SIZE;
53    rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
54    if(rc < 0)
55        tr_nerr("UDP", "Failed to set receive buffer: %s",
56                tr_strerror(errno));
57
58    size = large ? SEND_BUFFER_SIZE : SMALL_BUFFER_SIZE;
59    rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
60    if(rc < 0)
61        tr_nerr("UDP", "Failed to set send buffer: %s",
62                tr_strerror(errno));
63
64    if(large) {
65        rc = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &rbuf_len);
66        if(rc < 0)
67            rbuf = 0;
68
69        rc = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &sbuf_len);
70        if(rc < 0)
71            sbuf = 0;
72
73        if(rbuf < RECV_BUFFER_SIZE) {
74            tr_nerr("UDP", "Failed to set receive buffer: requested %d, got %d",
75                    RECV_BUFFER_SIZE, rbuf);
76#ifdef __linux__
77            tr_ninf("UDP",
78                    "Please add the line "
79                    "\"net.core.rmem_max = %d\" to /etc/sysctl.conf",
80                    RECV_BUFFER_SIZE);
81#endif
82        }
83
84        if(sbuf < SEND_BUFFER_SIZE) {
85            tr_nerr("UDP", "Failed to set send buffer: requested %d, got %d",
86                    SEND_BUFFER_SIZE, sbuf);
87#ifdef __linux__
88            tr_ninf("UDP",
89                    "Please add the line "
90                    "\"net.core.wmem_max = %d\" to /etc/sysctl.conf",
91                    SEND_BUFFER_SIZE);
92#endif
93        }
94    }
95}
96
97void
98tr_udpSetSocketBuffers(tr_session *session)
99{
100    bool utp = tr_sessionIsUTPEnabled(session);
101    if(session->udp_socket >= 0)
102        set_socket_buffers(session->udp_socket, utp);
103    if(session->udp6_socket >= 0)
104        set_socket_buffers(session->udp6_socket, utp);
105}
106
107
108
109
110/* BEP-32 has a rather nice explanation of why we need to bind to one
111   IPv6 address, if I may say so myself. */
112
113static void
114rebind_ipv6(tr_session *ss, bool force)
115{
116    bool is_default;
117    const struct tr_address * public_addr;
118    struct sockaddr_in6 sin6;
119    const unsigned char *ipv6 = tr_globalIPv6();
120    int s = -1, rc;
121    int one = 1;
122
123    /* We currently have no way to enable or disable IPv6 after initialisation.
124       No way to fix that without some surgery to the DHT code itself. */
125    if(ipv6 == NULL || (!force && ss->udp6_socket < 0)) {
126        if(ss->udp6_bound) {
127            free(ss->udp6_bound);
128            ss->udp6_bound = NULL;
129        }
130        return;
131    }
132
133    if(ss->udp6_bound != NULL && memcmp(ipv6, ss->udp6_bound, 16) == 0)
134        return;
135
136    s = socket(PF_INET6, SOCK_DGRAM, 0);
137    if(s < 0)
138        goto fail;
139
140#ifdef IPV6_V6ONLY
141        /* Since we always open an IPv4 socket on the same port, this
142           shouldn't matter.  But I'm superstitious. */
143        setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
144#endif
145
146    memset(&sin6, 0, sizeof(sin6));
147    sin6.sin6_family = AF_INET6;
148    if(ipv6)
149        memcpy(&sin6.sin6_addr, ipv6, 16);
150    sin6.sin6_port = htons(ss->udp_port);
151    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET6, &is_default);
152    if(public_addr && !is_default)
153        sin6.sin6_addr = public_addr->addr.addr6;
154
155    rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
156    if(rc < 0)
157        goto fail;
158
159    if(ss->udp6_socket < 0) {
160        ss->udp6_socket = s;
161    } else {
162        rc = dup2(s, ss->udp6_socket);
163        if(rc < 0)
164            goto fail;
165        close(s);
166    }
167
168    if(ss->udp6_bound == NULL)
169        ss->udp6_bound = malloc(16);
170    if(ss->udp6_bound)
171        memcpy(ss->udp6_bound, ipv6, 16);
172
173    return;
174
175 fail:
176    /* Something went wrong.  It's difficult to recover, so let's simply
177       set things up so that we try again next time. */
178    tr_nerr("UDP", "Couldn't rebind IPv6 socket");
179    if(s >= 0)
180        close(s);
181    if(ss->udp6_bound) {
182        free(ss->udp6_bound);
183        ss->udp6_bound = NULL;
184    }
185}
186
187static void
188event_callback(int s, short type UNUSED, void *sv)
189{
190    int rc;
191    socklen_t fromlen;
192    unsigned char buf[4096];
193    struct sockaddr_storage from;
194    tr_session *ss = sv;
195    tr_address addr;
196    tr_port port;
197
198    assert(tr_isSession(sv));
199    assert(type == EV_READ);
200
201    fromlen = sizeof(from);
202    rc = recvfrom(s, buf, 4096 - 1, 0,
203                  (struct sockaddr*)&from, &fromlen);
204
205    /* don't process messages from blocked addresses */
206    if( !tr_address_from_sockaddr_storage( &addr, &port, &from )
207            || tr_sessionIsAddressBlocked( ss, &addr ) )
208        return;
209
210    /* Since most packets we receive here are µTP, make quick inline
211       checks for the other protocols.  The logic is as follows:
212       - all DHT packets start with 'd';
213       - all UDP tracker packets start with a 32-bit (!) "action", which
214         is between 0 and 3;
215       - the above cannot be µTP packets, since these start with a 4-bit
216         version number (1). */
217    if(rc > 0) {
218        if( buf[0] == 'd' ) {
219            if( tr_sessionAllowsDHT( ss ) ) {
220                buf[rc] = '\0'; /* required by the DHT code */
221                tr_dhtCallback(buf, rc, (struct sockaddr*)&from, fromlen, sv);
222            }
223        } else if( rc >= 8 &&
224                   buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3 ) {
225            rc = tau_handle_message( ss, buf, rc );
226            if( !rc )
227                tr_ndbg("UDP", "Couldn't parse UDP tracker packet.");
228        } else {
229            if( tr_sessionIsUTPEnabled( ss ) ) {
230                rc = tr_utpPacket(buf, rc, (struct sockaddr*)&from, fromlen, ss);
231                if( !rc )
232                    tr_ndbg("UDP", "Unexpected UDP packet");
233            }
234        }
235    }
236}
237
238void
239tr_udpInit(tr_session *ss)
240{
241    bool is_default;
242    const struct tr_address * public_addr;
243    struct sockaddr_in sin;
244    int rc;
245
246    assert(ss->udp_socket < 0);
247    assert(ss->udp6_socket < 0);
248
249    ss->udp_port = tr_sessionGetPeerPort(ss);
250    if(ss->udp_port <= 0)
251        return;
252
253    ss->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
254    if(ss->udp_socket < 0) {
255        tr_nerr("UDP", "Couldn't create IPv4 socket");
256        goto ipv6;
257    }
258
259    memset(&sin, 0, sizeof(sin));
260    sin.sin_family = AF_INET;
261    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET, &is_default);
262    if(public_addr && !is_default)
263        memcpy(&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr));
264    sin.sin_port = htons(ss->udp_port);
265    rc = bind(ss->udp_socket, (struct sockaddr*)&sin, sizeof(sin));
266    if(rc < 0) {
267        tr_nerr("UDP", "Couldn't bind IPv4 socket");
268        close(ss->udp_socket);
269        ss->udp_socket = -1;
270        goto ipv6;
271    }
272    ss->udp_event =
273        event_new(ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
274                  event_callback, ss);
275    if( ss->udp_event == NULL )
276        tr_nerr("UDP", "Couldn't allocate IPv4 event");
277
278 ipv6:
279    if(tr_globalIPv6())
280        rebind_ipv6(ss, true);
281    if(ss->udp6_socket >= 0) {
282        ss->udp6_event =
283            event_new(ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
284                      event_callback, ss);
285        if(ss->udp6_event == NULL)
286            tr_nerr("UDP", "Couldn't allocate IPv6 event");
287    }
288
289    tr_udpSetSocketBuffers(ss);
290
291    if(ss->isDHTEnabled)
292        tr_dhtInit(ss);
293
294    if(ss->udp_event)
295        event_add(ss->udp_event, NULL);
296    if(ss->udp6_event)
297        event_add(ss->udp6_event, NULL);
298}
299
300void
301tr_udpUninit(tr_session *ss)
302{
303    tr_dhtUninit(ss);
304
305    if(ss->udp_socket >= 0) {
306        tr_netCloseSocket( ss->udp_socket );
307        ss->udp_socket = -1;
308    }
309
310    if(ss->udp_event) {
311        event_free(ss->udp_event);
312        ss->udp_event = NULL;
313    }
314
315    if(ss->udp6_socket >= 0) {
316        tr_netCloseSocket( ss->udp6_socket );
317        ss->udp6_socket = -1;
318    }
319
320    if(ss->udp6_event) {
321        event_free(ss->udp6_event);
322        ss->udp6_event = NULL;
323    }
324
325    if(ss->udp6_bound) {
326        free(ss->udp6_bound);
327        ss->udp6_bound = NULL;
328    }
329}
Note: See TracBrowser for help on using the repository browser.