hsocket.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #include "hsocket.h"
  2. #include "htime.h"
  3. #include "netinet.h"
  4. #ifdef OS_UNIX
  5. #include <sys/select.h>
  6. #endif
  7. char *socket_strerror(int err) {
  8. #ifdef OS_WIN
  9. static char buffer[128];
  10. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
  11. FORMAT_MESSAGE_IGNORE_INSERTS |
  12. FORMAT_MESSAGE_MAX_WIDTH_MASK,
  13. 0, err, 0, buffer, sizeof(buffer), NULL);
  14. return buffer;
  15. #else
  16. return strerror(err);
  17. #endif
  18. }
  19. int Resolver(const char* host, struct sockaddr* addr) {
  20. // IPv4
  21. struct sockaddr_in* addr4 = (struct sockaddr_in*)addr;
  22. addr4->sin_family = AF_INET;
  23. if (inet_pton(AF_INET, host, &addr4->sin_addr) == 1) {
  24. return 0; // host is ip, so easy ;)
  25. }
  26. struct hostent* phe = gethostbyname(host);
  27. if (phe == NULL) {
  28. printd("unknown host %s\n", host);
  29. return -h_errno;
  30. }
  31. memcpy(&addr4->sin_addr, phe->h_addr_list[0], phe->h_length);
  32. return 0;
  33. }
  34. int Bind(int port, const char* host, int type) {
  35. struct sockaddr_in localaddr;
  36. socklen_t addrlen = sizeof(localaddr);
  37. memset(&localaddr, 0, addrlen);
  38. localaddr.sin_family = AF_INET;
  39. if (host) {
  40. int ret = Resolver(host, (struct sockaddr*)&localaddr);
  41. if (ret != 0) return ret;
  42. }
  43. else {
  44. localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  45. }
  46. localaddr.sin_port = htons(port);
  47. // socket -> setsockopt -> bind
  48. int sockfd = socket(AF_INET, type, 0);
  49. if (sockfd < 0) {
  50. perror("socket");
  51. return -socket_errno();
  52. }
  53. // NOTE: SO_REUSEADDR means that you can reuse sockaddr of TIME_WAIT status
  54. int reuseaddr = 1;
  55. if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseaddr, sizeof(int)) < 0) {
  56. perror("setsockopt");
  57. goto error;
  58. }
  59. if (bind(sockfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
  60. perror("bind");
  61. goto error;
  62. }
  63. return sockfd;
  64. error:
  65. closesocket(sockfd);
  66. return socket_errno() > 0 ? -socket_errno() : -1;
  67. }
  68. int Listen(int port, const char* host) {
  69. int sockfd = Bind(port, host, SOCK_STREAM);
  70. if (sockfd < 0) return sockfd;
  71. if (listen(sockfd, SOMAXCONN) < 0) {
  72. perror("listen");
  73. goto error;
  74. }
  75. return sockfd;
  76. error:
  77. closesocket(sockfd);
  78. return socket_errno() > 0 ? -socket_errno() : -1;
  79. }
  80. int Connect(const char* host, int port, int nonblock) {
  81. // Resolver -> socket -> nonblocking -> connect
  82. struct sockaddr_in peeraddr;
  83. socklen_t addrlen = sizeof(peeraddr);
  84. memset(&peeraddr, 0, addrlen);
  85. int ret = Resolver(host, (struct sockaddr*)&peeraddr);
  86. if (ret != 0) return ret;
  87. peeraddr.sin_port = htons(port);
  88. int connfd = socket(AF_INET, SOCK_STREAM, 0);
  89. if (connfd < 0) {
  90. perror("socket");
  91. return -socket_errno();
  92. }
  93. if (nonblock) {
  94. nonblocking(connfd);
  95. }
  96. ret = connect(connfd, (struct sockaddr*)&peeraddr, addrlen);
  97. #ifdef OS_WIN
  98. if (ret < 0 && socket_errno() != WSAEWOULDBLOCK) {
  99. #else
  100. if (ret < 0 && socket_errno() != EINPROGRESS) {
  101. #endif
  102. perror("connect");
  103. goto error;
  104. }
  105. return connfd;
  106. error:
  107. closesocket(connfd);
  108. return socket_errno() > 0 ? -socket_errno() : -1;
  109. }
  110. int ConnectNonblock(const char* host, int port) {
  111. return Connect(host, port, 1);
  112. }
  113. int ConnectTimeout(const char* host, int port, int ms) {
  114. int connfd = Connect(host, port, 1);
  115. if (connfd < 0) return connfd;
  116. int err;
  117. socklen_t optlen = sizeof(err);
  118. struct timeval tv = {ms/1000, (ms%1000)*1000};
  119. fd_set writefds;
  120. FD_ZERO(&writefds);
  121. FD_SET(connfd, &writefds);
  122. int ret = select(connfd+1, 0, &writefds, 0, &tv);
  123. if (ret < 0) {
  124. perror("select");
  125. goto error;
  126. }
  127. if (ret == 0) {
  128. errno = ETIMEDOUT;
  129. goto error;
  130. }
  131. if (getsockopt(connfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) < 0 || err != 0) {
  132. goto error;
  133. }
  134. blocking(connfd);
  135. return connfd;
  136. error:
  137. closesocket(connfd);
  138. return socket_errno() > 0 ? -socket_errno() : -1;
  139. }
  140. #define PING_TIMEOUT 1000 // ms
  141. int Ping(const char* host, int cnt) {
  142. static uint16_t seq = 0;
  143. char ip[64] = {0};
  144. uint32_t start_tick, end_tick;
  145. uint64_t start_hrtime, end_hrtime;
  146. int timeout = 0;
  147. int sendbytes = 64;
  148. char sendbuf[64];
  149. char recvbuf[128]; // iphdr + icmp = 84 at least
  150. icmp_t* icmp_req = (icmp_t*)sendbuf;
  151. iphdr_t* ipheader = (iphdr_t*)recvbuf;
  152. icmp_t* icmp_res;
  153. // ping stat
  154. int send_cnt = 0;
  155. int recv_cnt = 0;
  156. int ok_cnt = 0;
  157. float rtt, min_rtt, max_rtt, total_rtt;
  158. rtt = max_rtt = total_rtt = 0.0f;
  159. min_rtt = 1000000.0f;
  160. //min_rtt = MIN(rtt, min_rtt);
  161. //max_rtt = MAX(rtt, max_rtt);
  162. // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
  163. struct sockaddr_in peeraddr;
  164. socklen_t addrlen = sizeof(peeraddr);
  165. memset(&peeraddr, 0, addrlen);
  166. int ret = Resolver(host, (struct sockaddr*)&peeraddr);
  167. if (ret != 0) return ret;
  168. inet_ntop(peeraddr.sin_family, &peeraddr.sin_addr, ip, sizeof(ip));
  169. int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  170. if (sockfd < 0) {
  171. perror("socket");
  172. if (errno == EPERM) {
  173. fprintf(stderr, "please use root or sudo to create a raw socket.\n");
  174. }
  175. return -socket_errno();
  176. }
  177. timeout = PING_TIMEOUT;
  178. ret = so_sndtimeo(sockfd, timeout);
  179. if (ret < 0) {
  180. perror("setsockopt");
  181. goto error;
  182. }
  183. timeout = PING_TIMEOUT;
  184. ret = so_rcvtimeo(sockfd, timeout);
  185. if (ret < 0) {
  186. perror("setsockopt");
  187. goto error;
  188. }
  189. icmp_req->icmp_type = ICMP_ECHO;
  190. icmp_req->icmp_code = 0;
  191. icmp_req->icmp_id = getpid();
  192. for (int i = 0; i < sendbytes - sizeof(icmphdr_t); ++i) {
  193. icmp_req->icmp_data[i] = i;
  194. }
  195. start_tick = gettick();
  196. while (cnt-- > 0) {
  197. // NOTE: checksum
  198. icmp_req->icmp_seq = ++seq;
  199. icmp_req->icmp_cksum = 0;
  200. icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
  201. start_hrtime = gethrtime();
  202. int nsend = sendto(sockfd, sendbuf, sendbytes, 0, (struct sockaddr*)&peeraddr, addrlen);
  203. if (nsend < 0) {
  204. perror("sendto");
  205. continue;
  206. }
  207. ++send_cnt;
  208. addrlen = sizeof(peeraddr);
  209. int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&peeraddr, &addrlen);
  210. if (nrecv < 0) {
  211. perror("recvfrom");
  212. continue;
  213. }
  214. ++recv_cnt;
  215. end_hrtime = gethrtime();
  216. // check valid
  217. bool valid = false;
  218. int iphdr_len = ipheader->ihl * 4;
  219. int icmp_len = nrecv - iphdr_len;
  220. if (icmp_len == sendbytes) {
  221. icmp_res = (icmp_t*)(recvbuf + ipheader->ihl*4);
  222. if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
  223. icmp_res->icmp_id == getpid() &&
  224. icmp_res->icmp_seq == seq) {
  225. valid = true;
  226. }
  227. }
  228. if (valid == false) {
  229. printd("recv invalid icmp packet!\n");
  230. continue;
  231. }
  232. rtt = (end_hrtime-start_hrtime) / 1000.0f;
  233. min_rtt = MIN(rtt, min_rtt);
  234. max_rtt = MAX(rtt, max_rtt);
  235. total_rtt += rtt;
  236. printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
  237. fflush(stdout);
  238. ++ok_cnt;
  239. if (cnt > 0) sleep(1); // sleep a while, then agian
  240. }
  241. end_tick = gettick();
  242. printd("--- %s ping statistics ---\n", host);
  243. printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
  244. send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
  245. printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
  246. min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
  247. closesocket(sockfd);
  248. return ok_cnt;
  249. error:
  250. closesocket(sockfd);
  251. return socket_errno() > 0 ? -socket_errno() : -1;
  252. }