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)
192 msg_Err(crd
, "TLS session handshake error");
194 vlc_tls_SessionDelete (session
);
199 mtime_t now
= mdate ();
204 ufd
[0] .events
= (val
== 1) ? POLLIN
: POLLOUT
;
206 vlc_restorecancel(canc
);
207 val
= poll (ufd
, 1, (deadline
- now
) / 1000);
208 canc
= vlc_savecancel();
211 msg_Err(crd
, "TLS session handshake timeout");
216 vlc_restorecancel(canc
);
220 vlc_tls_t
*vlc_tls_ServerSessionCreate(vlc_tls_creds_t
*crd
,
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
)
232 ufd
.fd
= vlc_tls_GetFD(session
);
237 for (size_t rcvd
= 0;;)
245 ssize_t val
= session
->readv(session
, &iov
, 1);
250 iov
.iov_base
= (char *)iov
.iov_base
+ val
;
254 if (iov
.iov_len
== 0 || val
== 0)
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
)
268 ufd
.fd
= vlc_tls_GetFD(session
);
269 ufd
.events
= POLLOUT
;
270 iov
.iov_base
= (void *)buf
;
273 for (size_t sent
= 0;;)
281 ssize_t val
= session
->writev(session
, &iov
, 1);
284 iov
.iov_base
= ((char *)iov
.iov_base
) + val
;
288 if (iov
.iov_len
== 0 || val
== 0)
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
)
300 size_t linelen
= 0, linesize
= 0;
304 if (linelen
== linesize
)
308 char *newline
= realloc(line
, linesize
);
309 if (unlikely(newline
== NULL
))
314 if (vlc_tls_Read(session
, line
+ linelen
, 1, false) <= 0)
317 while (line
[linelen
++] != '\n');
319 if (linelen
>= 2 && line
[linelen
- 2] == '\r')
320 line
[linelen
- 2] = '\0';
328 typedef struct vlc_tls_socket
333 struct sockaddr peer
[];
336 static int vlc_tls_SocketGetFD(vlc_tls_t
*tls
)
338 vlc_tls_socket_t
*sock
= (struct vlc_tls_socket
*)tls
;
343 static ssize_t
vlc_tls_SocketRead(vlc_tls_t
*tls
, struct iovec
*iov
,
352 return recvmsg(vlc_tls_SocketGetFD(tls
), &msg
, 0);
355 static ssize_t
vlc_tls_SocketWrite(vlc_tls_t
*tls
, const struct iovec
*iov
,
358 const struct msghdr msg
=
360 .msg_iov
= (struct iovec
*)iov
,
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
));
378 static vlc_tls_t
*vlc_tls_SocketAlloc(int fd
,
379 const struct sockaddr
*restrict peer
,
382 vlc_tls_socket_t
*sock
= malloc(sizeof (*sock
) + peerlen
);
383 if (unlikely(sock
== 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
;
396 sock
->peerlen
= peerlen
;
398 memcpy(sock
->peer
, peer
, peerlen
);
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])
411 if (vlc_socketpair(family
, SOCK_STREAM
, protocol
, fds
, true))
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
))
424 vlc_tls_SessionDelete(pair
[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 */);
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
))
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
);
463 ufd
.events
= POLLOUT
;
473 while (vlc_poll_i11e(&ufd
, 1, -1) <= 0);
476 socklen_t len
= sizeof (val
);
478 if (getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &val
, &len
))
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)
499 if (errno
!= EINPROGRESS
)
502 if (WSAGetLastError() != WSAEWOULDBLOCK
)
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
)
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
,
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
);
528 { /* Fast open in progress */
532 if (errno
== EINPROGRESS
)
534 if (vlc_tls_WaitConnect(tls
))
538 if (errno
!= EOPNOTSUPP
)
540 /* Fast open not supported or disabled... fallback to normal mode */
542 tls
->writev
= vlc_tls_SocketWrite
;
545 if (vlc_tls_Connect(tls
))
548 return vlc_tls_SocketWrite(tls
, iov
, count
);
551 vlc_tls_t
*vlc_tls_SocketOpenAddrInfo(const struct addrinfo
*restrict info
,
554 vlc_tls_t
*sock
= vlc_tls_SocketAddrInfo(info
);
559 { /* The socket is not connected yet.
560 * The connection will be triggered on the first send. */
561 sock
->writev
= vlc_tls_ConnectWrite
;
565 if (vlc_tls_Connect(sock
))
567 vlc_tls_SessionDelete(sock
);
574 vlc_tls_t
*vlc_tls_SocketOpenTCP(vlc_object_t
*obj
, const char *name
,
577 struct addrinfo hints
=
579 .ai_socktype
= SOCK_STREAM
,
580 .ai_protocol
= IPPROTO_TCP
,
583 assert(name
!= NULL
);
584 msg_Dbg(obj
, "resolving %s ...", name
);
586 int val
= vlc_getaddrinfo_i11e(name
, port
, &hints
, &res
);
588 { /* TODO: C locale for gai_strerror() */
589 msg_Err(obj
, "cannot resolve %s port %u: %s", name
, port
,
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);
602 msg_Err(obj
, "connection error: %s", vlc_strerror_c(errno
));
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
,
624 msg_Dbg(creds
, "resolving %s ...", name
);
626 int val
= vlc_getaddrinfo_i11e(name
, port
, &hints
, &res
);
628 { /* TODO: C locale for gai_strerror() */
629 msg_Err(creds
, "cannot resolve %s port %u: %s", name
, port
,
634 for (const struct addrinfo
*p
= res
; p
!= NULL
; p
= p
->ai_next
)
636 vlc_tls_t
*tcp
= vlc_tls_SocketOpenAddrInfo(p
, true);
639 msg_Err(creds
, "socket error: %s", vlc_strerror_c(errno
));
643 vlc_tls_t
*tls
= vlc_tls_ClientSessionCreate(creds
, tcp
, name
, service
,
651 msg_Err(creds
, "connection error: %s", vlc_strerror_c(errno
));
652 vlc_tls_SessionDelete(tcp
);