2894 lines
61 KiB
C
2894 lines
61 KiB
C
/*
|
|
* Copyright (c) 2020 YuQing <384681@qq.com>
|
|
*
|
|
* This program is free software: you can use, redistribute, and/or modify
|
|
* it under the terms of the Lesser GNU General Public License, version 3
|
|
* or later ("LGPL"), as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* You should have received a copy of the Lesser GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//socketopt.c
|
|
#include "common_define.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <netdb.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
|
|
#define SUB_NET_TYPE_INNER_10_STR2 "inner_10"
|
|
#define SUB_NET_TYPE_INNER_172_STR2 "inner_172"
|
|
#define SUB_NET_TYPE_INNER_192_STR2 "inner_192"
|
|
|
|
#define SUB_NET_TYPE_INNER_10_STR3 "inner10"
|
|
#define SUB_NET_TYPE_INNER_172_STR3 "inner172"
|
|
#define SUB_NET_TYPE_INNER_192_STR3 "inner192"
|
|
|
|
#if defined(OS_LINUX) || defined(OS_FREEBSD)
|
|
#include <ifaddrs.h>
|
|
#endif
|
|
|
|
#include <poll.h>
|
|
#include <sys/select.h>
|
|
#include "shared_func.h"
|
|
|
|
#ifdef OS_SUNOS
|
|
#include <sys/sockio.h>
|
|
#endif
|
|
|
|
#ifdef USE_SENDFILE
|
|
|
|
#ifdef OS_LINUX
|
|
#include <sys/sendfile.h>
|
|
#else
|
|
#ifdef OS_FREEBSD
|
|
#include <sys/sysctl.h>
|
|
#include <sys/uio.h>
|
|
#include <net/if_dl.h>
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#include "logger.h"
|
|
#include "hash.h"
|
|
#include "sockopt.h"
|
|
|
|
#ifdef WIN32
|
|
#define USE_SELECT
|
|
#else
|
|
#define USE_POLL
|
|
#endif
|
|
|
|
#ifdef OS_LINUX
|
|
#ifndef TCP_KEEPIDLE
|
|
#define TCP_KEEPIDLE 4 /* Start keeplives after this period */
|
|
#endif
|
|
|
|
#ifndef TCP_KEEPINTVL
|
|
#define TCP_KEEPINTVL 5 /* Interval between keepalives */
|
|
#endif
|
|
|
|
#ifndef TCP_KEEPCNT
|
|
#define TCP_KEEPCNT 6 /* Number of keepalives before death */
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef OS_LINUX
|
|
bool g_tcp_quick_ack = false;
|
|
#endif
|
|
|
|
static bool try_again_when_interrupt = true;
|
|
|
|
void tcp_set_try_again_when_interrupt(const bool value)
|
|
{
|
|
try_again_when_interrupt = value;
|
|
}
|
|
|
|
void tcp_set_quick_ack(const bool value)
|
|
{
|
|
#ifdef OS_LINUX
|
|
g_tcp_quick_ack = value;
|
|
#endif
|
|
}
|
|
|
|
int tcpgets(int sock, char* s, const int size, const int timeout)
|
|
{
|
|
int result;
|
|
char t;
|
|
int i=1;
|
|
|
|
if (s == NULL || size <= 0)
|
|
{
|
|
return EINVAL;
|
|
}
|
|
|
|
while (i < size)
|
|
{
|
|
result = tcprecvdata(sock, &t, 1, timeout);
|
|
if (result != 0)
|
|
{
|
|
*s = 0;
|
|
return result;
|
|
}
|
|
|
|
if (t == '\r')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (t == '\n')
|
|
{
|
|
*s = t;
|
|
s++;
|
|
*s = 0;
|
|
return 0;
|
|
}
|
|
|
|
*s = t;
|
|
s++,i++;
|
|
}
|
|
|
|
*s = 0;
|
|
return 0;
|
|
}
|
|
|
|
int tcprecvdata_ex(int sock, void *data, const int size, \
|
|
const int timeout, int *count)
|
|
{
|
|
int left_bytes;
|
|
int read_bytes;
|
|
int res;
|
|
int ret_code;
|
|
unsigned char* p;
|
|
#ifdef USE_SELECT
|
|
fd_set read_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&read_set);
|
|
FD_SET(sock, &read_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLIN;
|
|
#endif
|
|
|
|
ret_code = 0;
|
|
p = (unsigned char*)data;
|
|
left_bytes = size;
|
|
while (left_bytes > 0)
|
|
{
|
|
|
|
#ifdef USE_SELECT
|
|
if (timeout <= 0)
|
|
{
|
|
res = select(sock+1, &read_set, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = 0;
|
|
t.tv_sec = timeout;
|
|
res = select(sock+1, &read_set, NULL, NULL, &t);
|
|
}
|
|
#else
|
|
res = poll(&pollfds, 1, 1000 * timeout);
|
|
if (res > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (res < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
else if (res == 0)
|
|
{
|
|
ret_code = ETIMEDOUT;
|
|
break;
|
|
}
|
|
|
|
read_bytes = recv(sock, p, left_bytes, 0);
|
|
if (read_bytes < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
if (read_bytes == 0)
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
|
|
TCP_SET_QUICK_ACK(sock);
|
|
left_bytes -= read_bytes;
|
|
p += read_bytes;
|
|
}
|
|
|
|
if (count != NULL)
|
|
{
|
|
*count = size - left_bytes;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
int tcpsenddata(int sock, void *data, const int size, const int timeout)
|
|
{
|
|
int left_bytes;
|
|
int write_bytes;
|
|
int result;
|
|
unsigned char* p;
|
|
#ifdef USE_SELECT
|
|
fd_set write_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&write_set);
|
|
FD_SET(sock, &write_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLOUT;
|
|
#endif
|
|
|
|
p = (unsigned char*)data;
|
|
left_bytes = size;
|
|
while (left_bytes > 0)
|
|
{
|
|
#ifdef USE_SELECT
|
|
if (timeout <= 0)
|
|
{
|
|
result = select(sock+1, NULL, &write_set, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = 0;
|
|
t.tv_sec = timeout;
|
|
result = select(sock+1, NULL, &write_set, NULL, &t);
|
|
}
|
|
#else
|
|
result = poll(&pollfds, 1, 1000 * timeout);
|
|
if (result > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
return ENOTCONN;
|
|
}
|
|
#endif
|
|
|
|
if (result < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
return ETIMEDOUT;
|
|
}
|
|
|
|
write_bytes = send(sock, p, left_bytes, 0);
|
|
if (write_bytes < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
|
|
left_bytes -= write_bytes;
|
|
p += write_bytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcprecvdata_nb_ex(int sock, void *data, const int size, \
|
|
const int timeout, int *count)
|
|
{
|
|
return tcprecvdata_nb_ms(sock, data, size, timeout * 1000, count);
|
|
}
|
|
|
|
int tcprecvdata_nb_ms(int sock, void *data, const int size, \
|
|
const int timeout_ms, int *count)
|
|
{
|
|
int left_bytes;
|
|
int read_bytes;
|
|
int res;
|
|
int ret_code;
|
|
unsigned char* p;
|
|
#ifdef USE_SELECT
|
|
fd_set read_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&read_set);
|
|
FD_SET(sock, &read_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLIN;
|
|
#endif
|
|
|
|
ret_code = 0;
|
|
p = (unsigned char*)data;
|
|
left_bytes = size;
|
|
while (left_bytes > 0)
|
|
{
|
|
read_bytes = recv(sock, p, left_bytes, 0);
|
|
if (read_bytes > 0)
|
|
{
|
|
TCP_SET_QUICK_ACK(sock);
|
|
left_bytes -= read_bytes;
|
|
if (left_bytes == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
p += read_bytes;
|
|
continue;
|
|
}
|
|
|
|
if (read_bytes < 0)
|
|
{
|
|
if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
|
|
(errno == EINTR && try_again_when_interrupt)))
|
|
{
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_SELECT
|
|
if (timeout_ms <= 0)
|
|
{
|
|
res = select(sock+1, &read_set, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = (timeout_ms % 1000) * 1000;
|
|
t.tv_sec = timeout_ms / 1000;
|
|
res = select(sock+1, &read_set, NULL, NULL, &t);
|
|
}
|
|
#else
|
|
res = poll(&pollfds, 1, timeout_ms);
|
|
if (res > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (res < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
else if (res == 0)
|
|
{
|
|
ret_code = ETIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count != NULL)
|
|
{
|
|
*count = size - left_bytes;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
int tcpreadv_nb_ms(int sock, const int size, const struct iovec *iov,
|
|
const int iovcnt, const int timeout_ms, int *total_bytes)
|
|
{
|
|
int left_bytes;
|
|
int read_bytes;
|
|
int bytes;
|
|
int res;
|
|
int ret_code;
|
|
int remain_count;
|
|
int current_count;
|
|
int current_done;
|
|
int remain_len;
|
|
struct iovec *iob;
|
|
struct iovec iov_array[FC_IOV_BATCH_SIZE];
|
|
struct iovec *iovp;
|
|
|
|
#ifdef USE_SELECT
|
|
fd_set read_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&read_set);
|
|
FD_SET(sock, &read_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLIN;
|
|
#endif
|
|
|
|
ret_code = 0;
|
|
iovp = (struct iovec *)iov;
|
|
remain_count = current_count = iovcnt;
|
|
left_bytes = size;
|
|
while (left_bytes > 0)
|
|
{
|
|
read_bytes = readv(sock, iovp, current_count);
|
|
if (read_bytes > 0)
|
|
{
|
|
TCP_SET_QUICK_ACK(sock);
|
|
left_bytes -= read_bytes;
|
|
if (left_bytes <= 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
iob = iovp;
|
|
bytes = iob->iov_len;
|
|
while (bytes < read_bytes)
|
|
{
|
|
++iob;
|
|
bytes += iob->iov_len;
|
|
}
|
|
if (bytes == read_bytes)
|
|
{
|
|
++iob;
|
|
if (iob < iovp + current_count) {
|
|
bytes += iob->iov_len;
|
|
}
|
|
}
|
|
|
|
current_done = iob - iovp;
|
|
remain_count -= current_done;
|
|
if (remain_count == 0) {
|
|
ret_code = EOVERFLOW;
|
|
break;
|
|
}
|
|
|
|
if (current_done == current_count) //full done
|
|
{
|
|
current_count = ((remain_count <= FC_IOV_BATCH_SIZE) ?
|
|
remain_count : FC_IOV_BATCH_SIZE);
|
|
memcpy(iov_array, iov + (iovcnt - remain_count),
|
|
current_count * sizeof(struct iovec));
|
|
iovp = iov_array;
|
|
}
|
|
else //partial done
|
|
{
|
|
if (iovp == (struct iovec *)iov)
|
|
{
|
|
current_count = ((remain_count <= FC_IOV_BATCH_SIZE) ?
|
|
remain_count : FC_IOV_BATCH_SIZE);
|
|
memcpy(iov_array, iob, current_count *
|
|
sizeof(struct iovec));
|
|
iovp = iov_array;
|
|
}
|
|
else
|
|
{
|
|
current_count -= current_done;
|
|
iovp = iob;
|
|
}
|
|
|
|
/* adjust the first element */
|
|
remain_len = bytes - read_bytes;
|
|
if (remain_len < iovp->iov_len)
|
|
{
|
|
iovp->iov_base = (char *)iovp->iov_base +
|
|
(iovp->iov_len - remain_len);
|
|
iovp->iov_len = remain_len;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
else if (read_bytes == 0)
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
|
|
if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
|
|
(errno == EINTR && try_again_when_interrupt)))
|
|
{
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_SELECT
|
|
if (timeout_ms <= 0)
|
|
{
|
|
res = select(sock+1, &read_set, NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = (timeout_ms % 1000) * 1000;
|
|
t.tv_sec = timeout_ms / 1000;
|
|
res = select(sock+1, &read_set, NULL, NULL, &t);
|
|
}
|
|
#else
|
|
res = poll(&pollfds, 1, timeout_ms);
|
|
if (res > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
ret_code = ENOTCONN;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (res < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
ret_code = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
else if (res == 0)
|
|
{
|
|
ret_code = ETIMEDOUT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (total_bytes != NULL)
|
|
{
|
|
*total_bytes = size - left_bytes;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
int tcpsenddata_nb(int sock, void *data, const int size, const int timeout)
|
|
{
|
|
int left_bytes;
|
|
int write_bytes;
|
|
int result;
|
|
unsigned char *p;
|
|
#ifdef USE_SELECT
|
|
fd_set write_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&write_set);
|
|
FD_SET(sock, &write_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLOUT;
|
|
#endif
|
|
|
|
p = (unsigned char *)data;
|
|
left_bytes = size;
|
|
while (left_bytes > 0)
|
|
{
|
|
write_bytes = send(sock, p, left_bytes, 0);
|
|
if (write_bytes > 0)
|
|
{
|
|
left_bytes -= write_bytes;
|
|
if (left_bytes == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
p += write_bytes;
|
|
continue;
|
|
}
|
|
else if (write_bytes == 0)
|
|
{
|
|
return ENOTCONN;
|
|
}
|
|
|
|
if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
|
|
(errno == EINTR && try_again_when_interrupt)))
|
|
{
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
|
|
#ifdef USE_SELECT
|
|
if (timeout <= 0)
|
|
{
|
|
result = select(sock+1, NULL, &write_set, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = 0;
|
|
t.tv_sec = timeout;
|
|
result = select(sock+1, NULL, &write_set, NULL, &t);
|
|
}
|
|
#else
|
|
result = poll(&pollfds, 1, 1000 * timeout);
|
|
if (result > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
return ENOTCONN;
|
|
}
|
|
#endif
|
|
|
|
if (result < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
return ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpwritev_nb(int sock, const struct iovec *iov,
|
|
const int iovcnt, const int timeout)
|
|
{
|
|
int write_bytes;
|
|
int bytes;
|
|
int result;
|
|
int remain_count;
|
|
int current_count;
|
|
int current_done;
|
|
int remain_len;
|
|
struct iovec *iob;
|
|
struct iovec iov_array[FC_IOV_BATCH_SIZE];
|
|
struct iovec *iovp;
|
|
|
|
#ifdef USE_SELECT
|
|
fd_set write_set;
|
|
struct timeval t;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&write_set);
|
|
FD_SET(sock, &write_set);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLOUT;
|
|
#endif
|
|
|
|
iovp = (struct iovec *)iov;
|
|
remain_count = current_count = iovcnt;
|
|
while (remain_count > 0)
|
|
{
|
|
write_bytes = writev(sock, iovp, current_count);
|
|
if (write_bytes > 0)
|
|
{
|
|
iob = iovp;
|
|
bytes = iob->iov_len;
|
|
while (bytes < write_bytes)
|
|
{
|
|
++iob;
|
|
bytes += iob->iov_len;
|
|
}
|
|
if (bytes == write_bytes)
|
|
{
|
|
++iob;
|
|
if (iob < iovp + current_count) {
|
|
bytes += iob->iov_len;
|
|
}
|
|
}
|
|
|
|
current_done = iob - iovp;
|
|
remain_count -= current_done;
|
|
if (remain_count == 0) {
|
|
break;
|
|
}
|
|
|
|
if (current_done == current_count) //full done
|
|
{
|
|
current_count = ((remain_count <= FC_IOV_BATCH_SIZE) ?
|
|
remain_count : FC_IOV_BATCH_SIZE);
|
|
memcpy(iov_array, iov + (iovcnt - remain_count),
|
|
current_count * sizeof(struct iovec));
|
|
iovp = iov_array;
|
|
}
|
|
else //partial done
|
|
{
|
|
if (iovp == (struct iovec *)iov)
|
|
{
|
|
current_count = ((remain_count <= FC_IOV_BATCH_SIZE) ?
|
|
remain_count : FC_IOV_BATCH_SIZE);
|
|
memcpy(iov_array, iob, current_count *
|
|
sizeof(struct iovec));
|
|
iovp = iov_array;
|
|
}
|
|
else
|
|
{
|
|
current_count -= current_done;
|
|
iovp = iob;
|
|
}
|
|
|
|
/* adjust the first element */
|
|
remain_len = bytes - write_bytes;
|
|
if (remain_len < iovp->iov_len)
|
|
{
|
|
iovp->iov_base = (char *)iovp->iov_base +
|
|
(iovp->iov_len - remain_len);
|
|
iovp->iov_len = remain_len;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
else if (write_bytes == 0)
|
|
{
|
|
return ENOTCONN;
|
|
}
|
|
|
|
if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
|
|
(errno == EINTR && try_again_when_interrupt)))
|
|
{
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
|
|
#ifdef USE_SELECT
|
|
if (timeout <= 0)
|
|
{
|
|
result = select(sock+1, NULL, &write_set, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
t.tv_usec = 0;
|
|
t.tv_sec = timeout;
|
|
result = select(sock+1, NULL, &write_set, NULL, &t);
|
|
}
|
|
#else
|
|
result = poll(&pollfds, 1, 1000 * timeout);
|
|
if (result > 0 && (pollfds.revents & (POLLHUP | POLLERR)))
|
|
{
|
|
return ENOTCONN;
|
|
}
|
|
#endif
|
|
|
|
if (result < 0)
|
|
{
|
|
if (errno == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
else if (result == 0)
|
|
{
|
|
return ETIMEDOUT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int setsockaddrbyip(const char *ip, const uint16_t port,
|
|
sockaddr_convert_t *convert)
|
|
{
|
|
int af;
|
|
int result;
|
|
void *dest;
|
|
|
|
if (is_ipv6_addr(ip))
|
|
{
|
|
convert->len = sizeof(convert->sa.addr6);
|
|
if (strchr(ip, '%') != NULL)
|
|
{
|
|
struct addrinfo hints, *res;
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_INET6;
|
|
if ((result=getaddrinfo(ip, NULL, &hints, &res)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
convert->sa.addr6 = *((struct sockaddr_in6 *)res->ai_addr);
|
|
convert->sa.addr6.sin6_port = htons(port);
|
|
freeaddrinfo(res);
|
|
return 0;
|
|
}
|
|
|
|
af = AF_INET6;
|
|
dest = &convert->sa.addr6.sin6_addr;
|
|
convert->sa.addr6.sin6_family = AF_INET6;
|
|
convert->sa.addr6.sin6_port = htons(port);
|
|
convert->sa.addr6.sin6_flowinfo = 0;
|
|
convert->sa.addr6.sin6_scope_id = 0;
|
|
}
|
|
else //ipv4
|
|
{
|
|
af = AF_INET;
|
|
convert->len = sizeof(convert->sa.addr4);
|
|
dest = &convert->sa.addr4.sin_addr;
|
|
convert->sa.addr4.sin_family = AF_INET;
|
|
convert->sa.addr4.sin_port = htons(port);
|
|
}
|
|
|
|
if (inet_pton(af, ip, dest) == 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"invalid %s ip address: %s", __LINE__,
|
|
(af == AF_INET ? "IPv4" : "IPv6"), ip);
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int connectserverbyip(int sock, const char *server_ip, const uint16_t server_port)
|
|
{
|
|
int result;
|
|
sockaddr_convert_t convert;
|
|
|
|
if ((result=setsockaddrbyip(server_ip, server_port, &convert)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (connect(sock, &convert.sa.addr, convert.len) < 0)
|
|
{
|
|
return errno != 0 ? errno : EINTR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int connectserverbyip_nb_ex(int sock, const char *server_ip,
|
|
const uint16_t server_port, const int timeout,
|
|
const bool auto_detect)
|
|
{
|
|
int result;
|
|
int flags;
|
|
bool needRestore;
|
|
socklen_t len;
|
|
|
|
#ifdef USE_SELECT
|
|
fd_set rset;
|
|
fd_set wset;
|
|
struct timeval tval;
|
|
#else
|
|
struct pollfd pollfds;
|
|
#endif
|
|
|
|
sockaddr_convert_t convert;
|
|
|
|
if ((result=setsockaddrbyip(server_ip, server_port, &convert)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (auto_detect)
|
|
{
|
|
flags = fcntl(sock, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if ((flags & O_NONBLOCK) == 0)
|
|
{
|
|
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
needRestore = true;
|
|
}
|
|
else
|
|
{
|
|
needRestore = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
needRestore = false;
|
|
flags = 0;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (connect(sock, &convert.sa.addr, convert.len) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EINPROGRESS;
|
|
if (result != EINPROGRESS)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_SELECT
|
|
FD_ZERO(&rset);
|
|
FD_ZERO(&wset);
|
|
FD_SET(sock, &rset);
|
|
FD_SET(sock, &wset);
|
|
tval.tv_sec = timeout;
|
|
tval.tv_usec = 0;
|
|
|
|
result = select(sock+1, &rset, &wset, NULL, \
|
|
timeout > 0 ? &tval : NULL);
|
|
#else
|
|
pollfds.fd = sock;
|
|
pollfds.events = POLLIN | POLLOUT;
|
|
result = poll(&pollfds, 1, 1000 * timeout);
|
|
#endif
|
|
|
|
if (result == 0)
|
|
{
|
|
result = ETIMEDOUT;
|
|
break;
|
|
}
|
|
else if (result < 0)
|
|
{
|
|
result = errno != 0 ? errno : EINTR;
|
|
break;
|
|
}
|
|
|
|
len = sizeof(result);
|
|
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &result, &len) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EACCES;
|
|
break;
|
|
}
|
|
} while (0);
|
|
|
|
if (needRestore)
|
|
{
|
|
fcntl(sock, F_SETFL, flags);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int asyncconnectserverbyip(int sock, const char *server_ip,
|
|
const uint16_t server_port)
|
|
{
|
|
int result;
|
|
sockaddr_convert_t convert;
|
|
|
|
if ((result=setsockaddrbyip(server_ip, server_port, &convert)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
if (connect(sock, &convert.sa.addr, convert.len) == 0) {
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return errno != 0 ? errno : EINPROGRESS;
|
|
}
|
|
}
|
|
|
|
int socketCreateEx2(int af, const char *server_ip,
|
|
const int flags, const char *bind_ipaddr, int *err_no)
|
|
{
|
|
int sock;
|
|
|
|
if (!(af == AF_INET || af == AF_INET6))
|
|
{
|
|
af = is_ipv6_addr(server_ip) ? AF_INET6 : AF_INET;
|
|
}
|
|
|
|
sock = socket(af, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : EMFILE;
|
|
logError("file: "__FILE__", line: %d, "
|
|
"socket create failed, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return -1;
|
|
}
|
|
|
|
FC_SET_CLOEXEC(sock);
|
|
SET_SOCKOPT_NOSIGPIPE(sock);
|
|
if (flags != 0)
|
|
{
|
|
*err_no = fd_add_flags(sock, flags);
|
|
if (*err_no != 0)
|
|
{
|
|
close(sock);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if (bind_ipaddr != NULL && *bind_ipaddr != '\0')
|
|
{
|
|
*err_no = socketBind2(af, sock, bind_ipaddr, 0);
|
|
if (*err_no != 0)
|
|
{
|
|
close(sock);
|
|
return -3;
|
|
}
|
|
}
|
|
|
|
*err_no = 0;
|
|
return sock;
|
|
}
|
|
|
|
int socketClientEx2(int af, const char *server_ip,
|
|
const uint16_t server_port, const int timeout,
|
|
const int flags, const char *bind_ipaddr, int *err_no)
|
|
{
|
|
int sock;
|
|
bool auto_detect;
|
|
char formatted_ip[FORMATTED_IP_SIZE];
|
|
|
|
sock = socketCreateEx2(af, server_ip,
|
|
flags, bind_ipaddr, err_no);
|
|
if (sock < 0)
|
|
{
|
|
return sock;
|
|
}
|
|
|
|
auto_detect = ((flags & O_NONBLOCK) == 0);
|
|
*err_no = connectserverbyip_nb_ex(sock, server_ip,
|
|
server_port, timeout, auto_detect);
|
|
if (*err_no != 0)
|
|
{
|
|
format_ip_address(server_ip, formatted_ip);
|
|
logError("file: "__FILE__", line: %d, "
|
|
"connect to %s:%u fail, "
|
|
"errno: %d, error info: %s",
|
|
__LINE__, formatted_ip, server_port,
|
|
*err_no, STRERROR(*err_no));
|
|
close(sock);
|
|
return -4;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
const char *fc_inet_ntop(const struct sockaddr *addr,
|
|
char *buff, const int bufferSize)
|
|
{
|
|
int len;
|
|
|
|
if (addr->sa_family == AF_INET) {
|
|
len = sizeof(struct sockaddr_in);
|
|
} else if (addr->sa_family == AF_INET6) {
|
|
len = sizeof(struct sockaddr_in6);
|
|
} else {
|
|
*buff = '\0';
|
|
logWarning("file: "__FILE__", line: %d, "
|
|
"unkown family: %d", __LINE__, addr->sa_family);
|
|
return NULL;
|
|
}
|
|
|
|
if (getnameinfo(addr, len, buff, bufferSize, NULL, 0,
|
|
NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
{
|
|
*buff = '\0';
|
|
return NULL;
|
|
}
|
|
return buff;
|
|
}
|
|
|
|
in_addr_64_t getIpaddr(getnamefunc getname, int sock,
|
|
char *buff, const int bufferSize)
|
|
{
|
|
sockaddr_convert_t convert;
|
|
|
|
memset(&convert, 0, sizeof(convert));
|
|
convert.len = sizeof(convert.sa);
|
|
if (getname(sock, &convert.sa.addr, &convert.len) != 0)
|
|
{
|
|
*buff = '\0';
|
|
return INADDR_NONE;
|
|
}
|
|
|
|
if (convert.len > 0)
|
|
{
|
|
if (getnameinfo(&convert.sa.addr, convert.len, buff, bufferSize,
|
|
NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
|
|
if (convert.sa.addr.sa_family == AF_INET) {
|
|
return convert.sa.addr4.sin_addr.s_addr;
|
|
} else {
|
|
return *((in_addr_64_t *)((char *)&convert.sa.addr6.sin6_addr + 8));
|
|
}
|
|
}
|
|
|
|
int getIpAndPort(getnamefunc getname, int sock,
|
|
char *buff, const int bufferSize, int *port)
|
|
{
|
|
sockaddr_convert_t convert;
|
|
|
|
memset(&convert, 0, sizeof(convert));
|
|
convert.len = sizeof(convert.sa);
|
|
if (getname(sock, &convert.sa.addr, &convert.len) != 0)
|
|
{
|
|
*buff = '\0';
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
if (convert.len > 0)
|
|
{
|
|
if (getnameinfo(&convert.sa.addr, convert.len, buff, bufferSize,
|
|
NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
|
|
if (convert.sa.addr.sa_family == AF_INET) {
|
|
*port = ntohs(convert.sa.addr4.sin_port);
|
|
} else {
|
|
*port = ntohs(convert.sa.addr6.sin6_port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize)
|
|
{
|
|
struct hostent *ent;
|
|
sockaddr_convert_t convert;
|
|
|
|
if (setsockaddrbyip(szIpAddr, 0, &convert) != 0)
|
|
{
|
|
*buff = '\0';
|
|
return buff;
|
|
}
|
|
|
|
ent = gethostbyaddr(&convert.sa.addr, convert.len,
|
|
convert.sa.addr.sa_family);
|
|
if (ent == NULL || ent->h_name == NULL)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
else
|
|
{
|
|
fc_strlcpy(buff, ent->h_name, bufferSize);
|
|
}
|
|
|
|
return buff;
|
|
}
|
|
|
|
in_addr_64_t getIpaddrByNameEx(const char *name, char *buff,
|
|
const int bufferSize, uint8_t *af)
|
|
{
|
|
struct addrinfo hints, *res, *p;
|
|
struct in_addr addr4;
|
|
struct in6_addr addr6;
|
|
in_addr_64_t ip_addr;
|
|
|
|
if (strchr(name, ':') != NULL) //IPv6
|
|
{
|
|
if (strchr(name, '%') == NULL &&
|
|
inet_pton(AF_INET6, name, &addr6) == 1)
|
|
{
|
|
if (buff != NULL)
|
|
{
|
|
if (inet_ntop(AF_INET6, &addr6, buff, bufferSize) == NULL)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
*af = AF_INET6;
|
|
return *((in_addr_64_t *)((char *)&addr6 + 8));
|
|
}
|
|
}
|
|
else if ((*name >= '0' && *name <= '9') &&
|
|
inet_pton(AF_INET, name, &addr4) == 1)
|
|
{
|
|
if (buff != NULL)
|
|
{
|
|
if (inet_ntop(AF_INET, &addr4, buff, bufferSize) == NULL)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
*af = AF_INET;
|
|
return addr4.s_addr;
|
|
}
|
|
|
|
*af = AF_UNSPEC;
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_UNSPEC;
|
|
if (getaddrinfo(name, NULL, &hints, &res) != 0)
|
|
{
|
|
return INADDR_NONE;
|
|
}
|
|
|
|
ip_addr = INADDR_NONE;
|
|
for (p = res; p != NULL; p = p->ai_next)
|
|
{
|
|
if (p->ai_family == AF_INET) //IPv4 address
|
|
{
|
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
|
if (buff != NULL)
|
|
{
|
|
if (inet_ntop(AF_INET, &(ipv4->sin_addr), buff, bufferSize) == NULL)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
|
|
*af = p->ai_family;
|
|
ip_addr = ipv4->sin_addr.s_addr;
|
|
break;
|
|
}
|
|
else if (p->ai_family == AF_INET6) //IPv6 address
|
|
{
|
|
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
|
if (buff != NULL)
|
|
{
|
|
if (getnameinfo((struct sockaddr *)ipv6, sizeof(*ipv6),
|
|
buff, bufferSize, NULL, 0, NI_NUMERICHOST |
|
|
NI_NUMERICSERV) != 0)
|
|
{
|
|
if (inet_ntop(AF_INET6, &(ipv6->sin6_addr),
|
|
buff, bufferSize) == NULL)
|
|
{
|
|
*buff = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
*af = p->ai_family;
|
|
ip_addr = *((in_addr_64_t *)((char *)&ipv6->sin6_addr + 8));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
freeaddrinfo(res);
|
|
return ip_addr;
|
|
}
|
|
|
|
int getIpaddrsByName(const char *name,
|
|
ip_addr_t *ip_addr_arr, const int ip_addr_arr_size)
|
|
{
|
|
int result;
|
|
int ip_count;
|
|
int len;
|
|
struct addrinfo hints, *res, *res0;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (getaddrinfo(name, NULL, &hints, &res0) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
for (ip_count = 0, res = res0; res; res = res->ai_next) {
|
|
if (res->ai_family == AF_INET)
|
|
{
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
else if (res->ai_family == AF_INET6)
|
|
{
|
|
len = sizeof(struct sockaddr_in6);
|
|
}
|
|
else
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"unsupported family %d, "
|
|
"only suppport AF_INET and AF_INET6",
|
|
__LINE__, res->ai_family);
|
|
continue;
|
|
}
|
|
|
|
if (ip_addr_arr_size <= ip_count) {
|
|
break;
|
|
}
|
|
|
|
if ((result=getnameinfo(res->ai_addr, len, ip_addr_arr[ip_count].
|
|
ip_addr, IP_ADDRESS_SIZE, NULL, 0,
|
|
NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"getnameinfo fail, errno: %d, error info: %s",
|
|
__LINE__, result, gai_strerror(result));
|
|
continue;
|
|
}
|
|
|
|
ip_addr_arr[ip_count++].af = res->ai_family;
|
|
}
|
|
|
|
freeaddrinfo(res0);
|
|
|
|
return ip_count;
|
|
}
|
|
|
|
int nbaccept(int sock, const int timeout, int *err_no)
|
|
{
|
|
struct sockaddr_in inaddr;
|
|
socklen_t sockaddr_len;
|
|
fd_set read_set;
|
|
struct timeval t;
|
|
int result;
|
|
|
|
if (timeout > 0)
|
|
{
|
|
t.tv_usec = 0;
|
|
t.tv_sec = timeout;
|
|
|
|
FD_ZERO(&read_set);
|
|
FD_SET(sock, &read_set);
|
|
|
|
result = select(sock+1, &read_set, NULL, NULL, &t);
|
|
if (result == 0) //timeout
|
|
{
|
|
*err_no = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
else if (result < 0) //error
|
|
{
|
|
*err_no = errno != 0 ? errno : EINTR;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
if (!FD_ISSET(sock, &read_set))
|
|
{
|
|
*err_no = EAGAIN;
|
|
return -1;
|
|
}
|
|
*/
|
|
}
|
|
|
|
sockaddr_len = sizeof(inaddr);
|
|
result = accept(sock, (struct sockaddr*)&inaddr, &sockaddr_len);
|
|
if (result < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : EINTR;
|
|
}
|
|
else
|
|
{
|
|
*err_no = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int socketBind2(int af, int sock, const char *bind_ipaddr, const int port)
|
|
{
|
|
sockaddr_convert_t convert;
|
|
int result;
|
|
|
|
memset(&convert, 0, sizeof(convert));
|
|
convert.sa.addr.sa_family = af;
|
|
if (bind_ipaddr == NULL || *bind_ipaddr == '\0')
|
|
{
|
|
if (af == AF_INET)
|
|
{
|
|
convert.len = sizeof(convert.sa.addr4);
|
|
convert.sa.addr4.sin_port = htons(port);
|
|
convert.sa.addr4.sin_addr.s_addr = INADDR_ANY;
|
|
}
|
|
else
|
|
{
|
|
convert.len = sizeof(convert.sa.addr6);
|
|
convert.sa.addr6.sin6_port = htons(port);
|
|
convert.sa.addr6.sin6_addr = in6addr_any;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((result=setsockaddrbyip(bind_ipaddr, port, &convert)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (bind(sock, &convert.sa.addr, convert.len) < 0) {
|
|
char bind_ip_prompt[256];
|
|
if (bind_ipaddr == NULL || *bind_ipaddr == '\0') {
|
|
*bind_ip_prompt = '\0';
|
|
} else {
|
|
sprintf(bind_ip_prompt, "bind ip %s, ", bind_ipaddr);
|
|
}
|
|
logError("file: "__FILE__", line: %d, "
|
|
"%sbind port %d failed, "
|
|
"errno: %d, error info: %s.",
|
|
__LINE__, bind_ip_prompt, port,
|
|
errno, STRERROR(errno));
|
|
return errno != 0 ? errno : ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int socketBind(int sock, const char *bind_ipaddr, const int port)
|
|
{
|
|
return socketBind2(AF_INET, sock, bind_ipaddr, port);
|
|
}
|
|
|
|
int socketBindIPv6(int sock, const char *bind_ipaddr, const int port)
|
|
{
|
|
return socketBind2(AF_INET6, sock, bind_ipaddr, port);
|
|
}
|
|
|
|
int socketServer2(int af, const char *bind_ipaddr, const int port, int *err_no)
|
|
{
|
|
int sock;
|
|
int result;
|
|
|
|
sock = socket(af, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : EMFILE;
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"socket create failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return -1;
|
|
}
|
|
|
|
FC_SET_CLOEXEC(sock);
|
|
SET_SOCKOPT_NOSIGPIPE(sock);
|
|
|
|
result = 1;
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
|
&result, sizeof(int)) < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : ENOMEM;
|
|
logError("file: "__FILE__", line: %d, "
|
|
"setsockopt failed, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
close(sock);
|
|
return -2;
|
|
}
|
|
|
|
if (af == AF_INET6 && (bind_ipaddr == NULL || *bind_ipaddr == '\0'))
|
|
{
|
|
result = 1;
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
&result, sizeof(result)) < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : ENOMEM;
|
|
logError("file: "__FILE__", line: %d, "
|
|
"setsockopt failed, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
close(sock);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if ((*err_no=socketBind2(af, sock, bind_ipaddr, port)) != 0)
|
|
{
|
|
close(sock);
|
|
return -3;
|
|
}
|
|
|
|
if (listen(sock, 1024) < 0)
|
|
{
|
|
*err_no = errno != 0 ? errno : EINVAL;
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"listen port %d failed, " \
|
|
"errno: %d, error info: %s", \
|
|
__LINE__, port, errno, STRERROR(errno));
|
|
close(sock);
|
|
return -4;
|
|
}
|
|
|
|
*err_no = 0;
|
|
return sock;
|
|
}
|
|
|
|
int socketServer(const char *bind_ipaddr, const int port, int *err_no)
|
|
{
|
|
return socketServer2(AF_INET, bind_ipaddr, port, err_no);
|
|
}
|
|
|
|
int socketServerIPv6(const char *bind_ipaddr, const int port, int *err_no)
|
|
{
|
|
return socketServer2(AF_INET6, bind_ipaddr, port, err_no);
|
|
}
|
|
|
|
int tcprecvfile(int sock, const char *filename, const int64_t file_bytes, \
|
|
const int fsync_after_written_bytes, const int timeout, \
|
|
int64_t *true_file_bytes)
|
|
{
|
|
int write_fd;
|
|
char buff[FAST_WRITE_BUFF_SIZE];
|
|
int64_t remain_bytes;
|
|
int recv_bytes;
|
|
int written_bytes;
|
|
int result;
|
|
int flags;
|
|
int count;
|
|
tcprecvdata_exfunc recv_func;
|
|
|
|
*true_file_bytes = 0;
|
|
flags = fcntl(sock, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
recv_func = tcprecvdata_nb_ex;
|
|
}
|
|
else
|
|
{
|
|
recv_func = tcprecvdata_ex;
|
|
}
|
|
|
|
write_fd = open(filename, O_WRONLY | O_CREAT |
|
|
O_TRUNC | O_CLOEXEC, 0644);
|
|
if (write_fd < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
written_bytes = 0;
|
|
remain_bytes = file_bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
if (remain_bytes > sizeof(buff))
|
|
{
|
|
recv_bytes = sizeof(buff);
|
|
}
|
|
else
|
|
{
|
|
recv_bytes = remain_bytes;
|
|
}
|
|
|
|
result = recv_func(sock, buff, recv_bytes,
|
|
timeout, &count);
|
|
if (result != 0)
|
|
{
|
|
if (file_bytes != INFINITE_FILE_SIZE)
|
|
{
|
|
close(write_fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (count > 0 && write(write_fd, buff, count) != count)
|
|
{
|
|
result = errno != 0 ? errno: EIO;
|
|
close(write_fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
|
|
*true_file_bytes += count;
|
|
if (fsync_after_written_bytes > 0)
|
|
{
|
|
written_bytes += count;
|
|
if (written_bytes >= fsync_after_written_bytes)
|
|
{
|
|
written_bytes = 0;
|
|
if (fsync(write_fd) != 0)
|
|
{
|
|
result = errno != 0 ? errno: EIO;
|
|
close(write_fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != 0) //recv infinite file, does not delete the file
|
|
{
|
|
int read_fd;
|
|
read_fd = -1;
|
|
|
|
do
|
|
{
|
|
if (*true_file_bytes < 8)
|
|
{
|
|
break;
|
|
}
|
|
|
|
read_fd = open(filename, O_RDONLY | O_CLOEXEC);
|
|
if (read_fd < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if (lseek(read_fd, -8, SEEK_END) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
break;
|
|
}
|
|
|
|
if (read(read_fd, buff, 8) != 8)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
break;
|
|
}
|
|
|
|
*true_file_bytes -= 8;
|
|
if (buff2long(buff) != *true_file_bytes)
|
|
{
|
|
result = EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (ftruncate(write_fd, *true_file_bytes) != 0)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
break;
|
|
}
|
|
|
|
result = 0;
|
|
} while (0);
|
|
|
|
close(write_fd);
|
|
if (read_fd >= 0)
|
|
{
|
|
close(read_fd);
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
unlink(filename);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
remain_bytes -= count;
|
|
}
|
|
|
|
close(write_fd);
|
|
return 0;
|
|
}
|
|
|
|
int tcprecvfile_ex(int sock, const char *filename, const int64_t file_bytes, \
|
|
const int fsync_after_written_bytes, \
|
|
unsigned int *hash_codes, const int timeout)
|
|
{
|
|
int fd;
|
|
char buff[FAST_WRITE_BUFF_SIZE];
|
|
int64_t remain_bytes;
|
|
int recv_bytes;
|
|
int written_bytes;
|
|
int result;
|
|
int flags;
|
|
tcprecvdata_exfunc recv_func;
|
|
|
|
flags = fcntl(sock, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
recv_func = tcprecvdata_nb_ex;
|
|
}
|
|
else
|
|
{
|
|
recv_func = tcprecvdata_ex;
|
|
}
|
|
|
|
fd = open(filename, O_WRONLY | O_CREAT |
|
|
O_TRUNC | O_CLOEXEC, 0644);
|
|
if (fd < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
INIT_HASH_CODES4(hash_codes)
|
|
|
|
written_bytes = 0;
|
|
remain_bytes = file_bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
if (remain_bytes > sizeof(buff))
|
|
{
|
|
recv_bytes = sizeof(buff);
|
|
}
|
|
else
|
|
{
|
|
recv_bytes = remain_bytes;
|
|
}
|
|
|
|
if ((result=recv_func(sock, buff, recv_bytes, \
|
|
timeout, NULL)) != 0)
|
|
{
|
|
close(fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
|
|
if (write(fd, buff, recv_bytes) != recv_bytes)
|
|
{
|
|
result = errno != 0 ? errno: EIO;
|
|
close(fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
|
|
if (fsync_after_written_bytes > 0)
|
|
{
|
|
written_bytes += recv_bytes;
|
|
if (written_bytes >= fsync_after_written_bytes)
|
|
{
|
|
written_bytes = 0;
|
|
if (fsync(fd) != 0)
|
|
{
|
|
result = errno != 0 ? errno: EIO;
|
|
close(fd);
|
|
unlink(filename);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
CALC_HASH_CODES4(buff, recv_bytes, hash_codes)
|
|
|
|
remain_bytes -= recv_bytes;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
FINISH_HASH_CODES4(hash_codes)
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpdiscard(int sock, const int bytes, const int timeout, \
|
|
int64_t *total_recv_bytes)
|
|
{
|
|
char buff[FAST_WRITE_BUFF_SIZE];
|
|
int remain_bytes;
|
|
int recv_bytes;
|
|
int result;
|
|
int flags;
|
|
int count;
|
|
tcprecvdata_exfunc recv_func;
|
|
|
|
*total_recv_bytes = 0;
|
|
flags = fcntl(sock, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
recv_func = tcprecvdata_nb_ex;
|
|
}
|
|
else
|
|
{
|
|
recv_func = tcprecvdata_ex;
|
|
}
|
|
|
|
remain_bytes = bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
if (remain_bytes > sizeof(buff))
|
|
{
|
|
recv_bytes = sizeof(buff);
|
|
}
|
|
else
|
|
{
|
|
recv_bytes = remain_bytes;
|
|
}
|
|
|
|
result = recv_func(sock, buff, recv_bytes, \
|
|
timeout, &count);
|
|
*total_recv_bytes += count;
|
|
if (result != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
remain_bytes -= recv_bytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpsendfile_ex(int sock, const char *filename, const int64_t file_offset, \
|
|
const int64_t file_bytes, const int timeout, int64_t *total_send_bytes)
|
|
{
|
|
int fd;
|
|
int64_t send_bytes;
|
|
int result;
|
|
int flags;
|
|
#ifdef USE_SENDFILE
|
|
#if defined(OS_FREEBSD) || defined(OS_LINUX)
|
|
off_t offset;
|
|
int64_t remain_bytes;
|
|
#endif
|
|
#else
|
|
int64_t remain_bytes;
|
|
#endif
|
|
|
|
fd = open(filename, O_RDONLY | O_CLOEXEC);
|
|
if (fd < 0)
|
|
{
|
|
*total_send_bytes = 0;
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
flags = fcntl(sock, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
*total_send_bytes = 0;
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
#ifdef USE_SENDFILE
|
|
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
if (fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) < 0)
|
|
{
|
|
*total_send_bytes = 0;
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
}
|
|
|
|
#ifdef OS_LINUX
|
|
/*
|
|
result = 1;
|
|
if (setsockopt(sock, SOL_TCP, TCP_CORK, &result, sizeof(int)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s.", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
close(fd);
|
|
*total_send_bytes = 0;
|
|
return errno != 0 ? errno : EIO;
|
|
}
|
|
*/
|
|
|
|
#define FILE_1G_SIZE (1 * 1024 * 1024 * 1024)
|
|
|
|
result = 0;
|
|
offset = file_offset;
|
|
remain_bytes = file_bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
if (remain_bytes > FILE_1G_SIZE)
|
|
{
|
|
send_bytes = sendfile(sock, fd, &offset, FILE_1G_SIZE);
|
|
}
|
|
else
|
|
{
|
|
send_bytes = sendfile(sock, fd, &offset, remain_bytes);
|
|
}
|
|
|
|
if (send_bytes <= 0)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
if (result == EINTR && try_again_when_interrupt)
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
remain_bytes -= send_bytes;
|
|
}
|
|
|
|
#else
|
|
#ifdef OS_FREEBSD
|
|
offset = file_offset;
|
|
#if defined(DARWIN)
|
|
result = 0;
|
|
remain_bytes = file_bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
off_t len;
|
|
len = remain_bytes;
|
|
if (sendfile(fd, sock, offset, &len, NULL, 0) != 0) {
|
|
result = errno != 0 ? errno : EIO;
|
|
if (!(result == EINTR && try_again_when_interrupt))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
remain_bytes -= len;
|
|
}
|
|
#else
|
|
remain_bytes = file_bytes;
|
|
result = 0;
|
|
while (remain_bytes > 0)
|
|
{
|
|
off_t sbytes;
|
|
sbytes = 0;
|
|
if (sendfile(fd, sock, offset, remain_bytes, NULL, &sbytes, 0) != 0)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
if (!(result == EINTR && try_again_when_interrupt))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
remain_bytes -= sbytes;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
*total_send_bytes = file_bytes - remain_bytes;
|
|
#endif
|
|
|
|
if (flags & O_NONBLOCK) //restore
|
|
{
|
|
if (fcntl(sock, F_SETFL, flags) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EACCES;
|
|
}
|
|
}
|
|
|
|
#ifdef OS_LINUX
|
|
close(fd);
|
|
return result;
|
|
#endif
|
|
|
|
#ifdef OS_FREEBSD
|
|
close(fd);
|
|
return result;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
{
|
|
char buff[FAST_WRITE_BUFF_SIZE];
|
|
int64_t remain_bytes;
|
|
tcpsenddatafunc send_func;
|
|
|
|
if (file_offset > 0 && lseek(fd, file_offset, SEEK_SET) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
close(fd);
|
|
*total_send_bytes = 0;
|
|
return result;
|
|
}
|
|
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
send_func = tcpsenddata_nb;
|
|
}
|
|
else
|
|
{
|
|
send_func = tcpsenddata;
|
|
}
|
|
|
|
result = 0;
|
|
remain_bytes = file_bytes;
|
|
while (remain_bytes > 0)
|
|
{
|
|
if (remain_bytes > sizeof(buff))
|
|
{
|
|
send_bytes = sizeof(buff);
|
|
}
|
|
else
|
|
{
|
|
send_bytes = remain_bytes;
|
|
}
|
|
|
|
if (read(fd, buff, send_bytes) != send_bytes)
|
|
{
|
|
result = errno != 0 ? errno : EIO;
|
|
break;
|
|
}
|
|
|
|
if ((result=send_func(sock, buff, send_bytes, \
|
|
timeout)) != 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
remain_bytes -= send_bytes;
|
|
}
|
|
|
|
*total_send_bytes = file_bytes - remain_bytes;
|
|
}
|
|
|
|
close(fd);
|
|
return result;
|
|
}
|
|
|
|
int tcpsetserveropt(int fd, const int timeout)
|
|
{
|
|
struct linger linger;
|
|
struct timeval waittime;
|
|
|
|
SET_SOCKOPT_NOSIGPIPE(fd);
|
|
|
|
/*
|
|
linger.l_onoff = 1;
|
|
#ifdef OS_FREEBSD
|
|
linger.l_linger = timeout * 100;
|
|
#else
|
|
linger.l_linger = timeout;
|
|
#endif
|
|
*/
|
|
linger.l_onoff = 0;
|
|
linger.l_linger = 0;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, \
|
|
&linger, (socklen_t)sizeof(struct linger)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : ENOMEM;
|
|
}
|
|
|
|
waittime.tv_sec = timeout;
|
|
waittime.tv_usec = 0;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
|
|
&waittime, (socklen_t)sizeof(struct timeval)) < 0)
|
|
{
|
|
logWarning("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
}
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
|
|
&waittime, (socklen_t)sizeof(struct timeval)) < 0)
|
|
{
|
|
logWarning("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
}
|
|
|
|
return tcpsetnodelay(fd, timeout);
|
|
}
|
|
|
|
int tcpsetkeepalive(int fd, const int idleSeconds)
|
|
{
|
|
int keepAlive;
|
|
|
|
#ifdef OS_LINUX
|
|
int keepIdle;
|
|
int keepInterval;
|
|
int keepCount;
|
|
#endif
|
|
|
|
keepAlive = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, \
|
|
(char *)&keepAlive, sizeof(keepAlive)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
#ifdef OS_LINUX
|
|
keepIdle = idleSeconds;
|
|
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *)&keepIdle, \
|
|
sizeof(keepIdle)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
keepInterval = 10;
|
|
if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *)&keepInterval, \
|
|
sizeof(keepInterval)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
keepCount = 3;
|
|
if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *)&keepCount, \
|
|
sizeof(keepCount)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpprintkeepalive(int fd)
|
|
{
|
|
int keepAlive;
|
|
socklen_t len;
|
|
|
|
#ifdef OS_LINUX
|
|
int keepIdle;
|
|
int keepInterval;
|
|
int keepCount;
|
|
#endif
|
|
|
|
len = sizeof(keepAlive);
|
|
if (getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, \
|
|
(char *)&keepAlive, &len) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
#ifdef OS_LINUX
|
|
len = sizeof(keepIdle);
|
|
if (getsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *)&keepIdle, \
|
|
&len) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
len = sizeof(keepInterval);
|
|
if (getsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *)&keepInterval, \
|
|
&len) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
len = sizeof(keepCount);
|
|
if (getsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *)&keepCount, \
|
|
&len) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"setsockopt failed, errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
|
|
logDebug("keepAlive=%d, keepIdle=%d, keepInterval=%d, keepCount=%d",
|
|
keepAlive, keepIdle, keepInterval, keepCount);
|
|
#else
|
|
logDebug("keepAlive=%d", keepAlive);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpsetnonblockopt(int fd)
|
|
{
|
|
int flags;
|
|
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
if (flags < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"fcntl failed, errno: %d, error info: %s.", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"fcntl failed, errno: %d, error info: %s.", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EACCES;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tcpsetnodelay(int fd, const int timeout)
|
|
{
|
|
int flags;
|
|
int result;
|
|
|
|
if ((result=tcpsetkeepalive(fd, 2 * timeout + 1)) != 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
flags = 1;
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
|
(char *)&flags, sizeof(flags)) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"setsockopt failed, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EINVAL;
|
|
}
|
|
TCP_SET_QUICK_ACK(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(OS_LINUX) || defined(OS_FREEBSD)
|
|
int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \
|
|
const int max_count, int *count)
|
|
{
|
|
int result;
|
|
int len;
|
|
struct ifaddrs *ifc;
|
|
struct ifaddrs *ifc1;
|
|
|
|
*count = 0;
|
|
if (0 != getifaddrs(&ifc))
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"call getifaddrs fail, " \
|
|
"errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EMFILE;
|
|
}
|
|
|
|
ifc1 = ifc;
|
|
while (NULL != ifc)
|
|
{
|
|
if (NULL == ifc->ifa_addr ) {
|
|
ifc = ifc->ifa_next;
|
|
continue;
|
|
}
|
|
|
|
if (max_count <= *count)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "\
|
|
"max_count: %d < iterface count: %d", \
|
|
__LINE__, max_count, *count);
|
|
freeifaddrs(ifc1);
|
|
return ENOSPC;
|
|
}
|
|
|
|
do {
|
|
if (ifc->ifa_addr->sa_family == AF_INET)
|
|
{
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
else if (ifc->ifa_addr->sa_family == AF_INET6)
|
|
{
|
|
len = sizeof(struct sockaddr_in6);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((result=getnameinfo(ifc->ifa_addr, len, ip_addrs[*count],
|
|
IP_ADDRESS_SIZE, NULL, 0, NI_NUMERICHOST |
|
|
NI_NUMERICSERV)) == 0)
|
|
{
|
|
(*count)++;
|
|
}
|
|
else
|
|
{
|
|
logWarning("file: "__FILE__", line: %d, "
|
|
"getnameinfo fail, errno: %d, error info: %s",
|
|
__LINE__, result, gai_strerror(result));
|
|
}
|
|
} while (0);
|
|
|
|
ifc = ifc->ifa_next;
|
|
}
|
|
|
|
freeifaddrs(ifc1);
|
|
return *count > 0 ? 0 : ENOENT;
|
|
}
|
|
|
|
#else
|
|
|
|
int getlocaladdrs(char ip_addrs[][IP_ADDRESS_SIZE], \
|
|
const int max_count, int *count)
|
|
{
|
|
int sock;
|
|
int len;
|
|
struct ifconf ifconf;
|
|
struct ifreq ifr[32];
|
|
struct ifreq *ifrp;
|
|
char *p_end;
|
|
int result;
|
|
|
|
*count = 0;
|
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"socket create fail, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EMFILE;
|
|
}
|
|
|
|
ifconf.ifc_buf = (char *) ifr;
|
|
ifconf.ifc_len = sizeof(ifr);
|
|
if (ioctl(sock, SIOCGIFCONF, &ifconf) < 0)
|
|
{
|
|
result = errno != 0 ? errno : EMFILE;
|
|
logError("file: "__FILE__", line: %d, "
|
|
"call ioctl fail, errno: %d, error info: %s",
|
|
__LINE__, result, STRERROR(result));
|
|
close(sock);
|
|
return result;
|
|
}
|
|
|
|
ifrp = ifconf.ifc_req;
|
|
p_end = (char *)ifr + ifconf.ifc_len;
|
|
while ((char *)ifrp < p_end)
|
|
{
|
|
struct sockaddr *sa = &ifrp->ifr_addr;
|
|
|
|
if (*count >= max_count)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"max_count: %d < iterface count: %d",
|
|
__LINE__, max_count, *count);
|
|
close(sock);
|
|
return ENOSPC;
|
|
}
|
|
|
|
if (sa->sa_family == AF_INET6)
|
|
{
|
|
len = sizeof(struct sockaddr_in6);
|
|
} else
|
|
{
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
if ((result=getnameinfo(sa, len, ip_addrs[*count], IP_ADDRESS_SIZE,
|
|
NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"call getnameinfo fail, errno: %d, error info: %s",
|
|
__LINE__, result, gai_strerror(result));
|
|
close(sock);
|
|
return result;
|
|
}
|
|
(*count)++;
|
|
|
|
#ifdef OS_FREEBSD
|
|
ifrp = (struct ifreq*)((caddr_t)&ifrp->ifr_addr + sa->sa_len);
|
|
#else
|
|
ifrp++;
|
|
#endif
|
|
}
|
|
|
|
close(sock);
|
|
return *count > 0 ? 0 : ENOENT;
|
|
}
|
|
|
|
#endif
|
|
|
|
int gethostaddrs(char **if_alias_prefixes, const int prefix_count, \
|
|
char ip_addrs[][IP_ADDRESS_SIZE], const int max_count, int *count)
|
|
{
|
|
struct hostent *ent;
|
|
char hostname[128];
|
|
char *alias_prefixes1[1];
|
|
char **true_alias_prefixes;
|
|
int true_count;
|
|
int i;
|
|
int k;
|
|
int len;
|
|
int sock;
|
|
struct ifreq req;
|
|
struct sockaddr *addr;
|
|
int ret;
|
|
|
|
*count = 0;
|
|
if (prefix_count <= 0)
|
|
{
|
|
if (getlocaladdrs(ip_addrs, max_count, count) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef OS_FREEBSD
|
|
#define IF_NAME_PREFIX "bge"
|
|
#else
|
|
#ifdef OS_SUNOS
|
|
#define IF_NAME_PREFIX "e1000g"
|
|
#else
|
|
#ifdef OS_AIX
|
|
#define IF_NAME_PREFIX "en"
|
|
#else
|
|
#define IF_NAME_PREFIX "eth"
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
alias_prefixes1[0] = IF_NAME_PREFIX;
|
|
true_count = 1;
|
|
true_alias_prefixes = alias_prefixes1;
|
|
}
|
|
else
|
|
{
|
|
true_count = prefix_count;
|
|
true_alias_prefixes = if_alias_prefixes;
|
|
}
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"socket create failed, errno: %d, error info: %s.", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EMFILE;
|
|
}
|
|
|
|
for (i=0; i<true_count && *count<max_count; i++)
|
|
{
|
|
for (k=0; k<max_count; k++)
|
|
{
|
|
memset(&req, 0, sizeof(req));
|
|
sprintf(req.ifr_name, "%s%d", true_alias_prefixes[i], k);
|
|
ret = ioctl(sock, SIOCGIFADDR, &req);
|
|
if (ret == -1)
|
|
{
|
|
if (*count == 0 && k == 0) //maybe based 1
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
addr = &req.ifr_addr;
|
|
if (addr->sa_family == AF_INET6)
|
|
{
|
|
len = sizeof(struct sockaddr_in6);
|
|
}
|
|
else
|
|
{
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
if (getnameinfo(addr, len, ip_addrs[*count], IP_ADDRESS_SIZE,
|
|
NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0)
|
|
{
|
|
(*count)++;
|
|
if (*count >= max_count)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close(sock);
|
|
if (*count > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (gethostname(hostname, sizeof(hostname)) != 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"call gethostname fail, " \
|
|
"error no: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EFAULT;
|
|
}
|
|
|
|
ent = gethostbyname(hostname);
|
|
if (ent == NULL)
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"call gethostbyname fail, " \
|
|
"error no: %d, error info: %s", \
|
|
__LINE__, h_errno, STRERROR(h_errno));
|
|
return h_errno != 0 ? h_errno : EFAULT;
|
|
}
|
|
|
|
k = 0;
|
|
while (ent->h_addr_list[k] != NULL)
|
|
{
|
|
if (*count >= max_count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (inet_ntop(ent->h_addrtype, ent->h_addr_list[k],
|
|
ip_addrs[*count], IP_ADDRESS_SIZE) != NULL)
|
|
{
|
|
(*count)++;
|
|
}
|
|
|
|
k++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(OS_LINUX) || defined(OS_FREEBSD)
|
|
|
|
static inline int formatifmac(char *buff, const int buff_size,
|
|
unsigned char *hwaddr, const int addr_size)
|
|
{
|
|
int len;
|
|
unsigned char *ptr;
|
|
unsigned char *end;
|
|
char *dest;
|
|
|
|
for (end=hwaddr+(addr_size-1); end>=hwaddr; end--)
|
|
{
|
|
if (*end != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
++end;
|
|
|
|
len = end - hwaddr;
|
|
if (len == 0)
|
|
{
|
|
*buff = '\0';
|
|
return 0;
|
|
}
|
|
|
|
if (len < 6)
|
|
{
|
|
len = 6;
|
|
end = hwaddr + len;
|
|
}
|
|
if (len * 3 > buff_size)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"buff size: %d is too small, expect size: %d",
|
|
__LINE__, buff_size, len * 3);
|
|
*buff = '\0';
|
|
return 0;
|
|
}
|
|
|
|
dest = buff;
|
|
*dest++ = g_lower_hex_chars[*hwaddr >> 4];
|
|
*dest++ = g_lower_hex_chars[*hwaddr & 0x0F];
|
|
for (ptr=hwaddr+1; ptr<end; ptr++)
|
|
{
|
|
*dest++ = ':';
|
|
*dest++ = g_lower_hex_chars[*ptr >> 4];
|
|
*dest++ = g_lower_hex_chars[*ptr & 0x0F];
|
|
}
|
|
return dest - buff;
|
|
}
|
|
|
|
#if defined(OS_LINUX)
|
|
static int getifmac(FastIFConfig *config)
|
|
{
|
|
int sockfd;
|
|
int len;
|
|
struct ifreq req[1];
|
|
char cmd[256];
|
|
char output[64];
|
|
|
|
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (sockfd < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"unable to create socket, "
|
|
"errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EPERM;
|
|
}
|
|
|
|
memset(req, 0, sizeof(struct ifreq));
|
|
strcpy(req->ifr_name, config->name);
|
|
if (ioctl(sockfd, SIOCGIFHWADDR, req) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"ioctl error, errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
close(sockfd);
|
|
return errno != 0 ? errno : EPERM;
|
|
}
|
|
|
|
close(sockfd);
|
|
|
|
len = formatifmac(config->mac, sizeof(config->mac),
|
|
(unsigned char *)req->ifr_hwaddr.sa_data,
|
|
sizeof(req->ifr_hwaddr.sa_data));
|
|
if (len > 6)
|
|
{
|
|
snprintf(cmd, sizeof(cmd), "ip link | fgrep -A 1 %s: | "
|
|
"fgrep link/ | awk '{print $2}'", config->name);
|
|
if (getExecResult(cmd, output, sizeof(output)) == 0)
|
|
{
|
|
fc_trim(output);
|
|
if (*output != '\0')
|
|
{
|
|
fc_safe_strcpy(config->mac, output);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else //FreeBSD
|
|
static int getifmac(FastIFConfig *config)
|
|
{
|
|
int mib[6];
|
|
size_t len;
|
|
char buf[256];
|
|
unsigned char *ptr;
|
|
struct if_msghdr *ifm;
|
|
struct sockaddr_dl *sdl;
|
|
int size;
|
|
|
|
mib[0] = CTL_NET;
|
|
mib[1] = AF_ROUTE;
|
|
mib[2] = 0;
|
|
mib[3] = AF_LINK;
|
|
mib[4] = NET_RT_IFLIST;
|
|
|
|
if ((mib[5] = if_nametoindex(config->name)) == 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"call if_nametoindex fail, "
|
|
"errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EPERM;
|
|
}
|
|
|
|
len = sizeof(buf);
|
|
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "
|
|
"call sysctl fail, "
|
|
"errno: %d, error info: %s",
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EPERM;
|
|
}
|
|
|
|
|
|
ifm = (struct if_msghdr *)buf;
|
|
sdl = (struct sockaddr_dl *)(ifm + 1);
|
|
ptr = (unsigned char *)LLADDR(sdl);
|
|
size = (unsigned char *)(sdl->sdl_data + sizeof(sdl->sdl_data)) - ptr;
|
|
formatifmac(config->mac, sizeof(config->mac), ptr, size);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int getifconfigs(FastIFConfig *if_configs, const int max_count, int *count)
|
|
{
|
|
struct ifaddrs *ifc;
|
|
struct ifaddrs *ifc1;
|
|
FastIFConfig *config;
|
|
char *buff;
|
|
int result;
|
|
int buff_size;
|
|
int len;
|
|
int i;
|
|
|
|
*count = 0;
|
|
memset(if_configs, 0, sizeof(FastIFConfig) * max_count);
|
|
if (0 != getifaddrs(&ifc))
|
|
{
|
|
logError("file: "__FILE__", line: %d, " \
|
|
"call getifaddrs fail, " \
|
|
"errno: %d, error info: %s", \
|
|
__LINE__, errno, STRERROR(errno));
|
|
return errno != 0 ? errno : EMFILE;
|
|
}
|
|
|
|
ifc1 = ifc;
|
|
while (NULL != ifc)
|
|
{
|
|
struct sockaddr *s;
|
|
s = ifc->ifa_addr;
|
|
if (NULL != s)
|
|
{
|
|
if (AF_INET == s->sa_family || AF_INET6 == s->sa_family)
|
|
{
|
|
for (i=0; i<*count; i++)
|
|
{
|
|
if (strcmp(if_configs[i].name, ifc->ifa_name) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
config = if_configs + i;
|
|
if (i == *count) //not found
|
|
{
|
|
if (max_count <= *count)
|
|
{
|
|
logError("file: "__FILE__", line: %d, "\
|
|
"max_count: %d < iterface count: %d", \
|
|
__LINE__, max_count, *count);
|
|
freeifaddrs(ifc1);
|
|
return ENOSPC;
|
|
}
|
|
|
|
strcpy(config->name, ifc->ifa_name);
|
|
(*count)++;
|
|
}
|
|
|
|
if (AF_INET == s->sa_family)
|
|
{
|
|
buff = config->ipv4;
|
|
buff_size = sizeof(config->ipv4);
|
|
len = sizeof(struct sockaddr_in);
|
|
}
|
|
else
|
|
{
|
|
buff = config->ipv6;
|
|
buff_size = sizeof(config->ipv6);
|
|
len = sizeof(struct sockaddr_in6);
|
|
}
|
|
|
|
if ((result=getnameinfo(s, len, buff, buff_size, NULL, 0,
|
|
NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
|
|
{
|
|
logWarning("file: "__FILE__", line: %d, "
|
|
"getnameinfo fail, errno: %d, error info: %s",
|
|
__LINE__, result, gai_strerror(result));
|
|
}
|
|
}
|
|
}
|
|
|
|
ifc = ifc->ifa_next;
|
|
}
|
|
|
|
freeifaddrs(ifc1);
|
|
for (i=0; i<*count; i++)
|
|
{
|
|
getifmac(if_configs + i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int getifconfigs(FastIFConfig *if_configs, const int max_count, int *count)
|
|
{
|
|
*count = 0;
|
|
return EOPNOTSUPP;
|
|
}
|
|
#endif
|
|
|
|
int fc_get_net_type_by_ip(const char *ip)
|
|
{
|
|
int len;
|
|
if (ip == NULL)
|
|
{
|
|
return FC_NET_TYPE_NONE;
|
|
}
|
|
len = strlen(ip);
|
|
if (len < 8)
|
|
{
|
|
return (len < 7) ? FC_NET_TYPE_NONE : FC_NET_TYPE_OUTER;
|
|
}
|
|
|
|
if (memcmp(ip, "10.", 3) == 0)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_10;
|
|
}
|
|
|
|
if (memcmp(ip, "192.168.", 8) == 0)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_192;
|
|
}
|
|
|
|
if (memcmp(ip, "172.", 4) == 0)
|
|
{
|
|
int b;
|
|
b = atoi(ip + 4);
|
|
if (b >= 16 && b < 32)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_172;
|
|
}
|
|
}
|
|
|
|
return FC_NET_TYPE_OUTER;
|
|
}
|
|
|
|
int fc_get_net_type_by_name(const char *net_type)
|
|
{
|
|
if (net_type == NULL || *net_type == '\0') {
|
|
return FC_NET_TYPE_ANY;
|
|
}
|
|
|
|
if (strcasecmp(net_type, NET_TYPE_ANY_STR) == 0) {
|
|
return FC_NET_TYPE_ANY;
|
|
} else if (strcasecmp(net_type, NET_TYPE_OUTER_STR) == 0) {
|
|
return FC_NET_TYPE_OUTER;
|
|
} else if (strcasecmp(net_type, NET_TYPE_INNER_STR) == 0) {
|
|
return FC_NET_TYPE_INNER;
|
|
} else if (strcasecmp(net_type, SUB_NET_TYPE_INNER_10_STR) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_10_STR2) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_10_STR3) == 0)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_10;
|
|
} else if (strcasecmp(net_type, SUB_NET_TYPE_INNER_172_STR) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_172_STR2) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_172_STR3) == 0)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_172;
|
|
} else if (strcasecmp(net_type, SUB_NET_TYPE_INNER_192_STR) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_192_STR2) == 0 ||
|
|
strcasecmp(net_type, SUB_NET_TYPE_INNER_192_STR3) == 0)
|
|
{
|
|
return FC_SUB_NET_TYPE_INNER_192;
|
|
} else {
|
|
return FC_NET_TYPE_NONE;
|
|
}
|
|
}
|
|
|
|
bool tcp_socket_connected(int sock)
|
|
{
|
|
socklen_t len;
|
|
#if defined(OS_LINUX) || defined(OS_FREEBSD)
|
|
|
|
#ifdef OS_LINUX
|
|
struct tcp_info info;
|
|
#else
|
|
#include <netinet/tcp_fsm.h>
|
|
#define TCP_ESTABLISHED TCPS_ESTABLISHED
|
|
#ifndef TCP_INFO
|
|
#define TCP_INFO TCP_CONNECTION_INFO
|
|
struct tcp_connection_info info;
|
|
#else
|
|
struct tcp_info info;
|
|
#endif
|
|
#endif
|
|
|
|
len = sizeof(info);
|
|
if (getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len) < 0) {
|
|
return false;
|
|
}
|
|
if (info.tcpi_state == TCP_ESTABLISHED) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
#else
|
|
int result;
|
|
len = sizeof(result);
|
|
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
|
|
return false;
|
|
} else {
|
|
return (result == 0);
|
|
}
|
|
#endif
|
|
}
|