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 *****************************************************************************/
25 * Transport Layer Session protocol API.
38 # define SOL_TCP IPPROTO_TCP
41 #include <vlc_common.h>
45 #include <vlc_modules.h>
46 #include <vlc_interrupt.h>
48 /*** TLS credentials ***/
50 static int tls_server_load(void *func
, va_list ap
)
52 int (*activate
) (vlc_tls_creds_t
*, const char *, const char *) = func
;
53 vlc_tls_creds_t
*crd
= va_arg (ap
, vlc_tls_creds_t
*);
54 const char *cert
= va_arg (ap
, const char *);
55 const char *key
= va_arg (ap
, const char *);
57 return activate (crd
, cert
, key
);
60 static int tls_client_load(void *func
, va_list ap
)
62 int (*activate
) (vlc_tls_creds_t
*) = func
;
63 vlc_tls_creds_t
*crd
= va_arg (ap
, vlc_tls_creds_t
*);
65 return activate (crd
);
68 static void tls_unload(void *func
, va_list ap
)
70 void (*deactivate
) (vlc_tls_creds_t
*) = func
;
71 vlc_tls_creds_t
*crd
= va_arg (ap
, vlc_tls_creds_t
*);
77 vlc_tls_ServerCreate (vlc_object_t
*obj
, const char *cert_path
,
80 vlc_tls_creds_t
*srv
= vlc_custom_create (obj
, sizeof (*srv
),
82 if (unlikely(srv
== NULL
))
88 srv
->module
= vlc_module_load (srv
, "tls server", NULL
, false,
89 tls_server_load
, srv
, cert_path
, key_path
);
90 if (srv
->module
== NULL
)
92 msg_Err (srv
, "TLS server plugin not available");
93 vlc_object_release (srv
);
100 vlc_tls_creds_t
*vlc_tls_ClientCreate (vlc_object_t
*obj
)
102 vlc_tls_creds_t
*crd
= vlc_custom_create (obj
, sizeof (*crd
),
104 if (unlikely(crd
== NULL
))
107 crd
->module
= vlc_module_load (crd
, "tls client", NULL
, false,
108 tls_client_load
, crd
);
109 if (crd
->module
== NULL
)
111 msg_Err (crd
, "TLS client plugin not available");
112 vlc_object_release (crd
);
119 void vlc_tls_Delete (vlc_tls_creds_t
*crd
)
124 vlc_module_unload(crd
, crd
->module
, tls_unload
, crd
);
125 vlc_object_release (crd
);
129 /*** TLS session ***/
131 static vlc_tls_t
*vlc_tls_SessionCreate(vlc_tls_creds_t
*crd
,
134 const char *const *alpn
)
137 int canc
= vlc_savecancel();
138 session
= crd
->open(crd
, sock
, host
, alpn
);
139 vlc_restorecancel(canc
);
145 void vlc_tls_SessionDelete (vlc_tls_t
*session
)
147 int canc
= vlc_savecancel();
148 session
->ops
->close(session
);
149 vlc_restorecancel(canc
);
152 static void cleanup_tls(void *data
)
154 vlc_tls_t
*session
= data
;
156 vlc_tls_SessionDelete (session
);
159 vlc_tls_t
*vlc_tls_ClientSessionCreate(vlc_tls_creds_t
*crd
, vlc_tls_t
*sock
,
160 const char *host
, const char *service
,
161 const char *const *alpn
, char **alp
)
165 vlc_tls_t
*session
= vlc_tls_SessionCreate(crd
, sock
, host
, alpn
);
169 int canc
= vlc_savecancel();
170 vlc_tick_t deadline
= vlc_tick_now ();
171 deadline
+= VLC_TICK_FROM_MS( var_InheritInteger (crd
, "ipv4-timeout") );
173 struct pollfd ufd
[1];
174 ufd
[0].fd
= vlc_tls_GetFD(sock
);
176 vlc_cleanup_push (cleanup_tls
, session
);
177 while ((val
= crd
->handshake(crd
, session
, host
, service
, alp
)) != 0)
179 if (val
< 0 || vlc_killed() )
182 msg_Err(crd
, "TLS session handshake error");
184 vlc_tls_SessionDelete (session
);
189 vlc_tick_t now
= vlc_tick_now ();
194 ufd
[0] .events
= (val
== 1) ? POLLIN
: POLLOUT
;
196 vlc_restorecancel(canc
);
197 val
= vlc_poll_i11e(ufd
, 1, MS_FROM_VLC_TICK(deadline
- now
));
198 canc
= vlc_savecancel();
201 msg_Err(crd
, "TLS session handshake timeout");
206 vlc_restorecancel(canc
);
210 vlc_tls_t
*vlc_tls_ServerSessionCreate(vlc_tls_creds_t
*crd
,
212 const char *const *alpn
)
214 return vlc_tls_SessionCreate(crd
, sock
, NULL
, alpn
);
217 vlc_tls_t
*vlc_tls_SocketOpenTLS(vlc_tls_creds_t
*creds
, const char *name
,
218 unsigned port
, const char *service
,
219 const char *const *alpn
, char **alp
)
221 struct addrinfo hints
=
223 .ai_socktype
= SOCK_STREAM
,
224 .ai_protocol
= IPPROTO_TCP
,
227 msg_Dbg(creds
, "resolving %s ...", name
);
229 int val
= vlc_getaddrinfo_i11e(name
, port
, &hints
, &res
);
231 { /* TODO: C locale for gai_strerror() */
232 msg_Err(creds
, "cannot resolve %s port %u: %s", name
, port
,
237 for (const struct addrinfo
*p
= res
; p
!= NULL
; p
= p
->ai_next
)
239 vlc_tls_t
*tcp
= vlc_tls_SocketOpenAddrInfo(p
, true);
242 msg_Err(creds
, "socket error: %s", vlc_strerror_c(errno
));
246 vlc_tls_t
*tls
= vlc_tls_ClientSessionCreate(creds
, tcp
, name
, service
,
254 msg_Err(creds
, "connection error: %s", vlc_strerror_c(errno
));
255 vlc_tls_SessionDelete(tcp
);