1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright © 2004-2016 Rémi Denis-Courmont
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 *****************************************************************************/
24 * Transport Layer Socket abstraction.
26 * This file implements the Transport Layer Socket (vlc_tls) abstraction.
44 #ifdef HAVE_NETINET_TCP_H
45 # include <netinet/tcp.h>
48 # define SOL_TCP IPPROTO_TCP
51 #include <vlc_common.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
*);
87 vlc_tls_ServerCreate (vlc_object_t
*obj
, const char *cert_path
,
90 vlc_tls_creds_t
*srv
= vlc_custom_create (obj
, sizeof (*srv
),
92 if (unlikely(srv
== NULL
))
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
);
110 vlc_tls_creds_t
*vlc_tls_ClientCreate (vlc_object_t
*obj
)
112 vlc_tls_creds_t
*crd
= vlc_custom_create (obj
, sizeof (*crd
),
114 if (unlikely(crd
== 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
);
129 void vlc_tls_Delete (vlc_tls_creds_t
*crd
)
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
,
144 const char *const *alpn
)
147 int canc
= vlc_savecancel();
148 session
= crd
->open(crd
, sock
, host
, alpn
);
149 vlc_restorecancel(canc
);
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
)
176 vlc_tls_t
*session
= vlc_tls_SessionCreate(crd
, sock
, host
, alpn
);
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 || vlc_killed() )
193 msg_Err(crd
, "TLS session handshake error");
195 vlc_tls_SessionDelete (session
);
200 mtime_t now
= mdate ();
205 ufd
[0] .events
= (val
== 1) ? POLLIN
: POLLOUT
;
207 vlc_restorecancel(canc
);
208 val
= vlc_poll_i11e(ufd
, 1, (deadline
- now
) / 1000);
209 canc
= vlc_savecancel();
212 msg_Err(crd
, "TLS session handshake timeout");
217 vlc_restorecancel(canc
);
221 vlc_tls_t
*vlc_tls_ServerSessionCreate(vlc_tls_creds_t
*crd
,
223 const char *const *alpn
)
225 return vlc_tls_SessionCreate(crd
, sock
, NULL
, alpn
);
228 ssize_t
vlc_tls_Read(vlc_tls_t
*session
, void *buf
, size_t len
, bool waitall
)
233 ufd
.fd
= vlc_tls_GetFD(session
);
238 for (size_t rcvd
= 0;;)
246 ssize_t val
= session
->readv(session
, &iov
, 1);
251 iov
.iov_base
= (char *)iov
.iov_base
+ val
;
255 if (iov
.iov_len
== 0 || val
== 0)
261 if (errno
!= EINTR
&& errno
!= EAGAIN
)
262 return rcvd
? (ssize_t
)rcvd
: -1;
265 vlc_poll_i11e(&ufd
, 1, -1);
269 ssize_t
vlc_tls_Write(vlc_tls_t
*session
, const void *buf
, size_t len
)
274 ufd
.fd
= vlc_tls_GetFD(session
);
275 ufd
.events
= POLLOUT
;
276 iov
.iov_base
= (void *)buf
;
279 for (size_t sent
= 0;;)
287 ssize_t val
= session
->writev(session
, &iov
, 1);
290 iov
.iov_base
= ((char *)iov
.iov_base
) + val
;
294 if (iov
.iov_len
== 0 || val
== 0)
300 if (errno
!= EINTR
&& errno
!= EAGAIN
)
301 return sent
? (ssize_t
)sent
: -1;
304 vlc_poll_i11e(&ufd
, 1, -1);
308 char *vlc_tls_GetLine(vlc_tls_t
*session
)
311 size_t linelen
= 0, linesize
= 0;
315 if (linelen
== linesize
)
319 char *newline
= realloc(line
, linesize
);
320 if (unlikely(newline
== NULL
))
325 if (vlc_tls_Read(session
, line
+ linelen
, 1, false) <= 0)
328 while (line
[linelen
++] != '\n');
330 if (linelen
>= 2 && line
[linelen
- 2] == '\r')
331 line
[linelen
- 2] = '\0';
339 typedef struct vlc_tls_socket
344 struct sockaddr peer
[];
347 static int vlc_tls_SocketGetFD(vlc_tls_t
*tls
)
349 vlc_tls_socket_t
*sock
= (struct vlc_tls_socket
*)tls
;
354 static ssize_t
vlc_tls_SocketRead(vlc_tls_t
*tls
, struct iovec
*iov
,
363 return recvmsg(vlc_tls_SocketGetFD(tls
), &msg
, 0);
366 static ssize_t
vlc_tls_SocketWrite(vlc_tls_t
*tls
, const struct iovec
*iov
,
369 const struct msghdr msg
=
371 .msg_iov
= (struct iovec
*)iov
,
375 return sendmsg(vlc_tls_SocketGetFD(tls
), &msg
, MSG_NOSIGNAL
);
378 static int vlc_tls_SocketShutdown(vlc_tls_t
*tls
, bool duplex
)
380 return shutdown(vlc_tls_SocketGetFD(tls
), duplex
? SHUT_RDWR
: SHUT_WR
);
383 static void vlc_tls_SocketClose(vlc_tls_t
*tls
)
385 net_Close(vlc_tls_SocketGetFD(tls
));
389 static vlc_tls_t
*vlc_tls_SocketAlloc(int fd
,
390 const struct sockaddr
*restrict peer
,
393 vlc_tls_socket_t
*sock
= malloc(sizeof (*sock
) + peerlen
);
394 if (unlikely(sock
== NULL
))
397 vlc_tls_t
*tls
= &sock
->tls
;
399 tls
->get_fd
= vlc_tls_SocketGetFD
;
400 tls
->readv
= vlc_tls_SocketRead
;
401 tls
->writev
= vlc_tls_SocketWrite
;
402 tls
->shutdown
= vlc_tls_SocketShutdown
;
403 tls
->close
= vlc_tls_SocketClose
;
407 sock
->peerlen
= peerlen
;
409 memcpy(sock
->peer
, peer
, peerlen
);
413 vlc_tls_t
*vlc_tls_SocketOpen(int fd
)
415 return vlc_tls_SocketAlloc(fd
, NULL
, 0);
418 int vlc_tls_SocketPair(int family
, int protocol
, vlc_tls_t
*pair
[2])
422 if (vlc_socketpair(family
, SOCK_STREAM
, protocol
, fds
, true))
425 for (size_t i
= 0; i
< 2; i
++)
427 setsockopt(fds
[i
], SOL_SOCKET
, SO_REUSEADDR
,
428 &(int){ 1 }, sizeof (int));
430 pair
[i
] = vlc_tls_SocketAlloc(fds
[i
], NULL
, 0);
431 if (unlikely(pair
[i
] == NULL
))
435 vlc_tls_SessionDelete(pair
[0]);
445 * Allocates an unconnected transport layer socket.
447 static vlc_tls_t
*vlc_tls_SocketAddrInfo(const struct addrinfo
*restrict info
)
449 int fd
= vlc_socket(info
->ai_family
, info
->ai_socktype
, info
->ai_protocol
,
450 true /* nonblocking */);
454 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &(int){ 1 }, sizeof (int));
456 if (info
->ai_socktype
== SOCK_STREAM
&& info
->ai_protocol
== IPPROTO_TCP
)
457 setsockopt(fd
, SOL_TCP
, TCP_NODELAY
, &(int){ 1 }, sizeof (int));
459 vlc_tls_t
*sk
= vlc_tls_SocketAlloc(fd
, info
->ai_addr
, info
->ai_addrlen
);
460 if (unlikely(sk
== NULL
))
466 * Waits for pending transport layer socket connection.
468 static int vlc_tls_WaitConnect(vlc_tls_t
*tls
)
470 const int fd
= vlc_tls_GetFD(tls
);
474 ufd
.events
= POLLOUT
;
484 while (vlc_poll_i11e(&ufd
, 1, -1) <= 0);
487 socklen_t len
= sizeof (val
);
489 if (getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &val
, &len
))
501 * Connects a transport layer socket.
503 static ssize_t
vlc_tls_Connect(vlc_tls_t
*tls
)
505 const vlc_tls_socket_t
*sock
= (vlc_tls_socket_t
*)tls
;
507 if (connect(sock
->fd
, sock
->peer
, sock
->peerlen
) == 0)
510 if (errno
!= EINPROGRESS
)
513 if (WSAGetLastError() != WSAEWOULDBLOCK
)
516 return vlc_tls_WaitConnect(tls
);
519 /* Callback for combined connection establishment and initial send */
520 static ssize_t
vlc_tls_ConnectWrite(vlc_tls_t
*tls
,
521 const struct iovec
*iov
,unsigned count
)
524 vlc_tls_socket_t
*sock
= (vlc_tls_socket_t
*)tls
;
525 const struct msghdr msg
=
527 .msg_name
= sock
->peer
,
528 .msg_namelen
= sock
->peerlen
,
529 .msg_iov
= (struct iovec
*)iov
,
534 /* Next time, write directly. Do not retry to connect. */
535 tls
->writev
= vlc_tls_SocketWrite
;
537 ret
= sendmsg(vlc_tls_SocketGetFD(tls
), &msg
, MSG_NOSIGNAL
|MSG_FASTOPEN
);
539 { /* Fast open in progress */
543 if (errno
== EINPROGRESS
)
545 if (vlc_tls_WaitConnect(tls
))
549 if (errno
!= EOPNOTSUPP
)
551 /* Fast open not supported or disabled... fallback to normal mode */
553 tls
->writev
= vlc_tls_SocketWrite
;
556 if (vlc_tls_Connect(tls
))
559 return vlc_tls_SocketWrite(tls
, iov
, count
);
562 vlc_tls_t
*vlc_tls_SocketOpenAddrInfo(const struct addrinfo
*restrict info
,
565 vlc_tls_t
*sock
= vlc_tls_SocketAddrInfo(info
);
570 { /* The socket is not connected yet.
571 * The connection will be triggered on the first send. */
572 sock
->writev
= vlc_tls_ConnectWrite
;
576 if (vlc_tls_Connect(sock
))
578 vlc_tls_SessionDelete(sock
);
585 vlc_tls_t
*vlc_tls_SocketOpenTCP(vlc_object_t
*obj
, const char *name
,
588 struct addrinfo hints
=
590 .ai_socktype
= SOCK_STREAM
,
591 .ai_protocol
= IPPROTO_TCP
,
594 assert(name
!= NULL
);
595 msg_Dbg(obj
, "resolving %s ...", name
);
597 int val
= vlc_getaddrinfo_i11e(name
, port
, &hints
, &res
);
599 { /* TODO: C locale for gai_strerror() */
600 msg_Err(obj
, "cannot resolve %s port %u: %s", name
, port
,
605 msg_Dbg(obj
, "connecting to %s port %u ...", name
, port
);
607 /* TODO: implement RFC6555 */
608 for (const struct addrinfo
*p
= res
; p
!= NULL
; p
= p
->ai_next
)
610 vlc_tls_t
*tls
= vlc_tls_SocketOpenAddrInfo(p
, false);
613 msg_Err(obj
, "connection error: %s", vlc_strerror_c(errno
));
625 vlc_tls_t
*vlc_tls_SocketOpenTLS(vlc_tls_creds_t
*creds
, const char *name
,
626 unsigned port
, const char *service
,
627 const char *const *alpn
, char **alp
)
629 struct addrinfo hints
=
631 .ai_socktype
= SOCK_STREAM
,
632 .ai_protocol
= IPPROTO_TCP
,
635 msg_Dbg(creds
, "resolving %s ...", name
);
637 int val
= vlc_getaddrinfo_i11e(name
, port
, &hints
, &res
);
639 { /* TODO: C locale for gai_strerror() */
640 msg_Err(creds
, "cannot resolve %s port %u: %s", name
, port
,
645 for (const struct addrinfo
*p
= res
; p
!= NULL
; p
= p
->ai_next
)
647 vlc_tls_t
*tcp
= vlc_tls_SocketOpenAddrInfo(p
, true);
650 msg_Err(creds
, "socket error: %s", vlc_strerror_c(errno
));
654 vlc_tls_t
*tls
= vlc_tls_ClientSessionCreate(creds
, tcp
, name
, service
,
662 msg_Err(creds
, "connection error: %s", vlc_strerror_c(errno
));
663 vlc_tls_SessionDelete(tcp
);