badvpn/system/BDatagram_win.c
2020-06-06 02:22:20 +02:00

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;
}