chroma: chain: favor YUV10 if output is YUV10
[vlc.git] / src / network / stream.c
blob90744ac0f0105bba71ee3d826bfec4edee99f719
1 /*****************************************************************************
2 * stream.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 * @ingroup transport
24 * @file
25 * Transport-layer stream abstraction
27 * This file implements the transport-layer stream (vlc_tls) abstraction.
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #ifdef HAVE_POLL
35 # include <poll.h>
36 #endif
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #ifdef HAVE_SYS_UIO_H
41 # include <sys/uio.h>
42 #endif
43 #ifdef HAVE_NETINET_TCP_H
44 # include <netinet/tcp.h>
45 #endif
46 #ifndef SOL_TCP
47 # define SOL_TCP IPPROTO_TCP
48 #endif
50 #include <vlc_common.h>
51 #include <vlc_tls.h>
52 #include <vlc_interrupt.h>
54 ssize_t vlc_tls_Read(vlc_tls_t *session, void *buf, size_t len, bool waitall)
56 struct pollfd ufd;
57 struct iovec iov;
59 ufd.fd = vlc_tls_GetFD(session);
60 ufd.events = POLLIN;
61 iov.iov_base = buf;
62 iov.iov_len = len;
64 for (size_t rcvd = 0;;)
66 if (vlc_killed())
68 errno = EINTR;
69 return -1;
72 ssize_t val = session->readv(session, &iov, 1);
73 if (val > 0)
75 if (!waitall)
76 return val;
77 iov.iov_base = (char *)iov.iov_base + val;
78 iov.iov_len -= val;
79 rcvd += val;
81 if (iov.iov_len == 0 || val == 0)
82 return rcvd;
83 if (val == -1)
85 if (vlc_killed())
86 return -1;
87 if (errno != EINTR && errno != EAGAIN)
88 return rcvd ? (ssize_t)rcvd : -1;
91 vlc_poll_i11e(&ufd, 1, -1);
95 ssize_t vlc_tls_Write(vlc_tls_t *session, const void *buf, size_t len)
97 struct pollfd ufd;
98 struct iovec iov;
100 ufd.fd = vlc_tls_GetFD(session);
101 ufd.events = POLLOUT;
102 iov.iov_base = (void *)buf;
103 iov.iov_len = len;
105 for (size_t sent = 0;;)
107 if (vlc_killed())
109 errno = EINTR;
110 return -1;
113 ssize_t val = session->writev(session, &iov, 1);
114 if (val > 0)
116 iov.iov_base = ((char *)iov.iov_base) + val;
117 iov.iov_len -= val;
118 sent += val;
120 if (iov.iov_len == 0 || val == 0)
121 return sent;
122 if (val == -1)
124 if (vlc_killed())
125 return -1;
126 if (errno != EINTR && errno != EAGAIN)
127 return sent ? (ssize_t)sent : -1;
130 vlc_poll_i11e(&ufd, 1, -1);
134 char *vlc_tls_GetLine(vlc_tls_t *session)
136 char *line = NULL;
137 size_t linelen = 0, linesize = 0;
141 if (linelen == linesize)
143 linesize += 1024;
145 char *newline = realloc(line, linesize);
146 if (unlikely(newline == NULL))
147 goto error;
148 line = newline;
151 if (vlc_tls_Read(session, line + linelen, 1, false) <= 0)
152 goto error;
154 while (line[linelen++] != '\n');
156 if (linelen >= 2 && line[linelen - 2] == '\r')
157 line[linelen - 2] = '\0';
158 return line;
160 error:
161 free(line);
162 return NULL;
165 typedef struct vlc_tls_socket
167 struct vlc_tls tls;
168 int fd;
169 socklen_t peerlen;
170 struct sockaddr peer[];
171 } vlc_tls_socket_t;
173 static int vlc_tls_SocketGetFD(vlc_tls_t *tls)
175 vlc_tls_socket_t *sock = (struct vlc_tls_socket *)tls;
177 return sock->fd;
180 static ssize_t vlc_tls_SocketRead(vlc_tls_t *tls, struct iovec *iov,
181 unsigned count)
183 struct msghdr msg =
185 .msg_iov = iov,
186 .msg_iovlen = count,
189 return recvmsg(vlc_tls_SocketGetFD(tls), &msg, 0);
192 static ssize_t vlc_tls_SocketWrite(vlc_tls_t *tls, const struct iovec *iov,
193 unsigned count)
195 const struct msghdr msg =
197 .msg_iov = (struct iovec *)iov,
198 .msg_iovlen = count,
201 return sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL);
204 static int vlc_tls_SocketShutdown(vlc_tls_t *tls, bool duplex)
206 return shutdown(vlc_tls_SocketGetFD(tls), duplex ? SHUT_RDWR : SHUT_WR);
209 static void vlc_tls_SocketClose(vlc_tls_t *tls)
211 net_Close(vlc_tls_SocketGetFD(tls));
212 free(tls);
215 static vlc_tls_t *vlc_tls_SocketAlloc(int fd,
216 const struct sockaddr *restrict peer,
217 socklen_t peerlen)
219 vlc_tls_socket_t *sock = malloc(sizeof (*sock) + peerlen);
220 if (unlikely(sock == NULL))
221 return NULL;
223 vlc_tls_t *tls = &sock->tls;
225 tls->get_fd = vlc_tls_SocketGetFD;
226 tls->readv = vlc_tls_SocketRead;
227 tls->writev = vlc_tls_SocketWrite;
228 tls->shutdown = vlc_tls_SocketShutdown;
229 tls->close = vlc_tls_SocketClose;
230 tls->p = NULL;
232 sock->fd = fd;
233 sock->peerlen = peerlen;
234 if (peerlen > 0)
235 memcpy(sock->peer, peer, peerlen);
236 return tls;
239 vlc_tls_t *vlc_tls_SocketOpen(int fd)
241 return vlc_tls_SocketAlloc(fd, NULL, 0);
244 int vlc_tls_SocketPair(int family, int protocol, vlc_tls_t *pair[2])
246 int fds[2];
248 if (vlc_socketpair(family, SOCK_STREAM, protocol, fds, true))
249 return -1;
251 for (size_t i = 0; i < 2; i++)
253 setsockopt(fds[i], SOL_SOCKET, SO_REUSEADDR,
254 &(int){ 1 }, sizeof (int));
256 pair[i] = vlc_tls_SocketAlloc(fds[i], NULL, 0);
257 if (unlikely(pair[i] == NULL))
259 net_Close(fds[i]);
260 if (i)
261 vlc_tls_SessionDelete(pair[0]);
262 else
263 net_Close(fds[1]);
264 return -1;
267 return 0;
271 * Allocates an unconnected transport layer socket.
273 static vlc_tls_t *vlc_tls_SocketAddrInfo(const struct addrinfo *restrict info)
275 int fd = vlc_socket(info->ai_family, info->ai_socktype, info->ai_protocol,
276 true /* nonblocking */);
277 if (fd == -1)
278 return NULL;
280 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
282 if (info->ai_socktype == SOCK_STREAM && info->ai_protocol == IPPROTO_TCP)
283 setsockopt(fd, SOL_TCP, TCP_NODELAY, &(int){ 1 }, sizeof (int));
285 vlc_tls_t *sk = vlc_tls_SocketAlloc(fd, info->ai_addr, info->ai_addrlen);
286 if (unlikely(sk == NULL))
287 net_Close(fd);
288 return sk;
292 * Waits for pending transport layer socket connection.
294 static int vlc_tls_WaitConnect(vlc_tls_t *tls)
296 const int fd = vlc_tls_GetFD(tls);
297 struct pollfd ufd;
299 ufd.fd = fd;
300 ufd.events = POLLOUT;
304 if (vlc_killed())
306 errno = EINTR;
307 return -1;
310 while (vlc_poll_i11e(&ufd, 1, -1) <= 0);
312 int val;
313 socklen_t len = sizeof (val);
315 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len))
316 return -1;
318 if (val != 0)
320 errno = val;
321 return -1;
323 return 0;
327 * Connects a transport layer socket.
329 static ssize_t vlc_tls_Connect(vlc_tls_t *tls)
331 const vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
333 if (connect(sock->fd, sock->peer, sock->peerlen) == 0)
334 return 0;
335 #ifndef _WIN32
336 if (errno != EINPROGRESS)
337 return -1;
338 #else
339 if (WSAGetLastError() != WSAEWOULDBLOCK)
340 return -1;
341 #endif
342 return vlc_tls_WaitConnect(tls);
345 /* Callback for combined connection establishment and initial send */
346 static ssize_t vlc_tls_ConnectWrite(vlc_tls_t *tls,
347 const struct iovec *iov,unsigned count)
349 #ifdef MSG_FASTOPEN
350 vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
351 const struct msghdr msg =
353 .msg_name = sock->peer,
354 .msg_namelen = sock->peerlen,
355 .msg_iov = (struct iovec *)iov,
356 .msg_iovlen = count,
358 ssize_t ret;
360 /* Next time, write directly. Do not retry to connect. */
361 tls->writev = vlc_tls_SocketWrite;
363 ret = sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL|MSG_FASTOPEN);
364 if (ret >= 0)
365 { /* Fast open in progress */
366 return ret;
369 if (errno == EINPROGRESS)
371 if (vlc_tls_WaitConnect(tls))
372 return -1;
374 else
375 if (errno != EOPNOTSUPP)
376 return -1;
377 /* Fast open not supported or disabled... fallback to normal mode */
378 #else
379 tls->writev = vlc_tls_SocketWrite;
380 #endif
382 if (vlc_tls_Connect(tls))
383 return -1;
385 return vlc_tls_SocketWrite(tls, iov, count);
388 vlc_tls_t *vlc_tls_SocketOpenAddrInfo(const struct addrinfo *restrict info,
389 bool defer_connect)
391 vlc_tls_t *sock = vlc_tls_SocketAddrInfo(info);
392 if (sock == NULL)
393 return NULL;
395 if (defer_connect)
396 { /* The socket is not connected yet.
397 * The connection will be triggered on the first send. */
398 sock->writev = vlc_tls_ConnectWrite;
400 else
402 if (vlc_tls_Connect(sock))
404 vlc_tls_SessionDelete(sock);
405 sock = NULL;
408 return sock;
411 vlc_tls_t *vlc_tls_SocketOpenTCP(vlc_object_t *obj, const char *name,
412 unsigned port)
414 struct addrinfo hints =
416 .ai_socktype = SOCK_STREAM,
417 .ai_protocol = IPPROTO_TCP,
418 }, *res;
420 assert(name != NULL);
421 msg_Dbg(obj, "resolving %s ...", name);
423 int val = vlc_getaddrinfo_i11e(name, port, &hints, &res);
424 if (val != 0)
425 { /* TODO: C locale for gai_strerror() */
426 msg_Err(obj, "cannot resolve %s port %u: %s", name, port,
427 gai_strerror(val));
428 return NULL;
431 msg_Dbg(obj, "connecting to %s port %u ...", name, port);
433 /* TODO: implement RFC8305 */
434 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
436 vlc_tls_t *tls = vlc_tls_SocketOpenAddrInfo(p, false);
437 if (tls == NULL)
439 msg_Err(obj, "connection error: %s", vlc_strerror_c(errno));
440 continue;
443 freeaddrinfo(res);
444 return tls;
447 freeaddrinfo(res);
448 return NULL;