hsocket.c 8.4 KB

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