1 /* $Id$ */
2 /*
3  * Copyright (C) 2016 Teluu Inc. (http://www.teluu.com)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #pragma once
20 
21 
22 #include <pj/assert.h>
23 #include <pj/sock.h>
24 #include <pj/string.h>
25 #include <pj/unicode.h>
26 
27 
28 enum {
29     READ_TIMEOUT	= 60 * 1000,
30     WRITE_TIMEOUT	= 60 * 1000,
31     SEND_BUFFER_SIZE	= 128 * 1024,
32 };
33 
34 enum PjUwpSocketType {
35     SOCKTYPE_UNKNOWN, SOCKTYPE_LISTENER,
36     SOCKTYPE_STREAM, SOCKTYPE_DATAGRAM
37 };
38 
39 enum PjUwpSocketState {
40     SOCKSTATE_NULL, SOCKSTATE_INITIALIZED, SOCKSTATE_CONNECTING,
41     SOCKSTATE_CONNECTED, SOCKSTATE_DISCONNECTED, SOCKSTATE_ERROR
42 };
43 
44 ref class PjUwpSocketDatagramRecvHelper;
45 ref class PjUwpSocketListenerHelper;
46 class PjUwpSocket;
47 
48 
49 typedef struct PjUwpSocketCallback
50 {
51     void (*on_read)(PjUwpSocket *s, int bytes_read);
52     void (*on_write)(PjUwpSocket *s, int bytes_sent);
53     void (*on_accept)(PjUwpSocket *s);
54     void (*on_connect)(PjUwpSocket *s, pj_status_t status);
55 } PjUwpSocketCallback;
56 
57 
58 /*
59  * UWP Socket Wrapper.
60  */
61 class PjUwpSocket
62 {
63 public:
64     PjUwpSocket(int af_, int type_, int proto_);
65     virtual ~PjUwpSocket();
66     pj_status_t InitSocket(enum PjUwpSocketType sock_type_);
67     void DeinitSocket();
68 
GetUserData()69     void* GetUserData() { return user_data; }
SetNonBlocking(const PjUwpSocketCallback * cb_,void * user_data_)70     void SetNonBlocking(const PjUwpSocketCallback *cb_, void *user_data_)
71     {
72 	is_blocking = PJ_FALSE;
73 	cb=*cb_;
74 	user_data = user_data_;
75     }
76 
GetType()77     enum PjUwpSocketType GetType() { return sock_type; }
GetState()78     enum PjUwpSocketState GetState() { return sock_state; }
79 
GetLocalAddr()80     pj_sockaddr* GetLocalAddr() { return &local_addr; }
GetRemoteAddr()81     pj_sockaddr* GetRemoteAddr() { return &remote_addr; }
82 
83 
84     pj_status_t Bind(const pj_sockaddr_t *addr = NULL);
85     pj_status_t Send(const void *buf, pj_ssize_t *len);
86     pj_status_t SendTo(const void *buf, pj_ssize_t *len, const pj_sockaddr_t *to);
87     pj_status_t Recv(void *buf, pj_ssize_t *len);
88     pj_status_t RecvFrom(void *buf, pj_ssize_t *len, pj_sockaddr_t *from);
89     pj_status_t Connect(const pj_sockaddr_t *addr);
90     pj_status_t Listen();
91     pj_status_t Accept(PjUwpSocket **new_sock);
92 
93     void (*on_read)(PjUwpSocket *s, int bytes_read);
94     void (*on_write)(PjUwpSocket *s, int bytes_sent);
95     void (*on_accept)(PjUwpSocket *s, pj_status_t status);
96     void (*on_connect)(PjUwpSocket *s, pj_status_t status);
97 
98 private:
99     PjUwpSocket* CreateAcceptSocket(Windows::Networking::Sockets::StreamSocket^ stream_sock_);
100     pj_status_t SendImp(const void *buf, pj_ssize_t *len);
101     int ConsumeReadBuffer(void *buf, int max_len);
102 
103     int af;
104     int type;
105     int proto;
106     pj_sockaddr local_addr;
107     pj_sockaddr remote_addr;
108     pj_bool_t is_blocking;
109     pj_bool_t has_pending_bind;
110     pj_bool_t has_pending_send;
111     pj_bool_t has_pending_recv;
112     void *user_data;
113     PjUwpSocketCallback cb;
114 
115     enum PjUwpSocketType sock_type;
116     enum PjUwpSocketState sock_state;
117     Windows::Networking::Sockets::DatagramSocket^ datagram_sock;
118     Windows::Networking::Sockets::StreamSocket^ stream_sock;
119     Windows::Networking::Sockets::StreamSocketListener^ listener_sock;
120 
121     /* Helper objects */
122     PjUwpSocketDatagramRecvHelper^ dgram_recv_helper;
123     PjUwpSocketListenerHelper^ listener_helper;
124 
125     Windows::Storage::Streams::DataReader^ socket_reader;
126     Windows::Storage::Streams::DataWriter^ socket_writer;
127     Windows::Storage::Streams::IBuffer^ send_buffer;
128 
129     friend PjUwpSocketDatagramRecvHelper;
130     friend PjUwpSocketListenerHelper;
131 };
132 
133 
134 //////////////////////////////////
135 // Misc
136 
137 
wstr_addr_to_sockaddr(const wchar_t * waddr,const wchar_t * wport,pj_sockaddr_t * sockaddr)138 inline pj_status_t wstr_addr_to_sockaddr(const wchar_t *waddr,
139 					 const wchar_t *wport,
140 					 pj_sockaddr_t *sockaddr)
141 {
142 #if 0
143     char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1];
144     pj_assert(wcslen(waddr) < sizeof(tmp_str_buf));
145     pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf));
146     pj_str_t remote_host;
147     pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf));
148     pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &remote_host, (pj_sockaddr*)sockaddr);
149     pj_sockaddr_set_port((pj_sockaddr*)sockaddr,  (pj_uint16_t)_wtoi(wport));
150 
151     return PJ_SUCCESS;
152 #endif
153     char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1];
154     pj_assert(wcslen(waddr) < sizeof(tmp_str_buf));
155     pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf));
156     pj_str_t remote_host;
157     pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf));
158     pj_sockaddr *addr = (pj_sockaddr*)sockaddr;
159     pj_bool_t got_addr = PJ_FALSE;
160 
161     if (pj_inet_pton(PJ_AF_INET, &remote_host, &addr->ipv4.sin_addr)
162 	== PJ_SUCCESS)
163     {
164 	addr->addr.sa_family = PJ_AF_INET;
165 	got_addr = PJ_TRUE;
166     } else if (pj_inet_pton(PJ_AF_INET6, &remote_host, &addr->ipv6.sin6_addr)
167 	== PJ_SUCCESS)
168     {
169 	addr->addr.sa_family = PJ_AF_INET6;
170 	got_addr = PJ_TRUE;
171     }
172     if (!got_addr)
173 	return PJ_EINVAL;
174 
175     pj_sockaddr_set_port(addr, (pj_uint16_t)_wtoi(wport));
176     return PJ_SUCCESS;
177 }
178 
179 
180 inline pj_status_t sockaddr_to_hostname_port(const pj_sockaddr_t *sockaddr,
181 					     Windows::Networking::HostName ^&hostname,
182 					     int *port)
183 {
184     char tmp[PJ_INET6_ADDRSTRLEN];
185     wchar_t wtmp[PJ_INET6_ADDRSTRLEN];
186     pj_sockaddr_print(sockaddr, tmp, PJ_INET6_ADDRSTRLEN, 0);
187     pj_ansi_to_unicode(tmp, pj_ansi_strlen(tmp), wtmp,
188 	PJ_INET6_ADDRSTRLEN);
189     hostname = ref new Windows::Networking::HostName(ref new Platform::String(wtmp));
190     *port = pj_sockaddr_get_port(sockaddr);
191 
192     return PJ_SUCCESS;
193 }
194 
195 
196 /* Buffer helper */
197 
198 #include <Robuffer.h>
199 #include <wrl/client.h>
200 
201 inline Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> GetBufferByteAccess(Windows::Storage::Streams::IBuffer^ buffer)
202 {
203     auto pUnk = reinterpret_cast<IUnknown*>(buffer);
204 
205     Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> comBuff;
206     pUnk->QueryInterface(__uuidof(Windows::Storage::Streams::IBufferByteAccess), (void**)comBuff.ReleaseAndGetAddressOf());
207 
208     return comBuff;
209 }
210 
211 
212 inline void GetRawBufferFromIBuffer(Windows::Storage::Streams::IBuffer^ buffer, unsigned char** pbuffer)
213 {
214     Platform::Object^ obj = buffer;
215     Microsoft::WRL::ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(obj));
216     Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
217     insp.As(&bufferByteAccess);
218     bufferByteAccess->Buffer(pbuffer);
219 }
220 
221 inline void CopyToIBuffer(unsigned char* buffSource, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int writeStartPos = 0)
222 {
223     auto bufferLen = buffer->Capacity;
224     assert(copyByteCount <= bufferLen);
225 
226     unsigned char* pBuffer;
227 
228     GetRawBufferFromIBuffer(buffer, &pBuffer);
229 
230     memcpy(pBuffer + writeStartPos, buffSource, copyByteCount);
231 }
232 
233 inline void CopyFromIBuffer(unsigned char* buffDestination, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int readStartPos = 0)
234 {
235     assert(copyByteCount <= buffer->Capacity);
236 
237     unsigned char* pBuffer;
238 
239     GetRawBufferFromIBuffer(buffer, &pBuffer);
240 
241     memcpy(buffDestination, pBuffer + readStartPos, copyByteCount);
242 }
243 
244 
245 /* PPL helper */
246 
247 #include <ppltasks.h>
248 #include <agents.h>
249 
250 // Creates a task that completes after the specified delay, in ms.
complete_after(unsigned int timeout)251 inline concurrency::task<void> complete_after(unsigned int timeout)
252 {
253     // A task completion event that is set when a timer fires.
254     concurrency::task_completion_event<void> tce;
255 
256     // Create a non-repeating timer.
257     auto fire_once = new concurrency::timer<int>(timeout, 0, nullptr, false);
258     // Create a call object that sets the completion event after the timer fires.
259     auto callback = new concurrency::call<int>([tce](int)
260     {
261 	tce.set();
262     });
263 
264     // Connect the timer to the callback and start the timer.
265     fire_once->link_target(callback);
266     fire_once->start();
267 
268     // Create a task that completes after the completion event is set.
269     concurrency::task<void> event_set(tce);
270 
271     // Create a continuation task that cleans up resources and
272     // and return that continuation task.
273     return event_set.then([callback, fire_once]()
274     {
275 	delete callback;
276 	delete fire_once;
277     });
278 }
279 
280 // Cancels the provided task after the specifed delay, if the task
281 // did not complete.
282 template<typename T>
cancel_after_timeout(concurrency::task<T> t,concurrency::cancellation_token_source cts,unsigned int timeout)283 inline concurrency::task<T> cancel_after_timeout(concurrency::task<T> t, concurrency::cancellation_token_source cts, unsigned int timeout)
284 {
285     // Create a task that returns true after the specified task completes.
286     concurrency::task<bool> success_task = t.then([](T)
287     {
288 	return true;
289     });
290     // Create a task that returns false after the specified timeout.
291     concurrency::task<bool> failure_task = complete_after(timeout).then([]
292     {
293 	return false;
294     });
295 
296     // Create a continuation task that cancels the overall task
297     // if the timeout task finishes first.
298     return (failure_task || success_task).then([t, cts](bool success)
299     {
300 	if (!success)
301 	{
302 	    // Set the cancellation token. The task that is passed as the
303 	    // t parameter should respond to the cancellation and stop
304 	    // as soon as it can.
305 	    cts.cancel();
306 	}
307 
308 	// Return the original task.
309 	return t;
310     });
311 }
312