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