vlc | branch: master | Rémi Denis-Courmont <[email protected]> | Sat Feb 25 22:09:05 2017 +0200| [e2583c430e391b9397f89f35765ea3f4c6e402f8] | committer: Rémi Denis-Courmont
tls: revector > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e2583c430e391b9397f89f35765ea3f4c6e402f8 --- src/network/tls.c | 189 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 137 insertions(+), 52 deletions(-) diff --git a/src/network/tls.c b/src/network/tls.c index a4f35c9..2d04eb4 100644 --- a/src/network/tls.c +++ b/src/network/tls.c @@ -35,6 +35,7 @@ #include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #ifdef HAVE_SYS_UIO_H # include <sys/uio.h> #endif @@ -328,6 +329,8 @@ typedef struct vlc_tls_socket { struct vlc_tls tls; int fd; + socklen_t peerlen; + struct sockaddr peer[]; } vlc_tls_socket_t; static int vlc_tls_SocketGetFD(vlc_tls_t *tls) @@ -372,9 +375,11 @@ static void vlc_tls_SocketClose(vlc_tls_t *tls) free(tls); } -vlc_tls_t *vlc_tls_SocketOpen(int fd) +static vlc_tls_t *vlc_tls_SocketAlloc(int fd, + const struct sockaddr *restrict peer, + socklen_t peerlen) { - vlc_tls_socket_t *sock = malloc(sizeof (*sock)); + vlc_tls_socket_t *sock = malloc(sizeof (*sock) + peerlen); if (unlikely(sock == NULL)) return NULL; @@ -386,72 +391,115 @@ vlc_tls_t *vlc_tls_SocketOpen(int fd) tls->shutdown = vlc_tls_SocketShutdown; tls->close = vlc_tls_SocketClose; tls->p = NULL; + sock->fd = fd; + sock->peerlen = peerlen; + if (peerlen > 0) + memcpy(sock->peer, peer, peerlen); return tls; } -static vlc_tls_t *vlc_tls_SocketOpenAddrInfoSingle(vlc_object_t *obj, - const struct addrinfo *restrict info) +vlc_tls_t *vlc_tls_SocketOpen(int fd) +{ + return vlc_tls_SocketAlloc(fd, NULL, 0); +} + +/** + * Allocates an unconnected transport layer socket. + */ +static vlc_tls_t *vlc_tls_SocketAddrInfo(const struct addrinfo *restrict info) { int fd = vlc_socket(info->ai_family, info->ai_socktype, info->ai_protocol, true /* nonblocking */); if (fd == -1) - { - msg_Warn(obj, "socket error: %s", vlc_strerror_c(errno)); return NULL; - } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int)); - int val = connect(fd, info->ai_addr, info->ai_addrlen); - if (val != 0) - { - if (errno != EINPROGRESS) - { - msg_Err(obj, "connection error: %s", vlc_strerror_c(errno)); - goto giveup; - } + vlc_tls_t *sk = vlc_tls_SocketAlloc(fd, info->ai_addr, info->ai_addrlen); + if (unlikely(sk == NULL)) + net_Close(fd); + return sk; +} - struct pollfd ufd; +/** + * Waits for pending transport layer socket connection. + */ +static int vlc_tls_WaitConnect(vlc_tls_t *tls) +{ + const int fd = vlc_tls_GetFD(tls); + struct pollfd ufd; - ufd.fd = fd; - ufd.events = POLLOUT; + ufd.fd = fd; + ufd.events = POLLOUT; - do + do + { + if (vlc_killed()) { - if (vlc_killed()) - { - errno = EINTR; - goto giveup; - } + errno = EINTR; + return -1; } - while (vlc_poll_i11e(&ufd, 1, -1) <= 0); + } + while (vlc_poll_i11e(&ufd, 1, -1) <= 0); - socklen_t len = sizeof (val); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len)) - { - msg_Err(obj, "socket option error: %s", - vlc_strerror_c(errno)); - goto giveup; - } + int val; + socklen_t len = sizeof (val); - if (val != 0) - { - msg_Err(obj, "connection error: %s", vlc_strerror_c(val)); - errno = val; - goto giveup; - } + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len)) + return -1; + + if (val != 0) + { + errno = val; + return -1; } + return 0; +} - vlc_tls_t *tls = vlc_tls_SocketOpen(fd); - if (unlikely(tls == NULL)) - goto giveup; +/** + * Connects a transport layer socket. + */ +static ssize_t vlc_tls_Connect(vlc_tls_t *tls) +{ + const vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls; + + if (connect(sock->fd, sock->peer, sock->peerlen) == 0) + return 0; +#ifndef _WIN32 + if (errno != EINPROGRESS) + return -1; +#else + if (WSAGetLastError() != WSAEWOULDBLOCK) + return -1; +#endif + return vlc_tls_WaitConnect(tls); +} - return tls; +/* Callback for combined connection establishment and initial send */ +static ssize_t vlc_tls_ConnectWrite(vlc_tls_t *tls, + const struct iovec *iov,unsigned count) +{ + if (vlc_tls_Connect(tls)) + return -1; -giveup: - net_Close(fd); - return NULL; + /* Next time, write directly. Do not retry to connect. */ + tls->writev = vlc_tls_SocketWrite; + return vlc_tls_SocketWrite(tls, iov, count); +} + +static vlc_tls_t *vlc_tls_ConnectAddrInfo(const struct addrinfo *info) +{ + vlc_tls_t *tls = vlc_tls_SocketAddrInfo(info); + if (tls == NULL) + return NULL; + + if (vlc_tls_Connect(tls)) + { + vlc_tls_SessionDelete(tls); + tls = NULL; + } + return tls; } vlc_tls_t *vlc_tls_SocketOpenAddrInfo(vlc_object_t *obj, @@ -460,9 +508,11 @@ vlc_tls_t *vlc_tls_SocketOpenAddrInfo(vlc_object_t *obj, /* TODO: implement RFC6555 */ for (const struct addrinfo *p = info; p != NULL; p = p->ai_next) { - vlc_tls_t *tls = vlc_tls_SocketOpenAddrInfoSingle(obj, p); + vlc_tls_t *tls = vlc_tls_ConnectAddrInfo(p); if (tls != NULL) return tls; + + msg_Err(obj, "connection error: %s", vlc_strerror_c(errno)); } return NULL; } @@ -498,13 +548,48 @@ vlc_tls_t *vlc_tls_SocketOpenTLS(vlc_tls_creds_t *creds, const char *name, unsigned port, const char *service, const char *const *alpn, char **alp) { - vlc_tls_t *tcp = vlc_tls_SocketOpenTCP(VLC_OBJECT(creds), name, port); - if (tcp == NULL) + struct addrinfo hints = + { + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }, *res; + + msg_Dbg(creds, "resolving %s ...", name); + + int val = vlc_getaddrinfo_i11e(name, port, &hints, &res); + if (val != 0) + { /* TODO: C locale for gai_strerror() */ + msg_Err(creds, "cannot resolve %s port %u: %s", name, port, + gai_strerror(val)); return NULL; + } - vlc_tls_t *tls = vlc_tls_ClientSessionCreate(creds, tcp, name, service, - alpn, alp); - if (tls == NULL) + for (const struct addrinfo *p = res; p != NULL; p = p->ai_next) + { + vlc_tls_t *tcp = vlc_tls_SocketAddrInfo(p); + if (tcp == NULL) + { + msg_Err(creds, "socket error: %s", vlc_strerror_c(errno)); + continue; + } + + /* The socket is not connected yet. + * The connection will be triggered on the first send. */ + tcp->writev = vlc_tls_ConnectWrite; + + vlc_tls_t *tls = vlc_tls_ClientSessionCreate(creds, tcp, name, service, + alpn, alp); + if (tls != NULL) + { /* Success! */ + freeaddrinfo(res); + return tls; + } + + msg_Err(creds, "connection error: %s", vlc_strerror_c(errno)); vlc_tls_SessionDelete(tcp); - return tls; + } + + /* Failure! */ + freeaddrinfo(res); + return NULL; } _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
