blob: 27103a06b6c5fd12fac2a18158d930c9f6297ff4 [file] [log] [blame]
Roger Meier6cf0ffc2014-04-05 00:45:42 +02001//
2// Licensed to the Apache Software Foundation (ASF) under one
3// or more contributor license agreements. See the NOTICE file
4// distributed with this work for additional information
5// regarding copyright ownership. The ASF licenses this file
6// to you under the Apache License, Version 2.0 (the
7// "License"); you may not use this file except in compliance
8// with the License. You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing,
13// software distributed under the License is distributed on an
14// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15// KIND, either express or implied. See the License for the
16// specific language governing permissions and limitations
17// under the License.
18//
19
20#include <sys/time.h>
21#include <sys/types.h>
22#include <arpa/inet.h>
23#include <netdb.h>
24#include <string.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <errno.h>
28
29#include <stdio.h> // TODO REMOVE
30
31#include "socket.h"
32
33////////////////////////////////////////////////////////////////////////////////
34// Private
35
36// Num seconds since Jan 1 1970 (UTC)
37#ifdef _WIN32
38// SOL
39#else
40 double __gettime() {
41 struct timeval v;
42 gettimeofday(&v, (struct timezone*) NULL);
43 return v.tv_sec + v.tv_usec/1.0e6;
44 }
45#endif
46
47#define WAIT_MODE_R 1
48#define WAIT_MODE_W 2
49#define WAIT_MODE_C (WAIT_MODE_R|WAIT_MODE_W)
50T_ERRCODE socket_wait(p_socket sock, int mode, int timeout) {
51 int ret = 0;
52 fd_set rfds, wfds;
53 struct timeval tv;
54 double end, t;
55 if (!timeout) {
56 return TIMEOUT;
57 }
58
59 end = __gettime() + timeout/1000;
60 do {
James E. King, III36200902016-10-05 14:47:18 -040061 FD_ZERO(&rfds);
62 FD_ZERO(&wfds);
63
Roger Meier6cf0ffc2014-04-05 00:45:42 +020064 // Specify what I/O operations we care about
65 if (mode & WAIT_MODE_R) {
Roger Meier6cf0ffc2014-04-05 00:45:42 +020066 FD_SET(*sock, &rfds);
67 }
68 if (mode & WAIT_MODE_W) {
Roger Meier6cf0ffc2014-04-05 00:45:42 +020069 FD_SET(*sock, &wfds);
70 }
71
72 // Check for timeout
73 t = end - __gettime();
74 if (t < 0.0) {
75 break;
76 }
77
78 // Wait
79 tv.tv_sec = (int)t;
80 tv.tv_usec = (int)((t - tv.tv_sec) * 1.0e6);
81 ret = select(*sock+1, &rfds, &wfds, NULL, &tv);
82 } while (ret == -1 && errno == EINTR);
83 if (ret == -1) {
84 return errno;
85 }
86
87 // Check for timeout
88 if (ret == 0) {
89 return TIMEOUT;
90 }
91
Roger Meier6cf0ffc2014-04-05 00:45:42 +020092 return SUCCESS;
93}
94
95////////////////////////////////////////////////////////////////////////////////
96// General
97
98T_ERRCODE socket_create(p_socket sock, int domain, int type, int protocol) {
99 *sock = socket(domain, type, protocol);
100 if (*sock > 0) {
101 return SUCCESS;
102 } else {
103 return errno;
104 }
105}
106
107T_ERRCODE socket_destroy(p_socket sock) {
108 // TODO Figure out if I should be free-ing this
109 if (*sock > 0) {
James E. King, III533405e2017-10-28 18:25:45 -0400110 (void)socket_setblocking(sock);
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200111 close(*sock);
112 *sock = -1;
113 }
114 return SUCCESS;
115}
116
117T_ERRCODE socket_bind(p_socket sock, p_sa addr, int addr_len) {
James E. King, III533405e2017-10-28 18:25:45 -0400118 int ret = socket_setblocking(sock);
119 if (ret != SUCCESS) {
120 return ret;
121 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200122 if (bind(*sock, addr, addr_len)) {
123 ret = errno;
124 }
James E. King, III533405e2017-10-28 18:25:45 -0400125 int ret2 = socket_setnonblocking(sock);
126 return ret == SUCCESS ? ret2 : ret;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200127}
128
129T_ERRCODE socket_get_info(p_socket sock, short *port, char *buf, size_t len) {
James E. King, IIIea5ea8b2016-11-12 15:12:33 -0500130 struct sockaddr_storage sa;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200131 memset(&sa, 0, sizeof(sa));
James E. King, III36200902016-10-05 14:47:18 -0400132 socklen_t addrlen = sizeof(sa);
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200133 int rc = getsockname(*sock, (struct sockaddr*)&sa, &addrlen);
134 if (!rc) {
James E. King, IIIea5ea8b2016-11-12 15:12:33 -0500135 if (sa.ss_family == AF_INET6) {
136 struct sockaddr_in6* sin = (struct sockaddr_in6*)(&sa);
137 if (!inet_ntop(AF_INET6, &sin->sin6_addr, buf, len)) {
138 return errno;
139 }
140 *port = ntohs(sin->sin6_port);
141 } else {
142 struct sockaddr_in* sin = (struct sockaddr_in*)(&sa);
143 if (!inet_ntop(AF_INET, &sin->sin_addr, buf, len)) {
144 return errno;
145 }
146 *port = ntohs(sin->sin_port);
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200147 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200148 return SUCCESS;
149 }
James E. King, IIIea5ea8b2016-11-12 15:12:33 -0500150 return errno;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200151}
152
153////////////////////////////////////////////////////////////////////////////////
154// Server
155
156T_ERRCODE socket_accept(p_socket sock, p_socket client,
157 p_sa addr, socklen_t *addrlen, int timeout) {
158 int err;
159 if (*sock < 0) {
160 return CLOSED;
161 }
162 do {
163 *client = accept(*sock, addr, addrlen);
164 if (*client > 0) {
165 return SUCCESS;
166 }
James E. King, III533405e2017-10-28 18:25:45 -0400167 } while ((err = errno) == EINTR);
168
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200169 if (err == EAGAIN || err == ECONNABORTED) {
170 return socket_wait(sock, WAIT_MODE_R, timeout);
171 }
James E. King, III533405e2017-10-28 18:25:45 -0400172
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200173 return err;
174}
175
176T_ERRCODE socket_listen(p_socket sock, int backlog) {
James E. King, III533405e2017-10-28 18:25:45 -0400177 int ret = socket_setblocking(sock);
178 if (ret != SUCCESS) {
179 return ret;
180 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200181 if (listen(*sock, backlog)) {
182 ret = errno;
183 }
James E. King, III533405e2017-10-28 18:25:45 -0400184 int ret2 = socket_setnonblocking(sock);
185 return ret == SUCCESS ? ret2 : ret;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200186}
187
188////////////////////////////////////////////////////////////////////////////////
189// Client
190
191T_ERRCODE socket_connect(p_socket sock, p_sa addr, int addr_len, int timeout) {
192 int err;
193 if (*sock < 0) {
194 return CLOSED;
195 }
196
197 do {
198 if (connect(*sock, addr, addr_len) == 0) {
199 return SUCCESS;
200 }
201 } while ((err = errno) == EINTR);
202 if (err != EINPROGRESS && err != EAGAIN) {
203 return err;
204 }
205 return socket_wait(sock, WAIT_MODE_C, timeout);
206}
207
Thomaseb684d32024-07-28 15:32:23 +0200208#define SEND_RETRY_COUNT 5
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200209T_ERRCODE socket_send(
210 p_socket sock, const char *data, size_t len, int timeout) {
211 int err, put = 0;
212 if (*sock < 0) {
213 return CLOSED;
214 }
Thomaseb684d32024-07-28 15:32:23 +0200215 for(int i = 0; i < SEND_RETRY_COUNT; i++) {
216 do {
217 size_t l = len - put;
218 put = send(*sock, data + put, l, 0);
219 if (put > 0) {
220 if(put == l) {
221 return SUCCESS;
222 }
223 // Not all data was delivered, we need to try again.
224 err = EAGAIN;
225 break;
226 }
227 } while ((err = errno) == EINTR);
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200228
Thomaseb684d32024-07-28 15:32:23 +0200229 if (err == EAGAIN) {
230 err = socket_wait(sock, WAIT_MODE_W, timeout);
231 // Check if the socket is available again and try to resend.
232 if(err == SUCCESS) {
233 continue;
234 }
235 }
236 break;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200237 }
James E. King, III533405e2017-10-28 18:25:45 -0400238
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200239 return err;
240}
241
242T_ERRCODE socket_recv(
243 p_socket sock, char *data, size_t len, int timeout, int *received) {
244 int err, got = 0;
245 if (*sock < 0) {
246 return CLOSED;
247 }
James E. King, III533405e2017-10-28 18:25:45 -0400248 *received = 0;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200249
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200250 do {
251 got = recv(*sock, data, len, 0);
252 if (got > 0) {
253 *received = got;
254 return SUCCESS;
255 }
256 err = errno;
257
258 // Connection has been closed by peer
259 if (got == 0) {
260 return CLOSED;
261 }
James E. King, III533405e2017-10-28 18:25:45 -0400262 } while (err == EINTR);
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200263
264 if (err == EAGAIN) {
265 return socket_wait(sock, WAIT_MODE_R, timeout);
266 }
James E. King, III533405e2017-10-28 18:25:45 -0400267
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200268 return err;
269}
270
271////////////////////////////////////////////////////////////////////////////////
272// Util
273
James E. King, III533405e2017-10-28 18:25:45 -0400274T_ERRCODE socket_setnonblocking(p_socket sock) {
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200275 int flags = fcntl(*sock, F_GETFL, 0);
276 flags |= O_NONBLOCK;
James E. King, III533405e2017-10-28 18:25:45 -0400277 return fcntl(*sock, F_SETFL, flags) != -1 ? SUCCESS : errno;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200278}
279
James E. King, III533405e2017-10-28 18:25:45 -0400280T_ERRCODE socket_setblocking(p_socket sock) {
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200281 int flags = fcntl(*sock, F_GETFL, 0);
282 flags &= (~(O_NONBLOCK));
James E. King, III533405e2017-10-28 18:25:45 -0400283 return fcntl(*sock, F_SETFL, flags) != -1 ? SUCCESS : errno;
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200284}
285
286////////////////////////////////////////////////////////////////////////////////
287// TCP
288
289#define ERRORSTR_RETURN(err) \
290 if (err == SUCCESS) { \
291 return NULL; \
292 } else if (err == TIMEOUT) { \
293 return TIMEOUT_MSG; \
294 } else if (err == CLOSED) { \
295 return CLOSED_MSG; \
296 } \
297 return strerror(err)
298
299const char * tcp_create(p_socket sock) {
Jeffrey Hane872b352020-09-24 10:41:12 -0700300 // TODO support IPv6
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200301 int err = socket_create(sock, AF_INET, SOCK_STREAM, 0);
302 ERRORSTR_RETURN(err);
303}
304
305const char * tcp_destroy(p_socket sock) {
306 int err = socket_destroy(sock);
307 ERRORSTR_RETURN(err);
308}
309
310const char * tcp_bind(p_socket sock, const char *host, unsigned short port) {
Jeffrey Hane872b352020-09-24 10:41:12 -0700311 // TODO support IPv6
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200312 int err;
313 struct hostent *h;
314 struct sockaddr_in local;
315 memset(&local, 0, sizeof(local));
316 local.sin_family = AF_INET;
317 local.sin_addr.s_addr = htonl(INADDR_ANY);
318 local.sin_port = htons(port);
319 if (strcmp(host, "*") && !inet_aton(host, &local.sin_addr)) {
320 h = gethostbyname(host);
321 if (!h) {
322 return hstrerror(h_errno);
323 }
324 memcpy(&local.sin_addr,
325 (struct in_addr *)h->h_addr_list[0],
326 sizeof(struct in_addr));
327 }
328 err = socket_bind(sock, (p_sa) &local, sizeof(local));
329 ERRORSTR_RETURN(err);
330}
331
332const char * tcp_listen(p_socket sock, int backlog) {
333 int err = socket_listen(sock, backlog);
334 ERRORSTR_RETURN(err);
335}
336
337const char * tcp_accept(p_socket sock, p_socket client, int timeout) {
338 int err = socket_accept(sock, client, NULL, NULL, timeout);
339 ERRORSTR_RETURN(err);
340}
341
342const char * tcp_connect(p_socket sock,
343 const char *host,
344 unsigned short port,
345 int timeout) {
Jeffrey Hane872b352020-09-24 10:41:12 -0700346 // TODO support IPv6
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200347 int err;
348 struct hostent *h;
349 struct sockaddr_in remote;
350 memset(&remote, 0, sizeof(remote));
351 remote.sin_family = AF_INET;
352 remote.sin_port = htons(port);
353 if (strcmp(host, "*") && !inet_aton(host, &remote.sin_addr)) {
354 h = gethostbyname(host);
355 if (!h) {
356 return hstrerror(h_errno);
357 }
358 memcpy(&remote.sin_addr,
359 (struct in_addr *)h->h_addr_list[0],
360 sizeof(struct in_addr));
361 }
362 err = socket_connect(sock, (p_sa) &remote, sizeof(remote), timeout);
363 ERRORSTR_RETURN(err);
364}
365
Jeffrey Hane872b352020-09-24 10:41:12 -0700366const char * tcp_create_and_connect(p_socket sock,
367 const char *host,
368 unsigned short port,
369 int timeout) {
370 int err;
371 struct sockaddr_in sa4;
372 struct sockaddr_in6 sa6;
373
374 memset(&sa4, 0, sizeof(sa4));
375 sa4.sin_family = AF_INET;
376 sa4.sin_port = htons(port);
377 memset(&sa6, 0, sizeof(sa6));
378 sa6.sin6_family = AF_INET6;
379 sa6.sin6_port = htons(port);
380
381 if (inet_pton(AF_INET, host, &sa4.sin_addr)) {
382 socket_create(sock, AF_INET, SOCK_STREAM, 0);
383 err = socket_connect(sock, (p_sa) &sa4, sizeof(sa4), timeout);
384 ERRORSTR_RETURN(err);
385 } else if (inet_pton(AF_INET6, host, &sa6.sin6_addr)) {
386 socket_create(sock, AF_INET6, SOCK_STREAM, 0);
387 err = socket_connect(sock, (p_sa) &sa6, sizeof(sa6), timeout);
388 ERRORSTR_RETURN(err);
389 } else {
390 struct addrinfo hints, *servinfo, *rp;
391 char portStr[6];
392 int rv;
393
394 memset(&hints, 0, sizeof(hints));
395 hints.ai_family = AF_UNSPEC;
396 hints.ai_socktype = SOCK_STREAM;
397
398 sprintf(portStr, "%u", port);
399
400 if ((rv = getaddrinfo(host, portStr, &hints, &servinfo)) != 0) {
401 return gai_strerror(rv);
402 }
403
404 err = TIMEOUT;
405 for (rp = servinfo; rp != NULL; rp = rp->ai_next) {
406 err = socket_create(sock, rp->ai_family, rp->ai_socktype, rp->ai_protocol);
407 if (err != SUCCESS) {
408 continue;
409 }
410 err = socket_connect(sock, (p_sa) rp->ai_addr, rp->ai_addrlen, timeout);
411 if (err == SUCCESS) {
412 break;
413 }
414 close(*sock);
415 }
416 freeaddrinfo(servinfo);
417 if (rp == NULL) {
418 *sock = -1;
419 return "Failed to connect";
420 } else {
421 ERRORSTR_RETURN(err);
422 }
423 }
424}
425
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200426#define WRITE_STEP 8192
427const char * tcp_send(
428 p_socket sock, const char * data, size_t w_len, int timeout) {
429 int err;
430 size_t put = 0, step;
431 if (!w_len) {
432 return NULL;
433 }
434
435 do {
436 step = (WRITE_STEP < w_len - put ? WRITE_STEP : w_len - put);
437 err = socket_send(sock, data + put, step, timeout);
438 put += step;
439 } while (err == SUCCESS && put < w_len);
440 ERRORSTR_RETURN(err);
441}
442
443const char * tcp_raw_receive(
444 p_socket sock, char * data, size_t r_len, int timeout, int *received) {
445 int err = socket_recv(sock, data, r_len, timeout, received);
446 ERRORSTR_RETURN(err);
447}