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

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

(trunk libT) #3906 "DHT ignores bind-address-ipv6" -- test fix.

Add code to honor the ipv6 bind address. Thanks to jch for saving me a little work by confirming the bug and pointing out where in the code the change needed to be made.

File size: 6.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 <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, const tr_address * addr)
152{
153    struct sockaddr_in sin;
154    int rc;
155
156    assert(ss->udp_socket < 0);
157    assert(ss->udp6_socket < 0);
158
159    ss->udp_port = tr_sessionGetPeerPort(ss);
160    if(ss->udp_port <= 0)
161        return;
162
163    ss->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
164    if(ss->udp_socket < 0) {
165        tr_nerr("UDP", "Couldn't create IPv4 socket");
166        goto ipv6;
167    }
168
169    memset(&sin, 0, sizeof(sin));
170    sin.sin_family = AF_INET;
171    memcpy(&sin.sin_addr, &addr->addr.addr4, sizeof (struct in_addr));
172    sin.sin_port = htons(ss->udp_port);
173    rc = bind(ss->udp_socket, (struct sockaddr*)&sin, sizeof(sin));
174    if(rc < 0) {
175        tr_nerr("UDP", "Couldn't bind IPv4 socket");
176        close(ss->udp_socket);
177        ss->udp_socket = -1;
178        goto ipv6;
179    }
180    ss->udp_event =
181        event_new(ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
182                  event_callback, ss);
183    if( ss->udp_event == NULL )
184        tr_nerr("UDP", "Couldn't allocate IPv4 event");
185
186 ipv6:
187    if(tr_globalIPv6())
188        rebind_ipv6(ss, TRUE);
189    if(ss->udp6_socket >= 0) {
190        ss->udp6_event =
191            event_new(ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
192                      event_callback, ss);
193        if(ss->udp6_event == NULL)
194            tr_nerr("UDP", "Couldn't allocate IPv6 event");
195    }
196
197    if(ss->isDHTEnabled)
198        tr_dhtInit(ss);
199
200    if(ss->udp_event)
201        event_add(ss->udp_event, NULL);
202    if(ss->udp6_event)
203        event_add(ss->udp6_event, NULL);
204}
205
206void
207tr_udpUninit(tr_session *ss)
208{
209    tr_dhtUninit(ss);
210
211    if(ss->udp_socket >= 0) {
212        tr_netCloseSocket( ss->udp_socket );
213        ss->udp_socket = -1;
214    }
215
216    if(ss->udp_event) {
217        event_free(ss->udp_event);
218        ss->udp_event = NULL;
219    }
220
221    if(ss->udp6_socket >= 0) {
222        tr_netCloseSocket( ss->udp6_socket );
223        ss->udp6_socket = -1;
224    }
225
226    if(ss->udp6_event) {
227        event_free(ss->udp6_event);
228        ss->udp6_event = NULL;
229    }
230
231    if(ss->udp6_bound) {
232        free(ss->udp6_bound);
233        ss->udp6_bound = NULL;
234    }
235}
Note: See TracBrowser for help on using the repository browser.