1 /*****************************************************************************
2  * io.c: network I/O functions
3  *****************************************************************************
4  * Copyright (C) 2004-2005, 2007 VLC authors and VideoLAN
5  * Copyright © 2005-2006 Rémi Denis-Courmont
6  * $Id: 703a7928a223db48f2a0b79ec32f9d84f8b95210 $
7  *
8  * Authors: Laurent Aimar <fenrir@videolan.org>
9  *          Rémi Denis-Courmont
10  *          Christophe Mutricy <xtophe at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26 
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34 
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <limits.h>
38 #include <errno.h>
39 #include <assert.h>
40 
41 #include <unistd.h>
42 #ifdef HAVE_LINUX_DCCP_H
43 /* TODO: use glibc instead of linux-kernel headers */
44 # include <linux/dccp.h>
45 # define SOL_DCCP 269
46 #endif
47 
48 #include <vlc_common.h>
49 #include <vlc_network.h>
50 #include <vlc_interrupt.h>
51 
52 extern int rootwrap_bind (int family, int socktype, int protocol,
53                           const struct sockaddr *addr, size_t alen);
54 
net_Socket(vlc_object_t * p_this,int family,int socktype,int protocol)55 int net_Socket (vlc_object_t *p_this, int family, int socktype,
56                 int protocol)
57 {
58     int fd = vlc_socket (family, socktype, protocol, true);
59     if (fd == -1)
60     {
61         if (net_errno != EAFNOSUPPORT)
62             msg_Err (p_this, "cannot create socket: %s",
63                      vlc_strerror_c(net_errno));
64         return -1;
65     }
66 
67     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
68 
69 #ifdef IPV6_V6ONLY
70     /*
71      * Accepts only IPv6 connections on IPv6 sockets.
72      * If possible, we should open two sockets, but it is not always possible.
73      */
74     if (family == AF_INET6)
75         setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int));
76 #endif
77 
78 #if defined (_WIN32)
79 # ifndef IPV6_PROTECTION_LEVEL
80 #  warning Please update your C library headers.
81 #  define IPV6_PROTECTION_LEVEL 23
82 #  define PROTECTION_LEVEL_UNRESTRICTED 10
83 # endif
84     if (family == AF_INET6)
85         setsockopt (fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL,
86                     &(int){ PROTECTION_LEVEL_UNRESTRICTED }, sizeof (int));
87 #endif
88 
89 #ifdef DCCP_SOCKOPT_SERVICE
90     if (socktype == SOL_DCCP)
91     {
92         char *dccps = var_InheritString (p_this, "dccp-service");
93         if (dccps != NULL)
94         {
95             setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, dccps,
96                         (strlen (dccps) + 3) & ~3);
97             free (dccps);
98         }
99     }
100 #endif
101 
102     return fd;
103 }
104 
105 
net_Listen(vlc_object_t * p_this,const char * psz_host,int i_port,int type,int protocol)106 int *net_Listen (vlc_object_t *p_this, const char *psz_host,
107                  int i_port, int type, int protocol)
108 {
109     struct addrinfo hints = {
110         .ai_socktype = type,
111         .ai_protocol = protocol,
112         .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
113     }, *res;
114 
115     msg_Dbg (p_this, "net: listening to %s port %d",
116              (psz_host != NULL) ? psz_host : "*", i_port);
117 
118     int i_val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
119     if (i_val)
120     {
121         msg_Err (p_this, "Cannot resolve %s port %d : %s",
122                  (psz_host != NULL) ? psz_host : "", i_port,
123                  gai_strerror (i_val));
124         return NULL;
125     }
126 
127     int *sockv = NULL;
128     unsigned sockc = 0;
129 
130     for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
131     {
132         int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
133                              ptr->ai_protocol);
134         if (fd == -1)
135         {
136             msg_Dbg (p_this, "socket error: %s", vlc_strerror_c(net_errno));
137             continue;
138         }
139 
140         /* Bind the socket */
141 #if defined (_WIN32)
142         /*
143          * Under Win32 and for multicasting, we bind to INADDR_ANY.
144          * This is of course a severe bug, since the socket would logically
145          * receive unicast traffic, and multicast traffic of groups subscribed
146          * to via other sockets.
147          */
148         if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
149          && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
150         {
151             // This works for IPv4 too - don't worry!
152             struct sockaddr_in6 dumb =
153             {
154                 .sin6_family = ptr->ai_addr->sa_family,
155                 .sin6_port =  ((struct sockaddr_in *)(ptr->ai_addr))->sin_port
156             };
157 
158             bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
159         }
160         else
161 #endif
162         if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
163         {
164             int err = net_errno;
165             net_Close (fd);
166 #if !defined(_WIN32)
167             fd = rootwrap_bind (ptr->ai_family, ptr->ai_socktype,
168                                 ptr->ai_protocol,
169                                 ptr->ai_addr, ptr->ai_addrlen);
170             if (fd != -1)
171             {
172                 msg_Dbg (p_this, "got socket %d from rootwrap", fd);
173             }
174             else
175 #endif
176             {
177                 msg_Err (p_this, "socket bind error: %s", vlc_strerror_c(err));
178                 continue;
179             }
180         }
181 
182         if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen))
183         {
184             if (net_Subscribe (p_this, fd, ptr->ai_addr, ptr->ai_addrlen))
185             {
186                 net_Close (fd);
187                 continue;
188             }
189         }
190 
191         /* Listen */
192         switch (ptr->ai_socktype)
193         {
194             case SOCK_STREAM:
195             case SOCK_RDM:
196             case SOCK_SEQPACKET:
197 #ifdef SOCK_DCCP
198             case SOCK_DCCP:
199 #endif
200                 if (listen (fd, INT_MAX))
201                 {
202                     msg_Err (p_this, "socket listen error: %s",
203                              vlc_strerror_c(net_errno));
204                     net_Close (fd);
205                     continue;
206                 }
207         }
208 
209         int *nsockv = (int *)realloc (sockv, (sockc + 2) * sizeof (int));
210         if (nsockv != NULL)
211         {
212             nsockv[sockc++] = fd;
213             sockv = nsockv;
214         }
215         else
216             net_Close (fd);
217     }
218 
219     freeaddrinfo (res);
220 
221     if (sockv != NULL)
222         sockv[sockc] = -1;
223 
224     return sockv;
225 }
226 
227 /**
228  * Reads data from a socket, blocking until all requested data is received or
229  * the end of the stream is reached.
230  * This function is a cancellation point.
231  * @return -1 on error, or the number of bytes of read.
232  */
ssize_t(net_Read)233 ssize_t (net_Read)(vlc_object_t *restrict obj, int fd,
234                    void *restrict buf, size_t len)
235 {
236     size_t rd = 0;
237 
238     do
239     {
240         if (vlc_killed())
241         {
242             vlc_testcancel();
243             errno = EINTR;
244             return -1;
245         }
246 
247         ssize_t val = vlc_recv_i11e(fd, buf, len, 0);
248         if (val < 0)
249         {
250             if (errno == EINTR || errno == EAGAIN)
251                 continue;
252 #ifdef _WIN32
253             else if (WSAGetLastError() == WSAEMSGSIZE) /* datagram too big */
254             {
255                 msg_Warn(obj, "read truncated to %zu bytes", len);
256                 val = len;
257             }
258 #endif
259             else
260             {
261                 msg_Err(obj, "read error: %s", vlc_strerror_c(errno));
262                 return rd ? (ssize_t)rd : -1;
263             }
264         }
265 
266         rd += val;
267 
268         if (val == 0)
269             break;
270 
271         assert(len >= (size_t)val);
272         len -= val;
273         buf = ((char *)buf) + val;
274     }
275     while (len > 0);
276 
277     return rd;
278 }
279 
280 /**
281  * Writes data to a socket.
282  * This blocks until all data is written or an error occurs.
283  *
284  * This function is a cancellation point.
285  *
286  * @return the total number of bytes written, or -1 if an error occurs
287  * before any data is written.
288  */
ssize_t(net_Write)289 ssize_t (net_Write)(vlc_object_t *obj, int fd, const void *buf, size_t len)
290 {
291     size_t written = 0;
292 
293     do
294     {
295         if (vlc_killed())
296         {
297             vlc_testcancel();
298             errno = EINTR;
299             return -1;
300         }
301 
302         ssize_t val = vlc_send_i11e (fd, buf, len, MSG_NOSIGNAL);
303         if (val == -1)
304         {
305             if (errno == EINTR || errno == EAGAIN)
306                 continue;
307 
308             msg_Err(obj, "write error: %s", vlc_strerror_c(errno));
309             return written ? (ssize_t)written : -1;
310         }
311 
312         if (val == 0)
313             break;
314 
315         written += val;
316         assert(len >= (size_t)val);
317         len -= val;
318         buf = ((const char *)buf) + val;
319     }
320     while (len > 0);
321 
322     return written;
323 }
324 
325 #undef net_Gets
326 /**
327  * Reads a line from a file descriptor.
328  * This function is not thread-safe; the same file descriptor I/O cannot be
329  * read by another thread at the same time (although it can be written to).
330  *
331  * @note This only works with stream-oriented file descriptors, not with
332  * datagram or packet-oriented ones.
333  *
334  * @return nul-terminated heap-allocated string, or NULL on I/O error.
335  */
net_Gets(vlc_object_t * obj,int fd)336 char *net_Gets(vlc_object_t *obj, int fd)
337 {
338     char *buf = NULL;
339     size_t size = 0, len = 0;
340 
341     for (;;)
342     {
343         if (len == size)
344         {
345             if (unlikely(size >= (1 << 16)))
346             {
347                 errno = EMSGSIZE;
348                 goto error; /* put sane buffer size limit */
349             }
350 
351             char *newbuf = realloc(buf, size + 1024);
352             if (unlikely(newbuf == NULL))
353                 goto error;
354             buf = newbuf;
355             size += 1024;
356         }
357         assert(len < size);
358 
359         ssize_t val = vlc_recv_i11e(fd, buf + len, size - len, MSG_PEEK);
360         if (val <= 0)
361             goto error;
362 
363         char *end = memchr(buf + len, '\n', val);
364         if (end != NULL)
365             val = (end + 1) - (buf + len);
366         if (recv(fd, buf + len, val, 0) != val)
367             goto error;
368         len += val;
369         if (end != NULL)
370             break;
371     }
372 
373     assert(len > 0);
374     buf[--len] = '\0';
375     if (len > 0 && buf[--len] == '\r')
376         buf[len] = '\0';
377     return buf;
378 error:
379     msg_Err(obj, "read error: %s", vlc_strerror_c(errno));
380     free(buf);
381     return NULL;
382 }
383 
384 #undef net_Printf
net_Printf(vlc_object_t * p_this,int fd,const char * psz_fmt,...)385 ssize_t net_Printf( vlc_object_t *p_this, int fd, const char *psz_fmt, ... )
386 {
387     int i_ret;
388     va_list args;
389     va_start( args, psz_fmt );
390     i_ret = net_vaPrintf( p_this, fd, psz_fmt, args );
391     va_end( args );
392 
393     return i_ret;
394 }
395 
396 #undef net_vaPrintf
net_vaPrintf(vlc_object_t * p_this,int fd,const char * psz_fmt,va_list args)397 ssize_t net_vaPrintf( vlc_object_t *p_this, int fd,
398                       const char *psz_fmt, va_list args )
399 {
400     char    *psz;
401     int      i_ret;
402 
403     int i_size = vasprintf( &psz, psz_fmt, args );
404     if( i_size == -1 )
405         return -1;
406     i_ret = net_Write( p_this, fd, psz, i_size ) < i_size
407         ? -1 : i_size;
408     free( psz );
409 
410     return i_ret;
411 }
412