hsocket.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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. int Socketpair(int family, int type, int protocol, int sv[2]) {
  158. #ifdef OS_UNIX
  159. if (family == AF_UNIX) {
  160. return socketpair(family, type, protocol, sv);
  161. }
  162. #endif
  163. if (family != AF_INET || type != SOCK_STREAM) {
  164. return -1;
  165. }
  166. int listenfd, connfd, acceptfd;
  167. listenfd = connfd = acceptfd = INVALID_SOCKET;
  168. struct sockaddr_in localaddr;
  169. socklen_t addrlen = sizeof(localaddr);
  170. memset(&localaddr, 0, addrlen);
  171. localaddr.sin_family = AF_INET;
  172. localaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  173. localaddr.sin_port = 0;
  174. // listener
  175. listenfd = socket(AF_INET, SOCK_STREAM, 0);
  176. if (listenfd < 0) {
  177. perror("socket");
  178. goto error;
  179. }
  180. if (bind(listenfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
  181. perror("bind");
  182. goto error;
  183. }
  184. if (listen(listenfd, 1) < 0) {
  185. perror("listen");
  186. goto error;
  187. }
  188. if (getsockname(listenfd, (struct sockaddr*)&localaddr, &addrlen) < 0) {
  189. perror("getsockname");
  190. goto error;
  191. }
  192. // connector
  193. connfd = socket(AF_INET, SOCK_STREAM, 0);
  194. if (connfd < 0) {
  195. perror("socket");
  196. goto error;
  197. }
  198. if (connect(connfd, (struct sockaddr*)&localaddr, addrlen) < 0) {
  199. perror("connect");
  200. goto error;
  201. }
  202. // acceptor
  203. acceptfd = accept(listenfd, (struct sockaddr*)&localaddr, &addrlen);
  204. if (acceptfd < 0) {
  205. perror("accept");
  206. goto error;
  207. }
  208. closesocket(listenfd);
  209. sv[0] = connfd;
  210. sv[1] = acceptfd;
  211. return 0;
  212. error:
  213. if (listenfd != INVALID_SOCKET) {
  214. closesocket(listenfd);
  215. }
  216. if (connfd != INVALID_SOCKET) {
  217. closesocket(connfd);
  218. }
  219. if (acceptfd != INVALID_SOCKET) {
  220. closesocket(acceptfd);
  221. }
  222. return -1;
  223. }
  224. #define PING_TIMEOUT 1000 // ms
  225. int Ping(const char* host, int cnt) {
  226. static uint16_t seq = 0;
  227. char ip[64] = {0};
  228. uint32_t start_tick, end_tick;
  229. uint64_t start_hrtime, end_hrtime;
  230. int timeout = 0;
  231. int sendbytes = 64;
  232. char sendbuf[64];
  233. char recvbuf[128]; // iphdr + icmp = 84 at least
  234. icmp_t* icmp_req = (icmp_t*)sendbuf;
  235. iphdr_t* ipheader = (iphdr_t*)recvbuf;
  236. icmp_t* icmp_res;
  237. // ping stat
  238. int send_cnt = 0;
  239. int recv_cnt = 0;
  240. int ok_cnt = 0;
  241. float rtt, min_rtt, max_rtt, total_rtt;
  242. rtt = max_rtt = total_rtt = 0.0f;
  243. min_rtt = 1000000.0f;
  244. //min_rtt = MIN(rtt, min_rtt);
  245. //max_rtt = MAX(rtt, max_rtt);
  246. // gethostbyname -> socket -> setsockopt -> sendto -> recvfrom -> closesocket
  247. sockaddr_un peeraddr;
  248. socklen_t addrlen = sizeof(peeraddr);
  249. memset(&peeraddr, 0, addrlen);
  250. int ret = Resolver(host, &peeraddr);
  251. if (ret != 0) return ret;
  252. sockaddr_ip(&peeraddr, ip, sizeof(ip));
  253. int sockfd = socket(peeraddr.sa.sa_family, SOCK_RAW, IPPROTO_ICMP);
  254. if (sockfd < 0) {
  255. perror("socket");
  256. if (errno == EPERM) {
  257. fprintf(stderr, "please use root or sudo to create a raw socket.\n");
  258. }
  259. return -socket_errno();
  260. }
  261. timeout = PING_TIMEOUT;
  262. ret = so_sndtimeo(sockfd, timeout);
  263. if (ret < 0) {
  264. perror("setsockopt");
  265. goto error;
  266. }
  267. timeout = PING_TIMEOUT;
  268. ret = so_rcvtimeo(sockfd, timeout);
  269. if (ret < 0) {
  270. perror("setsockopt");
  271. goto error;
  272. }
  273. icmp_req->icmp_type = ICMP_ECHO;
  274. icmp_req->icmp_code = 0;
  275. icmp_req->icmp_id = getpid();
  276. for (int i = 0; i < sendbytes - sizeof(icmphdr_t); ++i) {
  277. icmp_req->icmp_data[i] = i;
  278. }
  279. start_tick = gettick();
  280. while (cnt-- > 0) {
  281. // NOTE: checksum
  282. icmp_req->icmp_seq = ++seq;
  283. icmp_req->icmp_cksum = 0;
  284. icmp_req->icmp_cksum = checksum((uint8_t*)icmp_req, sendbytes);
  285. start_hrtime = gethrtime();
  286. addrlen = sockaddrlen(&peeraddr);
  287. int nsend = sendto(sockfd, sendbuf, sendbytes, 0, &peeraddr.sa, addrlen);
  288. if (nsend < 0) {
  289. perror("sendto");
  290. continue;
  291. }
  292. ++send_cnt;
  293. addrlen = sizeof(peeraddr);
  294. int nrecv = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, &peeraddr.sa, &addrlen);
  295. if (nrecv < 0) {
  296. perror("recvfrom");
  297. continue;
  298. }
  299. ++recv_cnt;
  300. end_hrtime = gethrtime();
  301. // check valid
  302. bool valid = false;
  303. int iphdr_len = ipheader->ihl * 4;
  304. int icmp_len = nrecv - iphdr_len;
  305. if (icmp_len == sendbytes) {
  306. icmp_res = (icmp_t*)(recvbuf + ipheader->ihl*4);
  307. if (icmp_res->icmp_type == ICMP_ECHOREPLY &&
  308. icmp_res->icmp_id == getpid() &&
  309. icmp_res->icmp_seq == seq) {
  310. valid = true;
  311. }
  312. }
  313. if (valid == false) {
  314. printd("recv invalid icmp packet!\n");
  315. continue;
  316. }
  317. rtt = (end_hrtime-start_hrtime) / 1000.0f;
  318. min_rtt = MIN(rtt, min_rtt);
  319. max_rtt = MAX(rtt, max_rtt);
  320. total_rtt += rtt;
  321. printd("%d bytes from %s: icmp_seq=%u ttl=%u time=%.1f ms\n", icmp_len, ip, seq, ipheader->ttl, rtt);
  322. fflush(stdout);
  323. ++ok_cnt;
  324. if (cnt > 0) sleep(1); // sleep a while, then agian
  325. }
  326. end_tick = gettick();
  327. printd("--- %s ping statistics ---\n", host);
  328. printd("%d packets transmitted, %d received, %d%% packet loss, time %d ms\n",
  329. send_cnt, recv_cnt, (send_cnt-recv_cnt)*100/(send_cnt==0?1:send_cnt), end_tick-start_tick);
  330. printd("rtt min/avg/max = %.3f/%.3f/%.3f ms\n",
  331. min_rtt, total_rtt/(ok_cnt==0?1:ok_cnt), max_rtt);
  332. closesocket(sockfd);
  333. return ok_cnt;
  334. error:
  335. closesocket(sockfd);
  336. return socket_errno() > 0 ? -socket_errno() : -1;
  337. }