mirror of https://github.com/sipwise/rtpengine.git
This brings master up to date with branch `rfuchs/socket-rework` at commit `b1bcc096b7`. The branches have diverged too much for a proper merge, so this is a manual (squashed) merge. The old master before this merge can be found in branch `old-master-before-socket-rework` (commit `82199216b2`). This is a complete rewrite of all socket handling routines. The most important functional change is that sockets aren't indiscriminately bound to INADDR_ANY (or rather in6addr_any), but instead are always bound to their respective local interface address and with the correct address family. Side effects of this are that in multi-homed environments, multiple sockets must be opened (one per interface address and family) which must be taken into account when considering RLIMIT_NOFILE values. As a benefit, this change allows rtpengine to utilize the full UDP port space per interface address, instead of just one port space per machine. The socket abstraction also makes it possible to support RTP over TCP in the future. Change-Id: If6cf4f42136229490186d2d2482fb4fc140c2b53pull/197/head
parent
82199216b2
commit
6d3865b971
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,19 @@
|
||||
#ifndef CLI_UDP_H_
|
||||
#define CLI_UDP_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include "socket.h"
|
||||
#include "obj.h"
|
||||
|
||||
struct cli {
|
||||
struct obj obj;
|
||||
|
||||
struct callmaster *callmaster;
|
||||
int fd;
|
||||
socket_t sock;
|
||||
struct poller *poller;
|
||||
mutex_t lock;
|
||||
|
||||
};
|
||||
|
||||
struct cli *cli_new(struct poller *p, u_int32_t ip, u_int16_t port, struct callmaster *m);
|
||||
struct cli *cli_new(struct poller *p, const endpoint_t *, struct callmaster *m);
|
||||
|
||||
#endif /* CLI_UDP_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,112 @@
|
||||
#ifndef _MEDIA_SOCKET_H_
|
||||
#define _MEDIA_SOCKET_H_
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "str.h"
|
||||
#include "obj.h"
|
||||
#include "aux.h"
|
||||
#include "dtls.h"
|
||||
#include "crypto.h"
|
||||
#include "socket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct logical_intf {
|
||||
str name;
|
||||
sockfamily_t *preferred_family;
|
||||
GQueue list; /* struct local_intf */
|
||||
GHashTable *addr_hash;
|
||||
};
|
||||
struct port_pool {
|
||||
BIT_ARRAY_DECLARE(ports_used, 0x10000);
|
||||
volatile unsigned int last_used;
|
||||
volatile unsigned int free_ports;
|
||||
|
||||
unsigned int min, max;
|
||||
};
|
||||
struct intf_address {
|
||||
socktype_t *type;
|
||||
sockaddr_t addr;
|
||||
sockaddr_t advertised;
|
||||
};
|
||||
struct intf_config {
|
||||
str name;
|
||||
struct intf_address address;
|
||||
unsigned int port_min, port_max;
|
||||
};
|
||||
struct intf_spec {
|
||||
struct intf_address address;
|
||||
str ice_foundation;
|
||||
struct port_pool port_pool;
|
||||
};
|
||||
struct local_intf {
|
||||
struct intf_spec *spec;
|
||||
unsigned int unique_id; /* starting with 0 - serves as preference */
|
||||
const struct logical_intf *logical;
|
||||
};
|
||||
struct intf_list {
|
||||
const struct local_intf *local_intf;
|
||||
GQueue list;
|
||||
};
|
||||
struct stream_fd {
|
||||
struct obj obj;
|
||||
unsigned int unique_id; /* RO */
|
||||
socket_t socket; /* RO */
|
||||
const struct local_intf *local_intf; /* RO */
|
||||
struct call *call; /* RO */
|
||||
struct packet_stream *stream; /* LOCK: call->master_lock */
|
||||
struct crypto_context crypto; /* IN direction, LOCK: stream->in_lock */
|
||||
struct dtls_connection dtls; /* LOCK: stream->in_lock */
|
||||
};
|
||||
|
||||
|
||||
|
||||
void interfaces_init(GQueue *interfaces);
|
||||
|
||||
struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports);
|
||||
struct local_intf *get_interface_address(const struct logical_intf *lif, sockfamily_t *fam);
|
||||
struct local_intf *get_any_interface_address(const struct logical_intf *lif, sockfamily_t *fam);
|
||||
void interfaces_exclude_port(unsigned int port);
|
||||
|
||||
//int get_port(socket_t *r, unsigned int port, const struct local_intf *lif, const struct call *c);
|
||||
//void release_port(socket_t *r, const struct local_intf *);
|
||||
INLINE void set_tos(socket_t *s, unsigned int tos) {
|
||||
s->family->tos(s, tos);
|
||||
}
|
||||
int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port,
|
||||
struct intf_spec *spec);
|
||||
int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log);
|
||||
struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif);
|
||||
|
||||
void free_intf_list(struct intf_list *il);
|
||||
void free_socket_intf_list(struct intf_list *il);
|
||||
|
||||
INLINE int open_intf_socket(socket_t *r, unsigned int port, const struct local_intf *lif) {
|
||||
return open_socket(r, SOCK_DGRAM, port, &lif->spec->address.addr);
|
||||
}
|
||||
|
||||
void kernelize(struct packet_stream *);
|
||||
void __unkernelize(struct packet_stream *);
|
||||
void unkernelize(struct packet_stream *);
|
||||
void __stream_unconfirm(struct packet_stream *);
|
||||
|
||||
/* XXX shouldnt be necessary */
|
||||
/*
|
||||
INLINE struct local_intf *get_interface_from_address(const struct logical_intf *lif,
|
||||
const sockaddr_t *addr, socktype_t *type)
|
||||
{
|
||||
struct intf_address a;
|
||||
a.type = type;
|
||||
a.addr = *addr;
|
||||
return g_hash_table_lookup(lif->addr_hash, &a);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,550 @@
|
||||
#include "socket.h"
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "str.h"
|
||||
#include "media_socket.h"
|
||||
#include "xt_RTPENGINE.h"
|
||||
#include "call.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 __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_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 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 socktype_t __socket_types[] = {
|
||||
{
|
||||
.name = "udp",
|
||||
.name_uc = "UDP",
|
||||
},
|
||||
};
|
||||
|
||||
static struct socket_family __socket_families[__SF_LAST] = {
|
||||
[SF_IP4] = {
|
||||
.af = AF_INET,
|
||||
.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,
|
||||
.recvfrom = __ip_recvfrom,
|
||||
.sendmsg = __ip_sendmsg,
|
||||
.sendto = __ip_sendto,
|
||||
.tos = __ip4_tos,
|
||||
.endpoint2kernel = __ip4_endpoint2kernel,
|
||||
.kernel2endpoint = __ip4_kernel2endpoint,
|
||||
},
|
||||
[SF_IP6] = {
|
||||
.af = AF_INET6,
|
||||
.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,
|
||||
.recvfrom = __ip_recvfrom,
|
||||
.sendmsg = __ip_sendmsg,
|
||||
.sendto = __ip_sendto,
|
||||
.tos = __ip6_tos,
|
||||
.endpoint2kernel = __ip6_endpoint2kernel,
|
||||
.kernel2endpoint = __ip6_kernel2endpoint,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
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 succes, fd=%d, port=%d", s->fd, s->local.port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static ssize_t __ip_recvfrom(socket_t *s, void *buf, size_t len, endpoint_t *ep) {
|
||||
ssize_t ret;
|
||||
struct sockaddr_storage sin;
|
||||
socklen_t sinlen;
|
||||
|
||||
sinlen = s->family->sockaddr_size;
|
||||
ret = recvfrom(s->fd, buf, len, 0, (void *) &sin, &sinlen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
s->family->sockaddr2endpoint(ep, &sin);
|
||||
return ret;
|
||||
}
|
||||
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;
|
||||
|
||||
s->family->endpoint2sockaddr(&sin, ep);
|
||||
return sendto(s->fd, buf, len, 0, (void *) &sin, s->family->sockaddr_size);
|
||||
}
|
||||
static int __ip4_tos(socket_t *s, unsigned int tos) {
|
||||
unsigned char ctos;
|
||||
ctos = tos;
|
||||
setsockopt(s->fd, IPPROTO_IP, IP_TOS, &ctos, sizeof(ctos));
|
||||
return 0;
|
||||
}
|
||||
static int __ip6_tos(socket_t *s, unsigned int tos) {
|
||||
setsockopt(s->fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos));
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned int sockaddr_hash(const sockaddr_t *a) {
|
||||
return a->family->hash(a) ^ g_direct_hash(a->family);
|
||||
}
|
||||
int sockaddr_eq(const sockaddr_t *a, const sockaddr_t *b) {
|
||||
return a->family == b->family && a->family->eq(a, b);
|
||||
}
|
||||
unsigned int g_sockaddr_hash(const void *a) {
|
||||
return sockaddr_hash(a);
|
||||
}
|
||||
int g_sockaddr_eq(const void *a, const void *b) {
|
||||
return sockaddr_eq(a, b);
|
||||
}
|
||||
|
||||
|
||||
unsigned int endpoint_hash(const endpoint_t *a) {
|
||||
return sockaddr_hash(&a->address) ^ a->port;
|
||||
}
|
||||
int endpoint_eq(const endpoint_t *a, const endpoint_t *b) {
|
||||
return sockaddr_eq(&a->address, &b->address) && a->port == b->port;
|
||||
}
|
||||
unsigned int g_endpoint_hash(const void *a) {
|
||||
return endpoint_hash(a);
|
||||
}
|
||||
int g_endpoint_eq(const void *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->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(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 (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 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_nb(socket_t *r, int type, const endpoint_t *ep) {
|
||||
sockfamily_t *fam;
|
||||
int ret = 0;
|
||||
|
||||
fam = ep->address.family;
|
||||
|
||||
if (__socket(r, type, fam))
|
||||
return -1;
|
||||
nonblock(r->fd);
|
||||
if (fam->connect(r, ep)) {
|
||||
if (errno != EINPROGRESS)
|
||||
goto fail;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
r->remote = *ep;
|
||||
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
close_socket(r);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int close_socket(socket_t *r) {
|
||||
if (!r || 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void socket_init(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < __SF_LAST; i++)
|
||||
__socket_families[i].idx = i;
|
||||
}
|
||||
@ -0,0 +1,224 @@
|
||||
#ifndef _SOCKET_H_
|
||||
#define _SOCKET_H_
|
||||
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
|
||||
|
||||
enum socket_families {
|
||||
SF_IP4 = 0,
|
||||
SF_IP6,
|
||||
__SF_LAST
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct socket_address;
|
||||
struct socket_type;
|
||||
struct socket_family;
|
||||
struct endpoint;
|
||||
struct socket;
|
||||
struct re_address;
|
||||
|
||||
typedef struct socket_address sockaddr_t;
|
||||
typedef struct endpoint endpoint_t;
|
||||
typedef struct socket socket_t;
|
||||
typedef const struct socket_type socktype_t;
|
||||
typedef const struct socket_family sockfamily_t;
|
||||
|
||||
|
||||
#include "str.h"
|
||||
|
||||
|
||||
|
||||
struct local_intf;
|
||||
|
||||
|
||||
struct socket_type {
|
||||
const char *name; /* lower case */
|
||||
const char *name_uc; /* upper case */
|
||||
};
|
||||
struct socket_family {
|
||||
int idx;
|
||||
int af;
|
||||
size_t sockaddr_size;
|
||||
const char *name; /* "IPv4" */
|
||||
const char *rfc_name; /* "IP4" */
|
||||
const char *unspec_string; /* 0.0.0.0 or :: */
|
||||
unsigned int (*hash)(const sockaddr_t *);
|
||||
int (*eq)(const sockaddr_t *, const sockaddr_t *);
|
||||
int (*addr_parse)(sockaddr_t *, const char *);
|
||||
int (*addr_print)(const sockaddr_t *, char *, size_t);
|
||||
int (*addr_print_p)(const sockaddr_t *, char *, size_t);
|
||||
int (*is_specified)(const sockaddr_t *);
|
||||
int (*sockaddr2endpoint)(endpoint_t *, const void *);
|
||||
int (*endpoint2sockaddr)(void *, const endpoint_t *);
|
||||
int (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int);
|
||||
int (*bind)(socket_t *, unsigned int, const sockaddr_t *);
|
||||
int (*connect)(socket_t *, const endpoint_t *);
|
||||
ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *);
|
||||
ssize_t (*sendmsg)(socket_t *, struct msghdr *, const endpoint_t *);
|
||||
ssize_t (*sendto)(socket_t *, const void *, size_t, const endpoint_t *);
|
||||
int (*tos)(socket_t *, unsigned int);
|
||||
void (*endpoint2kernel)(struct re_address *, const endpoint_t *);
|
||||
void (*kernel2endpoint)(endpoint_t *, const struct re_address *);
|
||||
};
|
||||
struct socket_address {
|
||||
sockfamily_t *family;
|
||||
union {
|
||||
struct in_addr ipv4;
|
||||
struct in6_addr ipv6;
|
||||
} u;
|
||||
};
|
||||
struct endpoint {
|
||||
sockaddr_t address;
|
||||
unsigned int port;
|
||||
};
|
||||
struct socket {
|
||||
int fd;
|
||||
sockfamily_t *family;
|
||||
endpoint_t local;
|
||||
endpoint_t remote;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
INLINE int sockaddr_print(const sockaddr_t *a, char *buf, size_t len) {
|
||||
if (!a->family) {
|
||||
buf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return a->family->addr_print(a, buf, len);
|
||||
}
|
||||
INLINE char *sockaddr_print_buf(const sockaddr_t *a) {
|
||||
char *buf = get_thread_buf();
|
||||
if (!a->family) {
|
||||
buf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
sockaddr_print(a, buf, THREAD_BUF_SIZE);
|
||||
return buf;
|
||||
}
|
||||
INLINE int sockaddr_print_p(const sockaddr_t *a, char *buf, size_t len) {
|
||||
if (!a->family) {
|
||||
buf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
return a->family->addr_print_p(a, buf, len);
|
||||
}
|
||||
INLINE char *sockaddr_print_p_buf(const sockaddr_t *a) {
|
||||
char *buf = get_thread_buf();
|
||||
sockaddr_print_p(a, buf, THREAD_BUF_SIZE);
|
||||
return buf;
|
||||
}
|
||||
INLINE int sockaddr_print_port(const sockaddr_t *a, unsigned int port, char *buf, size_t len) {
|
||||
if (!a->family) {
|
||||
buf[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (a->family->addr_print_p(a, buf, len-6))
|
||||
return -1;
|
||||
sprintf(buf + strlen(buf), ":%u", port);
|
||||
return 0;
|
||||
}
|
||||
INLINE char *sockaddr_print_port_buf(const sockaddr_t *a, unsigned int port) {
|
||||
char *buf = get_thread_buf();
|
||||
sockaddr_print_port(a, port, buf, THREAD_BUF_SIZE);
|
||||
return buf;
|
||||
}
|
||||
INLINE int endpoint_print(const endpoint_t *ep, char *buf, size_t len) {
|
||||
return sockaddr_print_port(&ep->address, ep->port, buf, len);
|
||||
}
|
||||
INLINE char *endpoint_print_buf(const endpoint_t *ep) {
|
||||
return sockaddr_print_port_buf(&ep->address, ep->port);
|
||||
}
|
||||
INLINE int is_addr_unspecified(const sockaddr_t *a) {
|
||||
if (!a || !a->family)
|
||||
return 1;
|
||||
return !a->family->is_specified(a);
|
||||
}
|
||||
#define socket_recvfrom(s,a...) (s)->family->recvfrom((s), a)
|
||||
#define socket_sendmsg(s,a...) (s)->family->sendmsg((s), a)
|
||||
#define socket_sendto(s,a...) (s)->family->sendto((s), a)
|
||||
INLINE ssize_t socket_sendiov(socket_t *s, const struct iovec *v, unsigned int len, const endpoint_t *dst) {
|
||||
struct msghdr mh;
|
||||
ZERO(mh);
|
||||
mh.msg_iov = (void *) v;
|
||||
mh.msg_iovlen = len;
|
||||
return socket_sendmsg(s, &mh, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* XXX obsolete these? */
|
||||
INLINE void nonblock(int fd) {
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
INLINE void reuseaddr(int fd) {
|
||||
int one = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
}
|
||||
INLINE void ipv6only(int fd, int yn) {
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yn, sizeof(yn));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void socket_init(void);
|
||||
|
||||
int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *);
|
||||
int connect_socket(socket_t *r, int type, const endpoint_t *ep);
|
||||
int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep);
|
||||
int close_socket(socket_t *r);
|
||||
|
||||
sockfamily_t *get_socket_family_rfc(const str *s);
|
||||
sockfamily_t *__get_socket_family_enum(enum socket_families);
|
||||
int sockaddr_parse_any(sockaddr_t *dst, const char *src);
|
||||
int sockaddr_parse_any_str(sockaddr_t *dst, const str *src);
|
||||
int sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src);
|
||||
int endpoint_parse_any(endpoint_t *, const char *);
|
||||
void kernel2endpoint(endpoint_t *ep, const struct re_address *ra);
|
||||
|
||||
unsigned int sockaddr_hash(const sockaddr_t *);
|
||||
int sockaddr_eq(const sockaddr_t *, const sockaddr_t *); /* true/false */
|
||||
unsigned int g_sockaddr_hash(const void *);
|
||||
int g_sockaddr_eq(const void *, const void *); /* true/false */
|
||||
|
||||
unsigned int endpoint_hash(const endpoint_t *);
|
||||
int endpoint_eq(const endpoint_t *, const endpoint_t *); /* true/false */
|
||||
unsigned int g_endpoint_hash(const void *);
|
||||
int g_endpoint_eq(const void *, const void *); /* true/false */
|
||||
|
||||
INLINE sockfamily_t *get_socket_family_enum(enum socket_families i) {
|
||||
if (i >= __SF_LAST)
|
||||
return NULL;
|
||||
return __get_socket_family_enum(i);
|
||||
}
|
||||
INLINE int endpoint_parse_port_any(endpoint_t *e, const char *p, unsigned int port) {
|
||||
if (port > 0xffff)
|
||||
return -1;
|
||||
e->port = port;
|
||||
return sockaddr_parse_any(&e->address, p);
|
||||
}
|
||||
INLINE int ipv46_any_convert(endpoint_t *ep) {
|
||||
if (ep->address.family->af != AF_INET)
|
||||
return 0;
|
||||
if (!is_addr_unspecified(&ep->address))
|
||||
return 0;
|
||||
ep->address.family = __get_socket_family_enum(SF_IP6);
|
||||
ZERO(ep->address.u.ipv6);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
socktype_t *get_socket_type(const str *s);
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue