blob: 2fb1c8b02afa7f1baada2aa77be5830001707e0a
1 | /* |
2 | * Copyright (c) 2007 The FFmpeg Project |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with FFmpeg; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | #include <fcntl.h> |
22 | #include "network.h" |
23 | #include "tls.h" |
24 | #include "url.h" |
25 | #include "libavcodec/internal.h" |
26 | #include "libavutil/avutil.h" |
27 | #include "libavutil/mem.h" |
28 | #include "libavutil/time.h" |
29 | |
30 | int ff_tls_init(void) |
31 | { |
32 | #if CONFIG_TLS_OPENSSL_PROTOCOL |
33 | int ret; |
34 | if ((ret = ff_openssl_init()) < 0) |
35 | return ret; |
36 | #endif |
37 | #if CONFIG_TLS_GNUTLS_PROTOCOL |
38 | ff_gnutls_init(); |
39 | #endif |
40 | return 0; |
41 | } |
42 | |
43 | void ff_tls_deinit(void) |
44 | { |
45 | #if CONFIG_TLS_OPENSSL_PROTOCOL |
46 | ff_openssl_deinit(); |
47 | #endif |
48 | #if CONFIG_TLS_GNUTLS_PROTOCOL |
49 | ff_gnutls_deinit(); |
50 | #endif |
51 | } |
52 | |
53 | int ff_network_inited_globally; |
54 | |
55 | int ff_network_init(void) |
56 | { |
57 | #if HAVE_WINSOCK2_H |
58 | WSADATA wsaData; |
59 | #endif |
60 | |
61 | if (!ff_network_inited_globally) |
62 | av_log(NULL, AV_LOG_WARNING, "Using network protocols without global " |
63 | "network initialization. Please use " |
64 | "avformat_network_init(), this will " |
65 | "become mandatory later.\n"); |
66 | #if HAVE_WINSOCK2_H |
67 | if (WSAStartup(MAKEWORD(1,1), &wsaData)) |
68 | return 0; |
69 | #endif |
70 | return 1; |
71 | } |
72 | |
73 | int ff_network_wait_fd(int fd, int write) |
74 | { |
75 | int ev = write ? POLLOUT : POLLIN; |
76 | struct pollfd p = { .fd = fd, .events = ev, .revents = 0 }; |
77 | int ret; |
78 | ret = poll(&p, 1, POLLING_TIME); |
79 | return ret < 0 ? ff_neterrno() : p.revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN); |
80 | } |
81 | |
82 | int ff_network_wait_fd_timeout(int fd, int write, int64_t timeout, AVIOInterruptCB *int_cb) |
83 | { |
84 | int ret; |
85 | int64_t wait_start = 0; |
86 | |
87 | while (1) { |
88 | if (ff_check_interrupt(int_cb)) |
89 | return AVERROR_EXIT; |
90 | ret = ff_network_wait_fd(fd, write); |
91 | if (ret != AVERROR(EAGAIN)) |
92 | return ret; |
93 | if (timeout > 0) { |
94 | if (!wait_start) |
95 | wait_start = av_gettime_relative(); |
96 | else if (av_gettime_relative() - wait_start > timeout) |
97 | return AVERROR(ETIMEDOUT); |
98 | } |
99 | } |
100 | } |
101 | |
102 | void ff_network_close(void) |
103 | { |
104 | #if HAVE_WINSOCK2_H |
105 | WSACleanup(); |
106 | #endif |
107 | } |
108 | |
109 | #if HAVE_WINSOCK2_H |
110 | int ff_neterrno(void) |
111 | { |
112 | int err = WSAGetLastError(); |
113 | switch (err) { |
114 | case WSAEWOULDBLOCK: |
115 | return AVERROR(EAGAIN); |
116 | case WSAEINTR: |
117 | return AVERROR(EINTR); |
118 | case WSAEPROTONOSUPPORT: |
119 | return AVERROR(EPROTONOSUPPORT); |
120 | case WSAETIMEDOUT: |
121 | return AVERROR(ETIMEDOUT); |
122 | case WSAECONNREFUSED: |
123 | return AVERROR(ECONNREFUSED); |
124 | case WSAEINPROGRESS: |
125 | return AVERROR(EINPROGRESS); |
126 | } |
127 | return -err; |
128 | } |
129 | #endif |
130 | |
131 | int ff_is_multicast_address(struct sockaddr *addr) |
132 | { |
133 | if (addr->sa_family == AF_INET) { |
134 | return IN_MULTICAST(ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr)); |
135 | } |
136 | #if HAVE_STRUCT_SOCKADDR_IN6 |
137 | if (addr->sa_family == AF_INET6) { |
138 | return IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)addr)->sin6_addr); |
139 | } |
140 | #endif |
141 | |
142 | return 0; |
143 | } |
144 | |
145 | static int ff_poll_interrupt(struct pollfd *p, nfds_t nfds, int timeout, |
146 | AVIOInterruptCB *cb) |
147 | { |
148 | int runs = timeout / POLLING_TIME; |
149 | int ret = 0; |
150 | |
151 | do { |
152 | if (ff_check_interrupt(cb)) |
153 | return AVERROR_EXIT; |
154 | ret = poll(p, nfds, POLLING_TIME); |
155 | if (ret != 0) |
156 | break; |
157 | } while (timeout <= 0 || runs-- > 0); |
158 | |
159 | if (!ret) |
160 | return AVERROR(ETIMEDOUT); |
161 | if (ret < 0) |
162 | return AVERROR(errno); |
163 | return ret; |
164 | } |
165 | |
166 | int ff_socket(int af, int type, int proto) |
167 | { |
168 | int fd; |
169 | |
170 | #ifdef SOCK_CLOEXEC |
171 | fd = socket(af, type | SOCK_CLOEXEC, proto); |
172 | if (fd == -1 && errno == EINVAL) |
173 | #endif |
174 | { |
175 | fd = socket(af, type, proto); |
176 | #if HAVE_FCNTL |
177 | if (fd != -1) { |
178 | if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) |
179 | av_log(NULL, AV_LOG_DEBUG, "Failed to set close on exec\n"); |
180 | } |
181 | #endif |
182 | } |
183 | #ifdef SO_NOSIGPIPE |
184 | if (fd != -1) |
185 | setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int)); |
186 | #endif |
187 | return fd; |
188 | } |
189 | |
190 | int ff_listen(int fd, const struct sockaddr *addr, |
191 | socklen_t addrlen) |
192 | { |
193 | int ret; |
194 | int reuse = 1; |
195 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { |
196 | av_log(NULL, AV_LOG_WARNING, "setsockopt(SO_REUSEADDR) failed\n"); |
197 | } |
198 | ret = bind(fd, addr, addrlen); |
199 | if (ret) |
200 | return ff_neterrno(); |
201 | |
202 | ret = listen(fd, 1); |
203 | if (ret) |
204 | return ff_neterrno(); |
205 | return ret; |
206 | } |
207 | |
208 | int ff_accept(int fd, int timeout, URLContext *h) |
209 | { |
210 | int ret; |
211 | struct pollfd lp = { fd, POLLIN, 0 }; |
212 | |
213 | ret = ff_poll_interrupt(&lp, 1, timeout, &h->interrupt_callback); |
214 | if (ret < 0) |
215 | return ret; |
216 | |
217 | ret = accept(fd, NULL, NULL); |
218 | if (ret < 0) |
219 | return ff_neterrno(); |
220 | if (ff_socket_nonblock(ret, 1) < 0) |
221 | av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
222 | |
223 | return ret; |
224 | } |
225 | |
226 | int ff_listen_bind(int fd, const struct sockaddr *addr, |
227 | socklen_t addrlen, int timeout, URLContext *h) |
228 | { |
229 | int ret; |
230 | if ((ret = ff_listen(fd, addr, addrlen)) < 0) |
231 | return ret; |
232 | if ((ret = ff_accept(fd, timeout, h)) < 0) |
233 | return ret; |
234 | closesocket(fd); |
235 | return ret; |
236 | } |
237 | |
238 | int ff_listen_connect(int fd, const struct sockaddr *addr, |
239 | socklen_t addrlen, int timeout, URLContext *h, |
240 | int will_try_next) |
241 | { |
242 | struct pollfd p = {fd, POLLOUT, 0}; |
243 | int ret; |
244 | socklen_t optlen; |
245 | |
246 | if (ff_socket_nonblock(fd, 1) < 0) |
247 | av_log(NULL, AV_LOG_DEBUG, "ff_socket_nonblock failed\n"); |
248 | |
249 | while ((ret = connect(fd, addr, addrlen))) { |
250 | ret = ff_neterrno(); |
251 | switch (ret) { |
252 | case AVERROR(EINTR): |
253 | if (ff_check_interrupt(&h->interrupt_callback)) |
254 | return AVERROR_EXIT; |
255 | continue; |
256 | case AVERROR(EINPROGRESS): |
257 | case AVERROR(EAGAIN): |
258 | ret = ff_poll_interrupt(&p, 1, timeout, &h->interrupt_callback); |
259 | if (ret < 0) |
260 | return ret; |
261 | optlen = sizeof(ret); |
262 | if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen)) |
263 | ret = AVUNERROR(ff_neterrno()); |
264 | if (ret != 0) { |
265 | char errbuf[100]; |
266 | ret = AVERROR(ret); |
267 | av_strerror(ret, errbuf, sizeof(errbuf)); |
268 | if (will_try_next) |
269 | av_log(h, AV_LOG_WARNING, |
270 | "Connection to %s failed (%s), trying next address\n", |
271 | h->filename, errbuf); |
272 | else |
273 | av_log(h, AV_LOG_ERROR, "Connection to %s failed: %s\n", |
274 | h->filename, errbuf); |
275 | } |
276 | default: |
277 | return ret; |
278 | } |
279 | } |
280 | return ret; |
281 | } |
282 | |
283 | static int match_host_pattern(const char *pattern, const char *hostname) |
284 | { |
285 | int len_p, len_h; |
286 | if (!strcmp(pattern, "*")) |
287 | return 1; |
288 | // Skip a possible *. at the start of the pattern |
289 | if (pattern[0] == '*') |
290 | pattern++; |
291 | if (pattern[0] == '.') |
292 | pattern++; |
293 | len_p = strlen(pattern); |
294 | len_h = strlen(hostname); |
295 | if (len_p > len_h) |
296 | return 0; |
297 | // Simply check if the end of hostname is equal to 'pattern' |
298 | if (!strcmp(pattern, &hostname[len_h - len_p])) { |
299 | if (len_h == len_p) |
300 | return 1; // Exact match |
301 | if (hostname[len_h - len_p - 1] == '.') |
302 | return 1; // The matched substring is a domain and not just a substring of a domain |
303 | } |
304 | return 0; |
305 | } |
306 | |
307 | int ff_http_match_no_proxy(const char *no_proxy, const char *hostname) |
308 | { |
309 | char *buf, *start; |
310 | int ret = 0; |
311 | if (!no_proxy) |
312 | return 0; |
313 | if (!hostname) |
314 | return 0; |
315 | buf = av_strdup(no_proxy); |
316 | if (!buf) |
317 | return 0; |
318 | start = buf; |
319 | while (start) { |
320 | char *sep, *next = NULL; |
321 | start += strspn(start, " ,"); |
322 | sep = start + strcspn(start, " ,"); |
323 | if (*sep) { |
324 | next = sep + 1; |
325 | *sep = '\0'; |
326 | } |
327 | if (match_host_pattern(start, hostname)) { |
328 | ret = 1; |
329 | break; |
330 | } |
331 | start = next; |
332 | } |
333 | av_free(buf); |
334 | return ret; |
335 | } |
336 |