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