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

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

Fix UDP error handling.

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