diff options
Diffstat (limited to 'packages/bun-usockets/src/bsd.c')
-rw-r--r-- | packages/bun-usockets/src/bsd.c | 738 |
1 files changed, 738 insertions, 0 deletions
diff --git a/packages/bun-usockets/src/bsd.c b/packages/bun-usockets/src/bsd.c new file mode 100644 index 000000000..7683acd7d --- /dev/null +++ b/packages/bun-usockets/src/bsd.c @@ -0,0 +1,738 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Todo: this file should lie in networking/bsd.c */ + +#define __APPLE_USE_RFC_3542 + +#include "libusockets.h" +#include "internal/internal.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifndef _WIN32 +//#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#endif + +/* Internal structure of packet buffer */ +struct us_internal_udp_packet_buffer { +#if defined(_WIN32) || defined(__APPLE__) + char *buf[LIBUS_UDP_MAX_NUM]; + size_t len[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; +#else + struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; + struct iovec iov[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; + char control[LIBUS_UDP_MAX_NUM][256]; +#endif +}; + +/* We need to emulate sendmmsg, recvmmsg on platform who don't have it */ +int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { +#if defined(__APPLE__) + +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; + + struct mmsghdr *hdrs = (struct mmsghdr *) msgvec; + + for (int i = 0; i < vlen; i++) { + int ret = sendmsg(fd, &hdrs[i].msg_hdr, flags); + if (ret == -1) { + if (i) { + return i; + } else { + return -1; + } + } else { + hdrs[i].msg_len = ret; + } + } + + return vlen; + +#elif defined(_WIN32) + + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + /* Let's just use sendto here */ + /* Winsock does not have sendmsg, while macOS has, however, we simply use sendto since both macOS and Winsock has it. + * Besides, you should use Linux either way to get best performance with the sendmmsg */ + + + // while we do not get error, send next + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + // need to support ipv6 addresses also! + int ret = sendto(fd, packet_buffer->buf[i], packet_buffer->len[i], flags, (struct sockaddr *)&packet_buffer->addr[i], sizeof(struct sockaddr_in)); + + if (ret == -1) { + // if we fail then we need to buffer up, no that's not our problem + // we do need to register poll out though and have a callback for it + return i; + } + + //printf("sendto: %d\n", ret); + } + + return LIBUS_UDP_MAX_NUM; // one message +#else + return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags | MSG_NOSIGNAL); +#endif +} + +int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + socklen_t addr_len = sizeof(struct sockaddr_storage); + int ret = recvfrom(fd, packet_buffer->buf[i], LIBUS_UDP_MAX_SIZE, flags, (struct sockaddr *)&packet_buffer->addr[i], &addr_len); + + if (ret == -1) { + return i; + } + + packet_buffer->len[i] = ret; + } + + return LIBUS_UDP_MAX_NUM; +#else + // we need to set controllen for ip packet + for (int i = 0; i < vlen; i++) { + ((struct mmsghdr *)msgvec)[i].msg_hdr.msg_controllen = 256; + } + + return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0); +#endif +} + +// this one is needed for knowing the destination addr of udp packet +// an udp socket can only bind to one port, and that port never changes +// this function returns ONLY the IP address, not any port +int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { +#if defined(_WIN32) || defined(__APPLE__) + return 0; // not supported +#else + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // ipv6 or ipv4 + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg); + memcpy(ip, &pi->ipi_addr, 4); + return 4; + } + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pi6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + memcpy(ip, &pi6->ipi6_addr, 16); + return 16; + } + } + + return 0; // no length + +#endif +} + +char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return (char *)&packet_buffer->addr[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_name; +#endif +} + +char *bsd_udp_packet_buffer_payload(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->buf[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_iov[0].iov_base; +#endif +} + +int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->len[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_len; +#endif +} + +void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) send_buf; + + memcpy(packet_buffer->buf[index], payload, length); + memcpy(&packet_buffer->addr[index], peer_addr, sizeof(struct sockaddr_storage)); + + packet_buffer->len[index] = length; +#else + //printf("length: %d, offset: %d\n", length, offset); + + struct mmsghdr *ss = (struct mmsghdr *) send_buf; + + // copy the peer address + memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + + // set control length to 0 + ss[index].msg_hdr.msg_controllen = 0; + + // copy the payload + + ss[index].msg_hdr.msg_iov->iov_len = length + offset; + + + memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +#endif +} + +/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so. + * We do not support those jumbo packets currently, but will safely ignore them. + * Any sane sender would assume we don't support them if we consistently drop them. + * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ +void *bsd_create_udp_packet_buffer() { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + b->buf[i] = ((char *) b) + sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * i; + } + + return (struct us_udp_packet_buffer_t *) b; +#else + /* Allocate 64kb times 1024 */ + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) { + + b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE]; + b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; + + b->msgvec[n].msg_hdr = (struct msghdr) { + .msg_name = &b->addr, + .msg_namelen = sizeof (struct sockaddr_storage), + + .msg_iov = &b->iov[n], + .msg_iovlen = 1, + + .msg_control = b->control[n], + .msg_controllen = 256, + }; + } + + return (struct us_udp_packet_buffer_t *) b; +#endif +} + +LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef __APPLE__ + if (fd != LIBUS_SOCKET_ERROR) { + int no_sigpipe = 1; + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &no_sigpipe, sizeof(int)); + } +#endif + return fd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + /* Libuv will set windows sockets as non-blocking */ +#else + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); +#endif + return fd; +} + +void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled) { + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); +} + +void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd) { + // Linux TCP_CORK has the same underlying corking mechanism as with MSG_MORE +#ifdef TCP_CORK + int enabled = 0; + setsockopt(fd, IPPROTO_TCP, TCP_CORK, (void *) &enabled, sizeof(int)); +#endif +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol) { + // returns INVALID_SOCKET on error + int flags = 0; +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + flags = SOCK_CLOEXEC | SOCK_NONBLOCK; +#endif + + LIBUS_SOCKET_DESCRIPTOR created_fd = socket(domain, type | flags, protocol); + + return bsd_set_nonblocking(apple_no_sigpipe(created_fd)); +} + +void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + closesocket(fd); +#else + close(fd); +#endif +} + +void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + shutdown(fd, SD_SEND); +#else + shutdown(fd, SHUT_WR); +#endif +} + +void bsd_shutdown_socket_read(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + shutdown(fd, SD_RECEIVE); +#else + shutdown(fd, SHUT_RD); +#endif +} + +void internal_finalize_bsd_addr(struct bsd_addr_t *addr) { + // parse, so to speak, the address + if (addr->mem.ss_family == AF_INET6) { + addr->ip = (char *) &((struct sockaddr_in6 *) addr)->sin6_addr; + addr->ip_length = sizeof(struct in6_addr); + addr->port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); + } else if (addr->mem.ss_family == AF_INET) { + addr->ip = (char *) &((struct sockaddr_in *) addr)->sin_addr; + addr->ip_length = sizeof(struct in_addr); + addr->port = ntohs(((struct sockaddr_in *) addr)->sin_port); + } else { + addr->ip_length = 0; + addr->port = -1; + } +} + +int bsd_local_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + addr->len = sizeof(addr->mem); + if (getsockname(fd, (struct sockaddr *) &addr->mem, &addr->len)) { + return -1; + } + internal_finalize_bsd_addr(addr); + return 0; +} + +int bsd_remote_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + addr->len = sizeof(addr->mem); + if (getpeername(fd, (struct sockaddr *) &addr->mem, &addr->len)) { + return -1; + } + internal_finalize_bsd_addr(addr); + return 0; +} + +char *bsd_addr_get_ip(struct bsd_addr_t *addr) { + return addr->ip; +} + +int bsd_addr_get_ip_length(struct bsd_addr_t *addr) { + return addr->ip_length; +} + +int bsd_addr_get_port(struct bsd_addr_t *addr) { + return addr->port; +} + +// called by dispatch_ready_poll +LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + LIBUS_SOCKET_DESCRIPTOR accepted_fd; + addr->len = sizeof(addr->mem); + +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + // Linux, FreeBSD + accepted_fd = accept4(fd, (struct sockaddr *) addr, &addr->len, SOCK_CLOEXEC | SOCK_NONBLOCK); +#else + // Windows, OS X + accepted_fd = accept(fd, (struct sockaddr *) addr, &addr->len); + +#endif + + /* We cannot rely on addr since it is not initialized if failed */ + if (accepted_fd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + + internal_finalize_bsd_addr(addr); + + return bsd_set_nonblocking(apple_no_sigpipe(accepted_fd)); +} + +int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) { + return recv(fd, buf, length, flags); +} + +int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) { + + // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD) + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#ifdef MSG_MORE + + // for Linux we do not want signals + return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL); + +#else + + // use TCP_NOPUSH + + return send(fd, buf, length, MSG_NOSIGNAL); + +#endif +} + +int bsd_would_block() { +#ifdef _WIN32 + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EWOULDBLOCK;// || errno == EAGAIN; +#endif +} + +// return LIBUS_SOCKET_ERROR or the fd that represents listen socket +// listen both on ipv6 and ipv4 +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result)) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + struct addrinfo *listenAddr; + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET6) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + if (listenFd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (port != 0) { + /* Otherwise, always enable SO_REUSEPORT and SO_REUSEADDR _unless_ options specify otherwise */ +#ifdef _WIN32 + if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) { + int optval2 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2)); + } else { + int optval3 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3)); + } +#else + #if /*defined(__linux) &&*/ defined(SO_REUSEPORT) + if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) { + int optval = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval, sizeof(optval)); + } + #endif + int enabled = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); +#endif + + } + +#ifdef IPV6_V6ONLY + int disabled = 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); +#endif + + if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) { + bsd_close_socket(listenFd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + freeaddrinfo(result); + return listenFd; +} + +#ifndef _WIN32 +#include <sys/un.h> +#else +#include <afunix.h> +#include <io.h> +#endif +#include <sys/stat.h> +#include <stddef.h> +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options) { + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + + listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (listenFd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + +#ifndef _WIN32 + // 700 permission by default + fchmod(listenFd, S_IRWXU); +#else + _chmod(path, S_IREAD | S_IWRITE | S_IEXEC); +#endif + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); +#ifdef _WIN32 + _unlink(path); +#else + unlink(path); +#endif + + if (bind(listenFd, (struct sockaddr *)&server_address, size) || listen(listenFd, 512)) { + bsd_close_socket(listenFd); + return LIBUS_SOCKET_ERROR; + } + + return listenFd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result)) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + struct addrinfo *listenAddr; + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET6) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + if (listenFd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (port != 0) { + /* Should this also go for UDP? */ + int enabled = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); + } + +#ifdef IPV6_V6ONLY + int disabled = 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); +#endif + + /* We need destination address for udp packets in both ipv6 and ipv4 */ + +/* On FreeBSD this option seems to be called like so */ +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif + + int enabled = 1; + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *) &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, (void *) &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 pktinfo!\n"); + } + } else { + printf("Error setting IPv6 pktinfo!\n"); + } + } + + /* These are used for getting the ECN */ + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, (void *) &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, (void *) &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 ECN!\n"); + } + } else { + printf("Error setting IPv6 ECN!\n"); + } + } + + /* We bind here as well */ + if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) { + bsd_close_socket(listenFd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + freeaddrinfo(result); + return listenFd; +} + +int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { + +#if defined(_WIN32) || defined(__APPLE__) + printf("ECN not supported!\n"); +#else + // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // do we need to get TOS from ipv6 also? + if (cmsg->cmsg_level == IPPROTO_IP) { + if (cmsg->cmsg_type == IP_TOS) { + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_TCLASS) { + // is this correct? + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } + } +#endif + + printf("We got no ECN!\n"); + + return 0; // no ecn defaults to 0 +} + +static int bsd_do_connect(struct addrinfo *result, int fd) +{ + return connect(fd, result->ai_addr, (socklen_t) result->ai_addrlen); +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result) != 0) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (fd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (source_host) { + struct addrinfo *interface_result; + if (!getaddrinfo(source_host, NULL, NULL, &interface_result)) { + int ret = bind(fd, interface_result->ai_addr, (socklen_t) interface_result->ai_addrlen); + freeaddrinfo(interface_result); + if (ret == LIBUS_SOCKET_ERROR) { + bsd_close_socket(fd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + } + } + + do { + if (bsd_do_connect(result, fd) != 0 && errno != EINPROGRESS) { + bsd_close_socket(fd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + } while (errno == EINTR); + + freeaddrinfo(result); + + return fd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options) { + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, server_path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (fd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + + if (connect(fd, (struct sockaddr *)&server_address, size) != 0 && errno != EINPROGRESS) { + bsd_close_socket(fd); + return LIBUS_SOCKET_ERROR; + } + + return fd; +}
\ No newline at end of file |