hsocket.c 6.8 KB

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