You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

267 lines
5.2 KiB

  1. #include "netutil.h"
  2. #include "compat.h"
  3. #include <errno.h>
  4. #include <sys/types.h>
  5. #include <stdio.h>
  6. /* Initialize networking */
  7. void
  8. net_init (void)
  9. {
  10. #ifdef __WIN32__
  11. WSADATA blah;
  12. WSAStartup (0x0101, &blah);
  13. #endif
  14. }
  15. /* Set socket blocking/nonblocking */
  16. int
  17. soblock (int socket, int blocking)
  18. {
  19. #ifdef __WIN32__
  20. unsigned long arg = blocking ? 0 : 1;
  21. if (ioctlsocket (socket, FIONBIO, &arg) != 0)
  22. return -1;
  23. return 0;
  24. #else
  25. int sockopt;
  26. /* Get flags */
  27. sockopt = fcntl (socket, F_GETFL);
  28. if (sockopt == -1)
  29. {
  30. return -1;
  31. }
  32. /* Modify */
  33. if (blocking)
  34. sockopt &= ~O_NONBLOCK;
  35. else
  36. sockopt |= O_NONBLOCK;
  37. /* Set flags */
  38. if (fcntl (socket, F_SETFL, sockopt) != 0)
  39. return -1;
  40. return 0;
  41. #endif
  42. }
  43. /* Like connect(2), but with a timeout. Socket must be non-blocking. */
  44. int
  45. connect_timeout (int s, const struct sockaddr *serv_addr, socklen_t addrlen,
  46. struct timeval *timeout)
  47. {
  48. int ret;
  49. fd_set writefds;
  50. fd_set exceptfds;
  51. int optval;
  52. socklen_t optlen;
  53. /* Start connect */
  54. ret = connect (s, serv_addr, addrlen);
  55. if (ret == 0)
  56. {
  57. /* Success */
  58. return 0;
  59. }
  60. /* Check for immediate failure */
  61. #ifdef __WIN32__
  62. errno = WSAGetLastError ();
  63. if (ret < 0 && errno != WSAEWOULDBLOCK && errno != WSAEINVAL)
  64. return -1;
  65. #else
  66. if (ret < 0 && errno != EINPROGRESS && errno != EALREADY)
  67. return -1;
  68. #endif
  69. /* In progress, wait for result. */
  70. FD_ZERO (&writefds);
  71. FD_SET (s, &writefds);
  72. FD_ZERO (&exceptfds);
  73. FD_SET (s, &exceptfds);
  74. ret = select (s + 1, NULL, &writefds, &exceptfds, timeout);
  75. if (ret < 0)
  76. {
  77. /* Error */
  78. return -1;
  79. }
  80. if (ret == 0)
  81. {
  82. /* Timed out */
  83. errno = ETIMEDOUT;
  84. return -1;
  85. }
  86. /* Check the socket state */
  87. optlen = sizeof (optval);
  88. if (getsockopt (s, SOL_SOCKET, SO_ERROR, (void *) &optval, &optlen) != 0)
  89. return -1;
  90. if (optval != 0)
  91. {
  92. /* Connection failed. */
  93. errno = optval;
  94. return -1;
  95. }
  96. /* On Windows, SO_ERROR sometimes shows no error but the connection
  97. still failed. Sigh. */
  98. if (FD_ISSET (s, &exceptfds) || !FD_ISSET (s, &writefds))
  99. {
  100. errno = EIO;
  101. return -1;
  102. }
  103. /* Success */
  104. return 0;
  105. }
  106. /* Like send(2), but with a timeout. Socket must be non-blocking.
  107. The timeout only applies if no data at all is sent -- this function
  108. may still send less than requested. */
  109. ssize_t
  110. send_timeout (int s, const void *buf, size_t len, int flags,
  111. struct timeval * timeout)
  112. {
  113. fd_set writefds;
  114. int ret;
  115. FD_ZERO (&writefds);
  116. FD_SET (s, &writefds);
  117. ret = select (s + 1, NULL, &writefds, NULL, timeout);
  118. if (ret == 0)
  119. {
  120. /* Timed out */
  121. errno = ETIMEDOUT;
  122. return -1;
  123. }
  124. if (ret != 1)
  125. {
  126. /* Error */
  127. return -1;
  128. }
  129. return send (s, buf, len, flags);
  130. }
  131. /* Like recv(2), but with a timeout. Socket must be non-blocking.
  132. The timeout only applies if no data at all is received -- this
  133. function may still return less than requested. */
  134. ssize_t
  135. recv_timeout (int s, void *buf, size_t len, int flags,
  136. struct timeval * timeout)
  137. {
  138. fd_set readfds;
  139. int ret;
  140. FD_ZERO (&readfds);
  141. FD_SET (s, &readfds);
  142. ret = select (s + 1, &readfds, NULL, NULL, timeout);
  143. if (ret == 0)
  144. {
  145. /* Timed out */
  146. errno = ETIMEDOUT;
  147. return -1;
  148. }
  149. if (ret != 1)
  150. {
  151. /* Error */
  152. return -1;
  153. }
  154. return recv (s, buf, len, flags);
  155. }
  156. /* Like recvfrom(2), but with a timeout. Socket must be non-blocking.
  157. The timeout only applies if no data at all is received -- this
  158. function may still return less than requested. */
  159. ssize_t
  160. recvfrom_timeout (int s, void *buf, size_t len, int flags,
  161. struct sockaddr * address, socklen_t * address_len,
  162. struct timeval * timeout)
  163. {
  164. fd_set readfds;
  165. int ret;
  166. FD_ZERO (&readfds);
  167. FD_SET (s, &readfds);
  168. ret = select (s + 1, &readfds, NULL, NULL, timeout);
  169. if (ret == 0)
  170. {
  171. /* Timed out */
  172. errno = ETIMEDOUT;
  173. return -1;
  174. }
  175. if (ret != 1)
  176. {
  177. /* Error */
  178. return -1;
  179. }
  180. return recvfrom (s, buf, len, flags, address, address_len);
  181. }
  182. /* Like send_timeout, but retries (with the same timeout) in case of
  183. partial transfers. This is a stronger attempt to send all
  184. requested data. */
  185. ssize_t
  186. send_all_timeout (int s, const void *buf, size_t len, int flags,
  187. struct timeval * timeout)
  188. {
  189. struct timeval tv;
  190. size_t left = len;
  191. ssize_t ret;
  192. while (left > 0)
  193. {
  194. tv.tv_sec = timeout->tv_sec;
  195. tv.tv_usec = timeout->tv_usec;
  196. ret = send_timeout (s, buf, left, flags, &tv);
  197. if (ret < 0)
  198. return ret;
  199. if (ret == 0)
  200. break;
  201. left -= ret;
  202. buf += ret;
  203. }
  204. return len - left;
  205. }
  206. /* Like recv_timeout, but retries (with the same timeout) in case of
  207. partial transfers. This is a stronger attempt to recv all
  208. requested data. */
  209. ssize_t
  210. recv_all_timeout (int s, void *buf, size_t len, int flags,
  211. struct timeval * timeout)
  212. {
  213. struct timeval tv;
  214. size_t left = len;
  215. ssize_t ret;
  216. while (left > 0)
  217. {
  218. tv.tv_sec = timeout->tv_sec;
  219. tv.tv_usec = timeout->tv_usec;
  220. ret = recv_timeout (s, buf, left, flags, &tv);
  221. if (ret < 0)
  222. return ret;
  223. if (ret == 0)
  224. break;
  225. left -= ret;
  226. buf += ret;
  227. }
  228. return len - left;
  229. }