778 lines
22 KiB
C
778 lines
22 KiB
C
/**
|
|
* @file BDatagram_win.c
|
|
* @author Ambroz Bizjak <ambrop7@gmail.com>
|
|
*
|
|
* @section LICENSE
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the author nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* 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 THE AUTHOR 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 <stdlib.h>
|
|
|
|
#include <base/BLog.h>
|
|
|
|
#include "BDatagram.h"
|
|
|
|
#include <generated/blog_channel_BDatagram.h>
|
|
|
|
static int family_socket_to_sys (int family);
|
|
static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr);
|
|
static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr);
|
|
static void set_pktinfo (SOCKET sock, int family);
|
|
static void report_error (BDatagram *o);
|
|
static void datagram_abort (BDatagram *o);
|
|
static void start_send (BDatagram *o);
|
|
static void start_recv (BDatagram *o);
|
|
static void send_job_handler (BDatagram *o);
|
|
static void recv_job_handler (BDatagram *o);
|
|
static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len);
|
|
static void recv_if_handler_recv (BDatagram *o, uint8_t *data);
|
|
static void send_olap_handler (BDatagram *o, int event, DWORD bytes);
|
|
static void recv_olap_handler (BDatagram *o, int event, DWORD bytes);
|
|
|
|
static int family_socket_to_sys (int family)
|
|
{
|
|
switch (family) {
|
|
case BADDR_TYPE_IPV4:
|
|
return AF_INET;
|
|
case BADDR_TYPE_IPV6:
|
|
return AF_INET6;
|
|
}
|
|
|
|
ASSERT(0);
|
|
return 0;
|
|
}
|
|
|
|
static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr)
|
|
{
|
|
switch (addr.type) {
|
|
case BADDR_TYPE_IPV4: {
|
|
out->len = sizeof(out->addr.ipv4);
|
|
memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4));
|
|
out->addr.ipv4.sin_family = AF_INET;
|
|
out->addr.ipv4.sin_port = addr.ipv4.port;
|
|
out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip;
|
|
} break;
|
|
|
|
case BADDR_TYPE_IPV6: {
|
|
out->len = sizeof(out->addr.ipv6);
|
|
memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6));
|
|
out->addr.ipv6.sin6_family = AF_INET6;
|
|
out->addr.ipv6.sin6_port = addr.ipv6.port;
|
|
out->addr.ipv6.sin6_flowinfo = 0;
|
|
memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16);
|
|
out->addr.ipv6.sin6_scope_id = 0;
|
|
} break;
|
|
|
|
default: ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr)
|
|
{
|
|
switch (addr.addr.generic.sa_family) {
|
|
case AF_INET: {
|
|
ASSERT(addr.len == sizeof(struct sockaddr_in))
|
|
BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port);
|
|
} break;
|
|
|
|
case AF_INET6: {
|
|
ASSERT(addr.len == sizeof(struct sockaddr_in6))
|
|
BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port);
|
|
} break;
|
|
|
|
default: {
|
|
BAddr_InitNone(out);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static void set_pktinfo (SOCKET sock, int family)
|
|
{
|
|
DWORD opt = 1;
|
|
|
|
switch (family) {
|
|
case BADDR_TYPE_IPV4: {
|
|
if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt)) < 0) {
|
|
BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed");
|
|
}
|
|
} break;
|
|
|
|
case BADDR_TYPE_IPV6: {
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&opt, sizeof(opt)) < 0) {
|
|
BLog(BLOG_ERROR, "setsockopt(IPV6_PKTINFO) failed");
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static void report_error (BDatagram *o)
|
|
{
|
|
DebugError_AssertNoError(&o->d_err);
|
|
|
|
// report error
|
|
DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR));
|
|
return;
|
|
}
|
|
|
|
static void datagram_abort (BDatagram *o)
|
|
{
|
|
ASSERT(!o->aborted)
|
|
|
|
// cancel I/O
|
|
if ((o->recv.inited && o->recv.data_have && o->recv.data_busy) || (o->send.inited && o->send.data_len >= 0 && o->send.data_busy)) {
|
|
if (!CancelIo((HANDLE)o->sock)) {
|
|
BLog(BLOG_ERROR, "CancelIo failed");
|
|
}
|
|
}
|
|
|
|
// close socket
|
|
if (closesocket(o->sock) == SOCKET_ERROR) {
|
|
BLog(BLOG_ERROR, "closesocket failed");
|
|
}
|
|
|
|
// wait for receiving to complete
|
|
if (o->recv.inited && o->recv.data_have && o->recv.data_busy) {
|
|
BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL);
|
|
}
|
|
|
|
// wait for sending to complete
|
|
if (o->send.inited && o->send.data_len >= 0 && o->send.data_busy) {
|
|
BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL);
|
|
}
|
|
|
|
// free recv olap
|
|
BReactorIOCPOverlapped_Free(&o->recv.olap);
|
|
|
|
// free send olap
|
|
BReactorIOCPOverlapped_Free(&o->send.olap);
|
|
|
|
// set aborted
|
|
o->aborted = 1;
|
|
}
|
|
|
|
static void start_send (BDatagram *o)
|
|
{
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->send.inited)
|
|
ASSERT(o->send.data_len >= 0)
|
|
ASSERT(!o->send.data_busy)
|
|
ASSERT(o->send.have_addrs)
|
|
|
|
// convert destination address
|
|
addr_socket_to_sys(&o->send.sysaddr, o->send.remote_addr);
|
|
|
|
WSABUF buf;
|
|
buf.buf = (char *)o->send.data;
|
|
buf.len = (o->send.data_len > ULONG_MAX ? ULONG_MAX : o->send.data_len);
|
|
|
|
memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap));
|
|
|
|
if (o->fnWSASendMsg) {
|
|
o->send.msg.name = &o->send.sysaddr.addr.generic;
|
|
o->send.msg.namelen = o->send.sysaddr.len;
|
|
o->send.msg.lpBuffers = &buf;
|
|
o->send.msg.dwBufferCount = 1;
|
|
o->send.msg.Control.buf = (char *)&o->send.cdata;
|
|
o->send.msg.Control.len = sizeof(o->send.cdata);
|
|
o->send.msg.dwFlags = 0;
|
|
|
|
int sum = 0;
|
|
|
|
WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->send.msg);
|
|
|
|
switch (o->send.local_addr.type) {
|
|
case BADDR_TYPE_IPV4: {
|
|
memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo)));
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
cmsg->cmsg_type = IP_PKTINFO;
|
|
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo));
|
|
struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
|
|
pktinfo->ipi_addr.s_addr = o->send.local_addr.ipv4;
|
|
sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo));
|
|
} break;
|
|
case BADDR_TYPE_IPV6: {
|
|
memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)));
|
|
cmsg->cmsg_level = IPPROTO_IPV6;
|
|
cmsg->cmsg_type = IPV6_PKTINFO;
|
|
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in6_pktinfo));
|
|
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
|
|
memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16);
|
|
sum += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo));
|
|
} break;
|
|
}
|
|
|
|
o->send.msg.Control.len = sum;
|
|
|
|
if (o->send.msg.Control.len == 0) {
|
|
o->send.msg.Control.buf = NULL;
|
|
}
|
|
|
|
// send
|
|
int res = o->fnWSASendMsg(o->sock, &o->send.msg, 0, NULL, &o->send.olap.olap, NULL);
|
|
if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
|
|
report_error(o);
|
|
return;
|
|
}
|
|
} else {
|
|
// send
|
|
int res = WSASendTo(o->sock, &buf, 1, NULL, 0, &o->send.sysaddr.addr.generic, o->send.sysaddr.len, &o->send.olap.olap, NULL);
|
|
if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
|
|
report_error(o);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// set busy
|
|
o->send.data_busy = 1;
|
|
}
|
|
|
|
static void start_recv (BDatagram *o)
|
|
{
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->recv.inited)
|
|
ASSERT(o->recv.data_have)
|
|
ASSERT(!o->recv.data_busy)
|
|
ASSERT(o->recv.started)
|
|
|
|
WSABUF buf;
|
|
buf.buf = (char *)o->recv.data;
|
|
buf.len = (o->recv.mtu > ULONG_MAX ? ULONG_MAX : o->recv.mtu);
|
|
|
|
memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap));
|
|
|
|
if (o->fnWSARecvMsg) {
|
|
o->recv.msg.name = &o->recv.sysaddr.addr.generic;
|
|
o->recv.msg.namelen = sizeof(o->recv.sysaddr.addr);
|
|
o->recv.msg.lpBuffers = &buf;
|
|
o->recv.msg.dwBufferCount = 1;
|
|
o->recv.msg.Control.buf = (char *)&o->recv.cdata;
|
|
o->recv.msg.Control.len = sizeof(o->recv.cdata);
|
|
o->recv.msg.dwFlags = 0;
|
|
|
|
// recv
|
|
int res = o->fnWSARecvMsg(o->sock, &o->recv.msg, NULL, &o->recv.olap.olap, NULL);
|
|
if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
|
|
BLog(BLOG_ERROR, "WSARecvMsg failed (%d)", WSAGetLastError());
|
|
report_error(o);
|
|
return;
|
|
}
|
|
} else {
|
|
o->recv.sysaddr.len = sizeof(o->recv.sysaddr.addr);
|
|
|
|
// recv
|
|
DWORD flags = 0;
|
|
int res = WSARecvFrom(o->sock, &buf, 1, NULL, &flags, &o->recv.sysaddr.addr.generic, &o->recv.sysaddr.len, &o->recv.olap.olap, NULL);
|
|
if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
|
|
BLog(BLOG_ERROR, "WSARecvFrom failed (%d)", WSAGetLastError());
|
|
report_error(o);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// set busy
|
|
o->recv.data_busy = 1;
|
|
}
|
|
|
|
static void send_job_handler (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->send.inited)
|
|
ASSERT(o->send.data_len >= 0)
|
|
ASSERT(!o->send.data_busy)
|
|
ASSERT(o->send.have_addrs)
|
|
|
|
// send
|
|
start_send(o);
|
|
return;
|
|
}
|
|
|
|
static void recv_job_handler (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->recv.inited)
|
|
ASSERT(o->recv.data_have)
|
|
ASSERT(!o->recv.data_busy)
|
|
ASSERT(o->recv.started)
|
|
|
|
// recv
|
|
start_recv(o);
|
|
return;
|
|
}
|
|
|
|
static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->send.inited)
|
|
ASSERT(o->send.data_len == -1)
|
|
ASSERT(data_len >= 0)
|
|
ASSERT(data_len <= o->send.mtu)
|
|
|
|
// remember data
|
|
o->send.data = data;
|
|
o->send.data_len = data_len;
|
|
o->send.data_busy = 0;
|
|
|
|
// if have no addresses, wait
|
|
if (!o->send.have_addrs) {
|
|
return;
|
|
}
|
|
|
|
// send
|
|
start_send(o);
|
|
return;
|
|
}
|
|
|
|
static void recv_if_handler_recv (BDatagram *o, uint8_t *data)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->recv.inited)
|
|
ASSERT(!o->recv.data_have)
|
|
|
|
// remember data
|
|
o->recv.data = data;
|
|
o->recv.data_have = 1;
|
|
o->recv.data_busy = 0;
|
|
|
|
// if recv not started yet, wait
|
|
if (!o->recv.started) {
|
|
return;
|
|
}
|
|
|
|
// recv
|
|
start_recv(o);
|
|
return;
|
|
}
|
|
|
|
static void send_olap_handler (BDatagram *o, int event, DWORD bytes)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->send.inited)
|
|
ASSERT(o->send.data_len >= 0)
|
|
ASSERT(o->send.data_busy)
|
|
ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
|
|
|
|
// set not busy
|
|
o->send.data_busy = 0;
|
|
|
|
if (event == BREACTOR_IOCP_EVENT_FAILED) {
|
|
report_error(o);
|
|
return;
|
|
}
|
|
|
|
ASSERT(bytes >= 0)
|
|
ASSERT(bytes <= o->send.data_len)
|
|
|
|
if (bytes < o->send.data_len) {
|
|
BLog(BLOG_ERROR, "sent too little");
|
|
}
|
|
|
|
// if recv wasn't started yet, start it
|
|
if (!o->recv.started) {
|
|
// set recv started
|
|
o->recv.started = 1;
|
|
|
|
// continue receiving
|
|
if (o->recv.inited && o->recv.data_have) {
|
|
ASSERT(!o->recv.data_busy)
|
|
|
|
BPending_Set(&o->recv.job);
|
|
}
|
|
}
|
|
|
|
// set no data
|
|
o->send.data_len = -1;
|
|
|
|
// done
|
|
PacketPassInterface_Done(&o->send.iface);
|
|
}
|
|
|
|
static void recv_olap_handler (BDatagram *o, int event, DWORD bytes)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(o->recv.inited)
|
|
ASSERT(o->recv.data_have)
|
|
ASSERT(o->recv.data_busy)
|
|
ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED)
|
|
|
|
// set not busy
|
|
o->recv.data_busy = 0;
|
|
|
|
if (event == BREACTOR_IOCP_EVENT_FAILED) {
|
|
BLog(BLOG_ERROR, "receiving failed");
|
|
report_error(o);
|
|
return;
|
|
}
|
|
|
|
ASSERT(bytes >= 0)
|
|
ASSERT(bytes <= o->recv.mtu)
|
|
|
|
if (o->fnWSARecvMsg) {
|
|
o->recv.sysaddr.len = o->recv.msg.namelen;
|
|
}
|
|
|
|
// read remote address
|
|
addr_sys_to_socket(&o->recv.remote_addr, o->recv.sysaddr);
|
|
|
|
// read local address
|
|
BIPAddr_InitInvalid(&o->recv.local_addr);
|
|
if (o->fnWSARecvMsg) {
|
|
for (WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->recv.msg); cmsg; cmsg = WSA_CMSG_NXTHDR(&o->recv.msg, cmsg)) {
|
|
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
|
|
struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg);
|
|
BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr);
|
|
}
|
|
else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg);
|
|
BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// set have addresses
|
|
o->recv.have_addrs = 1;
|
|
|
|
// set no data
|
|
o->recv.data_have = 0;
|
|
|
|
// done
|
|
PacketRecvInterface_Done(&o->recv.iface, bytes);
|
|
}
|
|
|
|
int BDatagram_AddressFamilySupported (int family)
|
|
{
|
|
return (family == BADDR_TYPE_IPV4 || family == BADDR_TYPE_IPV6);
|
|
}
|
|
|
|
int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user,
|
|
BDatagram_handler handler)
|
|
{
|
|
ASSERT(BDatagram_AddressFamilySupported(family))
|
|
ASSERT(handler)
|
|
BNetwork_Assert();
|
|
|
|
// init arguments
|
|
o->reactor = reactor;
|
|
o->user = user;
|
|
o->handler = handler;
|
|
|
|
// init socket
|
|
if ((o->sock = WSASocket(family_socket_to_sys(family), SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {
|
|
BLog(BLOG_ERROR, "WSASocket failed");
|
|
goto fail0;
|
|
}
|
|
|
|
DWORD out_bytes;
|
|
|
|
// obtain WSASendMsg
|
|
GUID guid1 = WSAID_WSASENDMSG;
|
|
if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid1, sizeof(guid1), &o->fnWSASendMsg, sizeof(o->fnWSASendMsg), &out_bytes, NULL, NULL) != 0) {
|
|
o->fnWSASendMsg = NULL;
|
|
}
|
|
|
|
// obtain WSARecvMsg
|
|
GUID guid2 = WSAID_WSARECVMSG;
|
|
if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid2, sizeof(guid2), &o->fnWSARecvMsg, sizeof(o->fnWSARecvMsg), &out_bytes, NULL, NULL) != 0) {
|
|
BLog(BLOG_ERROR, "failed to obtain WSARecvMsg");
|
|
o->fnWSARecvMsg = NULL;
|
|
}
|
|
|
|
// associate with IOCP
|
|
if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) {
|
|
BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
|
|
goto fail1;
|
|
}
|
|
|
|
// enable receiving pktinfo
|
|
set_pktinfo(o->sock, family);
|
|
|
|
// set not aborted
|
|
o->aborted = 0;
|
|
|
|
// init send olap
|
|
BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)send_olap_handler);
|
|
|
|
// set have no send addrs
|
|
o->send.have_addrs = 0;
|
|
|
|
// set send not inited
|
|
o->send.inited = 0;
|
|
|
|
// init recv olap
|
|
BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler);
|
|
|
|
// set recv not started
|
|
o->recv.started = 0;
|
|
|
|
// set have no recv addrs
|
|
o->recv.have_addrs = 0;
|
|
|
|
// set recv not inited
|
|
o->recv.inited = 0;
|
|
|
|
DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
|
|
DebugObject_Init(&o->d_obj);
|
|
return 1;
|
|
|
|
fail1:
|
|
if (closesocket(o->sock) == SOCKET_ERROR) {
|
|
BLog(BLOG_ERROR, "closesocket failed");
|
|
}
|
|
fail0:
|
|
return 0;
|
|
}
|
|
|
|
void BDatagram_Free (BDatagram *o)
|
|
{
|
|
DebugObject_Free(&o->d_obj);
|
|
DebugError_Free(&o->d_err);
|
|
ASSERT(!o->recv.inited)
|
|
ASSERT(!o->send.inited)
|
|
|
|
if (!o->aborted) {
|
|
datagram_abort(o);
|
|
}
|
|
}
|
|
|
|
int BDatagram_Bind (BDatagram *o, BAddr addr)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(BDatagram_AddressFamilySupported(addr.type))
|
|
|
|
// translate address
|
|
struct BDatagram_sys_addr sysaddr;
|
|
addr_socket_to_sys(&sysaddr, addr);
|
|
|
|
// bind
|
|
if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) {
|
|
BLog(BLOG_ERROR, "bind failed");
|
|
return 0;
|
|
}
|
|
|
|
// if recv wasn't started yet, start it
|
|
if (!o->recv.started) {
|
|
// set recv started
|
|
o->recv.started = 1;
|
|
|
|
// continue receiving
|
|
if (o->recv.inited && o->recv.data_have) {
|
|
ASSERT(!o->recv.data_busy)
|
|
|
|
BPending_Set(&o->recv.job);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(BDatagram_AddressFamilySupported(remote_addr.type))
|
|
ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type))
|
|
|
|
// set addresses
|
|
o->send.remote_addr = remote_addr;
|
|
o->send.local_addr = local_addr;
|
|
|
|
// set have addresses
|
|
o->send.have_addrs = 1;
|
|
|
|
// start sending
|
|
if (o->send.inited && o->send.data_len >= 0 && !o->send.data_busy) {
|
|
BPending_Set(&o->send.job);
|
|
}
|
|
}
|
|
|
|
int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
if (!o->recv.have_addrs) {
|
|
return 0;
|
|
}
|
|
|
|
*remote_addr = o->recv.remote_addr;
|
|
*local_addr = o->recv.local_addr;
|
|
return 1;
|
|
}
|
|
|
|
int BDatagram_GetLocalAddr (BDatagram *o, BAddr *local_addr)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
|
|
struct BDatagram_sys_addr sysaddr;
|
|
socklen_t addr_size = sizeof(sysaddr.addr.generic);
|
|
if (getsockname(o->sock, &sysaddr.addr.generic, &addr_size) != 0) {
|
|
BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: getsockname failed");
|
|
return 0;
|
|
}
|
|
sysaddr.len = addr_size;
|
|
|
|
BAddr addr;
|
|
addr_sys_to_socket(&addr, sysaddr);
|
|
|
|
if (addr.type == BADDR_TYPE_NONE) {
|
|
BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: Unsupported address family "
|
|
"from getsockname: %d", sysaddr.addr.generic.sa_family);
|
|
return 0;
|
|
}
|
|
|
|
*local_addr = addr;
|
|
return 1;
|
|
}
|
|
|
|
int BDatagram_SetReuseAddr (BDatagram *o, int reuse)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(reuse == 0 || reuse == 1)
|
|
|
|
if (setsockopt(o->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void BDatagram_SendAsync_Init (BDatagram *o, int mtu)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(!o->send.inited)
|
|
ASSERT(mtu >= 0)
|
|
|
|
// init arguments
|
|
o->send.mtu = mtu;
|
|
|
|
// init interface
|
|
PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor));
|
|
|
|
// init job
|
|
BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o);
|
|
|
|
// set have no data
|
|
o->send.data_len = -1;
|
|
|
|
// set inited
|
|
o->send.inited = 1;
|
|
}
|
|
|
|
void BDatagram_SendAsync_Free (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(o->send.inited)
|
|
|
|
// abort if busy
|
|
if (o->send.data_len >= 0 && o->send.data_busy && !o->aborted) {
|
|
datagram_abort(o);
|
|
}
|
|
|
|
// free job
|
|
BPending_Free(&o->send.job);
|
|
|
|
// free interface
|
|
PacketPassInterface_Free(&o->send.iface);
|
|
|
|
// set not inited
|
|
o->send.inited = 0;
|
|
}
|
|
|
|
PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(o->send.inited)
|
|
|
|
return &o->send.iface;
|
|
}
|
|
|
|
void BDatagram_RecvAsync_Init (BDatagram *o, int mtu)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
DebugError_AssertNoError(&o->d_err);
|
|
ASSERT(!o->aborted)
|
|
ASSERT(!o->recv.inited)
|
|
ASSERT(mtu >= 0)
|
|
|
|
// init arguments
|
|
o->recv.mtu = mtu;
|
|
|
|
// init interface
|
|
PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor));
|
|
|
|
// init job
|
|
BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o);
|
|
|
|
// set have no data
|
|
o->recv.data_have = 0;
|
|
|
|
// set inited
|
|
o->recv.inited = 1;
|
|
}
|
|
|
|
void BDatagram_RecvAsync_Free (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(o->recv.inited)
|
|
|
|
// abort if busy
|
|
if (o->recv.data_have && o->recv.data_busy && !o->aborted) {
|
|
datagram_abort(o);
|
|
}
|
|
|
|
// free job
|
|
BPending_Free(&o->recv.job);
|
|
|
|
// free interface
|
|
PacketRecvInterface_Free(&o->recv.iface);
|
|
|
|
// set not inited
|
|
o->recv.inited = 0;
|
|
}
|
|
|
|
PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o)
|
|
{
|
|
DebugObject_Access(&o->d_obj);
|
|
ASSERT(o->recv.inited)
|
|
|
|
return &o->recv.iface;
|
|
}
|