1282 lines
41 KiB
C
1282 lines
41 KiB
C
/*
|
|
* Author: Christian Huitema
|
|
* Copyright (c) 2017, Private Octopus, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "picosocks.h"
|
|
#include "picoquic_utils.h"
|
|
|
|
int picoquic_bind_to_port(SOCKET_TYPE fd, int af, int port)
|
|
{
|
|
struct sockaddr_storage sa;
|
|
int addr_length = 0;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
if (af == AF_INET) {
|
|
struct sockaddr_in* s4 = (struct sockaddr_in*)&sa;
|
|
#ifdef _WINDOWS
|
|
s4->sin_family = (ADDRESS_FAMILY)af;
|
|
#else
|
|
s4->sin_family = af;
|
|
#endif
|
|
s4->sin_port = htons((unsigned short)port);
|
|
addr_length = sizeof(struct sockaddr_in);
|
|
} else {
|
|
struct sockaddr_in6* s6 = (struct sockaddr_in6*)&sa;
|
|
|
|
s6->sin6_family = AF_INET6;
|
|
s6->sin6_port = htons((unsigned short)port);
|
|
addr_length = sizeof(struct sockaddr_in6);
|
|
}
|
|
|
|
return bind(fd, (struct sockaddr*)&sa, addr_length);
|
|
}
|
|
|
|
int picoquic_get_local_address(SOCKET_TYPE sd, struct sockaddr_storage * addr)
|
|
{
|
|
socklen_t name_len = sizeof(struct sockaddr_storage);
|
|
return getsockname(sd, (struct sockaddr *)addr, &name_len);
|
|
}
|
|
|
|
int picoquic_socket_set_pkt_info(SOCKET_TYPE sd, int af)
|
|
{
|
|
int ret;
|
|
#ifdef _WINDOWS
|
|
int option_value = 1;
|
|
if (af == AF_INET6) {
|
|
ret = setsockopt(sd, IPPROTO_IPV6, IPV6_PKTINFO, (char*)&option_value, sizeof(int));
|
|
}
|
|
else {
|
|
ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&option_value, sizeof(int));
|
|
}
|
|
#else
|
|
if (af == AF_INET6) {
|
|
int val = 1;
|
|
ret = setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
&val, sizeof(val));
|
|
if (ret == 0) {
|
|
val = 1;
|
|
ret = setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&val, sizeof(int));
|
|
}
|
|
}
|
|
else {
|
|
int val = 1;
|
|
#ifdef IP_PKTINFO
|
|
ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&val, sizeof(int));
|
|
#else
|
|
/* The IP_PKTINFO structure is not defined on BSD */
|
|
ret = setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, (char*)&val, sizeof(int));
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int * send_set)
|
|
{
|
|
int ret = -1;
|
|
#ifdef _WINDOWS
|
|
|
|
if (af == AF_INET6) {
|
|
#ifdef IPV6_ECN
|
|
{
|
|
DWORD ecn = PICOQUIC_ECN_ECT_0;
|
|
DWORD recvEcn = 1;
|
|
/* Request receiving ECN reports in recvmsg */
|
|
ret = setsockopt(sd, IPPROTO_IPV6, IPV6_ECN, (char *)&recvEcn, sizeof(recvEcn));
|
|
if (ret < 0) {
|
|
DBG_PRINTF("setsockopt IPV6_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError());
|
|
ret = -1;
|
|
*recv_set = 0;
|
|
}
|
|
else {
|
|
*recv_set = 1;
|
|
ret = 0;
|
|
}
|
|
|
|
/* Request setting ECN_ECT_0 in outgoing packets */
|
|
if (setsockopt(sd, IPPROTO_IP, IPV6_ECN, (const char *)&ecn, sizeof(ecn)) < 0) {
|
|
DBG_PRINTF("setsockopt IPv6_ECN (0x%x) fails, errno: %d\n", ecn, errno);
|
|
*send_set = 0;
|
|
}
|
|
else {
|
|
*send_set = 1;
|
|
}
|
|
}
|
|
#else
|
|
* recv_set = 0;
|
|
* send_set = 0;
|
|
#endif
|
|
}
|
|
else {
|
|
/* Using IPv4 options. */
|
|
#if defined(IP_ECN)
|
|
{
|
|
DWORD ecn = PICOQUIC_ECN_ECT_0;
|
|
INT recvEcn =1;
|
|
|
|
/* Request receiving ECN reports in recvmsg */
|
|
ret = setsockopt(sd, IPPROTO_IP, IP_ECN, (CHAR*)&recvEcn, sizeof(recvEcn));
|
|
if (ret < 0) {
|
|
DBG_PRINTF("setsockopt IP_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError());
|
|
ret = -1;
|
|
*recv_set = 0;
|
|
}
|
|
else {
|
|
*recv_set = 1;
|
|
ret = 0;
|
|
}
|
|
|
|
/* Request setting ECN_ECT_0 in outgoing packets */
|
|
if (setsockopt(sd, IPPROTO_IP, IP_ECN, (const char*)&ecn, sizeof(ecn)) < 0) {
|
|
DBG_PRINTF("setsockopt IP_ECN (0x%x) fails, errno: %d\n", ecn, errno);
|
|
*send_set = 0;
|
|
}
|
|
else {
|
|
*send_set = 1;
|
|
}
|
|
}
|
|
#else
|
|
* recv_set = 0;
|
|
#endif
|
|
*send_set = 0;
|
|
}
|
|
#else
|
|
if (af == AF_INET6) {
|
|
#if defined(IPV6_TCLASS)
|
|
{
|
|
unsigned int ecn = PICOQUIC_ECN_ECT_0; /* Setting ECN_ECT_0 in outgoing packets */
|
|
if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &ecn, sizeof(ecn)) < 0) {
|
|
DBG_PRINTF("setsockopt IPV6_TCLASS (0x%x) fails, errno: %d\n", ecn, errno);
|
|
*send_set = 0;
|
|
}
|
|
else {
|
|
*send_set = 1;
|
|
}
|
|
}
|
|
#else
|
|
DBG_PRINTF("%s", "IPV6_TCLASS is not defined\n");
|
|
*send_set = 0;
|
|
#endif
|
|
#ifdef IPV6_RECVTCLASS
|
|
{
|
|
unsigned int set = 0x01;
|
|
|
|
/* Request receiving TOS reports in recvmsg */
|
|
if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVTCLASS, &set, sizeof(set)) < 0) {
|
|
DBG_PRINTF("setsockopt IPv6 IPV6_RECVTCLASS (0x%x) fails, errno: %d\n", set, errno);
|
|
ret = -1;
|
|
*recv_set = 0;
|
|
}
|
|
else {
|
|
*recv_set = 1;
|
|
ret = 0;
|
|
}
|
|
}
|
|
#else
|
|
DBG_PRINTF("%s", "IPV6_RECVTCLASS is not defined\n");
|
|
*recv_set = 0;
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
#if defined(IP_TOS)
|
|
{
|
|
unsigned int ecn = PICOQUIC_ECN_ECT_0;
|
|
/* Request setting ECN_ECT_0 in outgoing packets */
|
|
if (setsockopt(sd, IPPROTO_IP, IP_TOS, &ecn, sizeof(ecn)) < 0) {
|
|
DBG_PRINTF("setsockopt IPv4 IP_TOS (0x%x) fails, errno: %d\n", ecn, errno);
|
|
*send_set = 0;
|
|
}
|
|
else {
|
|
*send_set = 1;
|
|
}
|
|
}
|
|
#else
|
|
*send_set = 0;
|
|
DBG_PRINTF("%s", "IP_TOS is not defined\n");
|
|
#endif
|
|
|
|
#ifdef IP_RECVTOS
|
|
{
|
|
unsigned int set = 1;
|
|
|
|
/* Request receiving TOS reports in recvmsg */
|
|
if (setsockopt(sd, IPPROTO_IP, IP_RECVTOS, &set, sizeof(set)) < 0) {
|
|
DBG_PRINTF("setsockopt IPv4 IP_RECVTOS (0x%x) fails, errno: %d\n", set, errno);
|
|
ret = -1;
|
|
*recv_set = 0;
|
|
}
|
|
else {
|
|
*recv_set = 1;
|
|
ret = 0;
|
|
}
|
|
}
|
|
#else
|
|
*recv_set = 0;
|
|
DBG_PRINTF("%s", "IP_RECVTOS is not defined\n");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
SOCKET_TYPE picoquic_open_client_socket(int af)
|
|
{
|
|
#ifdef _WINDOWS
|
|
WSADATA wsaData = { 0 };
|
|
(void)WSA_START(MAKEWORD(2, 2), &wsaData);
|
|
SOCKET_TYPE sd = WSASocket(af, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
|
|
#else
|
|
SOCKET_TYPE sd = socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
|
#endif
|
|
|
|
if (sd != INVALID_SOCKET) {
|
|
int send_set = 0;
|
|
int recv_set = 0;
|
|
|
|
if (picoquic_socket_set_pkt_info(sd, af) != 0) {
|
|
DBG_PRINTF("Cannot set PKTINFO option (af=%d)\n", af);
|
|
}
|
|
if (picoquic_socket_set_ecn_options(sd, af, &recv_set, &send_set) != 0) {
|
|
DBG_PRINTF("Cannot set ECN options (af=%d)\n", af);
|
|
}
|
|
}
|
|
else {
|
|
#ifdef _WINDOWS
|
|
DBG_PRINTF("Cannot open socket(AF=%d), error: %d\n", af, GetLastError());
|
|
#else
|
|
DBG_PRINTF("Cannot open socket(AF=%d), error: %d\n", af, errno);
|
|
#endif
|
|
}
|
|
|
|
return sd;
|
|
}
|
|
|
|
int picoquic_open_server_sockets(picoquic_server_sockets_t* sockets, int port)
|
|
{
|
|
int ret = 0;
|
|
|
|
#ifdef _WINDOWS
|
|
WSADATA wsaData = { 0 };
|
|
if (WSA_START(MAKEWORD(2, 2), &wsaData)) {
|
|
ret = -1;
|
|
}
|
|
#endif
|
|
|
|
const int sock_af[] = { AF_INET6, AF_INET };
|
|
|
|
for (int i = 0; i < PICOQUIC_NB_SERVER_SOCKETS; i++) {
|
|
if (ret == 0) {
|
|
sockets->s_socket[i] = socket(sock_af[i], SOCK_DGRAM, IPPROTO_UDP);
|
|
} else {
|
|
sockets->s_socket[i] = INVALID_SOCKET;
|
|
}
|
|
|
|
if (sockets->s_socket[i] == INVALID_SOCKET) {
|
|
ret = -1;
|
|
}
|
|
else {
|
|
int recv_set = 0;
|
|
int send_set = 0;
|
|
if (picoquic_socket_set_ecn_options(sockets->s_socket[i], sock_af[i], &recv_set, &send_set) != 0) {
|
|
DBG_PRINTF("Cannot set ECN options (af=%d)\n", sock_af[i]);
|
|
}
|
|
ret = picoquic_socket_set_pkt_info(sockets->s_socket[i], sock_af[i]);
|
|
if (ret == 0) {
|
|
ret = picoquic_bind_to_port(sockets->s_socket[i], sock_af[i], port);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void picoquic_close_server_sockets(picoquic_server_sockets_t* sockets)
|
|
{
|
|
for (int i = 0; i < PICOQUIC_NB_SERVER_SOCKETS; i++) {
|
|
if (sockets->s_socket[i] != INVALID_SOCKET) {
|
|
SOCKET_CLOSE(sockets->s_socket[i]);
|
|
sockets->s_socket[i] = INVALID_SOCKET;
|
|
}
|
|
}
|
|
}
|
|
|
|
void picoquic_socks_cmsg_parse(
|
|
void* vmsg,
|
|
struct sockaddr_storage* addr_dest,
|
|
int* dest_if,
|
|
unsigned char* received_ecn,
|
|
size_t * udp_coalesced_size)
|
|
{
|
|
/* Assume that msg has been filled by a call to recvmsg */
|
|
#if _WINDOWS
|
|
struct cmsghdr* cmsg;
|
|
WSAMSG* msg = (WSAMSG*)vmsg;
|
|
|
|
/* Get the control information */
|
|
for (cmsg = WSA_CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = WSA_CMSG_NXTHDR(msg, cmsg)) {
|
|
if (cmsg->cmsg_level == IPPROTO_IP) {
|
|
if (cmsg->cmsg_type == IP_PKTINFO) {
|
|
if (addr_dest != NULL) {
|
|
IN_PKTINFO* pPktInfo = (IN_PKTINFO*)WSA_CMSG_DATA(cmsg);
|
|
((struct sockaddr_in*)addr_dest)->sin_family = AF_INET;
|
|
((struct sockaddr_in*)addr_dest)->sin_port = 0;
|
|
((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->ipi_addr.s_addr;
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = (int)pPktInfo->ipi_ifindex;
|
|
}
|
|
}
|
|
}
|
|
else if (cmsg->cmsg_type == IP_TOS
|
|
#ifdef IP_ECN
|
|
|| cmsg->cmsg_type == IP_ECN
|
|
#endif
|
|
) {
|
|
if (cmsg->cmsg_len > 0) {
|
|
if (received_ecn != NULL) {
|
|
*received_ecn = *((unsigned char*)WSA_CMSG_DATA(cmsg));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type);
|
|
}
|
|
}
|
|
else if (cmsg->cmsg_level == IPPROTO_IPV6) {
|
|
if (cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
if (addr_dest != NULL) {
|
|
IN6_PKTINFO* pPktInfo6 = (IN6_PKTINFO*)WSA_CMSG_DATA(cmsg);
|
|
((struct sockaddr_in6*)addr_dest)->sin6_family = AF_INET6;
|
|
((struct sockaddr_in6*)addr_dest)->sin6_port = 0;
|
|
memcpy(&((struct sockaddr_in6*)addr_dest)->sin6_addr, &pPktInfo6->ipi6_addr, sizeof(IN6_ADDR));
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = (int)pPktInfo6->ipi6_ifindex;
|
|
}
|
|
}
|
|
}
|
|
else if (cmsg->cmsg_type == IPV6_TCLASS
|
|
#ifdef IPV6_ECN
|
|
|| cmsg->cmsg_type == IPV6_ECN
|
|
#endif
|
|
) {
|
|
if (cmsg->cmsg_len > 0 && received_ecn != NULL) {
|
|
*received_ecn = *((unsigned char*)WSA_CMSG_DATA(cmsg));
|
|
}
|
|
}
|
|
else {
|
|
DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type);
|
|
}
|
|
}
|
|
#ifdef UDP_COALESCED_INFO
|
|
else if (cmsg->cmsg_level == UDP_COALESCED_INFO) {
|
|
if (cmsg->cmsg_len > 0) {
|
|
if (udp_coalesced_size != NULL) {
|
|
*udp_coalesced_size = *((DWORD*)WSA_CMSG_DATA(cmsg));
|
|
}
|
|
}
|
|
else {
|
|
DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type);
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type);
|
|
}
|
|
}
|
|
#else
|
|
/* Get the control information */
|
|
struct msghdr* msg = (struct msghdr*)vmsg;
|
|
struct cmsghdr* cmsg;
|
|
|
|
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
|
if (cmsg->cmsg_level == IPPROTO_IP) {
|
|
#ifdef IP_PKTINFO
|
|
if (cmsg->cmsg_type == IP_PKTINFO) {
|
|
if (addr_dest != NULL) {
|
|
struct in_pktinfo* pPktInfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
|
|
((struct sockaddr_in*)addr_dest)->sin_family = AF_INET;
|
|
((struct sockaddr_in*)addr_dest)->sin_port = 0;
|
|
((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->ipi_addr.s_addr;
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = (int)pPktInfo->ipi_ifindex;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
/* The IP_PKTINFO structure is not defined on BSD */
|
|
if (cmsg->cmsg_type == IP_RECVDSTADDR) {
|
|
if (addr_dest != NULL) {
|
|
struct in_addr* pPktInfo = (struct in_addr*)CMSG_DATA(cmsg);
|
|
((struct sockaddr_in*)addr_dest)->sin_family = AF_INET;
|
|
((struct sockaddr_in*)addr_dest)->sin_port = 0;
|
|
((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->s_addr;
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
else if ((cmsg->cmsg_type == IP_TOS || cmsg->cmsg_type == IP_RECVTOS) && cmsg->cmsg_len > 0) {
|
|
if (received_ecn != NULL) {
|
|
*received_ecn = *((unsigned char*)CMSG_DATA(cmsg));
|
|
}
|
|
}
|
|
}
|
|
else if (cmsg->cmsg_level == IPPROTO_IPV6) {
|
|
if (cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
if (addr_dest != NULL) {
|
|
struct in6_pktinfo* pPktInfo6 = (struct in6_pktinfo*)CMSG_DATA(cmsg);
|
|
|
|
((struct sockaddr_in6*)addr_dest)->sin6_family = AF_INET6;
|
|
((struct sockaddr_in6*)addr_dest)->sin6_port = 0;
|
|
memcpy(&((struct sockaddr_in6*)addr_dest)->sin6_addr, &pPktInfo6->ipi6_addr, sizeof(struct in6_addr));
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = (int)pPktInfo6->ipi6_ifindex;
|
|
}
|
|
}
|
|
}
|
|
else if (cmsg->cmsg_type == IPV6_TCLASS) {
|
|
if (cmsg->cmsg_len > 0 && received_ecn != NULL) {
|
|
*received_ecn = *((unsigned char*)CMSG_DATA(cmsg));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef _WINDOWS
|
|
static void* cmsg_format_header_return_data_ptr(WSAMSG* msg, struct cmsghdr** last_cmsg, int * control_length,
|
|
INT cmsg_level, INT cmsg_type, size_t cmsg_data_len)
|
|
{
|
|
void* cmsg_data_ptr = NULL;
|
|
WSACMSGHDR* cmsg = (*last_cmsg == NULL)? WSA_CMSG_FIRSTHDR(msg): WSA_CMSG_NXTHDR(msg, *last_cmsg);
|
|
|
|
if (cmsg != NULL) {
|
|
size_t cmsg_required_space = WSA_CMSG_SPACE(cmsg_data_len);
|
|
*control_length += (INT)cmsg_required_space;
|
|
memset(cmsg, 0, cmsg_required_space);
|
|
cmsg->cmsg_level = cmsg_level;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
cmsg->cmsg_len = WSA_CMSG_LEN(cmsg_data_len);
|
|
cmsg_data_ptr = (void*)WSA_CMSG_DATA(cmsg);
|
|
*last_cmsg = cmsg;
|
|
}
|
|
|
|
return cmsg_data_ptr;
|
|
}
|
|
#endif
|
|
|
|
void picoquic_socks_cmsg_format(
|
|
void* vmsg,
|
|
size_t message_length,
|
|
size_t send_msg_size,
|
|
struct sockaddr* addr_from,
|
|
int dest_if)
|
|
{
|
|
#ifdef _WINDOWS
|
|
WSAMSG* msg = (WSAMSG*)vmsg;
|
|
int control_length = 0;
|
|
struct cmsghdr* last_cmsg = NULL;
|
|
int is_null = 0;
|
|
|
|
/* Format the control message */
|
|
if (addr_from != NULL && addr_from->sa_family != 0) {
|
|
if (addr_from->sa_family == AF_INET) {
|
|
struct in_pktinfo* pktinfo = (struct in_pktinfo*)cmsg_format_header_return_data_ptr(msg, &last_cmsg,
|
|
&control_length, IPPROTO_IP, IP_PKTINFO, sizeof(struct in_pktinfo));
|
|
if (pktinfo != NULL) {
|
|
pktinfo->ipi_addr.s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr;
|
|
pktinfo->ipi_ifindex = (unsigned long)dest_if;
|
|
}
|
|
else {
|
|
is_null = 1;
|
|
}
|
|
|
|
if (!is_null && message_length > PICOQUIC_INITIAL_MTU_IPV4) {
|
|
int* pval = (int*)cmsg_format_header_return_data_ptr(msg, &last_cmsg,
|
|
&control_length, IPPROTO_IP, IP_DONTFRAGMENT, sizeof(int));
|
|
if (pval != NULL) {
|
|
*pval = 1;
|
|
}
|
|
else {
|
|
is_null = 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
struct in6_pktinfo* pktinfo6 = (struct in6_pktinfo*)cmsg_format_header_return_data_ptr(msg, &last_cmsg,
|
|
&control_length, IPPROTO_IPV6, IPV6_PKTINFO, sizeof(struct in6_pktinfo));
|
|
if (pktinfo6 != NULL) {
|
|
memcpy(&pktinfo6->ipi6_addr.u, &((struct sockaddr_in6*)addr_from)->sin6_addr.u, sizeof(IN6_ADDR));
|
|
pktinfo6->ipi6_ifindex = (unsigned long)dest_if;
|
|
}
|
|
else {
|
|
is_null = 1;
|
|
}
|
|
if (!is_null) {
|
|
int* pval = (int*)cmsg_format_header_return_data_ptr(msg, &last_cmsg,
|
|
&control_length, IPPROTO_IPV6, IPV6_DONTFRAG, sizeof(int));
|
|
if (pval != NULL) {
|
|
*pval = 1;
|
|
}
|
|
else {
|
|
is_null = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!is_null && send_msg_size > 0) {
|
|
DWORD* pdw = (DWORD*)cmsg_format_header_return_data_ptr(msg, &last_cmsg,
|
|
&control_length, IPPROTO_UDP, UDP_SEND_MSG_SIZE, sizeof(DWORD));
|
|
if (pdw != NULL) {
|
|
*pdw = (DWORD)send_msg_size;
|
|
}
|
|
}
|
|
|
|
msg->Control.len = control_length;
|
|
if (control_length == 0) {
|
|
msg->Control.buf = NULL;
|
|
}
|
|
|
|
#else
|
|
struct msghdr* msg = (struct msghdr*)vmsg;
|
|
int control_length = 0;
|
|
struct cmsghdr* cmsg;
|
|
/* Format the control message */
|
|
cmsg = CMSG_FIRSTHDR(msg);
|
|
|
|
if (addr_from != NULL && addr_from->sa_family != 0) {
|
|
if (addr_from->sa_family == AF_INET) {
|
|
#ifdef IP_PKTINFO
|
|
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
cmsg->cmsg_type = IP_PKTINFO;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
|
struct in_pktinfo* pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
|
|
pktinfo->ipi_addr.s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr;
|
|
pktinfo->ipi_ifindex = (unsigned int)dest_if;
|
|
control_length += CMSG_SPACE(sizeof(struct in_pktinfo));
|
|
#else
|
|
/* The IP_PKTINFO structure is not defined on BSD */
|
|
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr)));
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
cmsg->cmsg_type = IP_SENDSRCADDR;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
|
|
struct in_addr* pktinfo = (struct in_addr*)CMSG_DATA(cmsg);
|
|
pktinfo->s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr;
|
|
control_length += CMSG_SPACE(sizeof(struct in_addr));
|
|
#endif
|
|
}
|
|
else if (addr_from->sa_family == AF_INET6) {
|
|
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
|
|
cmsg->cmsg_level = IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IPV6_PKTINFO;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
|
struct in6_pktinfo* pktinfo6 = (struct in6_pktinfo*)CMSG_DATA(cmsg);
|
|
memcpy(&pktinfo6->ipi6_addr, &((struct sockaddr_in6*)addr_from)->sin6_addr, sizeof(struct in6_addr));
|
|
pktinfo6->ipi6_ifindex = dest_if;
|
|
|
|
control_length += CMSG_SPACE(sizeof(struct in6_pktinfo));
|
|
}
|
|
else {
|
|
DBG_PRINTF("Unexpected address family: %d\n", addr_from->sa_family);
|
|
}
|
|
#ifdef IPV6_DONTFRAG
|
|
if (addr_from->sa_family == AF_INET6) {
|
|
#ifdef CMSG_ALIGN
|
|
struct cmsghdr* cmsg_2 = (struct cmsghdr*)((unsigned char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len));
|
|
{
|
|
#else
|
|
struct cmsghdr* cmsg_2 = CMSG_NXTHDR((msg), cmsg);
|
|
if (cmsg_2 == NULL) {
|
|
DBG_PRINTF("Cannot obtain second CMSG (control_length: %d)\n", control_length);
|
|
}
|
|
else {
|
|
#endif
|
|
int val = 1;
|
|
cmsg_2->cmsg_level = IPPROTO_IPV6;
|
|
cmsg_2->cmsg_type = IPV6_DONTFRAG;
|
|
cmsg_2->cmsg_len = CMSG_LEN(sizeof(int));
|
|
memcpy(CMSG_DATA(cmsg_2), &val, sizeof(int));
|
|
control_length += CMSG_SPACE(sizeof(int));
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(IP_DONTFRAG)
|
|
if (addr_from->sa_family == AF_INET6 && length > PICOQUIC_INITIAL_MTU_IPV6) {
|
|
#ifdef CMSG_ALIGN
|
|
struct cmsghdr* cmsg_2 = (struct cmsghdr*)((unsigned char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len));
|
|
#else
|
|
struct cmsghdr* cmsg_2 = CMSG_NXTHDR((msg), cmsg);
|
|
#endif
|
|
if (cmsg_2 == NULL) {
|
|
DBG_PRINTF("Cannot obtain second CMSG (control_length: %d)\n", control_length);
|
|
}
|
|
else
|
|
{
|
|
/* On BSD systems, just use IP_DONTFRAG */
|
|
int val = 1;
|
|
cmsg_2->cmsg_level = IPPROTO_IP;
|
|
cmsg_2->cmsg_type = IP_DONTFRAG;
|
|
cmsg_2->cmsg_len = CMSG_LEN(sizeof(int));
|
|
memcpy(CMSG_DATA(cmsg_2), &val, sizeof(int));
|
|
control_length += CMSG_SPACE(sizeof(int));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
msg->msg_controllen = control_length;
|
|
if (control_length == 0) {
|
|
msg->msg_control = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef _WINDOWS
|
|
|
|
void picoquic_delete_async_socket(picoquic_recvmsg_async_ctx_t * ctx)
|
|
{
|
|
if (ctx->fd != INVALID_SOCKET) {
|
|
SOCKET_CLOSE(ctx->fd);
|
|
ctx->fd = INVALID_SOCKET;
|
|
}
|
|
|
|
if (ctx->overlap.hEvent != WSA_INVALID_EVENT) {
|
|
WSACloseEvent(ctx->overlap.hEvent);
|
|
ctx->overlap.hEvent = WSA_INVALID_EVENT;
|
|
}
|
|
|
|
free(ctx);
|
|
}
|
|
|
|
picoquic_recvmsg_async_ctx_t * picoquic_create_async_socket(int af, int recv_coalesced, int send_coalesced)
|
|
{
|
|
int ret = 0;
|
|
int last_error = 0;
|
|
picoquic_recvmsg_async_ctx_t * ctx = (picoquic_recvmsg_async_ctx_t *)malloc(sizeof(picoquic_recvmsg_async_ctx_t));
|
|
|
|
if (ctx == NULL) {
|
|
DBG_PRINTF("Could not create async socket context, AF = %d!\n", af);
|
|
}
|
|
else {
|
|
memset(ctx, 0, sizeof(picoquic_recvmsg_async_ctx_t));
|
|
ctx->overlap.hEvent = WSA_INVALID_EVENT;
|
|
|
|
ctx->fd = picoquic_open_client_socket(af);
|
|
|
|
if (ctx->fd == INVALID_SOCKET) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not initialize UDP socket, AF = %d, err=%d!\n",
|
|
af, last_error);
|
|
ret = -1;
|
|
}
|
|
else {
|
|
GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
|
|
DWORD NumberOfBytes;
|
|
int nResult = WSAIoctl(ctx->fd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
|
&WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID),
|
|
&ctx->WSARecvMsg, sizeof(ctx->WSARecvMsg),
|
|
&NumberOfBytes, NULL, NULL);
|
|
|
|
if (nResult == SOCKET_ERROR) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not initialize WSARecvMsg on UDP socket %d= %d!\n",
|
|
(int)ctx->fd, last_error);
|
|
ret = -1;
|
|
}
|
|
else {
|
|
GUID WSASendMsg_GUID = WSAID_WSASENDMSG;
|
|
nResult = WSAIoctl(ctx->fd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
|
&WSASendMsg_GUID, sizeof(WSASendMsg_GUID),
|
|
&ctx->WSASendMsg, sizeof(ctx->WSASendMsg),
|
|
&NumberOfBytes, NULL, NULL);
|
|
|
|
if (nResult == SOCKET_ERROR) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not initialize WSASendMsg on UDP socket %d= %d!\n",
|
|
(int)ctx->fd, last_error);
|
|
ret = -1;
|
|
}
|
|
else {
|
|
// ctx->overlap.hEvent = WSACreateEvent();
|
|
ctx->overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (ctx->overlap.hEvent == WSA_INVALID_EVENT) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not create WSA event for UDP socket %d= %d!\n",
|
|
(int)ctx->fd, last_error);
|
|
ret = -1;
|
|
}
|
|
}
|
|
#ifdef UDP_RECV_MAX_COALESCED_SIZE
|
|
if (ret == 0 && recv_coalesced) {
|
|
int n_messages = (recv_coalesced) ? 1 : 10;
|
|
DWORD coalesced_size = n_messages * PICOQUIC_MAX_PACKET_SIZE;
|
|
ctx->recv_buffer_size = coalesced_size;
|
|
ctx->recv_buffer = (uint8_t*)malloc(ctx->recv_buffer_size);
|
|
ctx->supports_udp_recv_coalesced = recv_coalesced;
|
|
ctx->supports_udp_send_coalesced = send_coalesced;
|
|
if (ctx->recv_buffer == NULL) {
|
|
DBG_PRINTF("Could allocate buffer size %zu for socket %d!\n",
|
|
ctx->recv_buffer_size, (int)ctx->fd);
|
|
ret = -1;
|
|
}
|
|
else if ((ret = setsockopt(ctx->fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&coalesced_size,
|
|
(int)sizeof(coalesced_size))) != 0) {
|
|
last_error = GetLastError();
|
|
DBG_PRINTF("Cannot set UDP_RECV_MAX_COALESCED_SIZE %d, returns %d (%d)",
|
|
coalesced_size, ret, last_error);
|
|
ret = -1;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ret != 0) {
|
|
picoquic_delete_async_socket(ctx);
|
|
ctx = NULL;
|
|
}
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
int picoquic_recvmsg_async_finish(
|
|
picoquic_recvmsg_async_ctx_t * ctx)
|
|
{
|
|
DWORD cbTransferred = 0;
|
|
DWORD ret = 0;
|
|
DWORD flags = 0;
|
|
|
|
if (ctx == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (!WSAGetOverlappedResult(ctx->fd, &ctx->overlap, &cbTransferred, FALSE, &flags)) {
|
|
ret = WSAGetLastError();
|
|
if (ret == WSAECONNRESET) {
|
|
ctx->bytes_recv = 0;
|
|
ret = 0;
|
|
}
|
|
else {
|
|
DBG_PRINTF("Could not complete async call (WSARecvMsg) on UDP socket %d = %d!\n",
|
|
(int)ctx->fd, ret);
|
|
ctx->bytes_recv = -1;
|
|
}
|
|
}
|
|
else {
|
|
ctx->bytes_recv = cbTransferred;
|
|
ctx->from_length = ctx->msg.namelen;
|
|
|
|
picoquic_socks_cmsg_parse(&ctx->msg, &ctx->addr_dest, &ctx->dest_if, &ctx->received_ecn, &ctx->udp_coalesced_size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int picoquic_recvmsg_async_start(picoquic_recvmsg_async_ctx_t* ctx)
|
|
{
|
|
int last_error;
|
|
int ret = 0;
|
|
DWORD numberOfBytesReceived = 0;
|
|
int should_retry;
|
|
|
|
do {
|
|
should_retry = 0;
|
|
ctx->from_length = 0;
|
|
ctx->dest_length = 0;
|
|
ctx->dest_if = 0;
|
|
ctx->received_ecn = 0;
|
|
ctx->bytes_recv = 0;
|
|
ctx->udp_coalesced_size = 0;
|
|
|
|
ctx->overlap.Internal = 0;
|
|
ctx->overlap.InternalHigh = 0;
|
|
ctx->overlap.Offset = 0;
|
|
ctx->overlap.OffsetHigh = 0;
|
|
|
|
ctx->dataBuf.buf = (char*)ctx->recv_buffer;
|
|
ctx->dataBuf.len = (ULONG)ctx->recv_buffer_size;
|
|
|
|
ctx->msg.name = (struct sockaddr*) & ctx->addr_from;
|
|
ctx->msg.namelen = sizeof(ctx->addr_from);
|
|
ctx->msg.lpBuffers = &ctx->dataBuf;
|
|
ctx->msg.dwBufferCount = 1;
|
|
ctx->msg.dwFlags = 0;
|
|
ctx->msg.Control.buf = ctx->cmsg_buffer;
|
|
ctx->msg.Control.len = sizeof(ctx->cmsg_buffer);
|
|
|
|
/* Setting the &nbReceived parameter to NULL to force async behavior */
|
|
ret = ctx->WSARecvMsg(ctx->fd, &ctx->msg, &numberOfBytesReceived, &ctx->overlap, NULL);
|
|
|
|
if (ret != 0) {
|
|
last_error = WSAGetLastError();
|
|
if (last_error == WSA_IO_PENDING) {
|
|
ret = 0;
|
|
}
|
|
else if (last_error == WSAECONNRESET) {
|
|
/* Ignore the ICMP errors */
|
|
should_retry = 1;
|
|
ret = 0;
|
|
}
|
|
else {
|
|
DBG_PRINTF("Could not start receive async (WSARecvMsg) on UDP socket %d = %d!\n",
|
|
(int)ctx->fd, last_error);
|
|
ctx->bytes_recv = -1;
|
|
}
|
|
}
|
|
else {
|
|
DBG_PRINTF("Receive async immediate (WSARecvMsg) on UDP socket %d -- %d bytes !\n",
|
|
(int)ctx->fd, numberOfBytesReceived);
|
|
ctx->nb_immediate_receive++;
|
|
}
|
|
} while (should_retry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
int picoquic_recvmsg(SOCKET_TYPE fd,
|
|
struct sockaddr_storage* addr_from,
|
|
struct sockaddr_storage* addr_dest,
|
|
int* dest_if,
|
|
unsigned char* received_ecn,
|
|
uint8_t* buffer, int buffer_max)
|
|
#ifdef _WINDOWS
|
|
{
|
|
GUID WSARecvMsg_GUID = WSAID_WSARECVMSG;
|
|
LPFN_WSARECVMSG WSARecvMsg;
|
|
char cmsg_buffer[1024];
|
|
DWORD NumberOfBytes;
|
|
int nResult;
|
|
WSAMSG msg;
|
|
WSABUF dataBuf;
|
|
int recv_ret = 0;
|
|
int bytes_recv;
|
|
int last_error;
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = 0;
|
|
}
|
|
|
|
if (received_ecn != NULL) {
|
|
*received_ecn = 0;
|
|
}
|
|
|
|
nResult = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
|
&WSARecvMsg_GUID, sizeof WSARecvMsg_GUID,
|
|
&WSARecvMsg, sizeof WSARecvMsg,
|
|
&NumberOfBytes, NULL, NULL);
|
|
|
|
if (nResult == SOCKET_ERROR) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not initialize WSARecvMsg) on UDP socket %d= %d!\n",
|
|
(int)fd, last_error);
|
|
bytes_recv = -1;
|
|
} else {
|
|
dataBuf.buf = (char*)buffer;
|
|
dataBuf.len = buffer_max;
|
|
|
|
msg.name = (struct sockaddr*)addr_from;
|
|
msg.namelen = sizeof(struct sockaddr_storage);
|
|
msg.lpBuffers = &dataBuf;
|
|
msg.dwBufferCount = 1;
|
|
msg.dwFlags = 0;
|
|
msg.Control.buf = cmsg_buffer;
|
|
msg.Control.len = sizeof(cmsg_buffer);
|
|
|
|
recv_ret = WSARecvMsg(fd, &msg, &NumberOfBytes, NULL, NULL);
|
|
|
|
if (recv_ret != 0) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not receive message (WSARecvMsg) on UDP socket %d = %d!\n",
|
|
(int)fd, last_error);
|
|
bytes_recv = -1;
|
|
} else {
|
|
bytes_recv = NumberOfBytes;
|
|
picoquic_socks_cmsg_parse(&msg, addr_dest, dest_if, received_ecn, NULL);
|
|
}
|
|
}
|
|
|
|
return bytes_recv;
|
|
}
|
|
#else
|
|
{
|
|
int bytes_recv = 0;
|
|
struct msghdr msg;
|
|
struct iovec dataBuf;
|
|
char cmsg_buffer[1024];
|
|
|
|
if (dest_if != NULL) {
|
|
*dest_if = 0;
|
|
}
|
|
|
|
dataBuf.iov_base = (char*)buffer;
|
|
dataBuf.iov_len = buffer_max;
|
|
|
|
msg.msg_name = (struct sockaddr*)addr_from;
|
|
msg.msg_namelen = sizeof(struct sockaddr_storage);
|
|
msg.msg_iov = &dataBuf;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_flags = 0;
|
|
msg.msg_control = (void*)cmsg_buffer;
|
|
msg.msg_controllen = sizeof(cmsg_buffer);
|
|
|
|
bytes_recv = recvmsg(fd, &msg, 0);
|
|
|
|
if (bytes_recv <= 0) {
|
|
addr_from->ss_family = 0;
|
|
} else {
|
|
picoquic_socks_cmsg_parse(&msg, addr_dest, dest_if, received_ecn, NULL);
|
|
}
|
|
|
|
return bytes_recv;
|
|
}
|
|
#endif
|
|
|
|
int picoquic_sendmsg(SOCKET_TYPE fd,
|
|
struct sockaddr* addr_dest,
|
|
struct sockaddr* addr_from,
|
|
int dest_if,
|
|
const char* bytes, int length)
|
|
#ifdef _WINDOWS
|
|
{
|
|
GUID WSASendMsg_GUID = WSAID_WSASENDMSG;
|
|
LPFN_WSASENDMSG WSASendMsg;
|
|
char cmsg_buffer[1024];
|
|
DWORD NumberOfBytes;
|
|
int ret = 0;
|
|
DWORD dwBytesSent = 0;
|
|
WSAMSG msg;
|
|
WSABUF dataBuf;
|
|
int bytes_sent;
|
|
int last_error;
|
|
|
|
ret = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
|
&WSASendMsg_GUID, sizeof WSASendMsg_GUID,
|
|
&WSASendMsg, sizeof WSASendMsg,
|
|
&NumberOfBytes, NULL, NULL);
|
|
|
|
if (ret == SOCKET_ERROR) {
|
|
last_error = WSAGetLastError();
|
|
DBG_PRINTF("Could not initialize WSASendMsg on UDP socket %d= %d!\n",
|
|
(int)fd, last_error);
|
|
bytes_sent = -1;
|
|
}
|
|
else {
|
|
/* Format the control message header */
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.name = addr_dest;
|
|
msg.namelen = picoquic_addr_length(addr_dest);
|
|
dataBuf.buf = (char*)bytes;
|
|
dataBuf.len = length;
|
|
msg.lpBuffers = &dataBuf;
|
|
msg.dwBufferCount = 1;
|
|
msg.Control.buf = (char*)cmsg_buffer;
|
|
msg.Control.len = sizeof(cmsg_buffer);
|
|
|
|
/* Format the control message */
|
|
picoquic_socks_cmsg_format(&msg, length, 0, addr_from, dest_if);
|
|
|
|
/* Send the message */
|
|
ret = WSASendMsg(fd, &msg, 0, &dwBytesSent, NULL, NULL);
|
|
|
|
if (ret != 0) {
|
|
bytes_sent = -1;
|
|
} else {
|
|
bytes_sent = (int)dwBytesSent;
|
|
}
|
|
}
|
|
|
|
return bytes_sent;
|
|
}
|
|
#else
|
|
{
|
|
struct msghdr msg;
|
|
struct iovec dataBuf;
|
|
char cmsg_buffer[1024];
|
|
int bytes_sent;
|
|
|
|
/* Format the message header */
|
|
|
|
dataBuf.iov_base = (char*)bytes;
|
|
dataBuf.iov_len = length;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_name = addr_dest;
|
|
msg.msg_namelen = picoquic_addr_length(addr_dest);
|
|
msg.msg_iov = &dataBuf;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = (void*)cmsg_buffer;
|
|
msg.msg_controllen = sizeof(cmsg_buffer);
|
|
|
|
/* Format the control message */
|
|
picoquic_socks_cmsg_format(&msg, length, 0, addr_from, dest_if);
|
|
|
|
bytes_sent = sendmsg(fd, &msg, 0);
|
|
|
|
return bytes_sent;
|
|
}
|
|
#endif
|
|
|
|
int picoquic_select_ex(SOCKET_TYPE* sockets,
|
|
int nb_sockets,
|
|
struct sockaddr_storage* addr_from,
|
|
struct sockaddr_storage* addr_dest,
|
|
int* dest_if,
|
|
unsigned char * received_ecn,
|
|
uint8_t* buffer, int buffer_max,
|
|
int64_t delta_t,
|
|
int * socket_rank,
|
|
uint64_t* current_time)
|
|
{
|
|
fd_set readfds;
|
|
struct timeval tv;
|
|
int ret_select = 0;
|
|
int bytes_recv = 0;
|
|
int sockmax = 0;
|
|
|
|
if (received_ecn != NULL) {
|
|
*received_ecn = 0;
|
|
}
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
for (int i = 0; i < nb_sockets; i++) {
|
|
if (sockmax < (int)sockets[i]) {
|
|
sockmax = (int)sockets[i];
|
|
}
|
|
FD_SET(sockets[i], &readfds);
|
|
}
|
|
|
|
if (delta_t <= 0) {
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
} else {
|
|
if (delta_t > 10000000) {
|
|
tv.tv_sec = (long)10;
|
|
tv.tv_usec = 0;
|
|
} else {
|
|
tv.tv_sec = (long)(delta_t / 1000000);
|
|
tv.tv_usec = (long)(delta_t % 1000000);
|
|
}
|
|
}
|
|
|
|
ret_select = select(sockmax + 1, &readfds, NULL, NULL, &tv);
|
|
|
|
if (ret_select < 0) {
|
|
bytes_recv = -1;
|
|
DBG_PRINTF("Error: select returns %d\n", ret_select);
|
|
} else if (ret_select > 0) {
|
|
for (int i = 0; i < nb_sockets; i++) {
|
|
if (FD_ISSET(sockets[i], &readfds)) {
|
|
*socket_rank = i;
|
|
bytes_recv = picoquic_recvmsg(sockets[i], addr_from,
|
|
addr_dest, dest_if, received_ecn,
|
|
buffer, buffer_max);
|
|
|
|
if (bytes_recv <= 0) {
|
|
#ifdef _WINDOWS
|
|
int last_error = WSAGetLastError();
|
|
|
|
if (last_error == WSAECONNRESET || last_error == WSAEMSGSIZE) {
|
|
bytes_recv = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
DBG_PRINTF("Could not receive packet on UDP socket[%d]= %d!\n",
|
|
i, (int)sockets[i]);
|
|
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*current_time = picoquic_current_time();
|
|
|
|
return bytes_recv;
|
|
}
|
|
|
|
int picoquic_select(SOCKET_TYPE* sockets,
|
|
int nb_sockets,
|
|
struct sockaddr_storage* addr_from,
|
|
struct sockaddr_storage* addr_dest,
|
|
int* dest_if,
|
|
unsigned char* received_ecn,
|
|
uint8_t* buffer, int buffer_max,
|
|
int64_t delta_t,
|
|
uint64_t* current_time) {
|
|
int socket_rank;
|
|
return picoquic_select_ex(sockets, nb_sockets, addr_from, addr_dest, dest_if,
|
|
received_ecn, buffer, buffer_max, delta_t, &socket_rank, current_time);
|
|
}
|
|
|
|
int picoquic_send_through_socket(
|
|
SOCKET_TYPE fd,
|
|
struct sockaddr* addr_dest,
|
|
struct sockaddr* addr_from, int from_if,
|
|
const char* bytes, int length, int* sock_err)
|
|
{
|
|
int sent = picoquic_sendmsg(fd, addr_dest, addr_from, from_if, bytes, length);
|
|
|
|
#ifndef DISABLE_DEBUG_PRINTF
|
|
if (sent <= 0) {
|
|
#ifdef _WINDOWS
|
|
int last_error = WSAGetLastError();
|
|
#else
|
|
int last_error = errno;
|
|
#endif
|
|
DBG_PRINTF("Could not send packet on UDP socket[AF=%d]= %d!\n",
|
|
addr_dest->sa_family, last_error);
|
|
if (sock_err != NULL) {
|
|
*sock_err = last_error;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return sent;
|
|
}
|
|
|
|
int picoquic_send_through_server_sockets(
|
|
picoquic_server_sockets_t* sockets,
|
|
struct sockaddr* addr_dest,
|
|
struct sockaddr* addr_from, int from_if,
|
|
const char* bytes, int length, int* sock_err)
|
|
{
|
|
/* Both Linux and Windows use separate sockets for V4 and V6 */
|
|
int socket_index = (addr_dest->sa_family == AF_INET) ? 1 : 0;
|
|
|
|
return picoquic_send_through_socket(sockets->s_socket[socket_index], addr_dest, addr_from, from_if, bytes, length, sock_err);
|
|
}
|
|
|
|
int picoquic_get_server_address(const char* ip_address_text, int server_port,
|
|
struct sockaddr_storage* server_address, int* is_name)
|
|
{
|
|
int ret = 0;
|
|
struct sockaddr_in* ipv4_dest = (struct sockaddr_in*)server_address;
|
|
struct sockaddr_in6* ipv6_dest = (struct sockaddr_in6*)server_address;
|
|
|
|
/* get the IP address of the server */
|
|
memset(server_address, 0, sizeof(struct sockaddr_storage));
|
|
*is_name = 0;
|
|
|
|
if (inet_pton(AF_INET, ip_address_text, &ipv4_dest->sin_addr) == 1) {
|
|
/* Valid IPv4 address */
|
|
ipv4_dest->sin_family = AF_INET;
|
|
ipv4_dest->sin_port = htons((unsigned short)server_port);
|
|
} else if (inet_pton(AF_INET6, ip_address_text, &ipv6_dest->sin6_addr) == 1) {
|
|
/* Valid IPv6 address */
|
|
ipv6_dest->sin6_family = AF_INET6;
|
|
ipv6_dest->sin6_port = htons((unsigned short)server_port);
|
|
} else {
|
|
/* Server is described by name. Do a lookup for the IP address,
|
|
* and then use the name as SNI parameter */
|
|
struct addrinfo* result = NULL;
|
|
struct addrinfo hints;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
hints.ai_protocol = IPPROTO_UDP;
|
|
|
|
if ((ret = getaddrinfo(ip_address_text, NULL, &hints, &result)) != 0) {
|
|
#ifdef _WINDOWS
|
|
int err = GetLastError();
|
|
#else
|
|
int err = ret;
|
|
#endif
|
|
fprintf(stderr, "Cannot get IP address for %s, err = %d (0x%x)\n", ip_address_text, err, err);
|
|
ret = -1;
|
|
} else {
|
|
*is_name = 1;
|
|
|
|
switch (result->ai_family) {
|
|
case AF_INET:
|
|
ipv4_dest->sin_family = AF_INET;
|
|
ipv4_dest->sin_port = htons((unsigned short)server_port);
|
|
#ifdef _WINDOWS
|
|
ipv4_dest->sin_addr.S_un.S_addr = ((struct sockaddr_in*)result->ai_addr)->sin_addr.S_un.S_addr;
|
|
#else
|
|
ipv4_dest->sin_addr.s_addr = ((struct sockaddr_in*)result->ai_addr)->sin_addr.s_addr;
|
|
#endif
|
|
break;
|
|
case AF_INET6:
|
|
ipv6_dest->sin6_family = AF_INET6;
|
|
ipv6_dest->sin6_port = htons((unsigned short)server_port);
|
|
memcpy(&ipv6_dest->sin6_addr,
|
|
&((struct sockaddr_in6*)result->ai_addr)->sin6_addr,
|
|
sizeof(ipv6_dest->sin6_addr));
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Error getting IPv6 address for %s, family = %d\n",
|
|
ip_address_text, result->ai_family);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
freeaddrinfo(result);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Wireshark needs the session keys in order to decrypt and analyze packets.
|
|
* In Unix and Windows, Wireshark reads these keys from a file. The name
|
|
* of the file is passed in the environment variable SSLKEYLOGFILE,
|
|
* which is accessed through system dependent API.
|
|
*/
|
|
|
|
void picoquic_set_key_log_file_from_env(picoquic_quic_t* quic)
|
|
{
|
|
char* keylog_filename = NULL;
|
|
|
|
#ifdef _WINDOWS
|
|
size_t len;
|
|
|
|
if (_dupenv_s(&keylog_filename, &len, "SSLKEYLOGFILE") != 0 ||
|
|
keylog_filename == NULL) {
|
|
return;
|
|
}
|
|
#else
|
|
keylog_filename = getenv("SSLKEYLOGFILE");
|
|
if (keylog_filename == NULL) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
picoquic_set_key_log_file(quic, keylog_filename);
|
|
}
|