mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
875 lines
22 KiB
875 lines
22 KiB
#include "socket.h"
|
|
#include <glib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip6.h>
|
|
#include <netinet/udp.h>
|
|
#include <sys/socket.h>
|
|
#include "str.h"
|
|
#include "xt_RTPENGINE.h"
|
|
#include "log.h"
|
|
|
|
static int __ip4_addr_parse(sockaddr_t *dst, const char *src);
|
|
static int __ip6_addr_parse(sockaddr_t *dst, const char *src);
|
|
static int __ip4_addr_print(const sockaddr_t *a, char *buf, size_t len);
|
|
static int __ip6_addr_print(const sockaddr_t *a, char *buf, size_t len);
|
|
static int __ip6_addr_print_p(const sockaddr_t *a, char *buf, size_t len);
|
|
static unsigned int __ip4_hash(const sockaddr_t *a);
|
|
static unsigned int __ip6_hash(const sockaddr_t *a);
|
|
static int __ip4_eq(const sockaddr_t *a, const sockaddr_t *b);
|
|
static int __ip6_eq(const sockaddr_t *a, const sockaddr_t *b);
|
|
static int __ip4_is_specified(const sockaddr_t *a);
|
|
static int __ip6_is_specified(const sockaddr_t *a);
|
|
static int __ip_bind(socket_t *s, unsigned int, const sockaddr_t *);
|
|
static int __ip_connect(socket_t *s, const endpoint_t *);
|
|
static int __ip_listen(socket_t *s, int backlog);
|
|
static int __ip_accept(socket_t *s, socket_t *new_sock);
|
|
static int __ip_timestamping(socket_t *s);
|
|
static int __ip4_sockaddr2endpoint(endpoint_t *, const void *);
|
|
static int __ip6_sockaddr2endpoint(endpoint_t *, const void *);
|
|
static int __ip4_endpoint2sockaddr(void *, const endpoint_t *);
|
|
static int __ip6_endpoint2sockaddr(void *, const endpoint_t *);
|
|
static int __ip4_addrport2sockaddr(void *, const sockaddr_t *, unsigned int);
|
|
static int __ip6_addrport2sockaddr(void *, const sockaddr_t *, unsigned int);
|
|
static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep);
|
|
static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *);
|
|
static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep);
|
|
static ssize_t __ip_sendto(socket_t *s, const void *buf, size_t len, const endpoint_t *ep);
|
|
static int __ip4_tos(socket_t *, unsigned int);
|
|
static int __ip6_tos(socket_t *, unsigned int);
|
|
static int __ip_error(socket_t *s);
|
|
static void __ip4_pmtu_disc(socket_t *, int);
|
|
static void __ip4_endpoint2kernel(struct re_address *, const endpoint_t *);
|
|
static void __ip6_endpoint2kernel(struct re_address *, const endpoint_t *);
|
|
static void __ip4_kernel2endpoint(endpoint_t *ep, const struct re_address *ra);
|
|
static void __ip6_kernel2endpoint(endpoint_t *ep, const struct re_address *ra);
|
|
static unsigned int __ip4_packet_header(unsigned char *, const endpoint_t *, const endpoint_t *,
|
|
unsigned int);
|
|
static unsigned int __ip6_packet_header(unsigned char *, const endpoint_t *, const endpoint_t *,
|
|
unsigned int);
|
|
|
|
|
|
|
|
static socktype_t __socket_types[] = {
|
|
{
|
|
.name = "udp",
|
|
.name_uc = "UDP",
|
|
},
|
|
};
|
|
|
|
static struct socket_family __socket_families[__SF_LAST] = {
|
|
[SF_IP4] = {
|
|
.af = AF_INET,
|
|
.ethertype = 0x0800,
|
|
.sockaddr_size = sizeof(struct sockaddr_in),
|
|
.name = "IPv4",
|
|
.rfc_name = "IP4",
|
|
.unspec_string = "0.0.0.0",
|
|
.hash = __ip4_hash,
|
|
.eq = __ip4_eq,
|
|
.addr_parse = __ip4_addr_parse,
|
|
.addr_print = __ip4_addr_print,
|
|
.addr_print_p = __ip4_addr_print,
|
|
.is_specified = __ip4_is_specified,
|
|
.sockaddr2endpoint = __ip4_sockaddr2endpoint,
|
|
.endpoint2sockaddr = __ip4_endpoint2sockaddr,
|
|
.addrport2sockaddr = __ip4_addrport2sockaddr,
|
|
.bind = __ip_bind,
|
|
.connect = __ip_connect,
|
|
.listen = __ip_listen,
|
|
.accept = __ip_accept,
|
|
.timestamping = __ip_timestamping,
|
|
.recvfrom = __ip_recvfrom,
|
|
.recvfrom_ts = __ip_recvfrom_ts,
|
|
.sendmsg = __ip_sendmsg,
|
|
.sendto = __ip_sendto,
|
|
.tos = __ip4_tos,
|
|
.error = __ip_error,
|
|
.pmtu_disc = __ip4_pmtu_disc,
|
|
.endpoint2kernel = __ip4_endpoint2kernel,
|
|
.kernel2endpoint = __ip4_kernel2endpoint,
|
|
.packet_header = __ip4_packet_header,
|
|
},
|
|
[SF_IP6] = {
|
|
.af = AF_INET6,
|
|
.ethertype = 0x86dd,
|
|
.sockaddr_size = sizeof(struct sockaddr_in6),
|
|
.name = "IPv6",
|
|
.rfc_name = "IP6",
|
|
.unspec_string = "::",
|
|
.hash = __ip6_hash,
|
|
.eq = __ip6_eq,
|
|
.addr_parse = __ip6_addr_parse,
|
|
.addr_print = __ip6_addr_print,
|
|
.addr_print_p = __ip6_addr_print_p,
|
|
.is_specified = __ip6_is_specified,
|
|
.sockaddr2endpoint = __ip6_sockaddr2endpoint,
|
|
.endpoint2sockaddr = __ip6_endpoint2sockaddr,
|
|
.addrport2sockaddr = __ip6_addrport2sockaddr,
|
|
.bind = __ip_bind,
|
|
.connect = __ip_connect,
|
|
.listen = __ip_listen,
|
|
.accept = __ip_accept,
|
|
.timestamping = __ip_timestamping,
|
|
.recvfrom = __ip_recvfrom,
|
|
.recvfrom_ts = __ip_recvfrom_ts,
|
|
.sendmsg = __ip_sendmsg,
|
|
.sendto = __ip_sendto,
|
|
.tos = __ip6_tos,
|
|
.error = __ip_error,
|
|
.endpoint2kernel = __ip6_endpoint2kernel,
|
|
.kernel2endpoint = __ip6_kernel2endpoint,
|
|
.packet_header = __ip6_packet_header,
|
|
},
|
|
};
|
|
|
|
|
|
|
|
socktype_t *socktype_udp;
|
|
|
|
|
|
|
|
|
|
static int __ip4_addr_parse(sockaddr_t *dst, const char *src) {
|
|
if (inet_pton(AF_INET, src, &dst->u.ipv4) == 1)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
static int __ip6_addr_parse(sockaddr_t *dst, const char *src) {
|
|
if (src[0] != '[') {
|
|
if (inet_pton(AF_INET6, src, &dst->u.ipv6) == 1)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
const char *ep;
|
|
ep = strchr(src, ']');
|
|
if (!ep)
|
|
return -1;
|
|
|
|
unsigned int len = ep - src - 1;
|
|
char buf[64];
|
|
memcpy(buf, src+1, len);
|
|
buf[len] = '\0';
|
|
|
|
if (inet_pton(AF_INET6, buf, &dst->u.ipv6) == 1)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
static int __ip4_addr_print(const sockaddr_t *a, char *buf, size_t len) {
|
|
buf[0] = '\0';
|
|
if (!inet_ntop(AF_INET, &a->u.ipv4, buf, len))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
static int __ip6_addr_print(const sockaddr_t *a, char *buf, size_t len) {
|
|
buf[0] = '\0';
|
|
if (!inet_ntop(AF_INET6, &a->u.ipv6, buf, len))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
static int __ip6_addr_print_p(const sockaddr_t *a, char *buf, size_t len) {
|
|
buf[0] = '\0';
|
|
if (!inet_ntop(AF_INET6, &a->u.ipv6, buf+1, len-2))
|
|
return -1;
|
|
buf[0] = '[';
|
|
strcpy(buf + strlen(buf), "]");
|
|
return 0;
|
|
}
|
|
static unsigned int __ip4_hash(const sockaddr_t *a) {
|
|
return a->u.ipv4.s_addr;
|
|
}
|
|
static unsigned int __ip6_hash(const sockaddr_t *a) {
|
|
return in6_addr_hash(&a->u.ipv6);
|
|
}
|
|
static int __ip4_eq(const sockaddr_t *a, const sockaddr_t *b) {
|
|
return !memcmp(&a->u.ipv4, &b->u.ipv4, sizeof(a->u.ipv4));
|
|
}
|
|
static int __ip6_eq(const sockaddr_t *a, const sockaddr_t *b) {
|
|
return !memcmp(&a->u.ipv6, &b->u.ipv6, sizeof(a->u.ipv6));
|
|
}
|
|
static int __ip4_is_specified(const sockaddr_t *a) {
|
|
return a->u.ipv4.s_addr != 0;
|
|
}
|
|
static int __ip6_is_specified(const sockaddr_t *a) {
|
|
return a->u.ipv6.s6_addr32[0] != 0
|
|
|| a->u.ipv6.s6_addr32[1] != 0
|
|
|| a->u.ipv6.s6_addr32[2] != 0
|
|
|| a->u.ipv6.s6_addr32[3] != 0;
|
|
}
|
|
static int __ip4_sockaddr2endpoint(endpoint_t *ep, const void *p) {
|
|
const struct sockaddr_in *sin = p;
|
|
if (sin->sin_family != AF_INET)
|
|
return -1;
|
|
ZERO(*ep);
|
|
ep->address.family = &__socket_families[SF_IP4];
|
|
ep->address.u.ipv4 = sin->sin_addr;
|
|
ep->port = ntohs(sin->sin_port);
|
|
return 0;
|
|
}
|
|
static int __ip6_sockaddr2endpoint(endpoint_t *ep, const void *p) {
|
|
const struct sockaddr_in6 *sin = p;
|
|
if (sin->sin6_family != AF_INET6)
|
|
return -1;
|
|
ZERO(*ep);
|
|
ep->address.family = &__socket_families[SF_IP6];
|
|
ep->address.u.ipv6 = sin->sin6_addr;
|
|
ep->port = ntohs(sin->sin6_port);
|
|
return 0;
|
|
}
|
|
void endpoint_parse_sockaddr_storage(endpoint_t *ep, struct sockaddr_storage *sa) {
|
|
if (sa->ss_family == AF_INET)
|
|
__ip4_sockaddr2endpoint(ep, sa);
|
|
else if (sa->ss_family == AF_INET6)
|
|
__ip6_sockaddr2endpoint(ep, sa);
|
|
else
|
|
abort();
|
|
}
|
|
static int __ip4_endpoint2sockaddr(void *p, const endpoint_t *ep) {
|
|
return __ip4_addrport2sockaddr(p, &ep->address, ep->port);
|
|
}
|
|
static int __ip6_endpoint2sockaddr(void *p, const endpoint_t *ep) {
|
|
return __ip6_addrport2sockaddr(p, &ep->address, ep->port);
|
|
}
|
|
static int __ip4_addrport2sockaddr(void *p, const sockaddr_t *sa, unsigned int port) {
|
|
struct sockaddr_in *sin = p;
|
|
ZERO(*sin);
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_port = htons(port);
|
|
if (sa)
|
|
sin->sin_addr = sa->u.ipv4;
|
|
return 0;
|
|
}
|
|
static int __ip6_addrport2sockaddr(void *p, const sockaddr_t *sa, unsigned int port) {
|
|
struct sockaddr_in6 *sin = p;
|
|
ZERO(*sin);
|
|
sin->sin6_family = AF_INET6;
|
|
sin->sin6_port = htons(port);
|
|
if (sa)
|
|
sin->sin6_addr = sa->u.ipv6;
|
|
return 0;
|
|
}
|
|
static int __ip_bind(socket_t *s, unsigned int port, const sockaddr_t *a) {
|
|
struct sockaddr_storage sin;
|
|
|
|
s->family->addrport2sockaddr(&sin, a, port);
|
|
if (bind(s->fd, (struct sockaddr *) &sin, s->family->sockaddr_size)) {
|
|
__C_DBG("bind fail, fd=%d, port=%d", s->fd, s->local.port);
|
|
return -1;
|
|
} else {
|
|
__C_DBG("bind success, fd=%d, port=%d", s->fd, s->local.port);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static int __ip_connect(socket_t *s, const endpoint_t *ep) {
|
|
struct sockaddr_storage sin;
|
|
|
|
s->family->endpoint2sockaddr(&sin, ep);
|
|
if (connect(s->fd, (struct sockaddr *) &sin, s->family->sockaddr_size)) {
|
|
__C_DBG("connect fail, fd=%d, port=%d", s->fd, s->local.port);
|
|
return -1;
|
|
} else {
|
|
__C_DBG("connect success, fd=%d, port=%d", s->fd, s->local.port);
|
|
}
|
|
return 0;
|
|
}
|
|
static int __ip_listen(socket_t *s, int backlog) {
|
|
return listen(s->fd, backlog);
|
|
}
|
|
static int __ip_accept(socket_t *s, socket_t *newsock) {
|
|
int nfd;
|
|
struct sockaddr_storage sin;
|
|
socklen_t sinlen;
|
|
|
|
ZERO(*newsock);
|
|
|
|
sinlen = sizeof(sin);
|
|
nfd = accept(s->fd, (struct sockaddr *) &sin, &sinlen);
|
|
if (nfd == -1) {
|
|
__C_DBG("accept fail, fd=%d, port=%d", s->fd, s->local.port);
|
|
return -1;
|
|
}
|
|
|
|
newsock->fd = nfd;
|
|
newsock->family = s->family;
|
|
newsock->local = s->local;
|
|
s->family->sockaddr2endpoint(&newsock->remote, &sin);
|
|
|
|
return 0;
|
|
}
|
|
static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *tv) {
|
|
ssize_t ret;
|
|
struct sockaddr_storage sin;
|
|
struct msghdr msg;
|
|
struct iovec iov;
|
|
char ctrl[64];
|
|
struct cmsghdr *cm;
|
|
|
|
ZERO(msg);
|
|
msg.msg_name = &sin;
|
|
msg.msg_namelen = s->family->sockaddr_size;
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = ctrl;
|
|
msg.msg_controllen = sizeof(ctrl);
|
|
ZERO(iov);
|
|
iov.iov_base = buf;
|
|
iov.iov_len = len;
|
|
|
|
ret = recvmsg(s->fd, &msg, 0);
|
|
if (ret < 0)
|
|
return ret;
|
|
s->family->sockaddr2endpoint(ep, &sin);
|
|
|
|
if (tv) {
|
|
for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
|
|
if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
|
|
*tv = *((struct timeval *) CMSG_DATA(cm));
|
|
tv = NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (G_UNLIKELY(tv)) {
|
|
ilog(LOG_WARNING, "No receive timestamp received from kernel");
|
|
ZERO(*tv);
|
|
}
|
|
}
|
|
if (G_UNLIKELY((msg.msg_flags & MSG_TRUNC)))
|
|
ilog(LOG_WARNING, "Kernel indicates that data was truncated");
|
|
if (G_UNLIKELY((msg.msg_flags & MSG_CTRUNC)))
|
|
ilog(LOG_WARNING, "Kernel indicates that ancillary data was truncated");
|
|
|
|
return ret;
|
|
}
|
|
static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep) {
|
|
return __ip_recvfrom_ts(s, buf, len, ep, NULL);
|
|
}
|
|
static ssize_t __ip_sendmsg(socket_t *s, struct msghdr *mh, const endpoint_t *ep) {
|
|
struct sockaddr_storage sin;
|
|
|
|
s->family->endpoint2sockaddr(&sin, ep);
|
|
mh->msg_name = &sin;
|
|
mh->msg_namelen = s->family->sockaddr_size;
|
|
|
|
return sendmsg(s->fd, mh, 0);
|
|
}
|
|
static ssize_t __ip_sendto(socket_t *s, const void *buf, size_t len, const endpoint_t *ep) {
|
|
struct sockaddr_storage sin;
|
|
|
|
ep->address.family->endpoint2sockaddr(&sin, ep);
|
|
return sendto(s->fd, buf, len, 0, (void *) &sin, ep->address.family->sockaddr_size);
|
|
}
|
|
static int __ip4_tos(socket_t *s, unsigned int tos) {
|
|
unsigned char ctos;
|
|
ctos = tos;
|
|
if (setsockopt(s->fd, IPPROTO_IP, IP_TOS, &ctos, sizeof(ctos)))
|
|
ilog(LOG_ERR, "Failed to set TOS on IPv4 socket: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
static int __ip6_tos(socket_t *s, unsigned int tos) {
|
|
if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)))
|
|
ilog(LOG_ERR, "Failed to set TOS on IPv6 socket: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
static int __ip_error(socket_t *s) {
|
|
int optval;
|
|
socklen_t optlen = sizeof(optval);
|
|
if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen))
|
|
return -1;
|
|
return optval;
|
|
}
|
|
static void __ip4_pmtu_disc(socket_t *s, int opt) {
|
|
if (setsockopt(s->fd, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)))
|
|
ilog(LOG_ERR, "Failed to set PMTU discovery option on IPv4 socket: %s", strerror(errno));
|
|
}
|
|
static int __ip_timestamping(socket_t *s) {
|
|
int one = 1;
|
|
if (setsockopt(s->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
static void __ip4_endpoint2kernel(struct re_address *ra, const endpoint_t *ep) {
|
|
ZERO(*ra);
|
|
ra->family = AF_INET;
|
|
ra->u.ipv4 = ep->address.u.ipv4.s_addr;
|
|
ra->port = ep->port;
|
|
}
|
|
static void __ip6_endpoint2kernel(struct re_address *ra, const endpoint_t *ep) {
|
|
ZERO(*ra);
|
|
ra->family = AF_INET6;
|
|
memcpy(ra->u.ipv6, &ep->address.u.ipv6, sizeof(ra->u.ipv6));
|
|
ra->port = ep->port;
|
|
}
|
|
void kernel2endpoint(endpoint_t *ep, const struct re_address *ra) {
|
|
ZERO(*ep);
|
|
if (ra->family == AF_INET)
|
|
ep->address.family = __get_socket_family_enum(SF_IP4);
|
|
else if (ra->family == AF_INET6)
|
|
ep->address.family = __get_socket_family_enum(SF_IP6);
|
|
else
|
|
abort();
|
|
ep->port = ra->port;
|
|
ep->address.family->kernel2endpoint(ep, ra);
|
|
}
|
|
static void __ip4_kernel2endpoint(endpoint_t *ep, const struct re_address *ra) {
|
|
ep->address.u.ipv4.s_addr = ra->u.ipv4;
|
|
}
|
|
static void __ip6_kernel2endpoint(endpoint_t *ep, const struct re_address *ra) {
|
|
memcpy(&ep->address.u.ipv6, ra->u.ipv6, sizeof(ep->address.u.ipv6));
|
|
}
|
|
static unsigned int __udp_packet_header(unsigned char *out, unsigned int src, unsigned int dst,
|
|
unsigned int payload_len)
|
|
{
|
|
struct udphdr *udp = (void *) out;
|
|
|
|
ZERO(*udp);
|
|
udp->source = htons(src);
|
|
udp->dest = htons(dst);
|
|
udp->len = htons(sizeof(*udp) + payload_len);
|
|
return sizeof(*udp);
|
|
}
|
|
static unsigned int __ip4_packet_header(unsigned char *out, const endpoint_t *src, const endpoint_t *dst,
|
|
unsigned int payload_len)
|
|
{
|
|
struct iphdr *iph = (void *) out;
|
|
unsigned char *nxt = (void *) out + sizeof(*iph);
|
|
|
|
unsigned int udp_len = __udp_packet_header(nxt, src->port, dst->port, payload_len);
|
|
|
|
ZERO(*iph);
|
|
iph->ihl = sizeof(*iph) >> 2; // normally 5 ~ 20 bytes
|
|
iph->version = 4;
|
|
iph->tot_len = htons(sizeof(*iph) + udp_len + payload_len);
|
|
iph->ttl = 64;
|
|
iph->protocol = 17; // UDP
|
|
iph->saddr = src->address.u.ipv4.s_addr;
|
|
iph->daddr = dst->address.u.ipv4.s_addr;
|
|
|
|
return sizeof(*iph) + udp_len;
|
|
}
|
|
static unsigned int __ip6_packet_header(unsigned char *out, const endpoint_t *src, const endpoint_t *dst,
|
|
unsigned int payload_len)
|
|
{
|
|
struct ip6_hdr *iph = (void *) out;
|
|
unsigned char *nxt = (void *) out + sizeof(*iph);
|
|
|
|
unsigned int udp_len = __udp_packet_header(nxt, src->port, dst->port, payload_len);
|
|
|
|
ZERO(*iph);
|
|
iph->ip6_vfc = 0x60; // version 6;
|
|
//iph->ip6_flow = htonl(0x60000000); // version 6
|
|
iph->ip6_plen = htons(udp_len + payload_len);
|
|
iph->ip6_nxt = 17; // UDP
|
|
iph->ip6_hlim = 64;
|
|
iph->ip6_src = src->address.u.ipv6;
|
|
iph->ip6_dst = dst->address.u.ipv6;
|
|
|
|
return sizeof(*iph) + udp_len;
|
|
}
|
|
|
|
|
|
|
|
unsigned int sockaddr_hash(const sockaddr_t *a) {
|
|
return a->family->hash(a) ^ g_direct_hash(a->family);
|
|
}
|
|
bool sockaddr_eq(const sockaddr_t *a, const sockaddr_t *b) {
|
|
return a->family == b->family && a->family->eq(a, b);
|
|
}
|
|
guint sockaddr_t_hash(gconstpointer a) {
|
|
return sockaddr_hash(a);
|
|
}
|
|
gint sockaddr_t_eq(gconstpointer a, gconstpointer b) {
|
|
return sockaddr_eq(a, b);
|
|
}
|
|
|
|
|
|
unsigned int endpoint_hash(const endpoint_t *a) {
|
|
return sockaddr_hash(&a->address) ^ a->port;
|
|
}
|
|
bool endpoint_eq(const endpoint_t *a, const endpoint_t *b) {
|
|
return sockaddr_eq(&a->address, &b->address) && a->port == b->port;
|
|
}
|
|
guint endpoint_t_hash(gconstpointer a) {
|
|
return endpoint_hash(a);
|
|
}
|
|
gint endpoint_t_eq(gconstpointer a, const void *b) {
|
|
return endpoint_eq(a, b);
|
|
}
|
|
|
|
|
|
|
|
int sockaddr_parse_any(sockaddr_t *dst, const char *src) {
|
|
int i;
|
|
sockfamily_t *fam;
|
|
|
|
for (i = 0; i < __SF_LAST; i++) {
|
|
fam = &__socket_families[i];
|
|
if (!fam->addr_parse(dst, src)) {
|
|
dst->family = fam;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
int sockaddr_parse_any_str(sockaddr_t *dst, const str *src) {
|
|
char buf[64];
|
|
if (!src || !src->s)
|
|
return -1;
|
|
if (src->len >= sizeof(buf))
|
|
return -1;
|
|
sprintf(buf, STR_FORMAT, STR_FMT(src));
|
|
return sockaddr_parse_any(dst, buf);
|
|
}
|
|
int sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src) {
|
|
char buf[64];
|
|
if (src->len >= sizeof(buf))
|
|
return -1;
|
|
if (!fam)
|
|
return -1;
|
|
sprintf(buf, STR_FORMAT, STR_FMT(src));
|
|
dst->family = fam;
|
|
return fam->addr_parse(dst, buf);
|
|
}
|
|
sockfamily_t *get_socket_family_rfc(const str *s) {
|
|
int i;
|
|
sockfamily_t *fam;
|
|
|
|
for (i = 0; i < __SF_LAST; i++) {
|
|
fam = &__socket_families[i];
|
|
if (!str_cmp(s, fam->rfc_name))
|
|
return fam;
|
|
}
|
|
return NULL;
|
|
}
|
|
sockfamily_t *__get_socket_family_enum(enum socket_families i) {
|
|
return &__socket_families[i];
|
|
}
|
|
int endpoint_parse_any(endpoint_t *d, const char *s) {
|
|
int i;
|
|
sockfamily_t *fam;
|
|
unsigned int len;
|
|
const char *ep;
|
|
char buf[64];
|
|
|
|
ep = strrchr(s, ':');
|
|
if (!ep) {
|
|
if (strchr(s, '.'))
|
|
return -1;
|
|
/* just a port number */
|
|
d->port = atoi(s);
|
|
ZERO(d->address);
|
|
d->address.family = __get_socket_family_enum(SF_IP4);
|
|
return 0;
|
|
}
|
|
len = ep - s;
|
|
if (len >= sizeof(buf))
|
|
return -1;
|
|
d->port = atoi(ep+1);
|
|
if (d->port > 0xffff)
|
|
return -1;
|
|
sprintf(buf, "%.*s", len, s);
|
|
|
|
for (i = 0; i < __SF_LAST; i++) {
|
|
fam = &__socket_families[i];
|
|
if (!fam->addr_parse(&d->address, buf)) {
|
|
d->address.family = fam;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int socket_addrinfo_convert(sockaddr_t *a, struct addrinfo *res) {
|
|
if (res->ai_family == AF_INET) { // IPv4
|
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *) res->ai_addr;
|
|
a->u.ipv4 = ipv4->sin_addr;
|
|
a->family = &__socket_families[SF_IP4];
|
|
}
|
|
else if (res->ai_family == AF_INET6) {
|
|
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) res->ai_addr;
|
|
a->u.ipv6 = ipv6->sin6_addr;
|
|
a->family = &__socket_families[SF_IP6];
|
|
}
|
|
else
|
|
return -1;
|
|
return 0;
|
|
}
|
|
int sockaddr_getaddrinfo_alt(sockaddr_t *a, sockaddr_t *a2, const char *s) {
|
|
struct addrinfo hints, *res;
|
|
int status;
|
|
int ret;
|
|
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_protocol = IPPROTO_UDP;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
if ((status = getaddrinfo(s, NULL, &hints, &res)) != 0) {
|
|
__C_DBG("getaddrinfo failed for %s, status is \"%s\"\n", s, gai_strerror(status));
|
|
return -1;
|
|
}
|
|
|
|
ret = socket_addrinfo_convert(a, res);
|
|
|
|
if (a2) {
|
|
if (ret == 0 && res->ai_next) {
|
|
struct addrinfo *next = res->ai_next;
|
|
ret = socket_addrinfo_convert(a2, next);
|
|
}
|
|
else
|
|
ZERO(*a2);
|
|
}
|
|
|
|
freeaddrinfo(res);
|
|
return ret;
|
|
}
|
|
|
|
int endpoint_parse_any_getaddrinfo_alt(endpoint_t *d, endpoint_t *d2, const char *s) {
|
|
unsigned int len;
|
|
const char *ep;
|
|
char buf[64];
|
|
|
|
ep = strrchr(s, ':');
|
|
if (!ep) {
|
|
if (strchr(s, '.'))
|
|
return -1;
|
|
/* just a port number */
|
|
d->port = atoi(s);
|
|
ZERO(d->address);
|
|
d->address.family = __get_socket_family_enum(SF_IP4);
|
|
if (d2) {
|
|
ZERO(*d2);
|
|
*d2 = *d;
|
|
ipv46_any_convert(d2);
|
|
}
|
|
return 0;
|
|
}
|
|
len = ep - s;
|
|
if (len >= sizeof(buf))
|
|
return -1;
|
|
d->port = atoi(ep+1);
|
|
if (d->port > 0xffff)
|
|
return -1;
|
|
|
|
/* original s was [IPv6]:port */
|
|
if ((len > 2) && (s[0] == '[') && (s[len - 1] == ']')) {
|
|
sprintf(buf, "%.*s", len - 2, s + 1);
|
|
} else {
|
|
sprintf(buf, "%.*s", len, s);
|
|
}
|
|
|
|
if (sockaddr_getaddrinfo_alt(&d->address, d2 ? &d2->address : NULL, buf))
|
|
return -1;
|
|
|
|
if (d2) {
|
|
if (d2->address.family)
|
|
d2->port = d->port;
|
|
else
|
|
ZERO(*d2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __socket(socket_t *r, int type, sockfamily_t *fam) {
|
|
ZERO(*r);
|
|
r->family = fam;
|
|
r->fd = socket(fam->af, type, 0);
|
|
if (r->fd == -1) {
|
|
__C_DBG("socket() syscall fail, fd=%d", r->fd);
|
|
return -1;
|
|
} else {
|
|
__C_DBG("socket() syscall success, fd=%d", r->fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa) {
|
|
sockfamily_t *fam;
|
|
|
|
fam = sa->family;
|
|
|
|
if (__socket(r, type, fam)) {
|
|
__C_DBG("open socket fail, fd=%d", r->fd);
|
|
return -1;
|
|
}
|
|
|
|
nonblock(r->fd);
|
|
reuseaddr(r->fd);
|
|
if (r->family->af == AF_INET6)
|
|
ipv6only(r->fd, 1);
|
|
|
|
if (port > 0xffff) {
|
|
__C_DBG("open socket fail, port=%d > 0xfffffd", port);
|
|
goto fail;
|
|
}
|
|
|
|
if (fam->bind(r, port, sa)) {
|
|
__C_DBG("open socket fail, fd=%d, port=%d", r->fd, port);
|
|
goto fail;
|
|
}
|
|
|
|
r->local.port = port;
|
|
r->local.address = *sa;
|
|
|
|
__C_DBG("open socket success, fd=%d, port=%d", r->fd, port);
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
close_socket(r);
|
|
return -1;
|
|
}
|
|
|
|
int open_v46_socket(socket_t *r, int type) {
|
|
sockfamily_t *fam = &__socket_families[SF_IP6];
|
|
|
|
if (__socket(r, type, fam)) {
|
|
__C_DBG("open socket fail, fd=%d", r->fd);
|
|
return -1;
|
|
}
|
|
|
|
nonblock(r->fd);
|
|
ipv6only(r->fd, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dummy_socket(socket_t *r, const sockaddr_t *sa) {
|
|
ZERO(*r);
|
|
r->fd = -1;
|
|
r->family = sa->family;
|
|
r->local.address = *sa;
|
|
r->remote.address.family = sa->family;
|
|
}
|
|
|
|
int connect_socket(socket_t *r, int type, const endpoint_t *ep) {
|
|
sockfamily_t *fam;
|
|
|
|
fam = ep->address.family;
|
|
|
|
if (__socket(r, type, fam))
|
|
return -1;
|
|
if (fam->connect(r, ep))
|
|
goto fail;
|
|
|
|
r->remote = *ep;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
close_socket(r);
|
|
return -1;
|
|
}
|
|
|
|
int connect_socket_retry(socket_t *r) {
|
|
int ret = 0;
|
|
|
|
if (r->family->connect(r, &r->remote)) {
|
|
if (errno != EINPROGRESS && errno != EALREADY && errno != EISCONN)
|
|
goto fail;
|
|
if (errno != EISCONN)
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
|
|
fail:
|
|
close_socket(r);
|
|
return -1;
|
|
}
|
|
|
|
int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep) {
|
|
sockfamily_t *fam;
|
|
|
|
fam = ep->address.family;
|
|
|
|
if (__socket(r, type, fam))
|
|
return -1;
|
|
nonblock(r->fd);
|
|
r->remote = *ep;
|
|
|
|
return connect_socket_retry(r);
|
|
}
|
|
|
|
int close_socket(socket_t *r) {
|
|
if (!r) {
|
|
__C_DBG("close() syscall not called, no socket");
|
|
return -1;
|
|
}
|
|
if (r->fd == -1) {
|
|
__C_DBG("close() syscall not called, fd=%d", r->fd);
|
|
return -1;
|
|
}
|
|
|
|
if (close(r->fd) != 0) {
|
|
__C_DBG("close() syscall fail, fd=%d", r->fd);
|
|
return -1;
|
|
}
|
|
|
|
__C_DBG("close() syscall success, fd=%d", r->fd);
|
|
|
|
r->fd = -1;
|
|
ZERO(r->local);
|
|
ZERO(r->remote);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// moves the contents of the socket object:
|
|
// dst must be initialised
|
|
// src will be reset and cleared, as if it was closed
|
|
// does not actually close the socket
|
|
void move_socket(socket_t *dst, socket_t *src) {
|
|
*dst = *src;
|
|
src->fd = -1;
|
|
ZERO(src->local);
|
|
ZERO(src->remote);
|
|
}
|
|
|
|
|
|
|
|
|
|
socktype_t *get_socket_type(const str *s) {
|
|
int i;
|
|
socktype_t *tp;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(__socket_types); i++) {
|
|
tp = &__socket_types[i];
|
|
if (!str_cmp(s, tp->name))
|
|
return tp;
|
|
if (!str_cmp(s, tp->name_uc))
|
|
return tp;
|
|
}
|
|
return NULL;
|
|
}
|
|
socktype_t *get_socket_type_c(const char *s) {
|
|
int i;
|
|
socktype_t *tp;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(__socket_types); i++) {
|
|
tp = &__socket_types[i];
|
|
if (!strcmp(s, tp->name))
|
|
return tp;
|
|
if (!strcmp(s, tp->name_uc))
|
|
return tp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
void socket_init(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < __SF_LAST; i++)
|
|
__socket_families[i].idx = i;
|
|
|
|
socktype_udp = get_socket_type_c("udp");
|
|
}
|