release 0.92.3
[cntlm.git] / socket.c
blobb241b0f57a39015b8ccadd7f9c194d34b90316b2
1 /*
2 * These are socket routines for the main module of CNTLM
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.
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.
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.
18 * Copyright (c) 2007 David Kubicek
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/time.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <netdb.h>
34 #include <syslog.h>
36 #include "utils.h"
38 extern int debug;
41 * gethostbyname() wrapper. Return 1 if OK, otherwise 0.
43 int so_resolv(struct in_addr *host, const char *name) {
45 struct hostent *resolv;
47 resolv = gethostbyname(name);
48 if (!resolv)
49 return 0;
51 memcpy(host, resolv->h_addr_list[0], resolv->h_length);
52 return 1;
54 struct addrinfo hints, *res, *p;
56 memset(&hints, 0, sizeof(hints));
57 hints.ai_family = AF_INET;
58 hints.ai_socktype = SOCK_STREAM;
59 int rc = getaddrinfo(name, NULL, &hints, &res);
60 if (rc != 0) {
61 if (debug)
62 printf("so_resolv: %s failed: %s (%d)\n", name, gai_strerror(rc), rc);
63 return 0;
66 if (debug)
67 printf("Resolve %s:\n", name);
68 int addr_set = 0;
69 for (p = res; p != NULL; p = p->ai_next) {
70 struct sockaddr_in *ad = (struct sockaddr_in*)(p->ai_addr);
71 if (ad == NULL) {
72 freeaddrinfo(res);
73 return 0;
75 if (!addr_set) {
76 memcpy(host, &ad->sin_addr, sizeof(ad->sin_addr));
77 addr_set = 1;
78 if (debug)
79 printf(" -> %s\n", inet_ntoa(ad->sin_addr));
80 } else
81 if (debug)
82 printf(" %s\n", inet_ntoa(ad->sin_addr));
85 freeaddrinfo(res);
87 return 1;
91 * Connect to a host. Host is required to be resolved
92 * in the struct in_addr already.
93 * Returns: socket descriptor
95 int so_connect(struct in_addr host, int port) {
96 int flags, fd, rc;
97 struct sockaddr_in saddr;
98 // struct timeval tv;
99 // fd_set fds;
101 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
102 if (debug)
103 printf("so_connect: create: %s\n", strerror(errno));
104 return -1;
107 memset(&saddr, 0, sizeof(saddr));
108 saddr.sin_family = AF_INET;
109 saddr.sin_port = htons(port);
110 saddr.sin_addr = host;
112 if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
113 if (debug)
114 printf("so_connect: get flags: %s\n", strerror(errno));
115 close(fd);
116 return -1;
119 /* NON-BLOCKING connect with timeout
120 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
121 if (debug)
122 printf("so_connect: set non-blocking: %s\n", strerror(errno));
123 close(fd);
124 return -1;
128 rc = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
131 printf("connect = %d\n", rc);
132 if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) {
133 FD_ZERO(&fds);
134 FD_SET(fd, &fds);
135 tv.tv_sec = 10;
136 tv.tv_usec = 0;
137 printf("select!\n");
138 rc = select(fd+1, NULL, &fds, NULL, &tv) - 1;
139 printf("select = %d\n", rc);
143 if (rc < 0) {
144 if (debug)
145 printf("so_connect: %s\n", strerror(errno));
146 close(fd);
147 return -1;
150 if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
151 if (debug)
152 printf("so_connect: set blocking: %s\n", strerror(errno));
153 close(fd);
154 return -1;
157 return fd;
161 * Bind the specified port and listen on it.
162 * Return socket descriptor if OK, otherwise 0.
164 int so_listen(int port, struct in_addr source) {
165 struct sockaddr_in saddr;
166 int fd;
167 socklen_t clen;
169 fd = socket(PF_INET, SOCK_STREAM, 0);
170 if (fd < 0) {
171 if (debug)
172 printf("so_listen: new socket: %s\n", strerror(errno));
173 close(fd);
174 return -1;
177 clen = 1;
178 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen));
179 memset((void *)&saddr, 0, sizeof(saddr));
180 saddr.sin_family = AF_INET;
181 saddr.sin_port = htons(port);
182 saddr.sin_addr.s_addr = source.s_addr;
184 if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) {
185 syslog(LOG_ERR, "Cannot bind port %d: %s!\n", port, strerror(errno));
186 close(fd);
187 return -1;
190 if (listen(fd, 5)) {
191 close(fd);
192 return -1;
195 return fd;
199 * Return 1 if data is available on the socket,
200 * 0 if connection was closed
201 * -1 if error (errno is set)
203 int so_recvtest(int fd) {
204 char buf;
205 int i;
206 #ifndef MSG_DONTWAIT
207 unsigned int flags;
209 flags = fcntl(fd, F_GETFL);
210 fcntl(fd, F_SETFL, flags | O_NONBLOCK);
211 i = recv(fd, &buf, 1, MSG_PEEK);
212 fcntl(fd, F_SETFL, flags);
213 #else
214 i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK);
215 #endif
217 return i;
221 * Return true if there are some data on the socket
223 int so_dataready(int fd) {
224 return so_recvtest(fd) > 0;
228 * Reliable way of finding out whether a connection was closed
229 * on the remote end, without actually reading from it.
231 int so_closed(int fd) {
232 int i;
234 if (fd == -1)
235 return 1;
237 i = so_recvtest(fd);
238 return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhap AIX devels could explain! :-( */
242 * Receive a single line from the socket. This is no super-efficient
243 * implementation, but more than we need to read in a few headers.
244 * What's more, the data is actually recv'd from a socket buffer.
246 * I had to time this in comparison to recv with block read :) and
247 * the performance was very similar. Given the fact that it keeps us
248 * from creating a whole buffering scheme around the socket (HTTP
249 * connection is both line and block oriented, switching back and forth),
250 * it is actually OK.
252 int so_recvln(int fd, char **buf, int *size) {
253 int len = 0;
254 int r = 1;
255 char c = 0;
256 char *tmp;
258 while (len < *size-1 && c != '\n') {
259 r = read(fd, &c, 1);
260 if (r <= 0)
261 break;
263 (*buf)[len++] = c;
266 * End of buffer, still no EOL? Resize the buffer
268 if (len == *size-1 && c != '\n') {
269 if (debug)
270 printf("so_recvln(%d): realloc %d\n", fd, *size*2);
271 *size *= 2;
272 tmp = realloc(*buf, *size);
273 if (tmp == NULL)
274 return -1;
275 else
276 *buf = tmp;
279 (*buf)[len] = 0;
281 return r;