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

Last change on this file since 11752 was 11752, checked in by jordan, 11 years ago

(trunk libT) #3906 "DHT ignores bind-address-ipv6" -- make the process of getting the binding address consistent between IPv4 and IPv6 sockets.

As suggested by Juiusz after the IPv6 binding commit in r11749. See: https://trac.transmissionbt.com/ticket/3906#comment:6

File size: 6.3 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 <unistd.h>
25#include <assert.h>
26
27#include <event2/event.h>
28
29#include "transmission.h"
30#include "net.h"
31#include "session.h"
32#include "tr-dht.h"
33#include "tr-udp.h"
34
35/* BEP-32 has a rather nice explanation of why we need to bind to one
36   IPv6 address, if I may say so myself. */
37
38static void
39rebind_ipv6(tr_session *ss, tr_bool force)
40{
41    tr_bool is_default;
42    const struct tr_address * public_addr;
43    struct sockaddr_in6 sin6;
44    const unsigned char *ipv6 = tr_globalIPv6();
45    int s = -1, rc;
46    int one = 1;
47
48    /* We currently have no way to enable or disable IPv6 after initialisation.
49       No way to fix that without some surgery to the DHT code itself. */
50    if(ipv6 == NULL || (!force && ss->udp6_socket < 0)) {
51        if(ss->udp6_bound) {
52            free(ss->udp6_bound);
53            ss->udp6_bound = NULL;
54        }
55        return;
56    }
57
58    if(ss->udp6_bound != NULL && memcmp(ipv6, ss->udp6_bound, 16) == 0)
59        return;
60
61    s = socket(PF_INET6, SOCK_DGRAM, 0);
62    if(s < 0)
63        goto fail;
64
65#ifdef IPV6_V6ONLY
66        /* Since we always open an IPv4 socket on the same port, this
67           shouldn't matter.  But I'm superstitious. */
68        setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
69#endif
70
71    memset(&sin6, 0, sizeof(sin6));
72    sin6.sin6_family = AF_INET6;
73    if(ipv6)
74        memcpy(&sin6.sin6_addr, ipv6, 16);
75    sin6.sin6_port = htons(ss->udp_port);
76    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET6, &is_default);
77    if(public_addr && !is_default)
78        sin6.sin6_addr = public_addr->addr.addr6;
79
80    rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
81    if(rc < 0)
82        goto fail;
83
84    if(ss->udp6_socket < 0) {
85        ss->udp6_socket = s;
86        s = -1;
87    } else {
88        rc = dup2(s, ss->udp6_socket);
89        if(rc < 0)
90            goto fail;
91        close(s);
92        s = -1;
93    }
94
95    if(ss->udp6_bound == NULL)
96        ss->udp6_bound = malloc(16);
97    if(ss->udp6_bound)
98        memcpy(ss->udp6_bound, ipv6, 16);
99
100    return;
101
102 fail:
103    /* Something went wrong.  It's difficult to recover, so let's simply
104       set things up so that we try again next time. */
105    tr_nerr("UDP", "Couldn't rebind IPv6 socket");
106    if(s >= 0)
107        close(s);
108    if(ss->udp6_bound) {
109        free(ss->udp6_bound);
110        ss->udp6_bound = NULL;
111    }
112}
113
114static void
115event_callback(int s, short type, void *sv)
116{
117    tr_session *ss = (tr_session*)sv;
118    unsigned char *buf;
119    struct sockaddr_storage from;
120    socklen_t fromlen;
121    int rc;
122
123    assert(tr_isSession(ss));
124    assert(type == EV_READ);
125
126    buf = malloc(4096);
127    if(buf == NULL) {
128        tr_nerr("UDP", "Couldn't allocate buffer");
129        return;
130    }
131
132    fromlen = sizeof(from);
133    rc = recvfrom(s, buf, 4096 - 1, 0,
134                  (struct sockaddr*)&from, &fromlen);
135    if(rc <= 0)
136        return;
137
138    if(buf[0] == 'd') {
139        /* DHT packet. */
140        buf[rc] = '\0';
141        tr_dhtCallback(buf, rc, (struct sockaddr*)&from, fromlen, sv);
142    } else {
143        /* Probably a UTP packet. */
144        /* Nothing yet. */
145    }
146
147    free(buf);
148}   
149
150void
151tr_udpInit(tr_session *ss)
152{
153    tr_bool is_default;
154    const struct tr_address * public_addr;
155    struct sockaddr_in sin;
156    int rc;
157
158    assert(ss->udp_socket < 0);
159    assert(ss->udp6_socket < 0);
160
161    ss->udp_port = tr_sessionGetPeerPort(ss);
162    if(ss->udp_port <= 0)
163        return;
164
165    ss->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
166    if(ss->udp_socket < 0) {
167        tr_nerr("UDP", "Couldn't create IPv4 socket");
168        goto ipv6;
169    }
170
171    memset(&sin, 0, sizeof(sin));
172    sin.sin_family = AF_INET;
173    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET, &is_default);
174    if(public_addr && !is_default)
175        memcpy(&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr));
176    sin.sin_port = htons(ss->udp_port);
177    rc = bind(ss->udp_socket, (struct sockaddr*)&sin, sizeof(sin));
178    if(rc < 0) {
179        tr_nerr("UDP", "Couldn't bind IPv4 socket");
180        close(ss->udp_socket);
181        ss->udp_socket = -1;
182        goto ipv6;
183    }
184    ss->udp_event =
185        event_new(ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
186                  event_callback, ss);
187    if( ss->udp_event == NULL )
188        tr_nerr("UDP", "Couldn't allocate IPv4 event");
189
190 ipv6:
191    if(tr_globalIPv6())
192        rebind_ipv6(ss, TRUE);
193    if(ss->udp6_socket >= 0) {
194        ss->udp6_event =
195            event_new(ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
196                      event_callback, ss);
197        if(ss->udp6_event == NULL)
198            tr_nerr("UDP", "Couldn't allocate IPv6 event");
199    }
200
201    if(ss->isDHTEnabled)
202        tr_dhtInit(ss);
203
204    if(ss->udp_event)
205        event_add(ss->udp_event, NULL);
206    if(ss->udp6_event)
207        event_add(ss->udp6_event, NULL);
208}
209
210void
211tr_udpUninit(tr_session *ss)
212{
213    tr_dhtUninit(ss);
214
215    if(ss->udp_socket >= 0) {
216        tr_netCloseSocket( ss->udp_socket );
217        ss->udp_socket = -1;
218    }
219
220    if(ss->udp_event) {
221        event_free(ss->udp_event);
222        ss->udp_event = NULL;
223    }
224
225    if(ss->udp6_socket >= 0) {
226        tr_netCloseSocket( ss->udp6_socket );
227        ss->udp6_socket = -1;
228    }
229
230    if(ss->udp6_event) {
231        event_free(ss->udp6_event);
232        ss->udp6_event = NULL;
233    }
234
235    if(ss->udp6_bound) {
236        free(ss->udp6_bound);
237        ss->udp6_bound = NULL;
238    }
239}
Note: See TracBrowser for help on using the repository browser.