PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/network/tcp.c

https://gitlab.com/evilbinary/vlc
C | 521 lines | 374 code | 79 blank | 68 comment | 109 complexity | 65fcb4c2d901fae1698fa54fe3434cb4 MD5 | raw file
  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: 6b592e93562188b2d99f3b52ee717384841fdc85 $
  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. * Preamble
  27. *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <errno.h>
  33. #include <assert.h>
  34. #include <unistd.h>
  35. #ifdef HAVE_POLL
  36. # include <poll.h>
  37. #endif
  38. #include <vlc_network.h>
  39. #if defined (_WIN32)
  40. # undef EINPROGRESS
  41. # define EINPROGRESS WSAEWOULDBLOCK
  42. # undef EWOULDBLOCK
  43. # define EWOULDBLOCK WSAEWOULDBLOCK
  44. # undef EAGAIN
  45. # define EAGAIN WSAEWOULDBLOCK
  46. #endif
  47. #include <vlc_interrupt.h>
  48. static int SocksNegotiate( vlc_object_t *, int fd, int i_socks_version,
  49. const char *psz_user, const char *psz_passwd );
  50. static int SocksHandshakeTCP( vlc_object_t *,
  51. int fd, int i_socks_version,
  52. const char *psz_user, const char *psz_passwd,
  53. const char *psz_host, int i_port );
  54. extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
  55. int i_protocol );
  56. #undef net_Connect
  57. /*****************************************************************************
  58. * net_Connect:
  59. *****************************************************************************
  60. * Open a network connection.
  61. * @return socket handler or -1 on error.
  62. *****************************************************************************/
  63. int net_Connect( vlc_object_t *p_this, const char *psz_host, int i_port,
  64. int type, int proto )
  65. {
  66. const char *psz_realhost;
  67. char *psz_socks;
  68. int i_realport, i_handle = -1;
  69. psz_socks = var_InheritString( p_this, "socks" );
  70. if( psz_socks != NULL )
  71. {
  72. char *psz = strchr( psz_socks, ':' );
  73. if( psz )
  74. *psz++ = '\0';
  75. psz_realhost = psz_socks;
  76. i_realport = ( psz != NULL ) ? atoi( psz ) : 1080;
  77. msg_Dbg( p_this, "net: connecting to %s port %d (SOCKS) "
  78. "for %s port %d", psz_realhost, i_realport,
  79. psz_host, i_port );
  80. /* We only implement TCP with SOCKS */
  81. switch( type )
  82. {
  83. case 0:
  84. type = SOCK_STREAM;
  85. case SOCK_STREAM:
  86. break;
  87. default:
  88. msg_Err( p_this, "Socket type not supported through SOCKS" );
  89. free( psz_socks );
  90. return -1;
  91. }
  92. switch( proto )
  93. {
  94. case 0:
  95. proto = IPPROTO_TCP;
  96. case IPPROTO_TCP:
  97. break;
  98. default:
  99. msg_Err( p_this, "Transport not supported through SOCKS" );
  100. free( psz_socks );
  101. return -1;
  102. }
  103. }
  104. else
  105. {
  106. psz_realhost = psz_host;
  107. i_realport = i_port;
  108. msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost,
  109. i_realport );
  110. }
  111. struct addrinfo hints = {
  112. .ai_socktype = type,
  113. .ai_protocol = proto,
  114. .ai_flags = AI_NUMERICSERV | AI_IDN,
  115. }, *res;
  116. int val = vlc_getaddrinfo (psz_realhost, i_realport, &hints, &res);
  117. if (val)
  118. {
  119. msg_Err (p_this, "cannot resolve %s port %d : %s", psz_realhost,
  120. i_realport, gai_strerror (val));
  121. free( psz_socks );
  122. return -1;
  123. }
  124. free( psz_socks );
  125. int timeout = var_InheritInteger (p_this, "ipv4-timeout");
  126. if (timeout < 0)
  127. timeout = -1;
  128. for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
  129. {
  130. int fd = net_Socket( p_this, ptr->ai_family,
  131. ptr->ai_socktype, ptr->ai_protocol );
  132. if( fd == -1 )
  133. {
  134. msg_Dbg( p_this, "socket error: %s", vlc_strerror_c(net_errno) );
  135. continue;
  136. }
  137. if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) )
  138. {
  139. if( net_errno != EINPROGRESS && errno != EINTR )
  140. {
  141. msg_Err( p_this, "connection failed: %s",
  142. vlc_strerror_c(net_errno) );
  143. goto next_ai;
  144. }
  145. struct pollfd ufd;
  146. ufd.fd = fd;
  147. ufd.events = POLLOUT;
  148. do /* NOTE: timeout screwed up if we catch a signal (EINTR) */
  149. {
  150. if (vlc_killed())
  151. goto next_ai;
  152. val = vlc_poll_i11e(&ufd, 1, timeout);
  153. }
  154. while (val == -1 && errno == EINTR);
  155. switch (val)
  156. {
  157. case -1: /* error */
  158. msg_Err (p_this, "polling error: %s",
  159. vlc_strerror_c(net_errno));
  160. goto next_ai;
  161. case 0: /* timeout */
  162. msg_Warn (p_this, "connection timed out");
  163. goto next_ai;
  164. }
  165. /* There is NO WAY around checking SO_ERROR.
  166. * Don't ifdef it out!!! */
  167. if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &val,
  168. &(socklen_t){ sizeof (val) }) || val)
  169. {
  170. msg_Err (p_this, "connection failed: %s",
  171. vlc_strerror_c(val));
  172. goto next_ai;
  173. }
  174. }
  175. msg_Dbg( p_this, "connection succeeded (socket = %d)", fd );
  176. i_handle = fd; /* success! */
  177. break;
  178. next_ai: /* failure */
  179. net_Close( fd );
  180. }
  181. freeaddrinfo( res );
  182. if( i_handle == -1 )
  183. return -1;
  184. if( psz_socks != NULL )
  185. {
  186. /* NOTE: psz_socks already free'd! */
  187. char *psz_user = var_InheritString( p_this, "socks-user" );
  188. char *psz_pwd = var_InheritString( p_this, "socks-pwd" );
  189. if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd,
  190. psz_host, i_port ) )
  191. {
  192. msg_Err( p_this, "SOCKS handshake failed" );
  193. net_Close( i_handle );
  194. i_handle = -1;
  195. }
  196. free( psz_user );
  197. free( psz_pwd );
  198. }
  199. return i_handle;
  200. }
  201. int net_AcceptSingle (vlc_object_t *obj, int lfd)
  202. {
  203. int fd = vlc_accept (lfd, NULL, NULL, true);
  204. if (fd == -1)
  205. {
  206. if (net_errno != EAGAIN)
  207. #if (EAGAIN != EWOULDBLOCK)
  208. if (net_errno != EWOULDBLOCK)
  209. #endif
  210. msg_Err (obj, "accept failed (from socket %d): %s", lfd,
  211. vlc_strerror_c(net_errno));
  212. return -1;
  213. }
  214. msg_Dbg (obj, "accepted socket %d (from socket %d)", fd, lfd);
  215. setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
  216. return fd;
  217. }
  218. #undef net_Accept
  219. /**
  220. * Accepts an new connection on a set of listening sockets.
  221. * If there are no pending connections, this function will wait.
  222. * @note If the thread needs to handle events other than incoming connections,
  223. * you need to use poll() and net_AcceptSingle() instead.
  224. *
  225. * @param p_this VLC object for logging and object kill signal
  226. * @param pi_fd listening socket set
  227. * @return -1 on error (may be transient error due to network issues),
  228. * a new socket descriptor on success.
  229. */
  230. int net_Accept (vlc_object_t *p_this, int *pi_fd)
  231. {
  232. assert (pi_fd != NULL);
  233. unsigned n = 0;
  234. while (pi_fd[n] != -1)
  235. n++;
  236. struct pollfd ufd[n];
  237. /* Initialize file descriptor set */
  238. for (unsigned i = 0; i < n; i++)
  239. {
  240. ufd[i].fd = pi_fd[i];
  241. ufd[i].events = POLLIN;
  242. }
  243. for (;;)
  244. {
  245. while (poll (ufd, n, -1) == -1)
  246. {
  247. if (net_errno != EINTR)
  248. {
  249. msg_Err (p_this, "poll error: %s", vlc_strerror_c(net_errno));
  250. return -1;
  251. }
  252. }
  253. for (unsigned i = 0; i < n; i++)
  254. {
  255. if (ufd[i].revents == 0)
  256. continue;
  257. int sfd = ufd[i].fd;
  258. int fd = net_AcceptSingle (p_this, sfd);
  259. if (fd == -1)
  260. continue;
  261. /*
  262. * Move listening socket to the end to let the others in the
  263. * set a chance next time.
  264. */
  265. memmove (pi_fd + i, pi_fd + i + 1, n - (i + 1));
  266. pi_fd[n - 1] = sfd;
  267. return fd;
  268. }
  269. }
  270. return -1;
  271. }
  272. /*****************************************************************************
  273. * SocksNegotiate:
  274. *****************************************************************************
  275. * Negotiate authentication with a SOCKS server.
  276. *****************************************************************************/
  277. static int SocksNegotiate( vlc_object_t *p_obj,
  278. int fd, int i_socks_version,
  279. const char *psz_socks_user,
  280. const char *psz_socks_passwd )
  281. {
  282. uint8_t buffer[128+2*256];
  283. int i_len;
  284. bool b_auth = false;
  285. if( i_socks_version != 5 )
  286. return VLC_SUCCESS;
  287. /* We negotiate authentication */
  288. buffer[0] = i_socks_version; /* SOCKS version */
  289. if( psz_socks_user != NULL && psz_socks_passwd != NULL )
  290. {
  291. buffer[1] = 2; /* Number of methods */
  292. buffer[2] = 0x00; /* - No auth required */
  293. buffer[3] = 0x02; /* - USer/Password */
  294. i_len = 4;
  295. b_auth = true;
  296. }
  297. else
  298. {
  299. buffer[1] = 1; /* Number of methods */
  300. buffer[2] = 0x00; /* - No auth required */
  301. i_len = 3;
  302. }
  303. if( net_Write( p_obj, fd, buffer, i_len ) != i_len )
  304. return VLC_EGENERIC;
  305. if( net_Read( p_obj, fd, buffer, 2) != 2 )
  306. return VLC_EGENERIC;
  307. msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] );
  308. if( buffer[1] == 0x00 )
  309. {
  310. msg_Dbg( p_obj, "socks: no authentication required" );
  311. }
  312. else if( buffer[1] == 0x02 )
  313. {
  314. int i_len1 = __MIN( strlen(psz_socks_user), 255 );
  315. int i_len2 = __MIN( strlen(psz_socks_passwd), 255 );
  316. msg_Dbg( p_obj, "socks: username/password authentication" );
  317. /* XXX: we don't support user/pwd > 255 (truncated)*/
  318. buffer[0] = i_socks_version; /* Version */
  319. buffer[1] = i_len1; /* User length */
  320. memcpy( &buffer[2], psz_socks_user, i_len1 );
  321. buffer[2+i_len1] = i_len2; /* Password length */
  322. memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 );
  323. i_len = 3 + i_len1 + i_len2;
  324. if( net_Write( p_obj, fd, buffer, i_len ) != i_len )
  325. return VLC_EGENERIC;
  326. if( net_Read( p_obj, fd, buffer, 2 ) != 2 )
  327. return VLC_EGENERIC;
  328. msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] );
  329. if( buffer[1] != 0x00 )
  330. {
  331. msg_Err( p_obj, "socks: authentication rejected" );
  332. return VLC_EGENERIC;
  333. }
  334. }
  335. else
  336. {
  337. if( b_auth )
  338. msg_Err( p_obj, "socks: unsupported authentication method %x",
  339. buffer[0] );
  340. else
  341. msg_Err( p_obj, "socks: authentication needed" );
  342. return VLC_EGENERIC;
  343. }
  344. return VLC_SUCCESS;
  345. }
  346. /*****************************************************************************
  347. * SocksHandshakeTCP:
  348. *****************************************************************************
  349. * Open a TCP connection using a SOCKS server and return a handle (RFC 1928)
  350. *****************************************************************************/
  351. static int SocksHandshakeTCP( vlc_object_t *p_obj,
  352. int fd,
  353. int i_socks_version,
  354. const char *psz_user, const char *psz_passwd,
  355. const char *psz_host, int i_port )
  356. {
  357. uint8_t buffer[128+2*256];
  358. if( i_socks_version != 4 && i_socks_version != 5 )
  359. {
  360. msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version );
  361. i_socks_version = 5;
  362. }
  363. if( i_socks_version == 5 &&
  364. SocksNegotiate( p_obj, fd, i_socks_version,
  365. psz_user, psz_passwd ) )
  366. return VLC_EGENERIC;
  367. if( i_socks_version == 4 )
  368. {
  369. /* v4 only support ipv4 */
  370. static const struct addrinfo hints = {
  371. .ai_family = AF_INET,
  372. .ai_socktype = SOCK_STREAM,
  373. .ai_protocol = IPPROTO_TCP,
  374. .ai_flags = AI_IDN,
  375. };
  376. struct addrinfo *res;
  377. if (vlc_getaddrinfo (psz_host, 0, &hints, &res))
  378. return VLC_EGENERIC;
  379. buffer[0] = i_socks_version;
  380. buffer[1] = 0x01; /* CONNECT */
  381. SetWBE( &buffer[2], i_port ); /* Port */
  382. memcpy (&buffer[4], /* Address */
  383. &((struct sockaddr_in *)(res->ai_addr))->sin_addr, 4);
  384. freeaddrinfo (res);
  385. buffer[8] = 0; /* Empty user id */
  386. if( net_Write( p_obj, fd, buffer, 9 ) != 9 )
  387. return VLC_EGENERIC;
  388. if( net_Read( p_obj, fd, buffer, 8 ) != 8 )
  389. return VLC_EGENERIC;
  390. msg_Dbg( p_obj, "socks: v=%d cd=%d",
  391. buffer[0], buffer[1] );
  392. if( buffer[1] != 90 )
  393. return VLC_EGENERIC;
  394. }
  395. else if( i_socks_version == 5 )
  396. {
  397. int i_hlen = __MIN(strlen( psz_host ), 255);
  398. int i_len;
  399. buffer[0] = i_socks_version; /* Version */
  400. buffer[1] = 0x01; /* Cmd: connect */
  401. buffer[2] = 0x00; /* Reserved */
  402. buffer[3] = 3; /* ATYP: for now domainname */
  403. buffer[4] = i_hlen;
  404. memcpy( &buffer[5], psz_host, i_hlen );
  405. SetWBE( &buffer[5+i_hlen], i_port );
  406. i_len = 5 + i_hlen + 2;
  407. if( net_Write( p_obj, fd, buffer, i_len ) != i_len )
  408. return VLC_EGENERIC;
  409. /* Read the header */
  410. if( net_Read( p_obj, fd, buffer, 5 ) != 5 )
  411. return VLC_EGENERIC;
  412. msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d",
  413. buffer[0], buffer[1], buffer[3] );
  414. if( buffer[1] != 0x00 )
  415. {
  416. msg_Err( p_obj, "socks: CONNECT request failed" );
  417. return VLC_EGENERIC;
  418. }
  419. /* Read the remaining bytes */
  420. if( buffer[3] == 0x01 )
  421. i_len = 4-1 + 2;
  422. else if( buffer[3] == 0x03 )
  423. i_len = buffer[4] + 2;
  424. else if( buffer[3] == 0x04 )
  425. i_len = 16-1+2;
  426. else
  427. return VLC_EGENERIC;
  428. if( net_Read( p_obj, fd, buffer, i_len ) != i_len )
  429. return VLC_EGENERIC;
  430. }
  431. return VLC_SUCCESS;
  432. }
  433. void net_ListenClose( int *pi_fd )
  434. {
  435. if( pi_fd != NULL )
  436. {
  437. int *pi;
  438. for( pi = pi_fd; *pi != -1; pi++ )
  439. net_Close( *pi );
  440. free( pi_fd );
  441. }
  442. }