1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
|
#ifndef _SOCKET_H_
#define _SOCKET_H_
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <stdbool.h>
#include "containers.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;
TYPED_GQUEUE(socket, socket_t)
#include "str.h"
#define MAX_PACKET_HEADER_LEN 48 // 40 bytes IPv6 + 8 bytes UDP
struct local_intf;
struct socket_type {
const char *name; /* lower case */
const char *name_uc; /* upper case */
};
struct socket_family {
int idx;
int af;
unsigned int ethertype;
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 *);
bool (*eq)(const sockaddr_t *, const sockaddr_t *);
bool (*addr_parse)(sockaddr_t *, const char *);
bool (*addr_print)(const sockaddr_t *, char *, size_t);
bool (*addr_print_p)(const sockaddr_t *, char *, size_t);
bool (*is_specified)(const sockaddr_t *);
bool (*sockaddr2endpoint)(endpoint_t *, const void *);
bool (*endpoint2sockaddr)(void *, const endpoint_t *);
bool (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int);
bool (*bind)(socket_t *, unsigned int, const sockaddr_t *);
bool (*connect)(socket_t *, const endpoint_t *);
bool (*listen)(socket_t *, int);
bool (*accept)(socket_t *, socket_t *);
bool (*getsockname)(socket_t *);
bool (*timestamping)(socket_t *);
bool (*pktinfo)(socket_t *);
ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *);
ssize_t (*recvfrom_ts)(socket_t *, void *, size_t, endpoint_t *, int64_t *);
ssize_t (*recvfrom_to)(socket_t *, void *, size_t, endpoint_t *, sockaddr_t *);
ssize_t (*sendmsg)(socket_t *, struct msghdr *, const endpoint_t *);
ssize_t (*sendto)(socket_t *, const void *, size_t, const endpoint_t *);
bool (*tos)(socket_t *, unsigned int);
void (*pmtu_disc)(socket_t *, int);
int (*error)(socket_t *);
void (*endpoint2kernel)(struct re_address *, const endpoint_t *);
void (*kernel2endpoint)(endpoint_t *, const struct re_address *);
unsigned int (*packet_header)(unsigned char *, const endpoint_t *, const endpoint_t *,
unsigned int);
void (*cmsg_pktinfo)(struct cmsghdr *, const sockaddr_t *);
};
struct socket_address {
sockfamily_t *family;
union {
struct in_addr ipv4;
struct in6_addr ipv6;
};
};
struct endpoint {
sockaddr_t address;
unsigned int port;
};
struct socket {
int fd;
sockfamily_t *family;
endpoint_t local;
endpoint_t remote;
};
extern socktype_t *socktype_udp;
#include "auxlib.h"
INLINE bool sockaddr_print(const sockaddr_t *a, char *buf, size_t len) {
if (!a->family) {
buf[0] = '\0';
return true;
}
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 buf;
}
sockaddr_print(a, buf, THREAD_BUF_SIZE);
return buf;
}
INLINE bool sockaddr_print_gstring(GString *s, const sockaddr_t *a) {
if (!a->family)
return true;
char buf[THREAD_BUF_SIZE];
if (!sockaddr_print(a, buf, THREAD_BUF_SIZE))
return false;
g_string_append(s, buf);
return true;
}
INLINE bool sockaddr_print_p(const sockaddr_t *a, char *buf, size_t len) {
if (!a->family) {
buf[0] = '\0';
return true;
}
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 bool sockaddr_print_port(const sockaddr_t *a, unsigned int port, char *buf, size_t len) {
if (!a->family) {
buf[0] = '\0';
return true;
}
if (!a->family->addr_print_p(a, buf, len-6))
return false;
sprintf(buf + strlen(buf), ":%u", port);
return true;
}
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 bool 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 bool is_addr_unspecified(const sockaddr_t *a) {
if (!a || !a->family)
return true;
return !a->family->is_specified(a);
}
#define socket_recvfrom(s,a...) (s)->family->recvfrom((s), a)
#define socket_recvfrom_ts(s,a...) (s)->family->recvfrom_ts((s), a)
#define socket_recvfrom_to(s,a...) (s)->family->recvfrom_to((s), a)
#define socket_sendmsg(s,a...) (s)->family->sendmsg((s), a)
#define socket_sendto(s,a...) (s)->family->sendto((s), a)
#define socket_error(s) (s)->family->error((s))
#define socket_timestamping(s) (s)->family->timestamping((s))
#define socket_pktinfo(s) (s)->family->pktinfo((s))
#define socket_getsockname(s) (s)->family->getsockname((s))
INLINE ssize_t socket_sendiov(socket_t *s, const struct iovec *v, unsigned int len, const endpoint_t *dst,
const sockaddr_t *src)
{
struct msghdr mh = {0};
char ctrl[64] = {0};
mh.msg_iov = (void *) v;
mh.msg_iovlen = len;
if (src && src->family) {
mh.msg_control = ctrl;
mh.msg_controllen = sizeof(ctrl);
struct cmsghdr *cm = CMSG_FIRSTHDR(&mh);
s->family->cmsg_pktinfo(cm, src);
cm = CMSG_NXTHDR(&mh, cm);
assert(cm != NULL);
mh.msg_controllen = (char *) cm - ctrl;
}
return socket_sendmsg(s, &mh, dst);
}
INLINE ssize_t socket_sendto_from(socket_t *s, const void *b, size_t l, const endpoint_t *dst, sockaddr_t *src) {
return socket_sendiov(s, &(struct iovec) { .iov_base = (void *) b, .iov_len = l }, l, dst, src);
}
struct timeval32 {
int32_t tv_sec;
int32_t tv_usec;
};
#ifndef SO_TIMESTAMP_OLD
#define SO_TIMESTAMP_OLD SO_TIMESTAMP // pointless, compiler should remove it
#endif
#define socket_recvfrom_parse_cmsg(tv, to, parse_to, msgh, firsthdr, nexthdr) do { \
if ((*tv) || (*to)) { \
struct cmsghdr *cm; \
for (cm = firsthdr; cm; cm = nexthdr) { \
if ((*tv) && cm->cmsg_level == SOL_SOCKET) { \
if (cm->cmsg_type == SO_TIMESTAMP) { \
*(*tv) = timeval_us(*((struct timeval *) CMSG_DATA(cm))); \
(*tv) = NULL; \
} \
else if (cm->cmsg_type == SO_TIMESTAMP_OLD) { \
struct timeval32 *tc = (struct timeval32 *) CMSG_DATA(cm); \
struct timeval tvv = { .tv_sec = tc->tv_sec, .tv_usec = tc->tv_usec }; \
*(*tv) = timeval_us(tvv); \
(*tv) = NULL; \
} \
} \
if (parse && (*to) && parse_to(cm, (*to))) \
(*to) = NULL; \
} \
if (G_UNLIKELY((*tv))) { \
ilog(LOG_WARNING, "No receive timestamp received from kernel"); \
ZERO(*(*tv)); \
} \
if (G_UNLIKELY((*to))) { \
ilog(LOG_WARNING, "No local address received from kernel"); \
ZERO(*(*to)); \
} \
} \
if (G_UNLIKELY(((msgh)->msg_flags & MSG_TRUNC))) \
ilog(LOG_WARNING, "Kernel indicates that data was truncated"); \
if (G_UNLIKELY(((msgh)->msg_flags & MSG_CTRUNC))) \
ilog(LOG_WARNING, "Kernel indicates that ancillary data was truncated"); \
} while (0)
INLINE void usertimeout(socket_t *s, unsigned int val) {
// coverity[check_return : FALSE]
setsockopt(s->fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &val, sizeof(val));
}
INLINE void nonblock(int fd) {
// coverity[check_return : FALSE]
fcntl(fd, F_SETFL, O_NONBLOCK);
}
INLINE void socket_rcvtimeout(socket_t *s, int64_t us) {
struct timeval tv = timeval_from_us(us);
// coverity[check_return : FALSE]
setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
INLINE int socket_cpu_affinity(socket_t *s, int cpu) {
#ifndef SO_INCOMING_CPU
errno = ENOTSUP;
return -1;
#else
return setsockopt(s->fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(cpu));
#endif
}
void socket_init(void);
bool open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *);
bool open_v46_socket(socket_t *r, int type);
bool connect_socket(socket_t *r, int type, const endpoint_t *ep);
int connect_socket_nb(socket_t *r, int type, const endpoint_t *ep); // 1 == in progress
int connect_socket_retry(socket_t *r); // retries connect() while in progress
bool close_socket(socket_t *r);
bool reset_socket(socket_t *r);
void move_socket(socket_t *dst, socket_t *src);
void dummy_socket(socket_t *r, const sockaddr_t *);
sockfamily_t *get_socket_family_rfc(const str *s);
sockfamily_t *__get_socket_family_enum(enum socket_families);
bool sockaddr_parse_any(sockaddr_t *dst, const char *src);
bool sockaddr_parse_any_str(sockaddr_t *dst, const str *src);
bool sockaddr_parse_str(sockaddr_t *dst, sockfamily_t *fam, const str *src);
bool endpoint_parse_any(endpoint_t *, const char *); // address (ip) optional
bool sockaddr_getaddrinfo_alt(sockaddr_t *a, sockaddr_t *a2, const char *s);
bool endpoint_parse_any_getaddrinfo_alt(endpoint_t *d, endpoint_t *d2, const char *s); // address (ip or hostname) optional
INLINE bool endpoint_parse_any_getaddrinfo(endpoint_t *d, const char *s);
void endpoint_parse_sockaddr_storage(endpoint_t *, struct sockaddr_storage *);
void kernel2endpoint(endpoint_t *ep, const struct re_address *ra);
unsigned int sockaddr_hash(const sockaddr_t *);
bool sockaddr_eq(const sockaddr_t *, const sockaddr_t *);
guint sockaddr_t_hash(gconstpointer); // for glib
gint sockaddr_t_eq(gconstpointer, gconstpointer); // true/false, for glib
unsigned int endpoint_hash(const endpoint_t *);
gboolean endpoint_eq(const endpoint_t *, const endpoint_t *); /* 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 bool endpoint_parse_port_any(endpoint_t *e, const char *p, unsigned int port) {
if (port > 0xffff)
return false;
e->port = port;
return sockaddr_parse_any(&e->address, p);
}
// address (ip or hostname) required
INLINE bool endpoint_parse_any_getaddrinfo_full(endpoint_t *d, const char *s) {
bool ret;
ret = endpoint_parse_any_getaddrinfo(d, s);
if (!ret)
return ret;
if (is_addr_unspecified(&d->address))
return false;
return true;
}
INLINE bool sockaddr_getaddrinfo(sockaddr_t *a, const char *s) {
return sockaddr_getaddrinfo_alt(a, NULL, s);
}
INLINE bool endpoint_parse_any_getaddrinfo(endpoint_t *d, const char *s) {
return endpoint_parse_any_getaddrinfo_alt(d, NULL, s);
}
INLINE bool ipv46_any_convert(endpoint_t *ep) {
if (ep->address.family->af != AF_INET)
return false;
if (!is_addr_unspecified(&ep->address))
return false;
ep->address.family = __get_socket_family_enum(SF_IP6);
ZERO(ep->address.ipv6);
return true;
}
// needs a writeable str
INLINE bool endpoint_parse_any_str(endpoint_t *d, str *s) {
char tmp = s->s[s->len];
s->s[s->len] = '\0';
bool ret = endpoint_parse_any(d, s->s);
s->s[s->len] = tmp;
return ret;
}
#define endpoint_packet_header(o, src, dst, len) (dst)->address.family->packet_header(o, src, dst, len)
INLINE void set_tos(socket_t *s, unsigned int tos) {
s->family->tos(s, tos);
}
INLINE void set_pmtu_disc(socket_t *s, int opt) {
if (s->family->pmtu_disc)
s->family->pmtu_disc(s, opt);
}
socktype_t *get_socket_type(const str *s);
socktype_t *get_socket_type_c(const char *s);
#endif
|