summaryrefslogtreecommitdiff
path: root/pjlib/src/pj/sock_uwp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src/pj/sock_uwp.cpp')
-rw-r--r--pjlib/src/pj/sock_uwp.cpp1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/pjlib/src/pj/sock_uwp.cpp b/pjlib/src/pj/sock_uwp.cpp
new file mode 100644
index 00000000..876c3287
--- /dev/null
+++ b/pjlib/src/pj/sock_uwp.cpp
@@ -0,0 +1,1502 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2016 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/math.h>
+#include <pj/os.h>
+#include <pj/compat/socket.h>
+
+#include <ppltasks.h>
+#include <string>
+
+#define THIS_FILE "sock_uwp.cpp"
+
+#include "sock_uwp.h"
+
+ /*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family.
+ */
+const pj_uint16_t PJ_AF_UNSPEC = AF_UNSPEC;
+const pj_uint16_t PJ_AF_UNIX = AF_UNIX;
+const pj_uint16_t PJ_AF_INET = AF_INET;
+const pj_uint16_t PJ_AF_INET6 = AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET = AF_PACKET;
+#else
+const pj_uint16_t PJ_AF_PACKET = 0xFFFF;
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA = AF_IRDA;
+#else
+const pj_uint16_t PJ_AF_IRDA = 0xFFFF;
+#endif
+
+/*
+* Socket types conversion.
+* The values here are indexed based on pj_sock_type
+*/
+const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM;
+
+/*
+* Socket level values.
+*/
+const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP = SOL_IP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+const pj_uint16_t PJ_SOL_IP = IPPROTO_IP;
+#else
+const pj_uint16_t PJ_SOL_IP = 0;
+#endif /* SOL_IP */
+
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP = SOL_TCP;
+#elif defined(IPPROTO_TCP)
+const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP;
+#else
+const pj_uint16_t PJ_SOL_TCP = 6;
+#endif /* SOL_TCP */
+
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP = SOL_UDP;
+#elif defined(IPPROTO_UDP)
+const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP;
+#else
+const pj_uint16_t PJ_SOL_UDP = 17;
+#endif /* SOL_UDP */
+
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+# if defined(IPPROTO_IPV6) || (_WIN32_WINNT >= 0x0501)
+const pj_uint16_t PJ_SOL_IPV6 = IPPROTO_IPV6;
+# else
+const pj_uint16_t PJ_SOL_IPV6 = 41;
+# endif
+#else
+const pj_uint16_t PJ_SOL_IPV6 = 41;
+#endif /* SOL_IPV6 */
+
+/* IP_TOS */
+#ifdef IP_TOS
+const pj_uint16_t PJ_IP_TOS = IP_TOS;
+#else
+const pj_uint16_t PJ_IP_TOS = 1;
+#endif
+
+
+/* TOS settings (declared in netinet/ip.h) */
+#ifdef IPTOS_LOWDELAY
+const pj_uint16_t PJ_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
+#else
+const pj_uint16_t PJ_IPTOS_LOWDELAY = 0x10;
+#endif
+#ifdef IPTOS_THROUGHPUT
+const pj_uint16_t PJ_IPTOS_THROUGHPUT = IPTOS_THROUGHPUT;
+#else
+const pj_uint16_t PJ_IPTOS_THROUGHPUT = 0x08;
+#endif
+#ifdef IPTOS_RELIABILITY
+const pj_uint16_t PJ_IPTOS_RELIABILITY = IPTOS_RELIABILITY;
+#else
+const pj_uint16_t PJ_IPTOS_RELIABILITY = 0x04;
+#endif
+#ifdef IPTOS_MINCOST
+const pj_uint16_t PJ_IPTOS_MINCOST = IPTOS_MINCOST;
+#else
+const pj_uint16_t PJ_IPTOS_MINCOST = 0x02;
+#endif
+
+
+/* optname values. */
+const pj_uint16_t PJ_SO_TYPE = SO_TYPE;
+const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF;
+const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF;
+const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY;
+const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR;
+#ifdef SO_NOSIGPIPE
+const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE;
+#else
+const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF;
+#endif
+#if defined(SO_PRIORITY)
+const pj_uint16_t PJ_SO_PRIORITY = SO_PRIORITY;
+#else
+/* This is from Linux, YMMV */
+const pj_uint16_t PJ_SO_PRIORITY = 12;
+#endif
+
+/* Multicasting is not supported e.g. in PocketPC 2003 SDK */
+#ifdef IP_MULTICAST_IF
+const pj_uint16_t PJ_IP_MULTICAST_IF = IP_MULTICAST_IF;
+const pj_uint16_t PJ_IP_MULTICAST_TTL = IP_MULTICAST_TTL;
+const pj_uint16_t PJ_IP_MULTICAST_LOOP = IP_MULTICAST_LOOP;
+const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = IP_ADD_MEMBERSHIP;
+const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = IP_DROP_MEMBERSHIP;
+#else
+const pj_uint16_t PJ_IP_MULTICAST_IF = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_TTL = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_LOOP = 0xFFFF;
+const pj_uint16_t PJ_IP_ADD_MEMBERSHIP = 0xFFFF;
+const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF;
+#endif
+
+/* recv() and send() flags */
+const int PJ_MSG_OOB = MSG_OOB;
+const int PJ_MSG_PEEK = MSG_PEEK;
+const int PJ_MSG_DONTROUTE = MSG_DONTROUTE;
+
+
+
+using namespace Platform;
+using namespace Windows::Foundation;
+using namespace Windows::Networking;
+using namespace Windows::Networking::Sockets;
+using namespace Windows::Storage::Streams;
+
+
+ref class PjUwpSocketDatagramRecvHelper sealed
+{
+internal:
+ PjUwpSocketDatagramRecvHelper(PjUwpSocket* uwp_sock_) :
+ uwp_sock(uwp_sock_), avail_data_len(0), recv_args(nullptr)
+ {
+ recv_wait = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
+ event_token = uwp_sock->datagram_sock->MessageReceived +=
+ ref new TypedEventHandler<DatagramSocket^,
+ DatagramSocketMessageReceivedEventArgs^>
+ (this, &PjUwpSocketDatagramRecvHelper::OnMessageReceived);
+ }
+
+ void OnMessageReceived(DatagramSocket ^sender,
+ DatagramSocketMessageReceivedEventArgs ^args)
+ {
+ try {
+ if (uwp_sock->sock_state >= SOCKSTATE_DISCONNECTED)
+ return;
+
+ recv_args = args;
+ avail_data_len = args->GetDataReader()->UnconsumedBufferLength;
+
+ if (uwp_sock->cb.on_read) {
+ (*uwp_sock->cb.on_read)(uwp_sock, avail_data_len);
+ }
+
+ WaitForSingleObjectEx(recv_wait, INFINITE, false);
+ } catch (...) {}
+ }
+
+ pj_status_t ReadDataIfAvailable(void *buf, pj_ssize_t *len,
+ pj_sockaddr_t *from)
+ {
+ if (avail_data_len <= 0)
+ return PJ_ENOTFOUND;
+
+ if (*len < avail_data_len)
+ return PJ_ETOOSMALL;
+
+ // Read data
+ auto reader = recv_args->GetDataReader();
+ auto buffer = reader->ReadBuffer(avail_data_len);
+ unsigned char *p;
+ GetRawBufferFromIBuffer(buffer, &p);
+ pj_memcpy((void*) buf, p, avail_data_len);
+ *len = avail_data_len;
+
+ // Get source address
+ wstr_addr_to_sockaddr(recv_args->RemoteAddress->CanonicalName->Data(),
+ recv_args->RemotePort->Data(), from);
+
+ // finally
+ avail_data_len = 0;
+ SetEvent(recv_wait);
+
+ return PJ_SUCCESS;
+ }
+
+private:
+
+ ~PjUwpSocketDatagramRecvHelper()
+ {
+ if (uwp_sock->datagram_sock)
+ uwp_sock->datagram_sock->MessageReceived -= event_token;
+
+ SetEvent(recv_wait);
+ CloseHandle(recv_wait);
+ }
+
+ PjUwpSocket* uwp_sock;
+ DatagramSocketMessageReceivedEventArgs^ recv_args;
+ EventRegistrationToken event_token;
+ HANDLE recv_wait;
+ int avail_data_len;
+};
+
+
+ref class PjUwpSocketListenerHelper sealed
+{
+internal:
+ PjUwpSocketListenerHelper(PjUwpSocket* uwp_sock_) :
+ uwp_sock(uwp_sock_), conn_args(nullptr)
+ {
+ conn_wait = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
+ event_token = uwp_sock->listener_sock->ConnectionReceived +=
+ ref new TypedEventHandler<StreamSocketListener^, StreamSocketListenerConnectionReceivedEventArgs^>
+ (this, &PjUwpSocketListenerHelper::OnConnectionReceived);
+ }
+
+ void OnConnectionReceived(StreamSocketListener ^sender,
+ StreamSocketListenerConnectionReceivedEventArgs ^args)
+ {
+ try {
+ conn_args = args;
+
+ if (uwp_sock->cb.on_accept) {
+ (*uwp_sock->cb.on_accept)(uwp_sock);
+ }
+
+ WaitForSingleObjectEx(conn_wait, INFINITE, false);
+ } catch (Exception^ e) {}
+ }
+
+ pj_status_t GetAcceptedSocket(StreamSocket^& stream_sock)
+ {
+ if (conn_args == nullptr)
+ return PJ_ENOTFOUND;
+
+ stream_sock = conn_args->Socket;
+
+ // finally
+ conn_args = nullptr;
+ SetEvent(conn_wait);
+
+ return PJ_SUCCESS;
+ }
+
+private:
+
+ ~PjUwpSocketListenerHelper()
+ {
+ if (uwp_sock->listener_sock)
+ uwp_sock->listener_sock->ConnectionReceived -= event_token;
+
+ SetEvent(conn_wait);
+ CloseHandle(conn_wait);
+ }
+
+ PjUwpSocket* uwp_sock;
+ StreamSocketListenerConnectionReceivedEventArgs^ conn_args;
+ EventRegistrationToken event_token;
+ HANDLE conn_wait;
+};
+
+
+PjUwpSocket::PjUwpSocket(int af_, int type_, int proto_) :
+ af(af_), type(type_), proto(proto_),
+ sock_type(SOCKTYPE_UNKNOWN),
+ sock_state(SOCKSTATE_NULL),
+ is_blocking(PJ_TRUE),
+ has_pending_bind(PJ_FALSE),
+ has_pending_send(PJ_FALSE),
+ has_pending_recv(PJ_FALSE)
+{
+ pj_sockaddr_init(pj_AF_INET(), &local_addr, NULL, 0);
+ pj_sockaddr_init(pj_AF_INET(), &remote_addr, NULL, 0);
+}
+
+PjUwpSocket::~PjUwpSocket()
+{
+ DeinitSocket();
+}
+
+PjUwpSocket* PjUwpSocket::CreateAcceptSocket(Windows::Networking::Sockets::StreamSocket^ stream_sock_)
+{
+ PjUwpSocket *new_sock = new PjUwpSocket(pj_AF_INET(), pj_SOCK_STREAM(), 0);
+ new_sock->stream_sock = stream_sock_;
+ new_sock->sock_type = SOCKTYPE_STREAM;
+ new_sock->sock_state = SOCKSTATE_CONNECTED;
+ new_sock->socket_reader = ref new DataReader(new_sock->stream_sock->InputStream);
+ new_sock->socket_writer = ref new DataWriter(new_sock->stream_sock->OutputStream);
+ new_sock->socket_reader->InputStreamOptions = InputStreamOptions::Partial;
+ new_sock->send_buffer = ref new Buffer(SEND_BUFFER_SIZE);
+ new_sock->is_blocking = is_blocking;
+
+ // Update local & remote address
+ wstr_addr_to_sockaddr(stream_sock_->Information->RemoteAddress->CanonicalName->Data(),
+ stream_sock_->Information->RemotePort->Data(),
+ &new_sock->remote_addr);
+ wstr_addr_to_sockaddr(stream_sock_->Information->LocalAddress->CanonicalName->Data(),
+ stream_sock_->Information->LocalPort->Data(),
+ &new_sock->local_addr);
+
+ return new_sock;
+}
+
+
+pj_status_t PjUwpSocket::InitSocket(enum PjUwpSocketType sock_type_)
+{
+ PJ_ASSERT_RETURN(sock_type_ > SOCKTYPE_UNKNOWN, PJ_EINVAL);
+
+ sock_type = sock_type_;
+ if (sock_type == SOCKTYPE_LISTENER) {
+ listener_sock = ref new StreamSocketListener();
+ } else if (sock_type == SOCKTYPE_STREAM) {
+ stream_sock = ref new StreamSocket();
+ } else if (sock_type == SOCKTYPE_DATAGRAM) {
+ datagram_sock = ref new DatagramSocket();
+ } else {
+ pj_assert(!"Invalid socket type");
+ return PJ_EINVAL;
+ }
+
+ if (sock_type == SOCKTYPE_DATAGRAM || sock_type == SOCKTYPE_STREAM) {
+ send_buffer = ref new Buffer(SEND_BUFFER_SIZE);
+ }
+
+ sock_state = SOCKSTATE_INITIALIZED;
+
+ return PJ_SUCCESS;
+}
+
+
+void PjUwpSocket::DeinitSocket()
+{
+ if (stream_sock) {
+ concurrency::create_task(stream_sock->CancelIOAsync()).wait();
+ }
+ if (datagram_sock && has_pending_send) {
+ concurrency::create_task(datagram_sock->CancelIOAsync()).wait();
+ }
+ if (listener_sock) {
+ concurrency::create_task(listener_sock->CancelIOAsync()).wait();
+ }
+ while (has_pending_recv) pj_thread_sleep(10);
+
+ stream_sock = nullptr;
+ datagram_sock = nullptr;
+ dgram_recv_helper = nullptr;
+ listener_sock = nullptr;
+ listener_helper = nullptr;
+ socket_writer = nullptr;
+ socket_reader = nullptr;
+ sock_state = SOCKSTATE_NULL;
+}
+
+pj_status_t PjUwpSocket::Bind(const pj_sockaddr_t *addr)
+{
+ /* Not initialized yet, socket type is perhaps TCP, just not decided yet
+ * whether it is a stream or a listener.
+ */
+ if (sock_state < SOCKSTATE_INITIALIZED) {
+ pj_sockaddr_cp(&local_addr, addr);
+ has_pending_bind = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ PJ_ASSERT_RETURN(sock_state == SOCKSTATE_INITIALIZED, PJ_EINVALIDOP);
+ if (sock_type != SOCKTYPE_DATAGRAM && sock_type != SOCKTYPE_LISTENER)
+ return PJ_EINVALIDOP;
+
+ if (has_pending_bind) {
+ has_pending_bind = PJ_FALSE;
+ if (!addr)
+ addr = &local_addr;
+ }
+
+ /* If no bound address is set, just return */
+ if (!pj_sockaddr_has_addr(addr) && !pj_sockaddr_get_port(addr))
+ return PJ_SUCCESS;
+
+ if (addr != &local_addr)
+ pj_sockaddr_cp(&local_addr, addr);
+
+ HRESULT err = 0;
+ concurrency::create_task([this, addr, &err]() {
+ HostName ^host;
+ int port;
+
+ sockaddr_to_hostname_port(addr, host, &port);
+ if (pj_sockaddr_has_addr(addr)) {
+ if (sock_type == SOCKTYPE_DATAGRAM)
+ return datagram_sock->BindEndpointAsync(host, port.ToString());
+ else
+ return listener_sock->BindEndpointAsync(host, port.ToString());
+ } else /* if (pj_sockaddr_get_port(addr) != 0) */ {
+ if (sock_type == SOCKTYPE_DATAGRAM)
+ return datagram_sock->BindServiceNameAsync(port.ToString());
+ else
+ return listener_sock->BindServiceNameAsync(port.ToString());
+ }
+ }).then([this, &err](concurrency::task<void> t)
+ {
+ try {
+ if (!err)
+ t.get();
+ } catch (Exception^ e) {
+ err = e->HResult;
+ }
+ }).get();
+
+ return (err? PJ_RETURN_OS_ERROR(err) : PJ_SUCCESS);
+}
+
+
+pj_status_t PjUwpSocket::SendImp(const void *buf, pj_ssize_t *len)
+{
+ if (has_pending_send)
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+
+ if (*len > (pj_ssize_t)send_buffer->Capacity)
+ return PJ_ETOOBIG;
+
+ CopyToIBuffer((unsigned char*)buf, *len, send_buffer);
+ send_buffer->Length = *len;
+ socket_writer->WriteBuffer(send_buffer);
+
+ /* Blocking version */
+ if (is_blocking) {
+ pj_status_t status = PJ_SUCCESS;
+ concurrency::cancellation_token_source cts;
+ auto cts_token = cts.get_token();
+ auto t = concurrency::create_task(socket_writer->StoreAsync(),
+ cts_token);
+ *len = cancel_after_timeout(t, cts, (unsigned int)WRITE_TIMEOUT).
+ then([cts_token, &status](concurrency::task<unsigned int> t_)
+ {
+ int sent = 0;
+ try {
+ if (cts_token.is_canceled())
+ status = PJ_ETIMEDOUT;
+ else
+ sent = t_.get();
+ } catch (Exception^ e) {
+ status = PJ_RETURN_OS_ERROR(e->HResult);
+ }
+ return sent;
+ }).get();
+
+ return status;
+ }
+
+ /* Non-blocking version */
+ has_pending_send = PJ_TRUE;
+ concurrency::create_task(socket_writer->StoreAsync()).
+ then([this](concurrency::task<unsigned int> t_)
+ {
+ try {
+ unsigned int l = t_.get();
+ has_pending_send = PJ_FALSE;
+
+ // invoke callback
+ if (cb.on_write) {
+ (*cb.on_write)(this, l);
+ }
+ } catch (...) {
+ has_pending_send = PJ_FALSE;
+ sock_state = SOCKSTATE_ERROR;
+ DeinitSocket();
+
+ // invoke callback
+ if (cb.on_write) {
+ (*cb.on_write)(this, -PJ_EUNKNOWN);
+ }
+ }
+ });
+
+ return PJ_SUCCESS;
+}
+
+
+pj_status_t PjUwpSocket::Send(const void *buf, pj_ssize_t *len)
+{
+ if ((sock_type!=SOCKTYPE_STREAM && sock_type!=SOCKTYPE_DATAGRAM) ||
+ (sock_state!=SOCKSTATE_CONNECTED))
+ {
+ return PJ_EINVALIDOP;
+ }
+
+ /* Sending for SOCKTYPE_DATAGRAM is implemented in pj_sock_sendto() */
+ if (sock_type == SOCKTYPE_DATAGRAM) {
+ return SendTo(buf, len, &remote_addr);
+ }
+
+ return SendImp(buf, len);
+}
+
+
+pj_status_t PjUwpSocket::SendTo(const void *buf, pj_ssize_t *len,
+ const pj_sockaddr_t *to)
+{
+ if (sock_type != SOCKTYPE_DATAGRAM || sock_state < SOCKSTATE_INITIALIZED
+ || sock_state >= SOCKSTATE_DISCONNECTED)
+ {
+ return PJ_EINVALIDOP;
+ }
+
+ if (has_pending_send)
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+
+ if (*len > (pj_ssize_t)send_buffer->Capacity)
+ return PJ_ETOOBIG;
+
+ HostName ^hostname;
+ int port;
+ sockaddr_to_hostname_port(to, hostname, &port);
+
+ concurrency::cancellation_token_source cts;
+ auto cts_token = cts.get_token();
+ auto t = concurrency::create_task(datagram_sock->GetOutputStreamAsync(
+ hostname, port.ToString()), cts_token);
+ pj_status_t status = PJ_SUCCESS;
+
+ cancel_after_timeout(t, cts, (unsigned int)WRITE_TIMEOUT).
+ then([this, cts_token, &status](concurrency::task<IOutputStream^> t_)
+ {
+ try {
+ if (cts_token.is_canceled()) {
+ status = PJ_ETIMEDOUT;
+ } else {
+ IOutputStream^ outstream = t_.get();
+ socket_writer = ref new DataWriter(outstream);
+ }
+ } catch (Exception^ e) {
+ status = PJ_RETURN_OS_ERROR(e->HResult);
+ }
+ }).get();
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = SendImp(buf, len);
+ if ((status == PJ_SUCCESS || status == PJ_EPENDING) &&
+ sock_state < SOCKSTATE_CONNECTED)
+ {
+ sock_state = SOCKSTATE_CONNECTED;
+ }
+
+ return status;
+}
+
+
+int PjUwpSocket::ConsumeReadBuffer(void *buf, int max_len)
+{
+ if (socket_reader->UnconsumedBufferLength == 0)
+ return 0;
+
+ int read_len = PJ_MIN((int)socket_reader->UnconsumedBufferLength,max_len);
+ IBuffer^ buffer = socket_reader->ReadBuffer(read_len);
+ read_len = buffer->Length;
+ CopyFromIBuffer((unsigned char*)buf, read_len, buffer);
+ return read_len;
+}
+
+
+pj_status_t PjUwpSocket::Recv(void *buf, pj_ssize_t *len)
+{
+ /* Only for TCP, at least for now! */
+ if (sock_type == SOCKTYPE_DATAGRAM)
+ return PJ_ENOTSUP;
+
+ if (sock_type != SOCKTYPE_STREAM || sock_state != SOCKSTATE_CONNECTED)
+ return PJ_EINVALIDOP;
+
+ if (has_pending_recv)
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+
+ /* First check if there is already some data in the read buffer */
+ if (buf) {
+ int avail_len = ConsumeReadBuffer(buf, *len);
+ if (avail_len > 0) {
+ *len = avail_len;
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Blocking version */
+ if (is_blocking) {
+ pj_status_t status = PJ_SUCCESS;
+ concurrency::cancellation_token_source cts;
+ auto cts_token = cts.get_token();
+ auto t = concurrency::create_task(socket_reader->LoadAsync(*len),
+ cts_token);
+ *len = cancel_after_timeout(t, cts, READ_TIMEOUT)
+ .then([this, len, buf, cts_token, &status]
+ (concurrency::task<unsigned int> t_)
+ {
+ try {
+ if (cts_token.is_canceled()) {
+ status = PJ_ETIMEDOUT;
+ return 0;
+ }
+ t_.get();
+ } catch (Exception^) {
+ status = PJ_ETIMEDOUT;
+ return 0;
+ }
+
+ *len = ConsumeReadBuffer(buf, *len);
+ return (int)*len;
+ }).get();
+
+ return status;
+ }
+
+ /* Non-blocking version */
+
+ concurrency::cancellation_token_source cts;
+ auto cts_token = cts.get_token();
+
+ has_pending_recv = PJ_TRUE;
+ concurrency::create_task(socket_reader->LoadAsync(*len), cts_token)
+ .then([this, cts_token](concurrency::task<unsigned int> t_)
+ {
+ try {
+ if (cts_token.is_canceled()) {
+ has_pending_recv = PJ_FALSE;
+
+ // invoke callback
+ if (cb.on_read) {
+ (*cb.on_read)(this, -PJ_EUNKNOWN);
+ }
+ return;
+ }
+
+ t_.get();
+ has_pending_recv = PJ_FALSE;
+
+ // invoke callback
+ int read_len = socket_reader->UnconsumedBufferLength;
+ if (read_len > 0 && cb.on_read) {
+ (*cb.on_read)(this, read_len);
+ }
+ } catch (...) {
+ has_pending_recv = PJ_FALSE;
+
+ // invoke callback
+ if (cb.on_read) {
+ (*cb.on_read)(this, -PJ_EUNKNOWN);
+ }
+ }
+ });
+
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+}
+
+
+pj_status_t PjUwpSocket::RecvFrom(void *buf, pj_ssize_t *len,
+ pj_sockaddr_t *from)
+{
+ if (sock_type != SOCKTYPE_DATAGRAM || sock_state < SOCKSTATE_INITIALIZED
+ || sock_state >= SOCKSTATE_DISCONNECTED)
+ {
+ return PJ_EINVALIDOP;
+ }
+
+ /* Start receive, if not yet */
+ if (dgram_recv_helper == nullptr) {
+ dgram_recv_helper = ref new PjUwpSocketDatagramRecvHelper(this);
+ }
+
+ /* Try to read any available data first */
+ if (buf || is_blocking) {
+ pj_status_t status;
+ status = dgram_recv_helper->ReadDataIfAvailable(buf, len, from);
+ if (status != PJ_ENOTFOUND)
+ return status;
+ }
+
+ /* Blocking version */
+ if (is_blocking) {
+ int max_loop = 0;
+ pj_status_t status = PJ_ENOTFOUND;
+ while (status == PJ_ENOTFOUND && sock_state <= SOCKSTATE_CONNECTED)
+ {
+ status = dgram_recv_helper->ReadDataIfAvailable(buf, len, from);
+ if (status != PJ_SUCCESS)
+ pj_thread_sleep(100);
+
+ if (++max_loop > 10)
+ return PJ_ETIMEDOUT;
+ }
+ return status;
+ }
+
+ /* For non-blocking version, just return PJ_EPENDING */
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+}
+
+
+pj_status_t PjUwpSocket::Connect(const pj_sockaddr_t *addr)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN((sock_type == SOCKTYPE_UNKNOWN && sock_state == SOCKSTATE_NULL) ||
+ (sock_type == SOCKTYPE_DATAGRAM && sock_state == SOCKSTATE_INITIALIZED),
+ PJ_EINVALIDOP);
+
+ if (sock_type == SOCKTYPE_UNKNOWN) {
+ InitSocket(SOCKTYPE_STREAM);
+ // No need to check pending bind, no bind for TCP client socket
+ }
+
+ pj_sockaddr_cp(&remote_addr, addr);
+
+ auto t = concurrency::create_task([this, addr]()
+ {
+ HostName ^hostname;
+ int port;
+ sockaddr_to_hostname_port(&remote_addr, hostname, &port);
+ if (sock_type == SOCKTYPE_STREAM)
+ return stream_sock->ConnectAsync(hostname, port.ToString(),
+ SocketProtectionLevel::PlainSocket);
+ else
+ return datagram_sock->ConnectAsync(hostname, port.ToString());
+ }).then([=](concurrency::task<void> t_)
+ {
+ try {
+ t_.get();
+
+ sock_state = SOCKSTATE_CONNECTED;
+
+ // Update local & remote address
+ HostName^ local_address;
+ String^ local_port;
+
+ if (sock_type == SOCKTYPE_STREAM) {
+ local_address = stream_sock->Information->LocalAddress;
+ local_port = stream_sock->Information->LocalPort;
+
+ socket_reader = ref new DataReader(stream_sock->InputStream);
+ socket_writer = ref new DataWriter(stream_sock->OutputStream);
+ socket_reader->InputStreamOptions = InputStreamOptions::Partial;
+ } else {
+ local_address = datagram_sock->Information->LocalAddress;
+ local_port = datagram_sock->Information->LocalPort;
+ }
+ if (local_address && local_port) {
+ wstr_addr_to_sockaddr(local_address->CanonicalName->Data(),
+ local_port->Data(),
+ &local_addr);
+ }
+
+ if (!is_blocking && cb.on_connect) {
+ (*cb.on_connect)(this, PJ_SUCCESS);
+ }
+ return (pj_status_t)PJ_SUCCESS;
+
+ } catch (Exception^ ex) {
+
+ SocketErrorStatus status = SocketError::GetStatus(ex->HResult);
+
+ switch (status)
+ {
+ case SocketErrorStatus::UnreachableHost:
+ break;
+ case SocketErrorStatus::ConnectionTimedOut:
+ break;
+ case SocketErrorStatus::ConnectionRefused:
+ break;
+ default:
+ break;
+ }
+
+ if (!is_blocking && cb.on_connect) {
+ (*cb.on_connect)(this, PJ_EUNKNOWN);
+ }
+
+ return (pj_status_t)PJ_EUNKNOWN;
+ }
+ });
+
+ if (!is_blocking)
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_CONNECT_ERROR_VAL);
+
+ try {
+ status = t.get();
+ } catch (Exception^) {
+ return PJ_EUNKNOWN;
+ }
+ return status;
+}
+
+pj_status_t PjUwpSocket::Listen()
+{
+ PJ_ASSERT_RETURN((sock_type == SOCKTYPE_UNKNOWN) ||
+ (sock_type == SOCKTYPE_LISTENER &&
+ sock_state == SOCKSTATE_INITIALIZED),
+ PJ_EINVALIDOP);
+
+ if (sock_type == SOCKTYPE_UNKNOWN)
+ InitSocket(SOCKTYPE_LISTENER);
+
+ if (has_pending_bind)
+ Bind();
+
+ /* Start listen */
+ if (listener_helper == nullptr) {
+ listener_helper = ref new PjUwpSocketListenerHelper(this);
+ }
+
+ return PJ_SUCCESS;
+}
+
+pj_status_t PjUwpSocket::Accept(PjUwpSocket **new_sock)
+{
+ if (sock_type != SOCKTYPE_LISTENER || sock_state != SOCKSTATE_INITIALIZED)
+ return PJ_EINVALIDOP;
+
+ StreamSocket^ accepted_sock;
+ pj_status_t status = listener_helper->GetAcceptedSocket(accepted_sock);
+ if (status == PJ_ENOTFOUND)
+ return PJ_RETURN_OS_ERROR(PJ_BLOCKING_ERROR_VAL);
+
+ if (status != PJ_SUCCESS)
+ return status;
+
+ *new_sock = CreateAcceptSocket(accepted_sock);
+ return PJ_SUCCESS;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PJLIB's sock.h implementation
+//
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+ return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+ return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+ return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+ return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
+{
+ return inet_ntoa(*(struct in_addr*)&inaddr);
+}
+
+/*
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to.
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
+{
+ char tempaddr[PJ_INET_ADDRSTRLEN];
+
+ /* Initialize output with PJ_INADDR_NONE.
+ * Some apps relies on this instead of the return value
+ * (and anyway the return value is quite confusing!)
+ */
+ inp->s_addr = PJ_INADDR_NONE;
+
+ /* Caution:
+ * this function might be called with cp->slen >= 16
+ * (i.e. when called with hostname to check if it's an IP addr).
+ */
+ PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
+ if (cp->slen >= PJ_INET_ADDRSTRLEN) {
+ return 0;
+ }
+
+ pj_memcpy(tempaddr, cp->ptr, cp->slen);
+ tempaddr[cp->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
+ return inet_aton(tempaddr, (struct in_addr*)inp);
+#else
+ inp->s_addr = inet_addr(tempaddr);
+ return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
+#endif
+}
+
+/*
+ * Convert text to IPv4/IPv6 address.
+ */
+PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst)
+{
+ char tempaddr[PJ_INET6_ADDRSTRLEN];
+
+ PJ_ASSERT_RETURN(af == PJ_AF_INET || af == PJ_AF_INET6, PJ_EAFNOTSUP);
+ PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL);
+
+ /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be
+ * compatible with pj_inet_aton()
+ */
+ if (af == PJ_AF_INET) {
+ ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE;
+ }
+
+ /* Caution:
+ * this function might be called with cp->slen >= 46
+ * (i.e. when called with hostname to check if it's an IP addr).
+ */
+ if (src->slen >= PJ_INET6_ADDRSTRLEN) {
+ return PJ_ENAMETOOLONG;
+ }
+
+ pj_memcpy(tempaddr, src->ptr, src->slen);
+ tempaddr[src->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0
+ /*
+ * Implementation using inet_pton()
+ */
+ if (inet_pton(af, tempaddr, dst) != 1) {
+ pj_status_t status = pj_get_netos_error();
+ if (status == PJ_SUCCESS)
+ status = PJ_EUNKNOWN;
+
+ return status;
+ }
+
+ return PJ_SUCCESS;
+
+#elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE)
+ /*
+ * Implementation on Windows, using WSAStringToAddress().
+ * Should also work on Unicode systems.
+ */
+ {
+ PJ_DECL_UNICODE_TEMP_BUF(wtempaddr, PJ_INET6_ADDRSTRLEN)
+ pj_sockaddr sock_addr;
+ int addr_len = sizeof(sock_addr);
+ int rc;
+
+ sock_addr.addr.sa_family = (pj_uint16_t)af;
+ rc = WSAStringToAddress(
+ PJ_STRING_TO_NATIVE(tempaddr, wtempaddr, sizeof(wtempaddr)),
+ af, NULL, (LPSOCKADDR)&sock_addr, &addr_len);
+ if (rc != 0) {
+ /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6,
+ * check that you have IPv6 enabled (install it in the network
+ * adapter).
+ */
+ pj_status_t status = pj_get_netos_error();
+ if (status == PJ_SUCCESS)
+ status = PJ_EUNKNOWN;
+
+ return status;
+ }
+
+ if (sock_addr.addr.sa_family == PJ_AF_INET) {
+ pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4);
+ return PJ_SUCCESS;
+ }
+ else if (sock_addr.addr.sa_family == PJ_AF_INET6) {
+ pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16);
+ return PJ_SUCCESS;
+ }
+ else {
+ pj_assert(!"Shouldn't happen");
+ return PJ_EBUG;
+ }
+ }
+#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0
+ /* IPv6 support is disabled, just return error without raising assertion */
+ return PJ_EIPV6NOTSUP;
+#else
+ pj_assert(!"Not supported");
+ return PJ_EIPV6NOTSUP;
+#endif
+}
+
+/*
+ * Convert IPv4/IPv6 address to text.
+ */
+PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src,
+ char *dst, int size)
+
+{
+ PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL);
+ PJ_ASSERT_RETURN(af == PJ_AF_INET || af == PJ_AF_INET6, PJ_EAFNOTSUP);
+
+ *dst = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0
+ /*
+ * Implementation using inet_ntop()
+ */
+ if (inet_ntop(af, src, dst, size) == NULL) {
+ pj_status_t status = pj_get_netos_error();
+ if (status == PJ_SUCCESS)
+ status = PJ_EUNKNOWN;
+
+ return status;
+ }
+
+ return PJ_SUCCESS;
+
+#elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE)
+ /*
+ * Implementation on Windows, using WSAAddressToString().
+ * Should also work on Unicode systems.
+ */
+ {
+ PJ_DECL_UNICODE_TEMP_BUF(wtempaddr, PJ_INET6_ADDRSTRLEN)
+ pj_sockaddr sock_addr;
+ DWORD addr_len, addr_str_len;
+ int rc;
+
+ pj_bzero(&sock_addr, sizeof(sock_addr));
+ sock_addr.addr.sa_family = (pj_uint16_t)af;
+ if (af == PJ_AF_INET) {
+ if (size < PJ_INET_ADDRSTRLEN)
+ return PJ_ETOOSMALL;
+ pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4);
+ addr_len = sizeof(pj_sockaddr_in);
+ addr_str_len = PJ_INET_ADDRSTRLEN;
+ }
+ else if (af == PJ_AF_INET6) {
+ if (size < PJ_INET6_ADDRSTRLEN)
+ return PJ_ETOOSMALL;
+ pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16);
+ addr_len = sizeof(pj_sockaddr_in6);
+ addr_str_len = PJ_INET6_ADDRSTRLEN;
+ }
+ else {
+ pj_assert(!"Unsupported address family");
+ return PJ_EAFNOTSUP;
+ }
+
+#if PJ_NATIVE_STRING_IS_UNICODE
+ rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len,
+ NULL, wtempaddr, &addr_str_len);
+ if (rc == 0) {
+ pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size);
+ }
+#else
+ rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len,
+ NULL, dst, &addr_str_len);
+#endif
+
+ if (rc != 0) {
+ pj_status_t status = pj_get_netos_error();
+ if (status == PJ_SUCCESS)
+ status = PJ_EUNKNOWN;
+
+ return status;
+ }
+
+ return PJ_SUCCESS;
+ }
+
+#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0
+ /* IPv6 support is disabled, just return error without raising assertion */
+ return PJ_EIPV6NOTSUP;
+#else
+ pj_assert(!"Not supported");
+ return PJ_EIPV6NOTSUP;
+#endif
+}
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+ static char buf[PJ_MAX_HOSTNAME];
+ static pj_str_t hostname;
+
+ PJ_CHECK_STACK();
+
+ if (hostname.ptr == NULL) {
+ hostname.ptr = buf;
+ if (gethostname(buf, sizeof(buf)) != 0) {
+ hostname.ptr[0] = '\0';
+ hostname.slen = 0;
+ }
+ else {
+ hostname.slen = strlen(buf);
+ }
+ }
+ return &hostname;
+}
+
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af,
+ int type,
+ int proto,
+ pj_sock_t *p_sock)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(p_sock!=NULL, PJ_EINVAL);
+
+ PjUwpSocket *s = new PjUwpSocket(af, type, proto);
+
+ /* Init UDP socket here */
+ if (type == pj_SOCK_DGRAM()) {
+ s->InitSocket(SOCKTYPE_DATAGRAM);
+ }
+
+ *p_sock = (pj_sock_t)s;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int len)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock, PJ_EINVAL);
+ PJ_ASSERT_RETURN(addr && len>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->Bind(addr);
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock,
+ pj_uint32_t addr32,
+ pj_uint16_t port)
+{
+ pj_sockaddr_in addr;
+
+ PJ_CHECK_STACK();
+
+ pj_bzero(&addr, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ addr.sin_addr.s_addr = pj_htonl(addr32);
+ addr.sin_port = pj_htons(port);
+
+ return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
+}
+
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock, PJ_EINVAL);
+
+ if (sock == PJ_INVALID_SOCKET)
+ return PJ_SUCCESS;
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ delete s;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && addr && namelen &&
+ *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ pj_sockaddr_cp(addr, s->GetRemoteAddr());
+ *namelen = pj_sockaddr_get_len(addr);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+ pj_sockaddr_t *addr,
+ int *namelen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && addr && namelen &&
+ *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ pj_sockaddr_cp(addr, s->GetLocalAddr());
+ *namelen = pj_sockaddr_get_len(addr);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+ PJ_UNUSED_ARG(flags);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->Send(buf, len);
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
+ const void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ const pj_sockaddr_t *to,
+ int tolen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+ PJ_UNUSED_ARG(flags);
+ PJ_UNUSED_ARG(tolen);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->SendTo(buf, len, to);
+}
+
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && len && *len > 0, PJ_EINVAL);
+
+ PJ_UNUSED_ARG(flags);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->Recv(buf, len);
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
+ void *buf,
+ pj_ssize_t *len,
+ unsigned flags,
+ pj_sockaddr_t *from,
+ int *fromlen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && buf && len && from && fromlen, PJ_EINVAL);
+ PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL);
+ PJ_ASSERT_RETURN(*fromlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+ PJ_UNUSED_ARG(flags);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ pj_status_t status = s->RecvFrom(buf, len, from);
+ if (status == PJ_SUCCESS)
+ *fromlen = pj_sockaddr_get_len(from);
+ return status;
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ void *optval,
+ int *optlen)
+{
+ // Not supported for now.
+ PJ_UNUSED_ARG(sock);
+ PJ_UNUSED_ARG(level);
+ PJ_UNUSED_ARG(optname);
+ PJ_UNUSED_ARG(optval);
+ PJ_UNUSED_ARG(optlen);
+ return PJ_ENOTSUP;
+}
+
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+ pj_uint16_t level,
+ pj_uint16_t optname,
+ const void *optval,
+ int optlen)
+{
+ // Not supported for now.
+ PJ_UNUSED_ARG(sock);
+ PJ_UNUSED_ARG(level);
+ PJ_UNUSED_ARG(optname);
+ PJ_UNUSED_ARG(optval);
+ PJ_UNUSED_ARG(optlen);
+ return PJ_ENOTSUP;
+}
+
+
+/*
+* Set socket option.
+*/
+PJ_DEF(pj_status_t) pj_sock_setsockopt_params( pj_sock_t sockfd,
+ const pj_sockopt_params *params)
+{
+ unsigned int i = 0;
+ pj_status_t retval = PJ_SUCCESS;
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(params, PJ_EINVAL);
+
+ for (;i<params->cnt && i<PJ_MAX_SOCKOPT_PARAMS;++i) {
+ pj_status_t status = pj_sock_setsockopt(sockfd,
+ (pj_uint16_t)params->options[i].level,
+ (pj_uint16_t)params->options[i].optname,
+ params->options[i].optval,
+ params->options[i].optlen);
+ if (status != PJ_SUCCESS) {
+ retval = status;
+ PJ_PERROR(4,(THIS_FILE, status,
+ "Warning: error applying sock opt %d",
+ params->options[i].optname));
+ }
+ }
+
+ return retval;
+}
+
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+ const pj_sockaddr_t *addr,
+ int namelen)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock && addr, PJ_EINVAL);
+ PJ_UNUSED_ARG(namelen);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->Connect(addr);
+}
+
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
+ int how)
+{
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(sock, PJ_EINVAL);
+ PJ_UNUSED_ARG(how);
+
+ return pj_sock_close(sock);
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+ int backlog)
+{
+ PJ_CHECK_STACK();
+ PJ_UNUSED_ARG(backlog);
+ PJ_ASSERT_RETURN(sock, PJ_EINVAL);
+
+ PjUwpSocket *s = (PjUwpSocket*)sock;
+ return s->Listen();
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+ pj_sock_t *newsock,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ pj_status_t status;
+
+ PJ_CHECK_STACK();
+ PJ_ASSERT_RETURN(serverfd && newsock, PJ_EINVAL);
+
+ PjUwpSocket *s = (PjUwpSocket*)serverfd;
+ PjUwpSocket *new_uwp_sock;
+
+ status = s->Accept(&new_uwp_sock);
+ if (status != PJ_SUCCESS)
+ return status;
+ if (newsock == NULL)
+ return PJ_ENOTFOUND;
+
+ *newsock = (pj_sock_t)new_uwp_sock;
+
+ if (addr)
+ pj_sockaddr_cp(addr, new_uwp_sock->GetRemoteAddr());
+
+ if (addrlen)
+ *addrlen = pj_sockaddr_get_len(addr);
+
+ return PJ_SUCCESS;
+}
+#endif /* PJ_HAS_TCP */