source: trunk/libtransmission/tr-udp.c

Last change on this file was 14548, checked in by mikedld, 5 years ago

Use sockerrno and tr_net_strerror for most of network-related errors

This ensures proper network errors formatting on Windows.

Also, disable IP_TOS socket option modification attempts on Windows
since it's not supported there and is considered deprecated: "Do not
use. Type of Service (TOS) settings should only be set using the
Quality of Service API" (c) MSDN. Using QoS API is a subject for
separate commit(s).

File size: 10.2 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#ifdef _WIN32
29 #include <io.h> /* dup2 () */
30#else
31 #include <unistd.h> /* dup2 () */
32#endif
33
34#include <event2/event.h>
35
36#include <libutp/utp.h>
37
38#include "transmission.h"
39#include "log.h"
40#include "net.h"
41#include "session.h"
42#include "tr-dht.h"
43#include "tr-utp.h"
44#include "tr-udp.h"
45
46/* Since we use a single UDP socket in order to implement multiple
47   uTP sockets, try to set up huge buffers. */
48
49#define RECV_BUFFER_SIZE (4 * 1024 * 1024)
50#define SEND_BUFFER_SIZE (1 * 1024 * 1024)
51#define SMALL_BUFFER_SIZE (32 * 1024)
52
53static void
54set_socket_buffers (tr_socket_t fd,
55                    int         large)
56{
57    int size, rbuf, sbuf, rc;
58    socklen_t rbuf_len = sizeof (rbuf), sbuf_len = sizeof (sbuf);
59    char err_buf[512];
60
61    size = large ? RECV_BUFFER_SIZE : SMALL_BUFFER_SIZE;
62    rc = setsockopt (fd, SOL_SOCKET, SO_RCVBUF, (const void *) &size, sizeof (size));
63    if (rc < 0)
64        tr_logAddNamedError ("UDP", "Failed to set receive buffer: %s",
65                             tr_net_strerror (err_buf, sizeof (err_buf), sockerrno));
66
67    size = large ? SEND_BUFFER_SIZE : SMALL_BUFFER_SIZE;
68    rc = setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (const void *) &size, sizeof (size));
69    if (rc < 0)
70        tr_logAddNamedError ("UDP", "Failed to set send buffer: %s",
71                             tr_net_strerror (err_buf, sizeof (err_buf), sockerrno));
72
73    if (large) {
74        rc = getsockopt (fd, SOL_SOCKET, SO_RCVBUF, (void *) &rbuf, &rbuf_len);
75        if (rc < 0)
76            rbuf = 0;
77
78        rc = getsockopt (fd, SOL_SOCKET, SO_SNDBUF, (void *) &sbuf, &sbuf_len);
79        if (rc < 0)
80            sbuf = 0;
81
82        if (rbuf < RECV_BUFFER_SIZE) {
83            tr_logAddNamedError ("UDP", "Failed to set receive buffer: requested %d, got %d",
84                    RECV_BUFFER_SIZE, rbuf);
85#ifdef __linux__
86            tr_logAddNamedInfo ("UDP",
87                    "Please add the line "
88                    "\"net.core.rmem_max = %d\" to /etc/sysctl.conf",
89                    RECV_BUFFER_SIZE);
90#endif
91        }
92
93        if (sbuf < SEND_BUFFER_SIZE) {
94            tr_logAddNamedError ("UDP", "Failed to set send buffer: requested %d, got %d",
95                    SEND_BUFFER_SIZE, sbuf);
96#ifdef __linux__
97            tr_logAddNamedInfo ("UDP",
98                    "Please add the line "
99                    "\"net.core.wmem_max = %d\" to /etc/sysctl.conf",
100                    SEND_BUFFER_SIZE);
101#endif
102        }
103    }
104}
105
106void
107tr_udpSetSocketBuffers (tr_session *session)
108{
109    bool utp = tr_sessionIsUTPEnabled (session);
110    if (session->udp_socket != TR_BAD_SOCKET)
111        set_socket_buffers (session->udp_socket, utp);
112    if (session->udp6_socket != TR_BAD_SOCKET)
113        set_socket_buffers (session->udp6_socket, utp);
114}
115
116
117
118
119/* BEP-32 has a rather nice explanation of why we need to bind to one
120   IPv6 address, if I may say so myself. */
121
122static void
123rebind_ipv6 (tr_session *ss, bool force)
124{
125    bool is_default;
126    const struct tr_address * public_addr;
127    struct sockaddr_in6 sin6;
128    const unsigned char *ipv6 = tr_globalIPv6 ();
129    tr_socket_t s = TR_BAD_SOCKET;
130    int rc;
131    int one = 1;
132
133    /* We currently have no way to enable or disable IPv6 after initialisation.
134       No way to fix that without some surgery to the DHT code itself. */
135    if (ipv6 == NULL || (!force && ss->udp6_socket == TR_BAD_SOCKET)) {
136        if (ss->udp6_bound) {
137            free (ss->udp6_bound);
138            ss->udp6_bound = NULL;
139        }
140        return;
141    }
142
143    if (ss->udp6_bound != NULL && memcmp (ipv6, ss->udp6_bound, 16) == 0)
144        return;
145
146    s = socket (PF_INET6, SOCK_DGRAM, 0);
147    if (s == TR_BAD_SOCKET)
148        goto fail;
149
150#ifdef IPV6_V6ONLY
151        /* Since we always open an IPv4 socket on the same port, this
152           shouldn't matter.  But I'm superstitious. */
153        setsockopt (s, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &one, sizeof (one));
154#endif
155
156    memset (&sin6, 0, sizeof (sin6));
157    sin6.sin6_family = AF_INET6;
158    if (ipv6)
159        memcpy (&sin6.sin6_addr, ipv6, 16);
160    sin6.sin6_port = htons (ss->udp_port);
161    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET6, &is_default);
162    if (public_addr && !is_default)
163        sin6.sin6_addr = public_addr->addr.addr6;
164
165    rc = bind (s, (struct sockaddr*)&sin6, sizeof (sin6));
166    if (rc < 0)
167        goto fail;
168
169    if (ss->udp6_socket == TR_BAD_SOCKET) {
170        ss->udp6_socket = s;
171    } else {
172        /* FIXME: dup2 doesn't work for sockets on Windows */
173        rc = dup2 (s, ss->udp6_socket);
174        if (rc < 0)
175            goto fail;
176        tr_netCloseSocket (s);
177    }
178
179    if (ss->udp6_bound == NULL)
180        ss->udp6_bound = malloc (16);
181    if (ss->udp6_bound)
182        memcpy (ss->udp6_bound, ipv6, 16);
183
184    return;
185
186 fail:
187    /* Something went wrong.  It's difficult to recover, so let's simply
188       set things up so that we try again next time. */
189    tr_logAddNamedError ("UDP", "Couldn't rebind IPv6 socket");
190    if (s != TR_BAD_SOCKET)
191        tr_netCloseSocket (s);
192    if (ss->udp6_bound) {
193        free (ss->udp6_bound);
194        ss->udp6_bound = NULL;
195    }
196}
197
198static void
199event_callback (evutil_socket_t s, short type UNUSED, void *sv)
200{
201    int rc;
202    socklen_t fromlen;
203    unsigned char buf[4096];
204    struct sockaddr_storage from;
205    tr_session *ss = sv;
206
207    assert (tr_isSession (sv));
208    assert (type == EV_READ);
209
210    fromlen = sizeof (from);
211    rc = recvfrom (s, (void *) buf, 4096 - 1, 0,
212                (struct sockaddr*)&from, &fromlen);
213
214    /* Since most packets we receive here are µTP, make quick inline
215       checks for the other protocols.  The logic is as follows:
216       - all DHT packets start with 'd';
217       - all UDP tracker packets start with a 32-bit (!) "action", which
218         is between 0 and 3;
219       - the above cannot be µTP packets, since these start with a 4-bit
220         version number (1). */
221    if (rc > 0) {
222        if (buf[0] == 'd') {
223            if (tr_sessionAllowsDHT (ss)) {
224                buf[rc] = '\0'; /* required by the DHT code */
225                tr_dhtCallback (buf, rc, (struct sockaddr*)&from, fromlen, sv);
226            }
227        } else if (rc >= 8 &&
228                   buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3) {
229            rc = tau_handle_message (ss, buf, rc);
230            if (!rc)
231                tr_logAddNamedDbg ("UDP", "Couldn't parse UDP tracker packet.");
232        } else {
233            if (tr_sessionIsUTPEnabled (ss)) {
234                rc = tr_utpPacket (buf, rc, (struct sockaddr*)&from, fromlen, ss);
235                if (!rc)
236                    tr_logAddNamedDbg ("UDP", "Unexpected UDP packet");
237            }
238        }
239    }
240}
241
242void
243tr_udpInit (tr_session *ss)
244{
245    bool is_default;
246    const struct tr_address * public_addr;
247    struct sockaddr_in sin;
248    int rc;
249
250    assert (ss->udp_socket == TR_BAD_SOCKET);
251    assert (ss->udp6_socket == TR_BAD_SOCKET);
252
253    ss->udp_port = tr_sessionGetPeerPort (ss);
254    if (ss->udp_port <= 0)
255        return;
256
257    ss->udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
258    if (ss->udp_socket == TR_BAD_SOCKET) {
259        tr_logAddNamedError ("UDP", "Couldn't create IPv4 socket");
260        goto ipv6;
261    }
262
263    memset (&sin, 0, sizeof (sin));
264    sin.sin_family = AF_INET;
265    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET, &is_default);
266    if (public_addr && !is_default)
267        memcpy (&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr));
268    sin.sin_port = htons (ss->udp_port);
269    rc = bind (ss->udp_socket, (struct sockaddr*)&sin, sizeof (sin));
270    if (rc < 0) {
271        tr_logAddNamedError ("UDP", "Couldn't bind IPv4 socket");
272        tr_netCloseSocket (ss->udp_socket);
273        ss->udp_socket = TR_BAD_SOCKET;
274        goto ipv6;
275    }
276    ss->udp_event =
277        event_new (ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
278                  event_callback, ss);
279    if (ss->udp_event == NULL)
280        tr_logAddNamedError ("UDP", "Couldn't allocate IPv4 event");
281
282 ipv6:
283    if (tr_globalIPv6 ())
284        rebind_ipv6 (ss, true);
285    if (ss->udp6_socket != TR_BAD_SOCKET) {
286        ss->udp6_event =
287            event_new (ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
288                      event_callback, ss);
289        if (ss->udp6_event == NULL)
290            tr_logAddNamedError ("UDP", "Couldn't allocate IPv6 event");
291    }
292
293    tr_udpSetSocketBuffers (ss);
294
295    if (ss->isDHTEnabled)
296        tr_dhtInit (ss);
297
298    if (ss->udp_event)
299        event_add (ss->udp_event, NULL);
300    if (ss->udp6_event)
301        event_add (ss->udp6_event, NULL);
302}
303
304void
305tr_udpUninit (tr_session *ss)
306{
307    tr_dhtUninit (ss);
308
309    if (ss->udp_socket != TR_BAD_SOCKET) {
310        tr_netCloseSocket (ss->udp_socket);
311        ss->udp_socket = TR_BAD_SOCKET;
312    }
313
314    if (ss->udp_event) {
315        event_free (ss->udp_event);
316        ss->udp_event = NULL;
317    }
318
319    if (ss->udp6_socket != TR_BAD_SOCKET) {
320        tr_netCloseSocket (ss->udp6_socket);
321        ss->udp6_socket = TR_BAD_SOCKET;
322    }
323
324    if (ss->udp6_event) {
325        event_free (ss->udp6_event);
326        ss->udp6_event = NULL;
327    }
328
329    if (ss->udp6_bound) {
330        free (ss->udp6_bound);
331        ss->udp6_bound = NULL;
332    }
333}
Note: See TracBrowser for help on using the repository browser.