codec: spudec: force osd start time for forced spu overlays
[vlc.git] / src / network / tls.c
blob4ebcfb319ce57a561b9fa5884754cc34f75aedd3
1 /*****************************************************************************
2 * tls.c
3 *****************************************************************************
4 * Copyright © 2004-2016 Rémi Denis-Courmont
5 * $Id$
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
22 /**
23 * @file
24 * Transport Layer Socket abstraction.
26 * This file implements the Transport Layer Socket (vlc_tls) abstraction.
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #ifdef HAVE_POLL
34 # include <poll.h>
35 #endif
36 #include <assert.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #ifdef HAVE_SYS_UIO_H
42 # include <sys/uio.h>
43 #endif
44 #ifdef HAVE_NETINET_TCP_H
45 # include <netinet/tcp.h>
46 #endif
47 #ifndef SOL_TCP
48 # define SOL_TCP IPPROTO_TCP
49 #endif
51 #include <vlc_common.h>
52 #include "libvlc.h"
54 #include <vlc_tls.h>
55 #include <vlc_modules.h>
56 #include <vlc_interrupt.h>
58 /*** TLS credentials ***/
60 static int tls_server_load(void *func, va_list ap)
62 int (*activate) (vlc_tls_creds_t *, const char *, const char *) = func;
63 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
64 const char *cert = va_arg (ap, const char *);
65 const char *key = va_arg (ap, const char *);
67 return activate (crd, cert, key);
70 static int tls_client_load(void *func, va_list ap)
72 int (*activate) (vlc_tls_creds_t *) = func;
73 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
75 return activate (crd);
78 static void tls_unload(void *func, va_list ap)
80 void (*deactivate) (vlc_tls_creds_t *) = func;
81 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
83 deactivate (crd);
86 vlc_tls_creds_t *
87 vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_path,
88 const char *key_path)
90 vlc_tls_creds_t *srv = vlc_custom_create (obj, sizeof (*srv),
91 "tls server");
92 if (unlikely(srv == NULL))
93 return NULL;
95 if (key_path == NULL)
96 key_path = cert_path;
98 srv->module = vlc_module_load (srv, "tls server", NULL, false,
99 tls_server_load, srv, cert_path, key_path);
100 if (srv->module == NULL)
102 msg_Err (srv, "TLS server plugin not available");
103 vlc_object_release (srv);
104 return NULL;
107 return srv;
110 vlc_tls_creds_t *vlc_tls_ClientCreate (vlc_object_t *obj)
112 vlc_tls_creds_t *crd = vlc_custom_create (obj, sizeof (*crd),
113 "tls client");
114 if (unlikely(crd == NULL))
115 return NULL;
117 crd->module = vlc_module_load (crd, "tls client", NULL, false,
118 tls_client_load, crd);
119 if (crd->module == NULL)
121 msg_Err (crd, "TLS client plugin not available");
122 vlc_object_release (crd);
123 return NULL;
126 return crd;
129 void vlc_tls_Delete (vlc_tls_creds_t *crd)
131 if (crd == NULL)
132 return;
134 vlc_module_unload(crd, crd->module, tls_unload, crd);
135 vlc_object_release (crd);
139 /*** TLS session ***/
141 static vlc_tls_t *vlc_tls_SessionCreate(vlc_tls_creds_t *crd,
142 vlc_tls_t *sock,
143 const char *host,
144 const char *const *alpn)
146 vlc_tls_t *session;
147 int canc = vlc_savecancel();
148 session = crd->open(crd, sock, host, alpn);
149 vlc_restorecancel(canc);
150 if (session != NULL)
151 session->p = sock;
152 return session;
155 void vlc_tls_SessionDelete (vlc_tls_t *session)
157 int canc = vlc_savecancel();
158 session->close(session);
159 vlc_restorecancel(canc);
162 static void cleanup_tls(void *data)
164 vlc_tls_t *session = data;
166 vlc_tls_SessionDelete (session);
169 #undef vlc_tls_ClientSessionCreate
170 vlc_tls_t *vlc_tls_ClientSessionCreate(vlc_tls_creds_t *crd, vlc_tls_t *sock,
171 const char *host, const char *service,
172 const char *const *alpn, char **alp)
174 int val;
176 vlc_tls_t *session = vlc_tls_SessionCreate(crd, sock, host, alpn);
177 if (session == NULL)
178 return NULL;
180 int canc = vlc_savecancel();
181 mtime_t deadline = mdate ();
182 deadline += var_InheritInteger (crd, "ipv4-timeout") * 1000;
184 struct pollfd ufd[1];
185 ufd[0].fd = vlc_tls_GetFD(sock);
187 vlc_cleanup_push (cleanup_tls, session);
188 while ((val = crd->handshake(crd, session, host, service, alp)) != 0)
190 if (val < 0)
192 msg_Err(crd, "TLS session handshake error");
193 error:
194 vlc_tls_SessionDelete (session);
195 session = NULL;
196 break;
199 mtime_t now = mdate ();
200 if (now > deadline)
201 now = deadline;
203 assert (val <= 2);
204 ufd[0] .events = (val == 1) ? POLLIN : POLLOUT;
206 vlc_restorecancel(canc);
207 val = poll (ufd, 1, (deadline - now) / 1000);
208 canc = vlc_savecancel();
209 if (val == 0)
211 msg_Err(crd, "TLS session handshake timeout");
212 goto error;
215 vlc_cleanup_pop();
216 vlc_restorecancel(canc);
217 return session;
220 vlc_tls_t *vlc_tls_ServerSessionCreate(vlc_tls_creds_t *crd,
221 vlc_tls_t *sock,
222 const char *const *alpn)
224 return vlc_tls_SessionCreate(crd, sock, NULL, alpn);
227 ssize_t vlc_tls_Read(vlc_tls_t *session, void *buf, size_t len, bool waitall)
229 struct pollfd ufd;
230 struct iovec iov;
232 ufd.fd = vlc_tls_GetFD(session);
233 ufd.events = POLLIN;
234 iov.iov_base = buf;
235 iov.iov_len = len;
237 for (size_t rcvd = 0;;)
239 if (vlc_killed())
241 errno = EINTR;
242 return -1;
245 ssize_t val = session->readv(session, &iov, 1);
246 if (val > 0)
248 if (!waitall)
249 return val;
250 iov.iov_base = (char *)iov.iov_base + val;
251 iov.iov_len -= val;
252 rcvd += val;
254 if (iov.iov_len == 0 || val == 0)
255 return rcvd;
256 if (val == -1 && errno != EINTR && errno != EAGAIN)
257 return rcvd ? (ssize_t)rcvd : -1;
259 vlc_poll_i11e(&ufd, 1, -1);
263 ssize_t vlc_tls_Write(vlc_tls_t *session, const void *buf, size_t len)
265 struct pollfd ufd;
266 struct iovec iov;
268 ufd.fd = vlc_tls_GetFD(session);
269 ufd.events = POLLOUT;
270 iov.iov_base = (void *)buf;
271 iov.iov_len = len;
273 for (size_t sent = 0;;)
275 if (vlc_killed())
277 errno = EINTR;
278 return -1;
281 ssize_t val = session->writev(session, &iov, 1);
282 if (val > 0)
284 iov.iov_base = ((char *)iov.iov_base) + val;
285 iov.iov_len -= val;
286 sent += val;
288 if (iov.iov_len == 0 || val == 0)
289 return sent;
290 if (val == -1 && errno != EINTR && errno != EAGAIN)
291 return sent ? (ssize_t)sent : -1;
293 vlc_poll_i11e(&ufd, 1, -1);
297 char *vlc_tls_GetLine(vlc_tls_t *session)
299 char *line = NULL;
300 size_t linelen = 0, linesize = 0;
304 if (linelen == linesize)
306 linesize += 1024;
308 char *newline = realloc(line, linesize);
309 if (unlikely(newline == NULL))
310 goto error;
311 line = newline;
314 if (vlc_tls_Read(session, line + linelen, 1, false) <= 0)
315 goto error;
317 while (line[linelen++] != '\n');
319 if (linelen >= 2 && line[linelen - 2] == '\r')
320 line[linelen - 2] = '\0';
321 return line;
323 error:
324 free(line);
325 return NULL;
328 typedef struct vlc_tls_socket
330 struct vlc_tls tls;
331 int fd;
332 socklen_t peerlen;
333 struct sockaddr peer[];
334 } vlc_tls_socket_t;
336 static int vlc_tls_SocketGetFD(vlc_tls_t *tls)
338 vlc_tls_socket_t *sock = (struct vlc_tls_socket *)tls;
340 return sock->fd;
343 static ssize_t vlc_tls_SocketRead(vlc_tls_t *tls, struct iovec *iov,
344 unsigned count)
346 struct msghdr msg =
348 .msg_iov = iov,
349 .msg_iovlen = count,
352 return recvmsg(vlc_tls_SocketGetFD(tls), &msg, 0);
355 static ssize_t vlc_tls_SocketWrite(vlc_tls_t *tls, const struct iovec *iov,
356 unsigned count)
358 const struct msghdr msg =
360 .msg_iov = (struct iovec *)iov,
361 .msg_iovlen = count,
364 return sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL);
367 static int vlc_tls_SocketShutdown(vlc_tls_t *tls, bool duplex)
369 return shutdown(vlc_tls_SocketGetFD(tls), duplex ? SHUT_RDWR : SHUT_WR);
372 static void vlc_tls_SocketClose(vlc_tls_t *tls)
374 net_Close(vlc_tls_SocketGetFD(tls));
375 free(tls);
378 static vlc_tls_t *vlc_tls_SocketAlloc(int fd,
379 const struct sockaddr *restrict peer,
380 socklen_t peerlen)
382 vlc_tls_socket_t *sock = malloc(sizeof (*sock) + peerlen);
383 if (unlikely(sock == NULL))
384 return NULL;
386 vlc_tls_t *tls = &sock->tls;
388 tls->get_fd = vlc_tls_SocketGetFD;
389 tls->readv = vlc_tls_SocketRead;
390 tls->writev = vlc_tls_SocketWrite;
391 tls->shutdown = vlc_tls_SocketShutdown;
392 tls->close = vlc_tls_SocketClose;
393 tls->p = NULL;
395 sock->fd = fd;
396 sock->peerlen = peerlen;
397 if (peerlen > 0)
398 memcpy(sock->peer, peer, peerlen);
399 return tls;
402 vlc_tls_t *vlc_tls_SocketOpen(int fd)
404 return vlc_tls_SocketAlloc(fd, NULL, 0);
407 int vlc_tls_SocketPair(int family, int protocol, vlc_tls_t *pair[2])
409 int fds[2];
411 if (vlc_socketpair(family, SOCK_STREAM, protocol, fds, true))
412 return -1;
414 for (size_t i = 0; i < 2; i++)
416 setsockopt(fds[i], SOL_SOCKET, SO_REUSEADDR,
417 &(int){ 1 }, sizeof (int));
419 pair[i] = vlc_tls_SocketAlloc(fds[i], NULL, 0);
420 if (unlikely(pair[i] == NULL))
422 net_Close(fds[i]);
423 if (i)
424 vlc_tls_SessionDelete(pair[0]);
425 else
426 net_Close(fds[1]);
427 return -1;
430 return 0;
434 * Allocates an unconnected transport layer socket.
436 static vlc_tls_t *vlc_tls_SocketAddrInfo(const struct addrinfo *restrict info)
438 int fd = vlc_socket(info->ai_family, info->ai_socktype, info->ai_protocol,
439 true /* nonblocking */);
440 if (fd == -1)
441 return NULL;
443 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
445 if (info->ai_socktype == SOCK_STREAM && info->ai_protocol == IPPROTO_TCP)
446 setsockopt(fd, SOL_TCP, TCP_NODELAY, &(int){ 1 }, sizeof (int));
448 vlc_tls_t *sk = vlc_tls_SocketAlloc(fd, info->ai_addr, info->ai_addrlen);
449 if (unlikely(sk == NULL))
450 net_Close(fd);
451 return sk;
455 * Waits for pending transport layer socket connection.
457 static int vlc_tls_WaitConnect(vlc_tls_t *tls)
459 const int fd = vlc_tls_GetFD(tls);
460 struct pollfd ufd;
462 ufd.fd = fd;
463 ufd.events = POLLOUT;
467 if (vlc_killed())
469 errno = EINTR;
470 return -1;
473 while (vlc_poll_i11e(&ufd, 1, -1) <= 0);
475 int val;
476 socklen_t len = sizeof (val);
478 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len))
479 return -1;
481 if (val != 0)
483 errno = val;
484 return -1;
486 return 0;
490 * Connects a transport layer socket.
492 static ssize_t vlc_tls_Connect(vlc_tls_t *tls)
494 const vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
496 if (connect(sock->fd, sock->peer, sock->peerlen) == 0)
497 return 0;
498 #ifndef _WIN32
499 if (errno != EINPROGRESS)
500 return -1;
501 #else
502 if (WSAGetLastError() != WSAEWOULDBLOCK)
503 return -1;
504 #endif
505 return vlc_tls_WaitConnect(tls);
508 /* Callback for combined connection establishment and initial send */
509 static ssize_t vlc_tls_ConnectWrite(vlc_tls_t *tls,
510 const struct iovec *iov,unsigned count)
512 #ifdef MSG_FASTOPEN
513 vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
514 const struct msghdr msg =
516 .msg_name = sock->peer,
517 .msg_namelen = sock->peerlen,
518 .msg_iov = (struct iovec *)iov,
519 .msg_iovlen = count,
521 ssize_t ret;
523 /* Next time, write directly. Do not retry to connect. */
524 tls->writev = vlc_tls_SocketWrite;
526 ret = sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL|MSG_FASTOPEN);
527 if (ret >= 0)
528 { /* Fast open in progress */
529 return ret;
532 if (errno == EINPROGRESS)
534 if (vlc_tls_WaitConnect(tls))
535 return -1;
537 else
538 if (errno != EOPNOTSUPP)
539 return -1;
540 /* Fast open not supported or disabled... fallback to normal mode */
541 #else
542 tls->writev = vlc_tls_SocketWrite;
543 #endif
545 if (vlc_tls_Connect(tls))
546 return -1;
548 return vlc_tls_SocketWrite(tls, iov, count);
551 vlc_tls_t *vlc_tls_SocketOpenAddrInfo(const struct addrinfo *restrict info,
552 bool defer_connect)
554 vlc_tls_t *sock = vlc_tls_SocketAddrInfo(info);
555 if (sock == NULL)
556 return NULL;
558 if (defer_connect)
559 { /* The socket is not connected yet.
560 * The connection will be triggered on the first send. */
561 sock->writev = vlc_tls_ConnectWrite;
563 else
565 if (vlc_tls_Connect(sock))
567 vlc_tls_SessionDelete(sock);
568 sock = NULL;
571 return sock;
574 vlc_tls_t *vlc_tls_SocketOpenTCP(vlc_object_t *obj, const char *name,
575 unsigned port)
577 struct addrinfo hints =
579 .ai_socktype = SOCK_STREAM,
580 .ai_protocol = IPPROTO_TCP,
581 }, *res;
583 assert(name != NULL);
584 msg_Dbg(obj, "resolving %s ...", name);
586 int val = vlc_getaddrinfo_i11e(name, port, &hints, &res);
587 if (val != 0)
588 { /* TODO: C locale for gai_strerror() */
589 msg_Err(obj, "cannot resolve %s port %u: %s", name, port,
590 gai_strerror(val));
591 return NULL;
594 msg_Dbg(obj, "connecting to %s port %u ...", name, port);
596 /* TODO: implement RFC6555 */
597 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
599 vlc_tls_t *tls = vlc_tls_SocketOpenAddrInfo(p, false);
600 if (tls == NULL)
602 msg_Err(obj, "connection error: %s", vlc_strerror_c(errno));
603 continue;
606 freeaddrinfo(res);
607 return tls;
610 freeaddrinfo(res);
611 return NULL;
614 vlc_tls_t *vlc_tls_SocketOpenTLS(vlc_tls_creds_t *creds, const char *name,
615 unsigned port, const char *service,
616 const char *const *alpn, char **alp)
618 struct addrinfo hints =
620 .ai_socktype = SOCK_STREAM,
621 .ai_protocol = IPPROTO_TCP,
622 }, *res;
624 msg_Dbg(creds, "resolving %s ...", name);
626 int val = vlc_getaddrinfo_i11e(name, port, &hints, &res);
627 if (val != 0)
628 { /* TODO: C locale for gai_strerror() */
629 msg_Err(creds, "cannot resolve %s port %u: %s", name, port,
630 gai_strerror(val));
631 return NULL;
634 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
636 vlc_tls_t *tcp = vlc_tls_SocketOpenAddrInfo(p, true);
637 if (tcp == NULL)
639 msg_Err(creds, "socket error: %s", vlc_strerror_c(errno));
640 continue;
643 vlc_tls_t *tls = vlc_tls_ClientSessionCreate(creds, tcp, name, service,
644 alpn, alp);
645 if (tls != NULL)
646 { /* Success! */
647 freeaddrinfo(res);
648 return tls;
651 msg_Err(creds, "connection error: %s", vlc_strerror_c(errno));
652 vlc_tls_SessionDelete(tcp);
655 /* Failure! */
656 freeaddrinfo(res);
657 return NULL;