]> git.sesse.net Git - vlc/blob - src/network/tcp.c
network: remove shadow variable
[vlc] / src / network / tcp.c
1 /*****************************************************************************
2  * tcp.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2005 VLC authors and VideoLAN
5  * Copyright (C) 2005-2006 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Laurent Aimar <fenrir@videolan.org>
9  *          Rémi Denis-Courmont <rem # videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34
35 #include <errno.h>
36 #include <assert.h>
37 #include <unistd.h>
38 #ifdef HAVE_POLL
39 # include <poll.h>
40 #endif
41
42 #include <vlc_network.h>
43 #if defined (_WIN32)
44 #   undef EINPROGRESS
45 #   define EINPROGRESS WSAEWOULDBLOCK
46 #   undef EWOULDBLOCK
47 #   define EWOULDBLOCK WSAEWOULDBLOCK
48 #   undef EAGAIN
49 #   define EAGAIN WSAEWOULDBLOCK
50 #   undef EINTR
51 #   define EINTR WSAEINTR
52 #endif
53
54 #include "libvlc.h" /* vlc_object_waitpipe */
55
56 static int SocksNegotiate( vlc_object_t *, int fd, int i_socks_version,
57                            const char *psz_user, const char *psz_passwd );
58 static int SocksHandshakeTCP( vlc_object_t *,
59                               int fd, int i_socks_version,
60                               const char *psz_user, const char *psz_passwd,
61                               const char *psz_host, int i_port );
62 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
63                        int i_protocol );
64
65 #undef net_Connect
66 /*****************************************************************************
67  * net_Connect:
68  *****************************************************************************
69  * Open a network connection.
70  * @return socket handler or -1 on error.
71  *****************************************************************************/
72 int net_Connect( vlc_object_t *p_this, const char *psz_host, int i_port,
73                  int type, int proto )
74 {
75     const char      *psz_realhost;
76     char            *psz_socks;
77     int             i_realport, i_handle = -1;
78
79     int evfd = vlc_object_waitpipe (p_this);
80     if (evfd == -1)
81         return -1;
82
83     psz_socks = var_InheritString( p_this, "socks" );
84     if( psz_socks != NULL )
85     {
86         char *psz = strchr( psz_socks, ':' );
87
88         if( psz )
89             *psz++ = '\0';
90
91         psz_realhost = psz_socks;
92         i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
93
94         msg_Dbg( p_this, "net: connecting to %s port %d (SOCKS) "
95                  "for %s port %d", psz_realhost, i_realport,
96                  psz_host, i_port );
97
98         /* We only implement TCP with SOCKS */
99         switch( type )
100         {
101             case 0:
102                 type = SOCK_STREAM;
103             case SOCK_STREAM:
104                 break;
105             default:
106                 msg_Err( p_this, "Socket type not supported through SOCKS" );
107                 free( psz_socks );
108                 return -1;
109         }
110         switch( proto )
111         {
112             case 0:
113                 proto = IPPROTO_TCP;
114             case IPPROTO_TCP:
115                 break;
116             default:
117                 msg_Err( p_this, "Transport not supported through SOCKS" );
118                 free( psz_socks );
119                 return -1;
120         }
121     }
122     else
123     {
124         psz_realhost = psz_host;
125         i_realport = i_port;
126
127         msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
128                  i_realport );
129     }
130
131     struct addrinfo hints = {
132         .ai_socktype = type,
133         .ai_protocol = proto,
134         .ai_flags = AI_NUMERICSERV | AI_IDN,
135     }, *res;
136
137     int val = vlc_getaddrinfo (psz_realhost, i_realport, &hints, &res);
138     if (val)
139     {
140         msg_Err (p_this, "cannot resolve %s port %d : %s", psz_realhost,
141                  i_realport, gai_strerror (val));
142         free( psz_socks );
143         return -1;
144     }
145     free( psz_socks );
146
147     int timeout = var_InheritInteger (p_this, "ipv4-timeout");
148     if (timeout < 0)
149         timeout = -1;
150
151     for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
152     {
153         int fd = net_Socket( p_this, ptr->ai_family,
154                              ptr->ai_socktype, ptr->ai_protocol );
155         if( fd == -1 )
156         {
157             msg_Dbg( p_this, "socket error: %s", vlc_strerror_c(net_errno) );
158             continue;
159         }
160
161         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
162         {
163             if( net_errno != EINPROGRESS && net_errno != EINTR )
164             {
165                 msg_Err( p_this, "connection failed: %s",
166                          vlc_strerror_c(net_errno) );
167                 goto next_ai;
168             }
169
170             struct pollfd ufd[2] = {
171                 { .fd = fd,   .events = POLLOUT },
172                 { .fd = evfd, .events = POLLIN },
173             };
174
175             do
176                 /* NOTE: timeout screwed up if we catch a signal (EINTR) */
177                 val = poll (ufd, sizeof (ufd) / sizeof (ufd[0]), timeout);
178             while ((val == -1) && (net_errno == EINTR));
179
180             switch (val)
181             {
182                  case -1: /* error */
183                      msg_Err (p_this, "polling error: %s",
184                               vlc_strerror_c(net_errno));
185                      goto next_ai;
186
187                  case 0: /* timeout */
188                      msg_Warn (p_this, "connection timed out");
189                      goto next_ai;
190
191                  default: /* something happended */
192                      if (ufd[1].revents)
193                          goto next_ai; /* LibVLC object killed */
194             }
195
196             /* There is NO WAY around checking SO_ERROR.
197              * Don't ifdef it out!!! */
198             if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &val,
199                             &(socklen_t){ sizeof (val) }) || val)
200             {
201                 msg_Err (p_this, "connection failed: %s",
202                          vlc_strerror_c(val));
203                 goto next_ai;
204             }
205         }
206
207         msg_Dbg( p_this, "connection succeeded (socket = %d)", fd );
208         i_handle = fd; /* success! */
209         break;
210
211 next_ai: /* failure */
212         net_Close( fd );
213         continue;
214     }
215
216     freeaddrinfo( res );
217
218     if( i_handle == -1 )
219         return -1;
220
221     if( psz_socks != NULL )
222     {
223         /* NOTE: psz_socks already free'd! */
224         char *psz_user = var_InheritString( p_this, "socks-user" );
225         char *psz_pwd  = var_InheritString( p_this, "socks-pwd" );
226
227         if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
228                                psz_host, i_port ) )
229         {
230             msg_Err( p_this, "SOCKS handshake failed" );
231             net_Close( i_handle );
232             i_handle = -1;
233         }
234
235         free( psz_user );
236         free( psz_pwd );
237     }
238
239     return i_handle;
240 }
241
242
243 int net_AcceptSingle (vlc_object_t *obj, int lfd)
244 {
245     int fd = vlc_accept (lfd, NULL, NULL, true);
246     if (fd == -1)
247     {
248         if (net_errno != EAGAIN && net_errno != EWOULDBLOCK)
249             msg_Err (obj, "accept failed (from socket %d): %s", lfd,
250                      vlc_strerror_c(net_errno));
251         return -1;
252     }
253
254     msg_Dbg (obj, "accepted socket %d (from socket %d)", fd, lfd);
255     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
256     return fd;
257 }
258
259
260 #undef net_Accept
261 /**
262  * Accepts an new connection on a set of listening sockets.
263  * If there are no pending connections, this function will wait.
264  * @note If the thread needs to handle events other than incoming connections,
265  * you need to use poll() and net_AcceptSingle() instead.
266  *
267  * @param p_this VLC object for logging and object kill signal
268  * @param pi_fd listening socket set
269  * @return -1 on error (may be transient error due to network issues),
270  * a new socket descriptor on success.
271  */
272 int net_Accept (vlc_object_t *p_this, int *pi_fd)
273 {
274     int evfd = vlc_object_waitpipe (p_this);
275
276     assert (pi_fd != NULL);
277
278     unsigned n = 0;
279     while (pi_fd[n] != -1)
280         n++;
281     struct pollfd ufd[n + 1];
282
283     /* Initialize file descriptor set */
284     for (unsigned i = 0; i <= n; i++)
285     {
286         ufd[i].fd = (i < n) ? pi_fd[i] : evfd;
287         ufd[i].events = POLLIN;
288     }
289     ufd[n].revents = 0;
290
291     for (;;)
292     {
293         while (poll (ufd, n + (evfd != -1), -1) == -1)
294         {
295             if (net_errno != EINTR)
296             {
297                 msg_Err (p_this, "poll error: %s", vlc_strerror_c(net_errno));
298                 return -1;
299             }
300         }
301
302         for (unsigned i = 0; i < n; i++)
303         {
304             if (ufd[i].revents == 0)
305                 continue;
306
307             int sfd = ufd[i].fd;
308             int fd = net_AcceptSingle (p_this, sfd);
309             if (fd == -1)
310                 continue;
311
312             /*
313              * Move listening socket to the end to let the others in the
314              * set a chance next time.
315              */
316             memmove (pi_fd + i, pi_fd + i + 1, n - (i + 1));
317             pi_fd[n - 1] = sfd;
318             return fd;
319         }
320
321         if (ufd[n].revents)
322         {
323             errno = EINTR;
324             break;
325         }
326     }
327     return -1;
328 }
329
330
331 /*****************************************************************************
332  * SocksNegotiate:
333  *****************************************************************************
334  * Negotiate authentication with a SOCKS server.
335  *****************************************************************************/
336 static int SocksNegotiate( vlc_object_t *p_obj,
337                            int fd, int i_socks_version,
338                            const char *psz_socks_user,
339                            const char *psz_socks_passwd )
340 {
341     uint8_t buffer[128+2*256];
342     int i_len;
343     bool b_auth = false;
344
345     if( i_socks_version != 5 )
346         return VLC_SUCCESS;
347
348     /* We negotiate authentication */
349     buffer[0] = i_socks_version;    /* SOCKS version */
350     if( psz_socks_user != NULL && psz_socks_passwd != NULL )
351     {
352         buffer[1] = 2;                  /* Number of methods */
353         buffer[2] = 0x00;               /* - No auth required */
354         buffer[3] = 0x02;               /* - USer/Password */
355         i_len = 4;
356         b_auth = true;
357     }
358     else
359     {
360         buffer[1] = 1;                  /* Number of methods */
361         buffer[2] = 0x00;               /* - No auth required */
362         i_len = 3;
363     }
364
365     if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
366         return VLC_EGENERIC;
367     if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 )
368         return VLC_EGENERIC;
369
370     msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
371
372     if( buffer[1] == 0x00 )
373     {
374         msg_Dbg( p_obj, "socks: no authentication required" );
375     }
376     else if( buffer[1] == 0x02 )
377     {
378         int i_len1 = __MIN( strlen(psz_socks_user), 255 );
379         int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
380         msg_Dbg( p_obj, "socks: username/password authentication" );
381
382         /* XXX: we don't support user/pwd > 255 (truncated)*/
383         buffer[0] = i_socks_version;        /* Version */
384         buffer[1] = i_len1;                 /* User length */
385         memcpy( &buffer[2], psz_socks_user, i_len1 );
386         buffer[2+i_len1] = i_len2;          /* Password length */
387         memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
388
389         i_len = 3 + i_len1 + i_len2;
390
391         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
392             return VLC_EGENERIC;
393
394         if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 )
395             return VLC_EGENERIC;
396
397         msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
398         if( buffer[1] != 0x00 )
399         {
400             msg_Err( p_obj, "socks: authentication rejected" );
401             return VLC_EGENERIC;
402         }
403     }
404     else
405     {
406         if( b_auth )
407             msg_Err( p_obj, "socks: unsupported authentication method %x",
408                      buffer[0] );
409         else
410             msg_Err( p_obj, "socks: authentication needed" );
411         return VLC_EGENERIC;
412     }
413
414     return VLC_SUCCESS;
415 }
416
417 /*****************************************************************************
418  * SocksHandshakeTCP:
419  *****************************************************************************
420  * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
421  *****************************************************************************/
422 static int SocksHandshakeTCP( vlc_object_t *p_obj,
423                               int fd,
424                               int i_socks_version,
425                               const char *psz_user, const char *psz_passwd,
426                               const char *psz_host, int i_port )
427 {
428     uint8_t buffer[128+2*256];
429
430     if( i_socks_version != 4 && i_socks_version != 5 )
431     {
432         msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
433         i_socks_version = 5;
434     }
435
436     if( i_socks_version == 5 &&
437         SocksNegotiate( p_obj, fd, i_socks_version,
438                         psz_user, psz_passwd ) )
439         return VLC_EGENERIC;
440
441     if( i_socks_version == 4 )
442     {
443         /* v4 only support ipv4 */
444         static const struct addrinfo hints = {
445             .ai_family = AF_INET,
446             .ai_socktype = SOCK_STREAM,
447             .ai_protocol = IPPROTO_TCP,
448             .ai_flags = AI_IDN,
449         };
450         struct addrinfo *res;
451
452         if (vlc_getaddrinfo (psz_host, 0, &hints, &res))
453             return VLC_EGENERIC;
454
455         buffer[0] = i_socks_version;
456         buffer[1] = 0x01;               /* CONNECT */
457         SetWBE( &buffer[2], i_port );   /* Port */
458         memcpy (&buffer[4],             /* Address */
459                 &((struct sockaddr_in *)(res->ai_addr))->sin_addr, 4);
460         freeaddrinfo (res);
461
462         buffer[8] = 0;                  /* Empty user id */
463
464         if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 )
465             return VLC_EGENERIC;
466         if( net_Read( p_obj, fd, NULL, buffer, 8, true ) != 8 )
467             return VLC_EGENERIC;
468
469         msg_Dbg( p_obj, "socks: v=%d cd=%d",
470                  buffer[0], buffer[1] );
471
472         if( buffer[1] != 90 )
473             return VLC_EGENERIC;
474     }
475     else if( i_socks_version == 5 )
476     {
477         int i_hlen = __MIN(strlen( psz_host ), 255);
478         int i_len;
479
480         buffer[0] = i_socks_version;    /* Version */
481         buffer[1] = 0x01;               /* Cmd: connect */
482         buffer[2] = 0x00;               /* Reserved */
483         buffer[3] = 3;                  /* ATYP: for now domainname */
484
485         buffer[4] = i_hlen;
486         memcpy( &buffer[5], psz_host, i_hlen );
487         SetWBE( &buffer[5+i_hlen], i_port );
488
489         i_len = 5 + i_hlen + 2;
490
491
492         if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len )
493             return VLC_EGENERIC;
494
495         /* Read the header */
496         if( net_Read( p_obj, fd, NULL, buffer, 5, true ) != 5 )
497             return VLC_EGENERIC;
498
499         msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
500                  buffer[0], buffer[1], buffer[3] );
501
502         if( buffer[1] != 0x00 )
503         {
504             msg_Err( p_obj, "socks: CONNECT request failed" );
505             return VLC_EGENERIC;
506         }
507
508         /* Read the remaining bytes */
509         if( buffer[3] == 0x01 )
510             i_len = 4-1 + 2;
511         else if( buffer[3] == 0x03 )
512             i_len = buffer[4] + 2;
513         else if( buffer[3] == 0x04 )
514             i_len = 16-1+2;
515         else
516             return VLC_EGENERIC;
517
518         if( net_Read( p_obj, fd, NULL, buffer, i_len, true ) != i_len )
519             return VLC_EGENERIC;
520     }
521
522     return VLC_SUCCESS;
523 }
524
525 void net_ListenClose( int *pi_fd )
526 {
527     if( pi_fd != NULL )
528     {
529         int *pi;
530
531         for( pi = pi_fd; *pi != -1; pi++ )
532             net_Close( *pi );
533         free( pi_fd );
534     }
535 }