PageRenderTime 47ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/cntlm-0.92/socket.c

#
C | 283 lines | 156 code | 37 blank | 90 comment | 43 complexity | ef2d32b2198dd8ffbcb06911298da312 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * These are socket routines for the main module of CNTLM
  3. *
  4. * CNTLM is free software; you can redistribute it and/or modify it under the
  5. * terms of the GNU General Public License as published by the Free Software
  6. * Foundation; either version 2 of the License, or (at your option) any later
  7. * version.
  8. *
  9. * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
  10. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. * details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with
  15. * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
  16. * St, Fifth Floor, Boston, MA 02110-1301, USA.
  17. *
  18. * Copyright (c) 2007 David Kubicek
  19. *
  20. */
  21. #include <sys/types.h>
  22. #include <sys/socket.h>
  23. #include <sys/time.h>
  24. #include <netinet/in.h>
  25. #include <arpa/inet.h>
  26. #include <errno.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <unistd.h>
  31. #include <fcntl.h>
  32. #include <netdb.h>
  33. #include <syslog.h>
  34. #include "utils.h"
  35. extern int debug;
  36. /*
  37. * gethostbyname() wrapper. Return 1 if OK, otherwise 0.
  38. */
  39. int so_resolv(struct in_addr *host, const char *name) {
  40. /*
  41. struct hostent *resolv;
  42. resolv = gethostbyname(name);
  43. if (!resolv)
  44. return 0;
  45. memcpy(host, resolv->h_addr_list[0], resolv->h_length);
  46. return 1;
  47. */
  48. struct addrinfo hints, *res, *p;
  49. memset(&hints, 0, sizeof(hints));
  50. hints.ai_family = AF_INET;
  51. hints.ai_socktype = SOCK_STREAM;
  52. int rc = getaddrinfo(name, NULL, &hints, &res);
  53. if (rc != 0) {
  54. if (debug)
  55. printf("so_resolv: %s failed (%d: %s)\n", name, rc, gai_strerror(rc));
  56. return 0;
  57. }
  58. if (debug)
  59. printf("Resolve %s:\n", name);
  60. int addr_set = 0;
  61. for (p = res; p != NULL; p = p->ai_next) {
  62. struct sockaddr_in *ad = (struct sockaddr_in*)(p->ai_addr);
  63. if (ad == NULL) {
  64. freeaddrinfo(res);
  65. return 0;
  66. }
  67. if (!addr_set) {
  68. memcpy(host, &ad->sin_addr, p->ai_addrlen);
  69. addr_set = 1;
  70. if (debug)
  71. printf(" -> %s\n", inet_ntoa(ad->sin_addr));
  72. } else
  73. if (debug)
  74. printf(" %s\n", inet_ntoa(ad->sin_addr));
  75. }
  76. freeaddrinfo(res);
  77. return 1;
  78. }
  79. /*
  80. * Connect to a host. Host is required to be resolved
  81. * in the struct in_addr already.
  82. * Returns: socket descriptor
  83. */
  84. int so_connect(struct in_addr host, int port) {
  85. int flags, fd, rc;
  86. struct sockaddr_in saddr;
  87. // struct timeval tv;
  88. // fd_set fds;
  89. if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
  90. if (debug)
  91. printf("so_connect: create: %s\n", strerror(errno));
  92. return -1;
  93. }
  94. memset(&saddr, 0, sizeof(saddr));
  95. saddr.sin_family = AF_INET;
  96. saddr.sin_port = htons(port);
  97. saddr.sin_addr = host;
  98. if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
  99. if (debug)
  100. printf("so_connect: get flags: %s\n", strerror(errno));
  101. close(fd);
  102. return -1;
  103. }
  104. /* NON-BLOCKING connect with timeout
  105. if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
  106. if (debug)
  107. printf("so_connect: set non-blocking: %s\n", strerror(errno));
  108. close(fd);
  109. return -1;
  110. }
  111. */
  112. rc = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
  113. /*
  114. printf("connect = %d\n", rc);
  115. if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) {
  116. FD_ZERO(&fds);
  117. FD_SET(fd, &fds);
  118. tv.tv_sec = 10;
  119. tv.tv_usec = 0;
  120. printf("select!\n");
  121. rc = select(fd+1, NULL, &fds, NULL, &tv) - 1;
  122. printf("select = %d\n", rc);
  123. }
  124. */
  125. if (rc < 0) {
  126. if (debug)
  127. printf("so_connect: %s\n", strerror(errno));
  128. close(fd);
  129. return -1;
  130. }
  131. if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
  132. if (debug)
  133. printf("so_connect: set blocking: %s\n", strerror(errno));
  134. close(fd);
  135. return -1;
  136. }
  137. return fd;
  138. }
  139. /*
  140. * Bind the specified port and listen on it.
  141. * Return socket descriptor if OK, otherwise 0.
  142. */
  143. int so_listen(int port, struct in_addr source) {
  144. struct sockaddr_in saddr;
  145. int fd;
  146. socklen_t clen;
  147. fd = socket(PF_INET, SOCK_STREAM, 0);
  148. if (fd < 0) {
  149. if (debug)
  150. printf("so_listen: new socket: %s\n", strerror(errno));
  151. close(fd);
  152. return -1;
  153. }
  154. clen = 1;
  155. setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen));
  156. memset((void *)&saddr, 0, sizeof(saddr));
  157. saddr.sin_family = AF_INET;
  158. saddr.sin_port = htons(port);
  159. saddr.sin_addr.s_addr = source.s_addr;
  160. if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) {
  161. syslog(LOG_ERR, "Cannot bind port %d: %s!\n", port, strerror(errno));
  162. close(fd);
  163. return -1;
  164. }
  165. if (listen(fd, 5)) {
  166. close(fd);
  167. return -1;
  168. }
  169. return fd;
  170. }
  171. /*
  172. * Return 1 if data is available on the socket,
  173. * 0 if connection was closed
  174. * -1 if error (errno is set)
  175. */
  176. int so_recvtest(int fd) {
  177. char buf;
  178. int i;
  179. #ifndef MSG_DONTWAIT
  180. unsigned int flags;
  181. flags = fcntl(fd, F_GETFL);
  182. fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  183. i = recv(fd, &buf, 1, MSG_PEEK);
  184. fcntl(fd, F_SETFL, flags);
  185. #else
  186. i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK);
  187. #endif
  188. return i;
  189. }
  190. /*
  191. * Return true if there are some data on the socket
  192. */
  193. int so_dataready(int fd) {
  194. return so_recvtest(fd) > 0;
  195. }
  196. /*
  197. * Reliable way of finding out whether a connection was closed
  198. * on the remote end, without actually reading from it.
  199. */
  200. int so_closed(int fd) {
  201. int i;
  202. if (fd == -1)
  203. return 1;
  204. i = so_recvtest(fd);
  205. return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhap AIX devels could explain! :-( */
  206. }
  207. /*
  208. * Receive a single line from the socket. This is no super-efficient
  209. * implementation, but more than we need to read in a few headers.
  210. * What's more, the data is actually recv'd from a socket buffer.
  211. *
  212. * I had to time this in comparison to recv with block read :) and
  213. * the performance was very similar. Given the fact that it keeps us
  214. * from creating a whole buffering scheme around the socket (HTTP
  215. * connection is both line and block oriented, switching back and forth),
  216. * it is actually OK.
  217. */
  218. int so_recvln(int fd, char **buf, int *size) {
  219. int len = 0;
  220. int r = 1;
  221. char c = 0;
  222. char *tmp;
  223. while (len < *size-1 && c != '\n') {
  224. r = read(fd, &c, 1);
  225. if (r <= 0)
  226. break;
  227. (*buf)[len++] = c;
  228. /*
  229. * End of buffer, still no EOL? Resize the buffer
  230. */
  231. if (len == *size-1 && c != '\n') {
  232. if (debug)
  233. printf("so_recvln(%d): realloc %d\n", fd, *size*2);
  234. *size *= 2;
  235. tmp = realloc(*buf, *size);
  236. if (tmp == NULL)
  237. return -1;
  238. else
  239. *buf = tmp;
  240. }
  241. }
  242. (*buf)[len] = 0;
  243. return r;
  244. }