PageRenderTime 26ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/src/UDP_Client/jsocket6.4.c

https://bitbucket.org/niclabs/induceddelay
C | 418 lines | 268 code | 73 blank | 77 comment | 42 complexity | afb342e1cd90209f1794d392e1fbbba5 MD5 | raw file
  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <sys/wait.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <netdb.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <strings.h>
  10. #include <string.h>
  11. #include <errno.h>
  12. #include "jsocket6.h"
  13. /*
  14. * ToDo: Revisar portabilidad con endians de codigo JOLD_STYLE de IPV4_MAPPED
  15. */
  16. /*
  17. * Version 2.0: con soporte transparente para IPv6
  18. * API full compatible con los viejos jsockets
  19. * Autores: Jo Piquer con ayuda de Sebastian Kreft, 2009
  20. * Version 2.1: Con mejor soporte para equipos no-IPv6
  21. * Version 3.0: Con soporte para gethostbyname2_r para ser reentrante
  22. * Version 4.0: Nueva API: j_sock_connect y j_sock_bind
  23. */
  24. /* Si se prefiere evitar getaddrinfo (hay versiones con bugs en Linux) */
  25. #define JOLD_STYLE /* beta testing now: No usar si no es en Linux! */
  26. /* Si se quiere debugging al intentar direcciones IP (Trying....) */
  27. #define JDEBUG
  28. /* Si queremos IPv6 junto con IPv4 */
  29. #define JIPv6
  30. /*
  31. * Parte I:
  32. * API Antigua: j_socket(), j_connect(), j_bind()
  33. * Se recomienda usar la nueva API, es más compatible con IPv6
  34. */
  35. /*
  36. * Retorna un socket para conexion
  37. * En dual-stack, usamos un socket IPv6 y IPv4 MAPPED para soportar IPv4
  38. */
  39. int j_socket()
  40. {
  41. int sz = 1;
  42. int fd;
  43. #ifdef JIPv6
  44. fd = socket(PF_INET6, SOCK_STREAM, 0);
  45. #else
  46. fd = socket(PF_INET, SOCK_STREAM, 0);
  47. #endif
  48. if(fd < 0) { fprintf(stderr, "no pude hacer un socket\n");
  49. return -1;
  50. }
  51. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &sz, 4);
  52. return(fd);
  53. }
  54. /*
  55. * Pone un "nombre" (port) a un socket
  56. * y lo prepara para recibir conexiones
  57. * retorna 0 si OK, -1 si no
  58. * En dual-stack hacemos bind en IPv6 y usamos IPv4 MAPPED para IPv4
  59. */
  60. int j_bind(s, port)
  61. int s;
  62. int port;
  63. {
  64. #ifdef JIPv6
  65. struct sockaddr_in6 portname;
  66. #else
  67. struct sockaddr_in portname;
  68. #endif
  69. /* ponemos el nombre */
  70. bzero(&portname, sizeof portname);
  71. #ifdef JIPv6
  72. portname.sin6_port = htons(port);
  73. portname.sin6_family = AF_INET6;
  74. portname.sin6_addr = in6addr_any;
  75. #else
  76. portname.sin_port = htons(port);
  77. portname.sin_family = AF_INET;
  78. portname.sin_addr.s_addr = INADDR_ANY;
  79. #endif
  80. /* lo asociamos al socket */
  81. if( bind(s, (struct sockaddr *) &portname, sizeof portname) != 0)
  82. return(-1);
  83. listen(s, 5);
  84. return(0);
  85. }
  86. /*
  87. * Se conecta con un port conocido
  88. * retorna 0 si OK, -1 si no
  89. * Debiera usar getaddrinfo() y listo, pero no me funciona bien
  90. * JOLD_STYLE implementa esto a mano y funciona bien en Linux/386
  91. */
  92. #define MAX_NAME INET6_ADDRSTRLEN
  93. int j_connect(s, host, port)
  94. int s;
  95. char *host;
  96. int port;
  97. {
  98. struct addrinfo hints;
  99. struct addrinfo *addresses, *hp;
  100. #ifdef JOLD_STYLE
  101. struct hostent hostbuf, *hp2 = NULL;
  102. size_t hstbuflen;
  103. char *tmphstbuf;
  104. #ifdef JIPv6
  105. struct sockaddr_in6 portname;
  106. #else
  107. struct sockaddr_in portname;
  108. #endif
  109. int i;
  110. int res, herr;
  111. #endif /* JOLD_STYLE */
  112. char sport[20];
  113. int ret;
  114. char name[MAX_NAME];
  115. int error_num;
  116. #ifdef JOLD_STYLE
  117. hstbuflen = 1024;
  118. tmphstbuf = malloc(hstbuflen);
  119. #ifdef JIPv6
  120. while((res=gethostbyname2_r(host, AF_INET6, &hostbuf, tmphstbuf,
  121. hstbuflen, &hp2, &herr)) == ERANGE) {
  122. /* Need more buffer */
  123. hstbuflen *= 2;
  124. tmphstbuf = realloc(tmphstbuf, hstbuflen);
  125. }
  126. if(res == 0 && hp2 != NULL) {
  127. bzero(&portname, sizeof portname);
  128. portname.sin6_port = htons(port);
  129. portname.sin6_family = AF_INET6;
  130. /* Trato de conectarme con todas las direcciones IP del servidor */
  131. for(i=0; hp2->h_addr_list[i] != NULL; i++) {
  132. bcopy(hp2->h_addr_list[i], &portname.sin6_addr.s6_addr, hp2->h_length);
  133. #ifdef JDEBUG
  134. inet_ntop(AF_INET6, portname.sin6_addr.s6_addr, name, sizeof name);
  135. fprintf(stderr, "Trying %s/%d...", name, port);
  136. #endif
  137. if(connect(s, (struct sockaddr *)&portname, sizeof portname) == 0) {
  138. fprintf(stderr, "done\n");
  139. free(tmphstbuf);
  140. return(0);
  141. }
  142. fprintf(stderr, "failed\n");
  143. }
  144. }
  145. #endif
  146. /* No logre' conectarme en IPv6, usamos IPv4-mapeado */
  147. while((res=gethostbyname2_r(host, AF_INET, &hostbuf, tmphstbuf,
  148. hstbuflen, &hp2, &herr)) == ERANGE) {
  149. /* Need more buffer */
  150. hstbuflen *= 2;
  151. tmphstbuf = realloc(tmphstbuf, hstbuflen);
  152. }
  153. if(res == 0 && hp2 != NULL) {
  154. bzero(&portname, sizeof portname);
  155. #ifdef JIPv6
  156. portname.sin6_port = htons(port);
  157. portname.sin6_family = AF_INET6;
  158. #else
  159. portname.sin_port = htons(port);
  160. portname.sin_family = AF_INET;
  161. #endif
  162. /* Trato de conectarme con todas las direcciones IP del servidor */
  163. for(i=0; hp2->h_addr_list[i] != NULL; i++) {
  164. #ifdef JIPv6
  165. uint32_t *p = portname.sin6_addr.s6_addr32;
  166. /* mapear dirs IPv4 a IPv6!! */
  167. bcopy(hp2->h_addr_list[i], p+3, hp2->h_length);
  168. p[0] = 0; p[1] = 0; p[2] = htonl(0xffff);
  169. #ifdef JDEBUG
  170. inet_ntop(AF_INET6, portname.sin6_addr.s6_addr, name, sizeof name);
  171. fprintf(stderr, "Trying %s/%d...", name, port);
  172. #endif
  173. #else /* JIPv6 */
  174. bcopy( hp2->h_addr_list[i], &portname.sin_addr.s_addr, hp2->h_length);
  175. #ifdef JDEBUG
  176. inet_ntop(AF_INET, &portname.sin_addr.s_addr, name, sizeof name);
  177. fprintf(stderr, "Trying %s/%d...", name, port);
  178. #endif
  179. #endif /* JIPv6 */
  180. if(connect(s, (struct sockaddr *)&portname, sizeof portname) == 0) {
  181. fprintf(stderr, "done\n");
  182. free(tmphstbuf);
  183. return(0);
  184. }
  185. else { perror("connect"); fprintf(stderr, "sock failed=%d\n", s);}
  186. }
  187. } else fprintf(stderr, "name fail!: %s\n", host);
  188. /* No logre' conectarme de ninguna forma */
  189. free(tmphstbuf);
  190. return -1;
  191. #else /* JOLD_STYLE */
  192. sprintf(sport, "%d", port);
  193. /* Traducir nombre a direccion IP */
  194. memset(&hints, 0, sizeof(struct addrinfo));
  195. #ifdef JIPv6
  196. hints.ai_family = AF_INET6;
  197. #else
  198. hints.ai_family = AF_INET;
  199. #endif
  200. hints.ai_socktype = SOCK_STREAM;
  201. // hints.ai_flags = AI_NUMERICSERV; /* | AI_IDN; */
  202. hints.ai_flags = AI_V4MAPPED|AI_ALL; /* Retornar todo IPv4 en IPv6 */
  203. // | AI_ADDRCONFIG /* retornar IPv6 solo si yo tengo: no funciona bien */
  204. /* | AI_IDN; *//* Aceptar nombres IDN: no estandar aun */
  205. ret = getaddrinfo(host, sport, &hints, &addresses);
  206. if( ret != 0 ) /* Mmmh, puede ser que no soporte bien IPv6? */
  207. {
  208. fprintf(stderr, "Name/port unknown: %s/%s err: %s\n", host, sport, gai_strerror(ret));
  209. return(-1);
  210. }
  211. /* Trato de conectarme con todas las direcciones IP del servidor */
  212. for(hp=addresses; hp != NULL; hp = hp->ai_next) {
  213. #ifdef JDEBUG
  214. getnameinfo(hp->ai_addr, hp->ai_addrlen, name, MAX_NAME, NULL, 0, NI_NUMERICHOST);
  215. fprintf(stderr, "Trying %s/%s...", name, sport);
  216. #endif
  217. if(connect(s, hp->ai_addr, hp->ai_addrlen) == 0) {
  218. fprintf(stderr, "done\n");
  219. break;
  220. }
  221. fprintf(stderr, "failed\n");
  222. }
  223. freeaddrinfo(addresses);
  224. if(hp == NULL)
  225. /* No logre' conectarme */
  226. return -1;
  227. else
  228. return 0;
  229. #endif /* JOLD_STYLE */
  230. }
  231. /*
  232. * Acepta una conexion pendiente o se bloquea esperando una
  233. * retorna un fd si OK, -1 si no
  234. * sockaddr_storage soporta IPv4 e IPv6
  235. * Válida para la nueva API también
  236. */
  237. int j_accept(s)
  238. int s;
  239. {
  240. struct sockaddr_storage from;
  241. unsigned int size = sizeof from;
  242. return( accept(s, (struct sockaddr *) &from, &size) );
  243. }
  244. /*
  245. * Parte II:
  246. * API Nueva: j_socket_bind(), j_socket_connect()
  247. * Se recomienda usar la nueva API, es más compatible con IPv6
  248. */
  249. int j_socket_udp_bind(char *port) {
  250. return j_socket_bind(SOCK_DGRAM, port);
  251. }
  252. int j_socket_tcp_bind(char *port) {
  253. return j_socket_bind(SOCK_STREAM, port);
  254. }
  255. int j_socket_bind(int type, char *port) {
  256. struct addrinfo hints, *res, *ressave;
  257. int n, sockfd;
  258. memset(&hints, 0, sizeof(struct addrinfo));
  259. /*
  260. * AI_PASSIVE flag: the resulting address is used to bind
  261. * to a socket for accepting incoming connections.
  262. * So, when the hostname==NULL, getaddrinfo function will
  263. * return one entry per allowed protocol family containing
  264. * the unspecified address for that family.
  265. *
  266. */
  267. hints.ai_flags = AI_PASSIVE | AI_V4MAPPED | AI_ALL;
  268. hints.ai_family = AF_INET6;
  269. hints.ai_socktype = type;
  270. n = getaddrinfo(NULL, port, &hints, &res);
  271. if (n <0) {
  272. fprintf(stderr,
  273. "getaddrinfo error:: [%s]\n",
  274. gai_strerror(n));
  275. return -1;
  276. }
  277. ressave=res;
  278. /*
  279. * Try open socket with each address getaddrinfo returned,
  280. * until getting a valid listening socket.
  281. */
  282. sockfd=-1;
  283. while (res) {
  284. sockfd = socket(res->ai_family,
  285. res->ai_socktype,
  286. res->ai_protocol);
  287. if (!(sockfd < 0)) {
  288. if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  289. break;
  290. close(sockfd);
  291. sockfd=-1;
  292. }
  293. res = res->ai_next;
  294. }
  295. if (sockfd < 0) {
  296. freeaddrinfo(ressave);
  297. fprintf(stderr,
  298. "socket error:: could not open socket\n");
  299. return -1;
  300. }
  301. listen(sockfd, 5);
  302. freeaddrinfo(ressave);
  303. return sockfd;
  304. }
  305. int j_socket_udp_connect(char *server, char *port) {
  306. return j_socket_connect(SOCK_DGRAM, server, port);
  307. }
  308. int j_socket_tcp_connect(char *server, char *port) {
  309. return j_socket_connect(SOCK_STREAM, server, port);
  310. }
  311. int j_socket_connect(int type, char *server, char *port) {
  312. struct addrinfo hints, *res, *ressave;
  313. int n, sockfd;
  314. memset(&hints, 0, sizeof(struct addrinfo));
  315. hints.ai_family = AF_UNSPEC;
  316. hints.ai_socktype = type;
  317. hints.ai_flags = AI_V4MAPPED | AI_ALL;
  318. n = getaddrinfo(server, port, &hints, &res);
  319. if (n <0) {
  320. fprintf(stderr,
  321. "getaddrinfo error:: [%s]\n",
  322. gai_strerror(n));
  323. return -1;
  324. }
  325. ressave=res;
  326. /*
  327. * Try open socket with each address getaddrinfo returned,
  328. * until getting a valid listening socket.
  329. */
  330. sockfd=-1;
  331. while (res) {
  332. sockfd = socket(res->ai_family,
  333. res->ai_socktype,
  334. res->ai_protocol);
  335. if (!(sockfd < 0)) {
  336. if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  337. break;
  338. close(sockfd);
  339. sockfd=-1;
  340. }
  341. res = res->ai_next;
  342. }
  343. freeaddrinfo(ressave);
  344. return sockfd;
  345. }