PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/socket.c

https://github.com/ia/connect
C | 685 lines | 437 code | 164 blank | 84 comment | 88 complexity | 8be9f734d266924874c6a2aab5583576 MD5 | raw file
  1. #include "connect.h"
  2. /*
  3. * TODO:
  4. * - move platform specific code to
  5. * - LOG_OUT_RET
  6. * - each error processing
  7. * - wrap server routine in macros/headers/defines/funcs
  8. * - add UDP support for listen/accept/recv
  9. * - char *msg -> void *data
  10. * - len for recv
  11. * - clean up release/debug output
  12. *
  13. */
  14. /* raw design api */
  15. /*
  16. sckt_addr = dhost, dport, shost, sport, type, proto,
  17. sckt_opts = opts, autoclose, reuse
  18. sckt;
  19. sckt_creat(dhost, dport, type, proto, opts, autoclose, reuse)
  20. sckt_create(addr, opts);
  21. sckt_connect(sckt, addr, opts);
  22. sckt_send(sckt, buf, len);
  23. sckt_recv(sckt, buf, len);
  24. sckt_delete(sckt);
  25. */
  26. /*
  27. sckt_creat(dhost, dport, type, proto, opts, autoclose, reuse)
  28. sckt_send
  29. sckt_delete
  30. sckt_addr = sckt_create_addr()
  31. sckt_opts = sckt_create_opts()
  32. sckt = sckt_create(addr, opts)
  33. */
  34. /* for Windows platform - init WinSock layer */
  35. int cnct_start()
  36. {
  37. LOG_IN;
  38. #ifdef CNCT_API_NT
  39. WORD wsa_version;
  40. WSADATA wsa_data;
  41. int r;
  42. wsa_version = MAKEWORD(2, 2);
  43. r = WSAStartup(wsa_version, &wsa_data);
  44. if (r != 0) {
  45. printf("WSAStartup failed with error: %d\n", r);
  46. return r;
  47. }
  48. if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
  49. printf("Could not find a usable version of Winsock.dll\n");
  50. WSACleanup();
  51. return 1;
  52. } else {
  53. printf("The Winsock 2.2 dll was found okay\n");
  54. }
  55. DBG_ON(printf("_WIN32_WINNT: 0x%x\n", _WIN32_WINNT));
  56. #endif /* CNCT_API_NT */
  57. /* TODO: FIXME */
  58. //DBG_ON(printf("build revision: %d\n", CNCT_BUILDREV));
  59. LOG_OUT;
  60. return 0;
  61. }
  62. /* for Windows platform - clean up WinSock layer */
  63. int cnct_finish()
  64. {
  65. LOG_IN;
  66. #ifdef CNCT_API_NT
  67. WSACleanup();
  68. #endif /* CNCT_API_NT */
  69. LOG_OUT;
  70. return 0;
  71. }
  72. /* get sockaddr : IPv4 or IPv6 */
  73. void *cnct_socket_getaddr(struct sockaddr *sa)
  74. {
  75. LOG_IN;
  76. if (sa->sa_family == AF_INET) {
  77. LOG_OUT;
  78. return &(((struct sockaddr_in *) sa)->sin_addr);
  79. }
  80. LOG_OUT;
  81. return &(((struct sockaddr_in6 *) sa)->sin6_addr);
  82. }
  83. /* get sockport : IPv4 or IPv6 */
  84. unsigned int cnct_socket_getport(struct sockaddr *sa)
  85. {
  86. LOG_IN;
  87. if (sa->sa_family == AF_INET) {
  88. LOG_OUT;
  89. return (((struct sockaddr_in *) sa)->sin_port);
  90. }
  91. LOG_OUT;
  92. return (((struct sockaddr_in6 *) sa)->sin6_port);
  93. }
  94. /* get address from addrinfo as string in addr */
  95. int cnct_socket_getstraddr(struct addrinfo *node, char *addr)
  96. {
  97. LOG_IN;
  98. /* clean up buffer */
  99. memset(addr, '\0', INET6_ADDRSTRLEN);
  100. #ifdef CNCT_API_BSD
  101. inet_ntop(node->ai_family, cnct_socket_getaddr((struct sockaddr *)node->ai_addr), addr, node->ai_addrlen);
  102. #else
  103. #ifdef MINGW
  104. DWORD len = INET6_ADDRSTRLEN;
  105. WSAAddressToString((LPSOCKADDR) node->ai_addr, (DWORD) node->ai_addrlen, NULL, addr, &len);
  106. #else
  107. InetNtop(node->ai_family, cnct_socket_getaddr((struct sockaddr *) node->ai_addr), addr, node->ai_addrlen);
  108. #endif /* MINGW */
  109. #endif /* CNCT_API_BSD */
  110. LOG_OUT;
  111. return 0;
  112. }
  113. /* set socket to non-blocking state */
  114. int cnct_socket_setnonblock(socket_t sd)
  115. {
  116. LOG_IN;
  117. #ifdef CNCT_API_BSD
  118. int flags, s;
  119. flags = fcntl(sd, F_GETFL, 0);
  120. if (flags == -1) {
  121. perror("fcntl");
  122. return -1;
  123. }
  124. flags |= O_NONBLOCK;
  125. s = fcntl(sd, F_SETFL, flags);
  126. if (s == -1) {
  127. perror("fcntl");
  128. return -1;
  129. }
  130. #endif /* CNCT_API_BSD */
  131. LOG_OUT;
  132. return 0;
  133. }
  134. int cnct_sockdata_print(char *msg, int size, int len)
  135. {
  136. MALLOC_TYPE_SIZE(char, str, size);
  137. snprintf(str, len, "%s", msg);
  138. printf("%s", str);
  139. FREE_PNTR(str);
  140. return 0;
  141. }
  142. /* create socket struct routine */
  143. //cnct_socket_t *cnct_socket_create(char *dhost, char *dport, int domain, int type, int reuse, int autoclose, int flags)
  144. cnct_socket_t *cnct_socket_creat(char *dhost, char *dport, int domain, int type, int protocol, int flags, int opts)
  145. {
  146. LOG_IN;
  147. MALLOC_TYPE(cnct_socket_t, socket);
  148. IF_NOT_NULL(dhost, MALLOC_PNTR_SIZE(char, socket->dhost, strlen(dhost)); strcpy(socket->dhost, dhost));
  149. IF_NOT_NULL(dport, MALLOC_PNTR_SIZE(char, socket->dport, strlen(dport)); strcpy(socket->dport, dport));
  150. socket->sd = -1;
  151. /* TODO: add verification of cross platform domain/type/protocol values */
  152. SET_VALUE(socket->domain, domain, AF_INET6, AF_INET);
  153. SET_VALUE(socket->type, type, SOCK_DGRAM, SOCK_STREAM);
  154. socket->protocol = protocol;
  155. socket->flags = flags;
  156. socket->opts = opts;
  157. socket->node = NULL;
  158. LOG_OUT;
  159. return socket;
  160. }
  161. /* clone socket struct routine */
  162. cnct_socket_t *cnct_socket_clone(cnct_socket_t *sckt_src)
  163. {
  164. /* TODO: change to memcpy-like behavior */
  165. LOG_IN;
  166. MALLOC_TYPE(cnct_socket_t, sckt_dst);
  167. IF_NOT_NULL(sckt_src->dhost, MALLOC_PNTR_SIZE(char, sckt_dst->dhost, strlen(sckt_src->dhost)); strcpy(sckt_dst->dhost, sckt_src->dhost));
  168. IF_NOT_NULL(sckt_src->dport, MALLOC_PNTR_SIZE(char, sckt_dst->dport, strlen(sckt_src->dport)); strcpy(sckt_dst->dport, sckt_src->dport));
  169. IF_NOT_NULL(sckt_src->node, MALLOC_PNTR_TYPE(struct addrinfo, sckt_dst->node); memcpy(sckt_dst->node, sckt_src->node, sizeof(struct addrinfo)));
  170. sckt_dst->sd = sckt_src->sd;
  171. sckt_dst->domain = sckt_src->domain;
  172. sckt_dst->type = sckt_src->type;
  173. sckt_dst->protocol = sckt_src->protocol;
  174. sckt_dst->flags = sckt_src->flags;
  175. sckt_dst->opts = sckt_src->opts;
  176. /* FIXME: copy */
  177. sckt_dst->node = NULL;
  178. LOG_OUT;
  179. return sckt_dst;
  180. }
  181. /* delete socket struct routine */
  182. int cnct_socket_delete(cnct_socket_t *socket)
  183. {
  184. LOG_IN;
  185. if (socket) {
  186. /* TODO: clean up more clearly */
  187. FREE_PNTR(socket->dhost);
  188. FREE_PNTR(socket->dport);
  189. FREE_PNTR(socket->node);
  190. //cnct_socket_close(socket->sd);
  191. cnct_socket_shutdown(socket->sd);
  192. free(socket);
  193. }
  194. LOG_OUT;
  195. return 0;
  196. }
  197. /* shutdown socket descriptor routine */
  198. int cnct_socket_shutdown(socket_t sd)
  199. {
  200. LOG_IN;
  201. shutdown(sd, CNCT_SHUTDOWN_DUPLEX);
  202. cnct_socket_close(sd);
  203. LOG_OUT;
  204. return 0;
  205. }
  206. /* set connection on socket */
  207. socket_t cnct_socket_connect(cnct_socket_t *sckt)
  208. {
  209. LOG_IN;
  210. struct addrinfo *nodes, *node;
  211. int resolv;
  212. socket_t sd;
  213. /* init routine */
  214. MALLOC_TYPE(struct addrinfo, hints);
  215. hints->ai_family = AF_UNSPEC;
  216. hints->ai_socktype = sckt->type;
  217. if ((resolv = getaddrinfo(sckt->host, sckt->port, hints, &nodes)) != 0) {
  218. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(resolv));
  219. return -1;
  220. }
  221. for (node = nodes; node != NULL; node = node->ai_next) {
  222. if ((sd = socket(node->ai_family, node->ai_socktype, node->ai_protocol)) == -1) {
  223. perror("client: socket");
  224. continue;
  225. }
  226. if (sckt->type == SOCK_STREAM) {
  227. /* call on TCP, not needed for UDP */
  228. if (connect(sd, node->ai_addr, node->ai_addrlen) == -1) {
  229. cnct_socket_close(sd);
  230. perror("client: connect");
  231. continue;
  232. }
  233. }
  234. break;
  235. }
  236. if (node == NULL) {
  237. fprintf(stderr, "failed to connect\n");
  238. return -1;
  239. }
  240. if (sckt->node) {
  241. free(sckt->node);
  242. sckt->node = NULL;
  243. }
  244. MALLOC_PNTR_TYPE(struct addrinfo, sckt->node);
  245. memcpy(sckt->node, node, sizeof(struct addrinfo));
  246. DBG_ON(
  247. char addr[INET6_ADDRSTRLEN];
  248. cnct_socket_getstraddr(node, addr);
  249. DBG_INFO(printf("connecting to %s\n", addr));
  250. );
  251. freeaddrinfo(nodes);
  252. LOG_OUT;
  253. return sd;
  254. }
  255. /* send-whole-buffer routine */
  256. int cnct_socket_send(cnct_socket_t *socket, char *msg, int len)
  257. {
  258. LOG_IN;
  259. int r = 0;
  260. int rx = len;
  261. int tx = 0;
  262. while (tx < len) {
  263. CNCT_SEND(socket, msg, tx, len, r);
  264. if (r == -1) {
  265. break;
  266. }
  267. tx += r;
  268. rx -= r;
  269. }
  270. LOG_OUT;
  271. return r == -1 ? -1 : tx;
  272. }
  273. /* connect - send - close */
  274. int cnct_socket_sendmsg(cnct_socket_t *socket, char *msg, int len)
  275. {
  276. LOG_IN;
  277. if (!(socket->sd != -1 && socket->reuse)) {
  278. socket->sd = cnct_socket_connect(socket);
  279. }
  280. if (cnct_socket_send(socket, msg, len) == -1) {
  281. printf("error: can't send all\n");
  282. }
  283. DBG_INFO(printf("send to server:[%s]\n", msg));
  284. if (socket->autoclose) {
  285. cnct_socket_close(socket->sd);
  286. }
  287. LOG_OUT;
  288. return 0;
  289. }
  290. socket_t cnct_socket_listen(cnct_socket_t *sckt)
  291. {
  292. LOG_IN;
  293. socket_t sd;
  294. struct addrinfo hints, *nodes, *node;
  295. int resolv;
  296. int on = 1;
  297. /* init routine */
  298. memset(&hints, 0, sizeof hints);
  299. hints.ai_family = sckt->ipv;
  300. hints.ai_socktype = sckt->type;
  301. hints.ai_flags = AI_PASSIVE;
  302. if ((resolv = getaddrinfo(NULL, sckt->port, &hints, &nodes)) != 0) {
  303. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(resolv));
  304. return 1;
  305. }
  306. /* loop through all the results and bind to the first we can */
  307. for (node = nodes; node != NULL; node = node->ai_next) {
  308. if ((sd = socket(node->ai_family, node->ai_socktype, node->ai_protocol)) == -1) {
  309. perror("server: socket");
  310. continue;
  311. }
  312. if (node->ai_family == AF_INET) {
  313. printf("bind AF_INET\n");
  314. } else if (node->ai_family == AF_INET6) {
  315. printf("bind AF_INET6\n");
  316. } else {
  317. printf("bind %d\n", node->ai_family);
  318. }
  319. // #ifdef CNCT_UNIXWARE
  320. if (sckt->type == SOCK_STREAM) {
  321. if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(int)) == -1) {
  322. perror("setsockopt");
  323. }
  324. }
  325. // #endif /* CNCT_UNIXWARE */
  326. printf("server: port = %d\n", (((struct sockaddr_in *) node->ai_addr)->sin_port));
  327. if (bind(sd, node->ai_addr, node->ai_addrlen) == -1) {
  328. cnct_socket_close(sd);
  329. perror("server: bind");
  330. continue;
  331. }
  332. break;
  333. }
  334. if (node == NULL) {
  335. fprintf(stderr, "server: failed to bind\n");
  336. return 2;
  337. }
  338. /* free structure since it's not using anymore */
  339. freeaddrinfo(nodes);
  340. if (sckt->type == SOCK_STREAM) {
  341. if (listen(sd, CNCT_SOCKET_BACKLOG) == -1) {
  342. perror("listen");
  343. exit(1);
  344. }
  345. }
  346. printf("server: waiting for connections...\n");
  347. LOG_OUT;
  348. return sd;
  349. }
  350. socket_t cnct_socket_accept(socket_t ld)
  351. {
  352. LOG_IN;
  353. socket_t ad;
  354. socklen_t slen;
  355. struct sockaddr_storage client;
  356. slen = sizeof(client);
  357. ad = accept(ld, (struct sockaddr *) &client, &slen);
  358. if (ad == -1) {
  359. perror("accept");
  360. }
  361. /* close parents' socket for accept() */
  362. cnct_socket_close(ld);
  363. LOG_OUT;
  364. return ad;
  365. }
  366. /* recv-whole-buffer routine */
  367. int cnct_socket_recv(cnct_socket_t *socket, socket_t sd, char *msg, int len)
  368. {
  369. LOG_IN;
  370. int r = 0;
  371. int rx = 0;
  372. int tx = len;
  373. int snap = 0;
  374. //socklen_t slen = sizeof(struct sockaddr_storage);
  375. if (!len) {
  376. snap = 1;
  377. tx = CNCT_SOCKET_DATASIZE;
  378. }
  379. do {
  380. //while (rx < len) {
  381. /*
  382. CNCT_RECV(socket, sd, msg, rx, len, r)
  383. CNCT_RECV(socket, sd, data, ptr, len, ret)
  384. ret = recvfrom(sd, data + ptr, len, socket->flags, (struct sockaddr *) &(socket->client), (socklen_t *) &slen);
  385. CNCT_RECV(socket, sd, msg, rx, len, r)
  386. */
  387. //r = recvfrom(sd, msg + rx, tx, socket->flags, (struct sockaddr *) &(socket->client), (socklen_t *) &slen);
  388. CNCT_RECV(socket, sd, msg, rx, tx, r)
  389. //r = recvfrom(sd, msg + rx, tx
  390. if (r == -1) {
  391. break;
  392. }
  393. rx += r;
  394. tx -= r;
  395. if (snap) {
  396. break;
  397. }
  398. //}
  399. } while (rx < len);
  400. LOG_OUT;
  401. return r == -1 ? -1 : rx;
  402. }
  403. /* listen - accept - recv - close */
  404. int cnct_socket_recvmsg(cnct_socket_t *socket, char *msg, int len)
  405. {
  406. LOG_IN;
  407. int rx;
  408. socket_t ad, ld;
  409. ld = cnct_socket_listen(socket);
  410. if (socket->type == SOCK_STREAM) {
  411. ad = cnct_socket_accept(ld);
  412. rx = cnct_socket_recv(socket, ad, msg, len);
  413. } else {
  414. rx = cnct_socket_recv(socket, ld, msg, len);
  415. }
  416. LOG_OUT;
  417. return rx;
  418. }
  419. #ifdef CNCT_API_NT
  420. /* required routine for WinSock callback support */
  421. struct thread_data {
  422. cnct_socket_t *socket;
  423. socket_t sd;
  424. struct sockaddr_storage client;
  425. cnct_sockdata_t udp_data;
  426. int (*cb)(cnct_socket_t *, socket_t, struct sockaddr_storage, cnct_sockdata_t);
  427. };
  428. DWORD WINAPI cnct_socket_request(void *data)
  429. {
  430. (*((struct thread_data *) data)->cb) (
  431. (((struct thread_data *) data)->socket),
  432. (((struct thread_data *) data)->sd),
  433. (((struct thread_data *) data)->client),
  434. (((struct thread_data *) data)->udp_data)
  435. );
  436. if (((struct thread_data *) data)->socket->type == SOCK_STREAM) {
  437. if (((struct thread_data *) data)->socket->autoclose) {
  438. cnct_socket_close((((struct thread_data *) data)->sd));
  439. }
  440. }
  441. return 0;
  442. }
  443. /* *** */
  444. #endif
  445. /* init server for processing accepted connections in callback */
  446. int cnct_socket_server(cnct_socket_t *socket, int (*callback)(cnct_socket_t *, socket_t, struct sockaddr_storage, cnct_sockdata_t))
  447. {
  448. LOG_IN;
  449. socket_t ld, ad;
  450. socklen_t slen;
  451. struct sockaddr_storage client;
  452. cnct_sockdata_t udp_data;
  453. memset(&udp_data.data, '\0', CNCT_SOCKET_DATASIZE);
  454. udp_data.len = -1;
  455. slen = sizeof(client);
  456. ld = cnct_socket_listen(socket);
  457. while (1) {
  458. /* TODO: FIXME: wrap me completely /!\ */
  459. if (socket->type == SOCK_STREAM) {
  460. ad = accept(ld, (struct sockaddr *) &client, &slen);
  461. if (ad == -1) {
  462. perror("accept");
  463. continue;
  464. }
  465. #ifdef CNCT_API_BSD
  466. if (!fork()) {
  467. cnct_socket_close(ld);
  468. (*callback)(socket, ad, client, udp_data);
  469. cnct_socket_close(ad);
  470. exit(0);
  471. }
  472. /* full disconnect from client */
  473. if (socket->autoclose) {
  474. cnct_socket_close(ad);
  475. }
  476. #else
  477. struct thread_data *tdata;
  478. tdata = (struct thread_data *) malloc(sizeof(struct thread_data)); /* TODO: free? */
  479. tdata->socket = socket;
  480. tdata->sd = ad;
  481. tdata->client = client;
  482. tdata->cb = callback;
  483. //tdata->cb = (int (*)(void *, socket_t)) callback;
  484. DWORD tid;
  485. CreateThread(NULL, NULL, cnct_socket_request, tdata, NULL, &tid);
  486. DBG_ON(printf("CREATE_THREAD\n"));
  487. #endif
  488. } else {
  489. memset(&udp_data.data, '\0', CNCT_SOCKET_DATASIZE);
  490. ad = recvfrom(ld, (char *) &udp_data.data, CNCT_SOCKET_DATASIZE-1, 0, (struct sockaddr *) &client, &slen);
  491. if (ad == -1) {
  492. perror("recvfrom");
  493. continue;
  494. }
  495. udp_data.len = ad;
  496. #ifdef CNCT_API_BSD
  497. if (!fork()) {
  498. (*callback)(socket, ld, client, udp_data);
  499. cnct_socket_close(ld);
  500. exit(0);
  501. }
  502. #else
  503. struct thread_data *tdata;
  504. tdata = (struct thread_data *) malloc(sizeof(struct thread_data)); /* TODO: free? */
  505. tdata->socket = socket;
  506. tdata->sd = ld;
  507. tdata->client = client;
  508. tdata->udp_data = udp_data;
  509. tdata->cb = callback;
  510. //tdata->cb = (int (*)(void *, socket_t)) callback;
  511. DWORD tid;
  512. CreateThread(NULL, NULL, cnct_socket_request, tdata, NULL, &tid);
  513. DBG_ON(printf("CREATE_THREAD\n"));
  514. #endif
  515. }
  516. }
  517. LOG_OUT;
  518. return 0;
  519. }