mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-12-15 09:20:52 +08:00
378 lines
9.1 KiB
C++
378 lines
9.1 KiB
C++
/**
|
|
* Copyright (C) 2015-2018
|
|
* All rights reserved.
|
|
*
|
|
* AUTHOR(S)
|
|
* Zheng Shuxin
|
|
* E-mail: zhengshuxin@qiyi.com
|
|
*
|
|
* VERSION
|
|
* Thu 25 May 2017 05:25:43 PM CST
|
|
*/
|
|
#include "stdafx.h"
|
|
#include "udp.h"
|
|
|
|
static bool host_port(char *buf, char **host, char **port)
|
|
{
|
|
const char *ptr = acl_host_port(buf, host, "", port, (char*) NULL);
|
|
|
|
if (ptr != NULL) {
|
|
acl_msg_error("%s(%d): invalid addr %s, %s",
|
|
__FILE__, __LINE__, buf, ptr);
|
|
return false;
|
|
}
|
|
|
|
if (*port == NULL || atoi(*port) < 0) {
|
|
acl_msg_error("%s(%d): invalid port: %s, addr: %s",
|
|
__FILE__, __LINE__, *port ? *port : "null", buf);
|
|
return false;
|
|
}
|
|
|
|
if (*host && **host == 0)
|
|
*host = 0;
|
|
if (*host == NULL)
|
|
*host = "0";
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct addrinfo *host_addrinfo(const char *addr)
|
|
{
|
|
int err;
|
|
struct addrinfo hints, *res0;
|
|
char *buf = acl_mystrdup(addr), *host = NULL, *port = NULL;
|
|
|
|
if (host_port(buf, &host, &port) == false) {
|
|
acl_myfree(buf);
|
|
return NULL;
|
|
}
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
#ifdef ACL_MACOSX
|
|
hints.ai_flags = AI_DEFAULT;
|
|
#elif defined(ACL_ANDROID)
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
#elif defined(ACL_WINDOWS)
|
|
hints.ai_protocol = IPPROTO_UDP;
|
|
# if _MSC_VER >= 1500
|
|
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
|
|
# endif
|
|
#else
|
|
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
|
|
#endif
|
|
if ((err = getaddrinfo(host, port, &hints, &res0))) {
|
|
acl_msg_error("%s(%d): getaddrinfo error %s, peer=%s",
|
|
__FILE__, __LINE__, gai_strerror(err), host);
|
|
acl_myfree(buf);
|
|
return NULL;
|
|
}
|
|
|
|
acl_myfree(buf);
|
|
return res0;
|
|
}
|
|
|
|
static int bind_addr(struct addrinfo *res0, struct addrinfo **res)
|
|
{
|
|
struct addrinfo *it;
|
|
int on, fd;
|
|
|
|
for (it = res0; it != NULL ; it = it->ai_next) {
|
|
fd = socket(it->ai_family, it->ai_socktype, it->ai_protocol);
|
|
if (fd == ACL_SOCKET_INVALID) {
|
|
acl_msg_error("%s(%d): create socket %s",
|
|
__FILE__, __LINE__, acl_last_serror());
|
|
return ACL_SOCKET_INVALID;
|
|
}
|
|
|
|
on = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const void *) &on, sizeof(on)) < 0)
|
|
{
|
|
acl_msg_warn("%s(%d): setsockopt(SO_REUSEADDR): %s",
|
|
__FILE__, __LINE__, acl_last_serror());
|
|
}
|
|
|
|
#if defined(SO_REUSEPORT)
|
|
on = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
|
|
(const void *) &on, sizeof(on)) < 0)
|
|
{
|
|
acl_msg_warn("%s(%d): setsocket(SO_REUSEPORT): %s",
|
|
__FILE__, __LINE__, acl_last_serror());
|
|
}
|
|
#endif
|
|
|
|
#ifdef ACL_WINDOWS
|
|
if (bind(fd, it->ai_addr, (int) it->ai_addrlen) == 0)
|
|
#else
|
|
if (bind(fd, it->ai_addr, it->ai_addrlen) == 0)
|
|
#endif
|
|
{
|
|
*res = it;
|
|
return fd;
|
|
}
|
|
|
|
acl_msg_error("%s(%d): bind error %s",
|
|
__FILE__, __LINE__, acl_last_serror());
|
|
acl_socket_close(fd);
|
|
}
|
|
|
|
return ACL_SOCKET_INVALID;
|
|
}
|
|
|
|
static SOCK_UDP *sock_open(const char *addr)
|
|
{
|
|
SOCK_UDP *sock;
|
|
struct addrinfo *res0, *res;
|
|
int fd;
|
|
|
|
res0 = host_addrinfo(addr);
|
|
if (res0 == NULL)
|
|
return NULL;
|
|
|
|
fd = bind_addr(res0, &res);
|
|
if (fd == ACL_SOCKET_INVALID) {
|
|
acl_msg_error("%s(%d): invalid socket", __FILE__, __LINE__);
|
|
freeaddrinfo(res0);
|
|
return NULL;
|
|
}
|
|
|
|
sock = (SOCK_UDP *) acl_mycalloc(1, sizeof(SOCK_UDP));
|
|
sock->fd = fd;
|
|
memcpy(&sock->sa_local, res->ai_addr, res->ai_addrlen);
|
|
sock->sa_local_len = res->ai_addrlen;
|
|
|
|
freeaddrinfo(res0);
|
|
return sock;
|
|
}
|
|
|
|
SOCK_UDP *udp_client_open(const char *local, const char *peer)
|
|
{
|
|
struct addrinfo *peer_res0 = host_addrinfo(peer);
|
|
SOCK_UDP *sock;
|
|
|
|
if (peer_res0 == NULL)
|
|
return NULL;
|
|
|
|
sock = sock_open(local);
|
|
if (sock == NULL) {
|
|
freeaddrinfo(peer_res0);
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(&sock->sa_peer, peer_res0->ai_addr, peer_res0->ai_addrlen);
|
|
sock->sa_peer_len = peer_res0->ai_addrlen;
|
|
freeaddrinfo(peer_res0);
|
|
return sock;
|
|
}
|
|
|
|
SOCK_UDP *udp_server_open(const char *local)
|
|
{
|
|
return sock_open(local);
|
|
}
|
|
|
|
void udp_close(SOCK_UDP *sock)
|
|
{
|
|
acl_socket_close(sock->fd);
|
|
acl_myfree(sock);
|
|
}
|
|
|
|
int udp_read(SOCK_UDP *sock, void *buf, size_t size)
|
|
{
|
|
ssize_t ret;
|
|
|
|
sock->sa_peer_len = sizeof(sock->sa_peer);
|
|
ret = recvfrom(sock->fd, buf, size, 0,
|
|
(struct sockaddr *) &sock->sa_peer, &sock->sa_peer_len);
|
|
return ret;
|
|
}
|
|
|
|
int udp_send(SOCK_UDP *sock, const void *data, size_t len)
|
|
{
|
|
ssize_t ret = sendto(sock->fd, data, len, 0,
|
|
(struct sockaddr *) &sock->sa_peer, sock->sa_peer_len);
|
|
return ret;
|
|
}
|
|
|
|
void udp_pkt_set_buf(SOCK_PKT *pkt, char *buf, size_t len)
|
|
{
|
|
pkt->iov.iov_base = buf;
|
|
pkt->iov.iov_len = len;
|
|
pkt->addr_len = (socklen_t) sizeof(SOCK_ADDR);
|
|
}
|
|
|
|
int udp_pkt_set_peer(SOCK_PKT *pkt, const char *addr)
|
|
{
|
|
struct addrinfo *peer_res0 = host_addrinfo(addr);
|
|
|
|
if (peer_res0 == NULL)
|
|
return -1;
|
|
memcpy(&pkt->addr, peer_res0->ai_addr, peer_res0->ai_addrlen);
|
|
pkt->addr_len = peer_res0->ai_addrlen;
|
|
freeaddrinfo(peer_res0);
|
|
return 0;
|
|
}
|
|
|
|
int udp_mread(SOCK_UDP *sock, SOCK_PKT pkts[], size_t pkts_cnt)
|
|
{
|
|
unsigned int flags = MSG_WAITFORONE /* | MSG_DONTWAIT */;
|
|
size_t i;
|
|
|
|
if (sock->msgvec == NULL) {
|
|
sock->vlen = pkts_cnt;
|
|
sock->msgvec = (struct mmsghdr *)
|
|
acl_mycalloc(pkts_cnt, sizeof(struct mmsghdr));
|
|
} else if (sock->vlen < pkts_cnt) {
|
|
acl_myfree(sock->msgvec);
|
|
sock->vlen = pkts_cnt;
|
|
sock->msgvec = (struct mmsghdr *)
|
|
acl_mycalloc(pkts_cnt, sizeof(struct mmsghdr));
|
|
}
|
|
|
|
sock->pkts = pkts;
|
|
sock->pkts_cnt = pkts_cnt;
|
|
|
|
memset(sock->msgvec, 0, sizeof(struct mmsghdr) * pkts_cnt);
|
|
|
|
for (i = 0; i < pkts_cnt; i++) {
|
|
sock->msgvec[i].msg_hdr.msg_iov = &pkts[i].iov;
|
|
sock->msgvec[i].msg_hdr.msg_iovlen = 1;
|
|
sock->msgvec[i].msg_hdr.msg_name = &pkts[i].addr;
|
|
sock->msgvec[i].msg_hdr.msg_namelen = sizeof(pkts[i].addr);
|
|
sock->msgvec[i].msg_len = 0;
|
|
}
|
|
|
|
return recvmmsg(sock->fd, sock->msgvec, pkts_cnt, flags, NULL);
|
|
}
|
|
|
|
int udp_msend(SOCK_UDP *sock, SOCK_PKT pkts[], size_t pkts_cnt)
|
|
{
|
|
size_t i;
|
|
unsigned int flags = 0;
|
|
#ifndef __linux3__
|
|
int n = 0;
|
|
#endif
|
|
|
|
if (sock->msgvec == NULL) {
|
|
sock->vlen = pkts_cnt;
|
|
sock->msgvec = (struct mmsghdr *)
|
|
acl_mycalloc(pkts_cnt, sizeof(struct mmsghdr));
|
|
} else if (sock->vlen < pkts_cnt) {
|
|
acl_myfree(sock->msgvec);
|
|
sock->vlen = pkts_cnt;
|
|
sock->msgvec = (struct mmsghdr *)
|
|
acl_mycalloc(pkts_cnt, sizeof(struct mmsghdr));
|
|
}
|
|
|
|
sock->pkts = pkts;
|
|
sock->pkts_cnt = pkts_cnt;
|
|
|
|
memset(sock->msgvec, 0, sizeof(struct mmsghdr) * pkts_cnt);
|
|
|
|
for (i = 0; i < pkts_cnt; i++) {
|
|
sock->msgvec[i].msg_hdr.msg_iov = &pkts[i].iov;
|
|
sock->msgvec[i].msg_hdr.msg_iovlen = 1;
|
|
sock->msgvec[i].msg_hdr.msg_name = &pkts[i].addr;
|
|
sock->msgvec[i].msg_hdr.msg_namelen = sizeof(pkts[i].addr);
|
|
sock->msgvec[i].msg_len = 0;
|
|
#ifndef __linux3__
|
|
if (sendmsg(sock->fd, &sock->msgvec[i].msg_hdr, flags) < 0)
|
|
return -1;
|
|
n++;
|
|
#endif
|
|
}
|
|
|
|
#ifdef __linux3__
|
|
return sendmmsg(sock->fd, sock->msgvec, pkts_cnt, flags);
|
|
#else
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
int pkt_port(SOCK_PKT *pkt)
|
|
{
|
|
if (pkt->addr.sa.sa.sa_family == AF_INET) {
|
|
struct sockaddr_in *in = &pkt->addr.sa.in;
|
|
return ntohs(in->sin_port);
|
|
}
|
|
#ifdef AF_INET6
|
|
else if (pkt->addr.sa.sa.sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *in = &pkt->addr.sa.in6;
|
|
return ntohl(in->sin6_port);
|
|
}
|
|
#endif
|
|
else {
|
|
acl_msg_error("%s(%d): unkown sa_family=%d",
|
|
__FUNCTION__, __LINE__, pkt->addr.sa.sa.sa_family);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int udp_port(SOCK_UDP *sock, size_t i)
|
|
{
|
|
if (sock->msgvec == NULL) {
|
|
acl_msg_error("%s(%d): msgvec NULL", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
if (sock->pkts == NULL) {
|
|
acl_msg_error("%s(%d): pkts NULL", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
if (sock->pkts_cnt == 0) {
|
|
acl_msg_error("%s(%d): pkts_cnt 0", __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
if (i >= sock->pkts_cnt) {
|
|
acl_msg_error("%s(%d): invalid i=%d >= %d",
|
|
__FUNCTION__, __LINE__, (int) i, (int) sock->pkts_cnt);
|
|
return -1;
|
|
}
|
|
|
|
return pkt_port(&sock->pkts[i]);
|
|
}
|
|
|
|
const char *pkt_ip(SOCK_PKT *pkt, char *buf, size_t size)
|
|
{
|
|
if (pkt->addr.sa.sa.sa_family == AF_INET) {
|
|
struct sockaddr_in *in = &pkt->addr.sa.in;
|
|
return inet_ntop(in->sin_family, &in->sin_addr, buf, size);
|
|
}
|
|
#ifdef AF_INET6
|
|
else if (pkt->addr.sa.sa.sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *in = &pkt->addr.sa.in6;
|
|
return inet_ntop(in->sin6_family, &in->sin6_addr, buf, size);
|
|
}
|
|
#endif
|
|
else {
|
|
acl_msg_error("%s(%d): unkown sa_family=%d",
|
|
__FUNCTION__, __LINE__, pkt->addr.sa.sa.sa_family);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const char *udp_ip(SOCK_UDP *sock, size_t i, char *buf, size_t size)
|
|
{
|
|
if (sock->msgvec == NULL) {
|
|
acl_msg_error("%s(%d): msgvec NULL", __FUNCTION__, __LINE__);
|
|
return NULL;
|
|
}
|
|
if (sock->pkts == NULL) {
|
|
acl_msg_error("%s(%d): pkts NULL", __FUNCTION__, __LINE__);
|
|
return NULL;
|
|
}
|
|
if (sock->pkts_cnt == 0) {
|
|
acl_msg_error("%s(%d): pkts_cnt 0", __FUNCTION__, __LINE__);
|
|
return NULL;
|
|
}
|
|
if (i >= sock->pkts_cnt) {
|
|
acl_msg_error("%s(%d): invalid i=%d >= %d",
|
|
__FUNCTION__, __LINE__, (int) i, (int) sock->pkts_cnt);
|
|
return NULL;
|
|
}
|
|
|
|
return pkt_ip(&sock->pkts[i], buf, size);
|
|
}
|
|
|