Path: blob/master/drivers/windows/net_socket_winsock.cpp
20849 views
/**************************************************************************/1/* net_socket_winsock.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#ifdef WINDOWS_ENABLED3132#include "net_socket_winsock.h"3334#include <winsock2.h>35#include <ws2tcpip.h>3637#include <mswsock.h>38// Workaround missing flag in MinGW39#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)40#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)41#endif4243size_t NetSocketWinSock::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {44memset(p_addr, 0, sizeof(struct sockaddr_storage));45if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket.4647// IPv6 only socket with IPv4 address.48ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);4950struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;51addr6->sin6_family = AF_INET6;52addr6->sin6_port = htons(p_port);53if (p_ip.is_valid()) {54memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);55} else {56addr6->sin6_addr = in6addr_any;57}58return sizeof(sockaddr_in6);59} else { // IPv4 socket.6061// IPv4 socket with IPv6 address.62ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);6364struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;65addr4->sin_family = AF_INET;66addr4->sin_port = htons(p_port); // Short, network byte order.6768if (p_ip.is_valid()) {69memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);70} else {71addr4->sin_addr.s_addr = INADDR_ANY;72}7374return sizeof(sockaddr_in);75}76}7778void NetSocketWinSock::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {79if (p_addr->ss_family == AF_INET) {80struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;81if (r_ip) {82r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));83}84if (r_port) {85*r_port = ntohs(addr4->sin_port);86}87} else if (p_addr->ss_family == AF_INET6) {88struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;89if (r_ip) {90r_ip->set_ipv6(addr6->sin6_addr.s6_addr);91}92if (r_port) {93*r_port = ntohs(addr6->sin6_port);94}95}96}9798NetSocket *NetSocketWinSock::_create_func() {99return memnew(NetSocketWinSock);100}101102void NetSocketWinSock::make_default() {103ERR_FAIL_COND(_create != nullptr);104105WSADATA data;106WSAStartup(MAKEWORD(2, 2), &data);107_create = _create_func;108}109110void NetSocketWinSock::cleanup() {111ERR_FAIL_COND(_create == nullptr);112113WSACleanup();114_create = nullptr;115}116117NetSocketWinSock::NetSocketWinSock() {118}119120NetSocketWinSock::~NetSocketWinSock() {121close();122}123124NetSocketWinSock::NetError NetSocketWinSock::_get_socket_error() const {125int err = WSAGetLastError();126if (err == WSAEISCONN) {127return ERR_NET_IS_CONNECTED;128}129if (err == WSAEINPROGRESS || err == WSAEALREADY) {130return ERR_NET_IN_PROGRESS;131}132if (err == WSAEWOULDBLOCK) {133return ERR_NET_WOULD_BLOCK;134}135if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) {136return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;137}138if (err == WSAEACCES) {139return ERR_NET_UNAUTHORIZED;140}141if (err == WSAEMSGSIZE || err == WSAENOBUFS) {142return ERR_NET_BUFFER_TOO_SMALL;143}144print_verbose("Socket error: " + itos(err) + ".");145return ERR_NET_OTHER;146}147148bool NetSocketWinSock::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {149if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {150return false;151} else if (!p_for_bind && !p_ip.is_valid()) {152return false;153}154// Check if socket support this IP type.155IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;156return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);157}158159_FORCE_INLINE_ Error NetSocketWinSock::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {160ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);161ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);162163// Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.164IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type;165// This needs to be the proper level for the multicast group, no matter if the socket is dual stacking.166int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;167int ret = -1;168169IPAddress if_ip;170uint32_t if_v6id = 0;171HashMap<String, IP::Interface_Info> if_info;172IP::get_singleton()->get_local_interfaces(&if_info);173for (KeyValue<String, IP::Interface_Info> &E : if_info) {174IP::Interface_Info &c = E.value;175if (c.name != p_if_name) {176continue;177}178179if_v6id = (uint32_t)c.index.to_int();180if (type == IP::TYPE_IPV6) {181break; // IPv6 uses index.182}183184for (const IPAddress &F : c.ip_addresses) {185if (!F.is_ipv4()) {186continue; // Wrong IP type.187}188if_ip = F;189break;190}191break;192}193194if (level == IPPROTO_IP) {195ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER);196struct ip_mreq greq;197int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;198memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4);199memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4);200ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));201} else {202struct ipv6_mreq greq;203int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP;204memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16);205greq.ipv6mr_interface = if_v6id;206ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));207}208ERR_FAIL_COND_V(ret != 0, FAILED);209210return OK;211}212213void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream) {214_sock = p_sock;215_ip_type = p_ip_type;216_is_stream = p_is_stream;217}218219Error NetSocketWinSock::open(Family p_family, Type p_sock_type, IP::Type &ip_type) {220ERR_FAIL_COND_V(p_family != Family::INET, ERR_UNAVAILABLE);221ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);222ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);223224int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;225int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP;226int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;227_sock = socket(family, type, protocol);228229if (_sock == INVALID_SOCKET && ip_type == IP::TYPE_ANY) {230// Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket231// in place of a dual stack one, and further calls to _set_sock_addr will work as expected.232ip_type = IP::TYPE_IPV4;233family = AF_INET;234_sock = socket(family, type, protocol);235}236237ERR_FAIL_COND_V(_sock == INVALID_SOCKET, FAILED);238_ip_type = ip_type;239240if (family == AF_INET6) {241// Select IPv4 over IPv6 mapping.242set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);243}244245if (protocol == IPPROTO_UDP) {246// Make sure to disable broadcasting for UDP sockets.247// Depending on the OS, this option might or might not be enabled by default. Let's normalize it.248set_broadcasting_enabled(false);249}250251_is_stream = p_sock_type == TYPE_TCP;252253if (!_is_stream) {254// Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when255// recv/recvfrom and an ICMP reply was received from a previous send/sendto.256unsigned long disable = 0;257if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) {258print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows.");259}260if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) {261// This feature seems not to be supported on wine.262print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows.");263}264}265return OK;266}267268void NetSocketWinSock::close() {269if (_sock != INVALID_SOCKET) {270closesocket(_sock);271}272273_sock = INVALID_SOCKET;274_ip_type = IP::TYPE_NONE;275_is_stream = false;276}277278Error NetSocketWinSock::bind(Address p_addr) {279ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);280ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);281ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), true), ERR_INVALID_PARAMETER);282283sockaddr_storage addr;284size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);285286if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {287NetError err = _get_socket_error();288print_verbose("Failed to bind socket. Error: " + itos(err) + ".");289close();290return ERR_UNAVAILABLE;291}292293return OK;294}295296Error NetSocketWinSock::listen(int p_max_pending) {297ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);298299if (::listen(_sock, p_max_pending) != 0) {300_get_socket_error();301print_verbose("Failed to listen from socket.");302close();303return FAILED;304}305306return OK;307}308309Error NetSocketWinSock::connect_to_host(Address p_addr) {310ERR_FAIL_COND_V(!p_addr.is_inet(), ERR_UNAVAILABLE);311ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);312ERR_FAIL_COND_V(!_can_use_ip(p_addr.ip(), false), ERR_INVALID_PARAMETER);313314struct sockaddr_storage addr;315size_t addr_size = _set_addr_storage(&addr, p_addr.ip(), p_addr.port(), _ip_type);316317if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) {318NetError err = _get_socket_error();319320switch (err) {321// We are already connected.322case ERR_NET_IS_CONNECTED:323return OK;324// Still waiting to connect, try again in a while.325case ERR_NET_WOULD_BLOCK:326case ERR_NET_IN_PROGRESS:327return ERR_BUSY;328default:329print_verbose("Connection to remote host failed.");330close();331return FAILED;332}333}334335return OK;336}337338Error NetSocketWinSock::poll(PollType p_type, int p_timeout) const {339ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);340341bool ready = false;342fd_set rd, wr, ex;343fd_set *rdp = nullptr;344fd_set *wrp = nullptr;345FD_ZERO(&rd);346FD_ZERO(&wr);347FD_ZERO(&ex);348FD_SET(_sock, &ex);349struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 };350// For blocking operation, pass nullptr timeout pointer to select.351struct timeval *tp = nullptr;352if (p_timeout >= 0) {353// If timeout is non-negative, we want to specify the timeout instead.354tp = &timeout;355}356357switch (p_type) {358case POLL_TYPE_IN:359FD_SET(_sock, &rd);360rdp = &rd;361break;362case POLL_TYPE_OUT:363FD_SET(_sock, &wr);364wrp = ≀365break;366case POLL_TYPE_IN_OUT:367FD_SET(_sock, &rd);368FD_SET(_sock, &wr);369rdp = &rd;370wrp = ≀371}372// WSAPoll is broken: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/.373int ret = select(1, rdp, wrp, &ex, tp);374375if (ret == SOCKET_ERROR) {376return FAILED;377}378379if (ret == 0) {380return ERR_BUSY;381}382383if (FD_ISSET(_sock, &ex)) {384_get_socket_error();385print_verbose("Exception when polling socket.");386return FAILED;387}388389if (rdp && FD_ISSET(_sock, rdp)) {390ready = true;391}392if (wrp && FD_ISSET(_sock, wrp)) {393ready = true;394}395396return ready ? OK : ERR_BUSY;397}398399Error NetSocketWinSock::recv(uint8_t *p_buffer, int p_len, int &r_read) {400ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);401402r_read = ::recv(_sock, (char *)p_buffer, p_len, 0);403404if (r_read < 0) {405NetError err = _get_socket_error();406if (err == ERR_NET_WOULD_BLOCK) {407return ERR_BUSY;408}409410if (err == ERR_NET_BUFFER_TOO_SMALL) {411return ERR_OUT_OF_MEMORY;412}413414return FAILED;415}416417return OK;418}419420Error NetSocketWinSock::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {421ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);422423struct sockaddr_storage from;424socklen_t len = sizeof(struct sockaddr_storage);425memset(&from, 0, len);426427r_read = ::recvfrom(_sock, (char *)p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);428429if (r_read < 0) {430NetError err = _get_socket_error();431if (err == ERR_NET_WOULD_BLOCK) {432return ERR_BUSY;433}434435if (err == ERR_NET_BUFFER_TOO_SMALL) {436return ERR_OUT_OF_MEMORY;437}438439return FAILED;440}441442if (from.ss_family == AF_INET) {443struct sockaddr_in *sin_from = (struct sockaddr_in *)&from;444r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr);445r_port = ntohs(sin_from->sin_port);446} else if (from.ss_family == AF_INET6) {447struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from;448r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr);449r_port = ntohs(s6_from->sin6_port);450} else {451// Unsupported socket family, should never happen.452ERR_FAIL_V(FAILED);453}454455return OK;456}457458Error NetSocketWinSock::send(const uint8_t *p_buffer, int p_len, int &r_sent) {459ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);460461int flags = 0;462r_sent = ::send(_sock, (const char *)p_buffer, p_len, flags);463464if (r_sent < 0) {465NetError err = _get_socket_error();466if (err == ERR_NET_WOULD_BLOCK) {467return ERR_BUSY;468}469if (err == ERR_NET_BUFFER_TOO_SMALL) {470return ERR_OUT_OF_MEMORY;471}472473return FAILED;474}475476return OK;477}478479Error NetSocketWinSock::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {480ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);481482struct sockaddr_storage addr;483size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);484r_sent = ::sendto(_sock, (const char *)p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size);485486if (r_sent < 0) {487NetError err = _get_socket_error();488if (err == ERR_NET_WOULD_BLOCK) {489return ERR_BUSY;490}491if (err == ERR_NET_BUFFER_TOO_SMALL) {492return ERR_OUT_OF_MEMORY;493}494495return FAILED;496}497498return OK;499}500501Error NetSocketWinSock::set_broadcasting_enabled(bool p_enabled) {502ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);503// IPv6 has no broadcast support.504if (_ip_type == IP::TYPE_IPV6) {505return ERR_UNAVAILABLE;506}507508int par = p_enabled ? 1 : 0;509if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, (const char *)&par, sizeof(int)) != 0) {510WARN_PRINT("Unable to change broadcast setting.");511return FAILED;512}513return OK;514}515516void NetSocketWinSock::set_blocking_enabled(bool p_enabled) {517ERR_FAIL_COND(!is_open());518519int ret = 0;520unsigned long par = p_enabled ? 0 : 1;521ret = ioctlsocket(_sock, FIONBIO, &par);522if (ret != 0) {523WARN_PRINT("Unable to change non-block mode.");524}525}526527void NetSocketWinSock::set_ipv6_only_enabled(bool p_enabled) {528ERR_FAIL_COND(!is_open());529// This option is only available in IPv6 sockets.530ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4);531532int par = p_enabled ? 1 : 0;533if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&par, sizeof(int)) != 0) {534WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option.");535}536}537538void NetSocketWinSock::set_tcp_no_delay_enabled(bool p_enabled) {539ERR_FAIL_COND(!is_open());540ERR_FAIL_COND(!_is_stream); // Not TCP.541542int par = p_enabled ? 1 : 0;543if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&par, sizeof(int)) < 0) {544WARN_PRINT("Unable to set TCP no delay option.");545}546}547548void NetSocketWinSock::set_reuse_address_enabled(bool p_enabled) {549ERR_FAIL_COND(!is_open());550551// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying...552// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT553}554555bool NetSocketWinSock::is_open() const {556return _sock != INVALID_SOCKET;557}558559int NetSocketWinSock::get_available_bytes() const {560ERR_FAIL_COND_V(!is_open(), -1);561562unsigned long len;563int ret = ioctlsocket(_sock, FIONREAD, &len);564if (ret == -1) {565_get_socket_error();566print_verbose("Error when checking available bytes on socket.");567return -1;568}569return len;570}571572Error NetSocketWinSock::get_socket_address(Address *r_addr) const {573ERR_FAIL_COND_V(!is_open(), FAILED);574575struct sockaddr_storage saddr;576socklen_t len = sizeof(saddr);577if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {578_get_socket_error();579print_verbose("Error when reading local socket address.");580return FAILED;581}582IPAddress ip;583uint16_t port = 0;584_set_ip_port(&saddr, &ip, &port);585if (r_addr) {586*r_addr = Address(ip, port);587}588return OK;589}590591Ref<NetSocket> NetSocketWinSock::accept(Address &r_addr) {592Ref<NetSocket> out;593ERR_FAIL_COND_V(!is_open(), out);594595struct sockaddr_storage their_addr;596socklen_t size = sizeof(their_addr);597SOCKET fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);598if (fd == INVALID_SOCKET) {599_get_socket_error();600print_verbose("Error when accepting socket connection.");601return out;602}603604IPAddress ip;605uint16_t port = 0;606_set_ip_port(&their_addr, &ip, &port);607r_addr = Address(ip, port);608609NetSocketWinSock *ns = memnew(NetSocketWinSock);610ns->_set_socket(fd, _ip_type, _is_stream);611ns->set_blocking_enabled(false);612return Ref<NetSocket>(ns);613}614615Error NetSocketWinSock::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {616return _change_multicast_group(p_multi_address, p_if_name, true);617}618619Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {620return _change_multicast_group(p_multi_address, p_if_name, false);621}622623#endif // WINDOWS_ENABLED624625626