1 /* Interface for TCP functions
2  *
3  * Copyright (C) 2003-2004  Narcis Ilisei <inarcis2002@hotpop.com>
4  * Copyright (C) 2010-2021  Joachim Wiberg <troglobit@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, visit the Free Software Foundation
18  * website at http://www.gnu.org/licenses/gpl-2.0.html or write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include <errno.h>
24 #include <poll.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <arpa/nameser.h>
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <resolv.h>
36 
37 #include "log.h"
38 #include "tcp.h"
39 
tcp_construct(tcp_sock_t * tcp)40 int tcp_construct(tcp_sock_t *tcp)
41 {
42 	ASSERT(tcp);
43 
44 	memset(tcp, 0, sizeof(tcp_sock_t));
45 
46 	tcp->initialized = 0;
47 	tcp->socket      = -1; /* Initialize to 'error', not a possible socket id. */
48 	tcp->timeout     = TCP_DEFAULT_TIMEOUT;
49 
50 	return 0;
51 }
52 
tcp_destruct(tcp_sock_t * tcp)53 int tcp_destruct(tcp_sock_t *tcp)
54 {
55 	ASSERT(tcp);
56 
57 	if (tcp->initialized == 1)
58 		tcp_exit(tcp);
59 
60 	return 0;
61 }
62 
soerror(int sd)63 static int soerror(int sd)
64 {
65 	int code = 0;
66 	socklen_t len = sizeof(code);
67 
68 	if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &code, &len))
69 		return 1;
70 
71 	return errno = code;
72 }
73 
74 /*
75  * In the wonderful world of network programming the manual states that
76  * EINPROGRESS is only a possible error on non-blocking sockets.  Real world
77  * experience, however, suggests otherwise.  Simply poll() for completion and
78  * then continue. --Joachim
79  */
check_error(int sd,int msec)80 static int check_error(int sd, int msec)
81 {
82 	struct pollfd pfd = { sd, POLLOUT, 0 };
83 
84 	if (EINPROGRESS == errno) {
85 		logit(LOG_INFO, "Waiting (%d sec) for three-way handshake to complete ...", msec / 1000);
86 		if (poll (&pfd, 1, msec) > 0 && !soerror(sd)) {
87 			logit(LOG_INFO, "Connected.");
88 			return 0;
89 		}
90 	}
91 
92 	return 1;
93 }
94 
set_timeouts(int sd,int timeout)95 static void set_timeouts(int sd, int timeout)
96 {
97 	struct timeval sv;
98 
99 	memset(&sv, 0, sizeof(sv));
100 	sv.tv_sec  =  timeout / 1000;
101 	sv.tv_usec = (timeout % 1000) * 1000;
102 	if (-1 == setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &sv, sizeof(sv)))
103 		logit(LOG_INFO, "Failed setting receive timeout socket option: %s", strerror(errno));
104 	if (-1 == setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, &sv, sizeof(sv)))
105 		logit(LOG_INFO, "Failed setting send timeout socket option: %s", strerror(errno));
106 }
107 
tcp_init(tcp_sock_t * tcp,char * msg)108 int tcp_init(tcp_sock_t *tcp, char *msg)
109 {
110 	int rc = 0;
111 
112 	ASSERT(tcp);
113 
114 	if (tcp->initialized == 1)
115 		return 0;
116 
117 	do {
118 		int s, sd, tries = 0;
119 		char port[10];
120 		char host[NI_MAXHOST];
121 		struct addrinfo hints, *servinfo, *ai;
122 		struct sockaddr *sa;
123 		socklen_t len;
124 
125 		/* remote address */
126 		if (!tcp->remote_host)
127 			break;
128 
129 		/* Clear DNS cache before calling getaddrinfo(). */
130 		res_init();
131 
132 		/* Obtain address(es) matching host/port */
133 		memset(&hints, 0, sizeof(struct addrinfo));
134 		hints.ai_family = AF_UNSPEC;		/* Allow IPv4 or IPv6 */
135 		hints.ai_socktype = SOCK_STREAM;	/* Stream socket */
136 		hints.ai_flags = AI_NUMERICSERV;	/* No service name lookup */
137 		snprintf(port, sizeof(port), "%d", tcp->port);
138 
139 		s = getaddrinfo(tcp->remote_host, port, &hints, &servinfo);
140 		if (s != 0 || !servinfo) {
141 			logit(LOG_WARNING, "Failed resolving hostname %s: %s", tcp->remote_host, gai_strerror(s));
142 			rc = RC_TCP_INVALID_REMOTE_ADDR;
143 			break;
144 		}
145 		ai = servinfo;
146 
147 		while (1) {
148 			sd = socket(ai->ai_family, SOCK_STREAM, 0);
149 			if (sd == -1) {
150 				logit(LOG_ERR, "Error creating client socket: %s", strerror(errno));
151 				rc = RC_TCP_SOCKET_CREATE_ERROR;
152 				break;
153 			}
154 
155 			/* Now we try connecting to the server, on connect fail, try next DNS record */
156 			sa  = ai->ai_addr;
157 			len = ai->ai_addrlen;
158 
159 			if (getnameinfo(sa, len, host, sizeof(host), NULL, 0, NI_NUMERICHOST))
160 				goto next;
161 
162 			set_timeouts(sd, tcp->timeout);
163 
164 			logit(LOG_INFO, "%s, %sconnecting to %s([%s]:%d)", msg, tries ? "re" : "",
165 			      tcp->remote_host, host, tcp->port);
166 			if (connect(sd, sa, len) && check_error(sd, tcp->timeout)) {
167 			next:
168 				tries++;
169 
170 				ai = ai->ai_next;
171 				if (ai) {
172 					logit(LOG_INFO, "Failed connecting to that server: %s",
173 					      errno != EINPROGRESS ? strerror(errno) : "retrying ...");
174 					close(sd);
175 					continue;
176 				}
177 
178 				logit(LOG_WARNING, "Failed connecting to %s: %s", tcp->remote_host, strerror(errno));
179 				close(sd);
180 				rc = RC_TCP_CONNECT_FAILED;
181 			} else {
182 				tcp->socket = sd;
183 				tcp->initialized = 1;
184 			}
185 
186 			break;
187 		}
188 
189 		freeaddrinfo(servinfo);
190 	}
191 	while (0);
192 
193 	if (rc) {
194 		tcp_exit(tcp);
195 		return rc;
196 	}
197 
198 	return 0;
199 }
200 
tcp_exit(tcp_sock_t * tcp)201 int tcp_exit(tcp_sock_t *tcp)
202 {
203 	ASSERT(tcp);
204 
205 	if (!tcp->initialized)
206 		return 0;
207 
208 	if (tcp->socket > -1) {
209 		close(tcp->socket);
210 		tcp->socket = -1;
211 	}
212 
213 	tcp->initialized = 0;
214 
215 	return 0;
216 }
217 
tcp_send(tcp_sock_t * tcp,const char * buf,int len)218 int tcp_send(tcp_sock_t *tcp, const char *buf, int len)
219 {
220 	ASSERT(tcp);
221 
222 	if (!tcp->initialized)
223 		return RC_TCP_OBJECT_NOT_INITIALIZED;
224 again:
225 	if (send(tcp->socket, buf, len, 0) == -1) {
226 		int err = errno;
227 		if(err == EAGAIN || err == EWOULDBLOCK){
228 			goto again;
229 		}
230 		logit(LOG_WARNING, "Network error while sending query/update: %s", strerror(errno));
231 		return RC_TCP_SEND_ERROR;
232 	}
233 
234 	return 0;
235 }
236 
tcp_recv(tcp_sock_t * tcp,char * buf,int len,int * recv_len)237 int tcp_recv(tcp_sock_t *tcp, char *buf, int len, int *recv_len)
238 {
239 	int rc = 0;
240 	int remaining_bytes = len;
241 	int total_bytes = 0;
242 
243 	ASSERT(tcp);
244 	ASSERT(buf);
245 	ASSERT(recv_len);
246 
247 	if (!tcp->initialized)
248 		return RC_TCP_OBJECT_NOT_INITIALIZED;
249 
250 	while (remaining_bytes > 0) {
251 		int bytes;
252 		int err = 0;
253 		int chunk_size = remaining_bytes > TCP_DEFAULT_READ_CHUNK_SIZE
254 			? TCP_DEFAULT_READ_CHUNK_SIZE
255 			: remaining_bytes;
256 
257 		bytes = recv(tcp->socket, buf + total_bytes, chunk_size, 0);
258 		err = errno;
259 		if(bytes == -1 && (err == EAGAIN || err == EWOULDBLOCK)){
260 			continue;
261 		}
262 		if (bytes < 0) {
263 			logit(LOG_WARNING, "Network error while waiting for reply: %s", strerror(errno));
264 			rc = RC_TCP_RECV_ERROR;
265 			break;
266 		}
267 
268 		if (bytes == 0) {
269 			if (total_bytes == 0)
270 				rc = RC_TCP_RECV_ERROR;
271 			break;
272 		}
273 
274 		total_bytes    += bytes;
275 		remaining_bytes = len - total_bytes;
276 	}
277 
278 	*recv_len = total_bytes;
279 
280 	return rc;
281 }
282 
tcp_set_port(tcp_sock_t * tcp,int port)283 int tcp_set_port(tcp_sock_t *tcp, int port)
284 {
285 	ASSERT(tcp);
286 
287 	if (port < 0 || port > TCP_SOCKET_MAX_PORT)
288 		return RC_TCP_BAD_PARAMETER;
289 
290 	tcp->port = port;
291 
292 	return 0;
293 }
294 
tcp_get_port(tcp_sock_t * tcp,int * port)295 int tcp_get_port(tcp_sock_t *tcp, int *port)
296 {
297 	ASSERT(tcp);
298 	ASSERT(port);
299 	*port = tcp->port;
300 
301 	return 0;
302 }
303 
tcp_set_remote_name(tcp_sock_t * tcp,const char * name)304 int tcp_set_remote_name(tcp_sock_t *tcp, const char *name)
305 {
306 	ASSERT(tcp);
307 	tcp->remote_host = name;
308 
309 	return 0;
310 }
311 
tcp_get_remote_name(tcp_sock_t * tcp,const char ** name)312 int tcp_get_remote_name(tcp_sock_t *tcp, const char **name)
313 {
314 	ASSERT(tcp);
315 	ASSERT(name);
316 	*name = tcp->remote_host;
317 
318 	return 0;
319 }
320 
tcp_set_remote_timeout(tcp_sock_t * tcp,int timeout)321 int tcp_set_remote_timeout(tcp_sock_t *tcp, int timeout)
322 {
323 	ASSERT(tcp);
324 	tcp->timeout = timeout;
325 
326 	return 0;
327 }
328 
tcp_get_remote_timeout(tcp_sock_t * tcp,int * timeout)329 int tcp_get_remote_timeout(tcp_sock_t *tcp, int *timeout)
330 {
331 	ASSERT(tcp);
332 	ASSERT(timeout);
333 	*timeout = tcp->timeout;
334 
335 	return 0;
336 }
337 
338 /**
339  * Local Variables:
340  *  indent-tabs-mode: t
341  *  c-file-style: "linux"
342  * End:
343  */
344