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

Last change on this file since 11926 was 11926, checked in by jch, 11 years ago

Push testig for UTP enabled further down.

There's no need to test for DHT/uTP being enabled in tr-udp. The DHT
will silently discard any packets directed at the wrong session (see the
beginning of dhtCallback). As to uTP, we need to grok uTP packets
to close any remaining connections after we disabled uTP, so it's better
to participate in uTP, just reject any incoming connections.

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