PageRenderTime 52ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/network/io.c

https://github.com/mstorsjo/vlc
C | 445 lines | 341 code | 63 blank | 41 comment | 91 complexity | d1351f1b7a13faa11410da09e16d71a6 MD5 | raw file
  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. *
  7. * Authors: Laurent Aimar <fenrir@videolan.org>
  8. * Rémi Denis-Courmont <rem # videolan.org>
  9. * Christophe Mutricy <xtophe at videolan dot 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 <stdlib.h>
  32. #include <stdio.h>
  33. #include <limits.h>
  34. #include <errno.h>
  35. #include <assert.h>
  36. #include <unistd.h>
  37. #ifdef HAVE_POLL
  38. # include <poll.h>
  39. #endif
  40. #ifdef HAVE_LINUX_DCCP_H
  41. /* TODO: use glibc instead of linux-kernel headers */
  42. # include <linux/dccp.h>
  43. # define SOL_DCCP 269
  44. #endif
  45. #include <vlc_common.h>
  46. #include <vlc_network.h>
  47. #include <vlc_interrupt.h>
  48. #if defined (_WIN32)
  49. # undef EINPROGRESS
  50. # define EINPROGRESS WSAEWOULDBLOCK
  51. # undef EWOULDBLOCK
  52. # define EWOULDBLOCK WSAEWOULDBLOCK
  53. # undef EAGAIN
  54. # define EAGAIN WSAEWOULDBLOCK
  55. #endif
  56. extern int rootwrap_bind (int family, int socktype, int protocol,
  57. const struct sockaddr *addr, size_t alen);
  58. int net_Socket (vlc_object_t *p_this, int family, int socktype,
  59. int protocol)
  60. {
  61. int fd = vlc_socket (family, socktype, protocol, true);
  62. if (fd == -1)
  63. {
  64. if (net_errno != EAFNOSUPPORT)
  65. msg_Err (p_this, "cannot create socket: %s",
  66. vlc_strerror_c(net_errno));
  67. return -1;
  68. }
  69. setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
  70. #ifdef IPV6_V6ONLY
  71. /*
  72. * Accepts only IPv6 connections on IPv6 sockets.
  73. * If possible, we should open two sockets, but it is not always possible.
  74. */
  75. if (family == AF_INET6)
  76. setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int));
  77. #endif
  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. #ifdef DCCP_SOCKOPT_SERVICE
  89. if (socktype == SOL_DCCP)
  90. {
  91. char *dccps = var_InheritString (p_this, "dccp-service");
  92. if (dccps != NULL)
  93. {
  94. setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, dccps,
  95. (strlen (dccps) + 3) & ~3);
  96. free (dccps);
  97. }
  98. }
  99. #endif
  100. return fd;
  101. }
  102. int (net_Connect)(vlc_object_t *obj, const char *host, int serv,
  103. int type, int proto)
  104. {
  105. struct addrinfo hints = {
  106. .ai_socktype = type,
  107. .ai_protocol = proto,
  108. .ai_flags = AI_NUMERICSERV | AI_IDN,
  109. }, *res;
  110. int ret = -1;
  111. int val = vlc_getaddrinfo_i11e(host, serv, &hints, &res);
  112. if (val)
  113. {
  114. msg_Err(obj, "cannot resolve %s port %d : %s", host, serv,
  115. gai_strerror (val));
  116. return -1;
  117. }
  118. vlc_tick_t timeout = VLC_TICK_FROM_MS(var_InheritInteger(obj,
  119. "ipv4-timeout"));
  120. for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
  121. {
  122. int fd = net_Socket(obj, ptr->ai_family,
  123. ptr->ai_socktype, ptr->ai_protocol);
  124. if (fd == -1)
  125. {
  126. msg_Dbg(obj, "socket error: %s", vlc_strerror_c(net_errno));
  127. continue;
  128. }
  129. if (connect(fd, ptr->ai_addr, ptr->ai_addrlen))
  130. {
  131. if (net_errno != EINPROGRESS && errno != EINTR)
  132. {
  133. msg_Err(obj, "connection failed: %s",
  134. vlc_strerror_c(net_errno));
  135. goto next_ai;
  136. }
  137. struct pollfd ufd;
  138. vlc_tick_t deadline = VLC_TICK_INVALID;
  139. ufd.fd = fd;
  140. ufd.events = POLLOUT;
  141. deadline = vlc_tick_now() + timeout;
  142. do
  143. {
  144. vlc_tick_t now = vlc_tick_now();
  145. if (vlc_killed())
  146. goto next_ai;
  147. if (now > deadline)
  148. now = deadline;
  149. val = vlc_poll_i11e(&ufd, 1, MS_FROM_VLC_TICK(deadline - now));
  150. }
  151. while (val == -1 && errno == EINTR);
  152. switch (val)
  153. {
  154. case -1: /* error */
  155. msg_Err(obj, "polling error: %s",
  156. vlc_strerror_c(net_errno));
  157. goto next_ai;
  158. case 0: /* timeout */
  159. msg_Warn(obj, "connection timed out");
  160. goto next_ai;
  161. }
  162. /* There is NO WAY around checking SO_ERROR.
  163. * Don't ifdef it out!!! */
  164. if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val,
  165. &(socklen_t){ sizeof (val) }) || val)
  166. {
  167. msg_Err(obj, "connection failed: %s", vlc_strerror_c(val));
  168. goto next_ai;
  169. }
  170. }
  171. msg_Dbg(obj, "connection succeeded (socket = %d)", fd);
  172. ret = fd; /* success! */
  173. break;
  174. next_ai: /* failure */
  175. net_Close(fd);
  176. }
  177. freeaddrinfo(res);
  178. return ret;
  179. }
  180. int *net_Listen (vlc_object_t *p_this, const char *psz_host,
  181. unsigned i_port, int type, int protocol)
  182. {
  183. struct addrinfo hints = {
  184. .ai_socktype = type,
  185. .ai_protocol = protocol,
  186. .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
  187. }, *res;
  188. msg_Dbg (p_this, "net: listening to %s port %u",
  189. (psz_host != NULL) ? psz_host : "*", i_port);
  190. int i_val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
  191. if (i_val)
  192. {
  193. msg_Err (p_this, "Cannot resolve %s port %u : %s",
  194. (psz_host != NULL) ? psz_host : "", i_port,
  195. gai_strerror (i_val));
  196. return NULL;
  197. }
  198. int *sockv = NULL;
  199. unsigned sockc = 0;
  200. for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
  201. {
  202. int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
  203. ptr->ai_protocol);
  204. if (fd == -1)
  205. {
  206. msg_Dbg (p_this, "socket error: %s", vlc_strerror_c(net_errno));
  207. continue;
  208. }
  209. /* Bind the socket */
  210. if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
  211. {
  212. int err = net_errno;
  213. net_Close (fd);
  214. #if !defined(_WIN32)
  215. fd = rootwrap_bind (ptr->ai_family, ptr->ai_socktype,
  216. ptr->ai_protocol,
  217. ptr->ai_addr, ptr->ai_addrlen);
  218. if (fd != -1)
  219. {
  220. msg_Dbg (p_this, "got socket %d from rootwrap", fd);
  221. }
  222. else
  223. #endif
  224. {
  225. msg_Err (p_this, "socket bind error: %s", vlc_strerror_c(err));
  226. continue;
  227. }
  228. }
  229. /* Listen */
  230. if (listen(fd, INT_MAX))
  231. {
  232. msg_Err(p_this, "socket listen error: %s",
  233. vlc_strerror_c(net_errno));
  234. net_Close(fd);
  235. continue;
  236. }
  237. int *nsockv = (int *)realloc (sockv, (sockc + 2) * sizeof (int));
  238. if (nsockv != NULL)
  239. {
  240. nsockv[sockc++] = fd;
  241. sockv = nsockv;
  242. }
  243. else
  244. net_Close (fd);
  245. }
  246. freeaddrinfo (res);
  247. if (sockv != NULL)
  248. sockv[sockc] = -1;
  249. return sockv;
  250. }
  251. void net_ListenClose(int *fds)
  252. {
  253. if (fds != NULL)
  254. {
  255. for (int *p = fds; *p != -1; p++)
  256. net_Close(*p);
  257. free(fds);
  258. }
  259. }
  260. #undef net_Accept
  261. int net_Accept(vlc_object_t *obj, int *fds)
  262. {
  263. assert(fds != NULL);
  264. unsigned n = 0;
  265. while (fds[n] != -1)
  266. n++;
  267. struct pollfd ufd[n];
  268. /* Initialize file descriptor set */
  269. for (unsigned i = 0; i < n; i++)
  270. {
  271. ufd[i].fd = fds[i];
  272. ufd[i].events = POLLIN;
  273. }
  274. for (;;)
  275. {
  276. while (poll(ufd, n, -1) == -1)
  277. {
  278. if (net_errno != EINTR)
  279. {
  280. msg_Err(obj, "poll error: %s", vlc_strerror_c(net_errno));
  281. return -1;
  282. }
  283. }
  284. for (unsigned i = 0; i < n; i++)
  285. {
  286. if (ufd[i].revents == 0)
  287. continue;
  288. int sfd = ufd[i].fd;
  289. int fd = vlc_accept(sfd, NULL, NULL, true);
  290. if (fd == -1)
  291. {
  292. if (net_errno != EAGAIN)
  293. #if (EAGAIN != EWOULDBLOCK)
  294. if (net_errno != EWOULDBLOCK)
  295. #endif
  296. msg_Err(obj, "accept failed (from socket %d): %s", sfd,
  297. vlc_strerror_c(net_errno));
  298. continue;
  299. }
  300. msg_Dbg(obj, "accepted socket %d (from socket %d)", fd, sfd);
  301. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  302. &(int){ 1 }, sizeof (int));
  303. /*
  304. * Move listening socket to the end to let the others in the
  305. * set a chance next time.
  306. */
  307. memmove(fds + i, fds + i + 1, n - (i + 1));
  308. fds[n - 1] = sfd;
  309. return fd;
  310. }
  311. }
  312. return -1;
  313. }
  314. ssize_t (net_Read)(vlc_object_t *restrict obj, int fd,
  315. void *restrict buf, size_t len)
  316. {
  317. size_t rd = 0;
  318. do
  319. {
  320. if (vlc_killed())
  321. {
  322. vlc_testcancel();
  323. errno = EINTR;
  324. return -1;
  325. }
  326. ssize_t val = vlc_recv_i11e(fd, buf, len, 0);
  327. if (val < 0)
  328. {
  329. if (errno == EINTR || errno == EAGAIN)
  330. continue;
  331. #ifdef _WIN32
  332. else if (WSAGetLastError() == WSAEMSGSIZE) /* datagram too big */
  333. {
  334. msg_Warn(obj, "read truncated to %zu bytes", len);
  335. val = len;
  336. }
  337. #endif
  338. else
  339. {
  340. msg_Err(obj, "read error: %s", vlc_strerror_c(errno));
  341. return rd ? (ssize_t)rd : -1;
  342. }
  343. }
  344. rd += val;
  345. if (val == 0)
  346. break;
  347. assert(len >= (size_t)val);
  348. len -= val;
  349. buf = ((char *)buf) + val;
  350. }
  351. while (len > 0);
  352. return rd;
  353. }
  354. ssize_t (net_Write)(vlc_object_t *obj, int fd, const void *buf, size_t len)
  355. {
  356. size_t written = 0;
  357. do
  358. {
  359. if (vlc_killed())
  360. {
  361. vlc_testcancel();
  362. errno = EINTR;
  363. return -1;
  364. }
  365. ssize_t val = vlc_send_i11e (fd, buf, len, MSG_NOSIGNAL);
  366. if (val == -1)
  367. {
  368. if (errno == EINTR || errno == EAGAIN)
  369. continue;
  370. msg_Err(obj, "write error: %s", vlc_strerror_c(errno));
  371. return written ? (ssize_t)written : -1;
  372. }
  373. if (val == 0)
  374. break;
  375. written += val;
  376. assert(len >= (size_t)val);
  377. len -= val;
  378. buf = ((const char *)buf) + val;
  379. }
  380. while (len > 0);
  381. return written;
  382. }