From f6d78bf855f2d564f9e383508908631a4668e2af Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 23 Jan 2017 04:32:34 +0000 Subject: Re #1900: Reintegrated works in UWP branch to trunk. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5539 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib/build/pjlib.vcxproj | 351 +++++++- pjlib/build/pjlib.vcxproj.filters | 36 +- pjlib/build/pjlib_test.vcxproj | 252 +++++- pjlib/build/pjlib_test.vcxproj.filters | 3 - pjlib/include/pj/compat/os_winphone8.h | 150 ++++ pjlib/include/pj/compat/os_winuwp.h | 148 ++++ pjlib/include/pj/config.h | 42 +- pjlib/src/pj/file_access_win32.c | 140 ++- pjlib/src/pj/file_io_win32.c | 77 +- pjlib/src/pj/ioqueue_common_abs.c | 4 +- pjlib/src/pj/ioqueue_common_abs.h | 2 +- pjlib/src/pj/ioqueue_select.c | 13 + pjlib/src/pj/ioqueue_uwp.cpp | 336 +++++++ pjlib/src/pj/ip_helper_winphone8.c | 55 ++ pjlib/src/pj/os_core_win32.c | 107 ++- pjlib/src/pj/os_info.c | 20 +- pjlib/src/pj/sock_uwp.cpp | 1502 ++++++++++++++++++++++++++++++++ pjlib/src/pj/sock_uwp.h | 311 +++++++ 18 files changed, 3383 insertions(+), 166 deletions(-) create mode 100644 pjlib/include/pj/compat/os_winphone8.h create mode 100644 pjlib/include/pj/compat/os_winuwp.h create mode 100644 pjlib/src/pj/ioqueue_uwp.cpp create mode 100644 pjlib/src/pj/ip_helper_winphone8.c create mode 100644 pjlib/src/pj/sock_uwp.cpp create mode 100644 pjlib/src/pj/sock_uwp.h (limited to 'pjlib') diff --git a/pjlib/build/pjlib.vcxproj b/pjlib/build/pjlib.vcxproj index 8afd015a..abf09ec4 100644 --- a/pjlib/build/pjlib.vcxproj +++ b/pjlib/build/pjlib.vcxproj @@ -1,6 +1,10 @@  + + Debug-Dynamic + ARM + Debug-Dynamic Win32 @@ -9,6 +13,10 @@ Debug-Dynamic x64 + + Debug-Static + ARM + Debug-Static Win32 @@ -17,6 +25,10 @@ Debug-Static x64 + + Debug + ARM + Debug Win32 @@ -25,6 +37,10 @@ Debug x64 + + Release-Dynamic + ARM + Release-Dynamic Win32 @@ -33,6 +49,10 @@ Release-Dynamic x64 + + Release-Static + ARM + Release-Static Win32 @@ -41,6 +61,10 @@ Release-Static x64 + + Release + ARM + Release Win32 @@ -50,150 +74,214 @@ x64 + + {DA0E03ED-53A7-4050-8A85-90541C5509F8} pjlib + + en-US StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte + + + StaticLibrary + v140 + false StaticLibrary v140 false - MultiByte StaticLibrary v140 false - MultiByte StaticLibrary v140 false - MultiByte StaticLibrary v140 false - MultiByte StaticLibrary v140 false - MultiByte StaticLibrary v140 false - MultiByte + + + + $(BuildToolset) + + - + + + + + + - - + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + <_ProjectFileVersion>14.0.22823.1 + + + + MultiThreadedDebugDLL + + ../include;%(AdditionalIncludeDirectories) @@ -201,6 +289,13 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + X64 @@ -219,6 +314,14 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + + X64 @@ -236,6 +339,13 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + X64 @@ -254,6 +364,13 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + X64 @@ -271,6 +388,13 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + X64 @@ -289,6 +413,13 @@ + + + ../include;%(AdditionalIncludeDirectories) + _LIB;%(PreprocessorDefinitions) + + + X64 @@ -300,19 +431,39 @@ + + true + true + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalUsingDirectories) + MultiThreadedDLL + MultiThreadedDLL + MultiThreadedDLL + MultiThreadedDLL + true + true true true + true true true + true true true + true true true + true true true + true true @@ -325,181 +476,258 @@ true + true true true + true true true + true true true + true true true + true true true + true true - + true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true - + true + true true true + true true true + true true true + true true true + true true true + true true - + + true + + + true + true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true @@ -507,45 +735,63 @@ true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true @@ -555,16 +801,22 @@ true + true true true + true true true + true true true + true true true + true true true + true true @@ -574,16 +826,22 @@ true + true true true + true true true + true true true + true true true + true true true + true true @@ -597,16 +855,22 @@ true + true true true + true true true + true true true + true true true + true true true + true true @@ -614,6 +878,7 @@ + @@ -636,6 +901,8 @@ + + @@ -643,12 +910,12 @@ - + - + diff --git a/pjlib/build/pjlib.vcxproj.filters b/pjlib/build/pjlib.vcxproj.filters index f41b8fdb..0b5cbf10 100644 --- a/pjlib/build/pjlib.vcxproj.filters +++ b/pjlib/build/pjlib.vcxproj.filters @@ -18,6 +18,12 @@ {4befc994-9aa4-47c8-99e7-5a51301220dd} + + {4a92fce6-2308-44cf-aa66-00ccd5b333bd} + + + {3550aa38-c59d-4d5f-b458-1f93e0b16bbd} + @@ -179,9 +185,6 @@ Source Files\Other Targets - - Source Files\Other Targets - Source Files\Other Targets @@ -215,6 +218,18 @@ Source Files\Other Targets + + Source Files\winrt + + + Source Files + + + Source Files + + + Source Files + @@ -247,9 +262,6 @@ Header Files - - Header Files - Header Files @@ -409,9 +421,6 @@ Header Files\compat - - Header Files\compat - Inline Files @@ -421,5 +430,14 @@ Inline Files + + Header Files\compat + + + Header Files\winrt + + + Header Files\compat + \ No newline at end of file diff --git a/pjlib/build/pjlib_test.vcxproj b/pjlib/build/pjlib_test.vcxproj index 66058f68..53e1d40b 100644 --- a/pjlib/build/pjlib_test.vcxproj +++ b/pjlib/build/pjlib_test.vcxproj @@ -1,6 +1,10 @@  + + Debug-Dynamic + ARM + Debug-Dynamic Win32 @@ -9,6 +13,10 @@ Debug-Dynamic x64 + + Debug-Static + ARM + Debug-Static Win32 @@ -17,6 +25,10 @@ Debug-Static x64 + + Debug + ARM + Debug Win32 @@ -25,6 +37,10 @@ Debug x64 + + Release-Dynamic + ARM + Release-Dynamic Win32 @@ -33,6 +49,10 @@ Release-Dynamic x64 + + Release-Static + ARM + Release-Static Win32 @@ -41,6 +61,10 @@ Release-Static x64 + + Release + ARM + Release Win32 @@ -50,9 +74,13 @@ x64 + + {6AC3EF61-5A9E-4F43-A809-5B2FD1A43B16} - pjlib_test + pjlib_test + + en-US @@ -61,36 +89,72 @@ false MultiByte + + Application + v140 + false + MultiByte + Application v140 false MultiByte + + Application + v140 + false + MultiByte + Application v140 false MultiByte + + Application + v140 + false + MultiByte + Application v140 false MultiByte + + Application + v140 + false + MultiByte + Application v140 false MultiByte + + Application + v140 + false + MultiByte + Application v140 false MultiByte + + Application + v140 + false + MultiByte + Application v140 @@ -127,74 +191,121 @@ false MultiByte + + + $(BuildToolset) + + + Application + StaticLibrary + - + + + + + + - - + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + <_ProjectFileVersion>14.0.22823.1 - pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration) + pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration) + + + + MultiThreadedDebugDLL + + + ..\lib\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).lib + + ../include;%(AdditionalIncludeDirectories) @@ -206,6 +317,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -233,6 +356,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -259,6 +394,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -286,6 +433,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -312,6 +471,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -339,6 +510,18 @@ ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + + ../include;%(AdditionalIncludeDirectories) + _CONSOLE;%(PreprocessorDefinitions) + + + + + netapi32.lib;mswsock.lib;ws2_32.lib;odbc32.lib;odbccp32.lib;oleaut32.lib;ole32.lib;%(AdditionalDependencies) + ..\bin\pjlib-test-$(TargetCPU)-$(Platform)-vc$(VSVer)-$(Configuration).exe + + X64 @@ -368,33 +551,46 @@ - + + true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true true + true true diff --git a/pjlib/build/pjlib_test.vcxproj.filters b/pjlib/build/pjlib_test.vcxproj.filters index 0e122c6e..ed8f1d9f 100644 --- a/pjlib/build/pjlib_test.vcxproj.filters +++ b/pjlib/build/pjlib_test.vcxproj.filters @@ -54,9 +54,6 @@ Source Files - - Source Files - Source Files diff --git a/pjlib/include/pj/compat/os_winphone8.h b/pjlib/include/pj/compat/os_winphone8.h new file mode 100644 index 00000000..a7c26e9c --- /dev/null +++ b/pjlib/include/pj/compat/os_winphone8.h @@ -0,0 +1,150 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ +#ifndef __PJ_COMPAT_OS_WINPHONE8_H__ +#define __PJ_COMPAT_OS_WINPHONE8_H__ + +/** + * @file os_winphone8.h + * @brief Describes Windows Phone 8 operating system family specifics. + */ + +#define PJ_OS_NAME "winphone8" + +#define WIN32_LEAN_AND_MEAN +#define RPC_NO_WINDOWS_H +#define PJ_WIN32_WINNT 0x0602 /*_WIN32_WINNT_WIN8*/ +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT PJ_WIN32_WINNT +#endif + +#define PJ_HAS_ARPA_INET_H 0 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 0 +#define PJ_HAS_NETINET_IN_H 0 +#define PJ_HAS_NETINET_TCP_H 0 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 0 +#define PJ_HAS_SYS_SELECT_H 0 +#define PJ_HAS_SYS_SOCKET_H 0 +#define PJ_HAS_SYS_TIME_H 0 +#define PJ_HAS_SYS_TIMEB_H 0 /* Doesn't have sys/timeb.h */ +#define PJ_HAS_SYS_TYPES_H 0 /* Doesn't have sys/types.h */ +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 0 + +#define PJ_HAS_MSWSOCK_H 1 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 1 + +#define PJ_SOCK_HAS_INET_ATON 0 + +/* Set 1 if native sockaddr_in has sin_len member. + * Default: 0 + */ +#define PJ_SOCKADDR_HAS_LEN 0 + +/* Is errno a good way to retrieve OS errors? (no) + */ +#define PJ_HAS_ERRNO_VAR 0 + +/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return + * the status of non-blocking connect() operation. + */ +#define PJ_HAS_SO_ERROR 0 + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket recv() or send() can not return immediately. + */ +#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket connect() can not get connected immediately. + */ +#define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK + +/** + * If this macro is set, it tells select I/O Queue that select() needs to + * be given correct value of nfds (i.e. largest fd + 1). This requires + * select ioqueue to re-scan the descriptors on each registration and + * unregistration. + * If this macro is not set, then ioqueue will always give FD_SETSIZE for + * nfds argument when calling select(). + * + * Default: 0 + */ +#define PJ_SELECT_NEEDS_NFDS 0 + +/* Endianness */ +#ifndef PJ_IS_LITTLE_ENDIAN +# define PJ_IS_LITTLE_ENDIAN 1 +# define PJ_IS_BIG_ENDIAN 0 +#endif + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 +#define PJ_OS_HAS_CHECK_STACK 1 + +#define PJ_ATOMIC_VALUE_TYPE long + +/* No console. */ +#define PJ_TERM_HAS_COLOR 0 + +/* TlsAlloc() error value. */ +#ifndef TLS_OUT_OF_INDEXES + #define TLS_OUT_OF_INDEXES 0xFFFFFFFF +#endif + +/* No rdtsc */ +#define PJ_TIMESTAMP_USE_RDTSC 0 + +/* Native string is Unicode. */ +#define PJ_NATIVE_STRING_IS_UNICODE 1 + +/* If 1, use Read/Write mutex emulation for platforms that don't support it */ +#define PJ_EMULATE_RWMUTEX 1 + +/* If 1, pj_thread_create() should enforce the stack size when creating + * threads. + * Default: 0 (let OS decide the thread's stack size). + */ +#define PJ_THREAD_SET_STACK_SIZE 0 + +/* If 1, pj_thread_create() should allocate stack from the pool supplied. + * Default: 0 (let OS allocate memory for thread's stack). + */ +#define PJ_THREAD_ALLOCATE_STACK 0 + + +#endif /* __PJ_COMPAT_OS_WINPHONE8_H__ */ diff --git a/pjlib/include/pj/compat/os_winuwp.h b/pjlib/include/pj/compat/os_winuwp.h new file mode 100644 index 00000000..9cc8ee96 --- /dev/null +++ b/pjlib/include/pj/compat/os_winuwp.h @@ -0,0 +1,148 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * 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 + */ +#ifndef __PJ_COMPAT_OS_UWP_H__ +#define __PJ_COMPAT_OS_UWP_H__ + +/** + * @file os_winuwp.h + * @brief Describes Windows UWP operating system family specifics. + */ + +#define PJ_OS_NAME "winuwp" + +#define WIN32_LEAN_AND_MEAN +#define RPC_NO_WINDOWS_H +#define PJ_WIN32_WINNT 0x0602 /*_WIN32_WINNT_WIN8*/ +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#define _WIN32_WINNT PJ_WIN32_WINNT +#endif + +#define PJ_HAS_ARPA_INET_H 0 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 0 +#define PJ_HAS_NETINET_IN_H 0 +#define PJ_HAS_NETINET_TCP_H 0 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 0 +#define PJ_HAS_SYS_SELECT_H 0 +#define PJ_HAS_SYS_SOCKET_H 0 +#define PJ_HAS_SYS_TIME_H 0 +#define PJ_HAS_SYS_TIMEB_H 0 /* Doesn't have sys/timeb.h */ +#define PJ_HAS_SYS_TYPES_H 0 /* Doesn't have sys/types.h */ +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 0 + +#define PJ_HAS_MSWSOCK_H 1 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 1 + +#define PJ_SOCK_HAS_INET_ATON 0 + +/* Set 1 if native sockaddr_in has sin_len member. + * Default: 0 + */ +#define PJ_SOCKADDR_HAS_LEN 0 + +/* Is errno a good way to retrieve OS errors? (no) + */ +#define PJ_HAS_ERRNO_VAR 0 + +/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return + * the status of non-blocking connect() operation. + */ +#define PJ_HAS_SO_ERROR 0 + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket recv() or send() can not return immediately. + */ +#define PJ_BLOCKING_ERROR_VAL WSAEWOULDBLOCK + +/* This value specifies the value set in errno by the OS when a non-blocking + * socket connect() can not get connected immediately. + */ +#define PJ_BLOCKING_CONNECT_ERROR_VAL WSAEWOULDBLOCK + +/** + * If this macro is set, it tells select I/O Queue that select() needs to + * be given correct value of nfds (i.e. largest fd + 1). This requires + * select ioqueue to re-scan the descriptors on each registration and + * unregistration. + * If this macro is not set, then ioqueue will always give FD_SETSIZE for + * nfds argument when calling select(). + * + * Default: 0 + */ +#define PJ_SELECT_NEEDS_NFDS 0 + +/* Endianness */ +#ifndef PJ_IS_LITTLE_ENDIAN +# define PJ_IS_LITTLE_ENDIAN 1 +# define PJ_IS_BIG_ENDIAN 0 +#endif + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 + +#ifndef PJ_OS_HAS_CHECK_STACK +# define PJ_OS_HAS_CHECK_STACK 0 +#endif + +#define PJ_ATOMIC_VALUE_TYPE long + +/* No console. */ +#define PJ_TERM_HAS_COLOR 0 + +/* No rdtsc */ +#define PJ_TIMESTAMP_USE_RDTSC 0 + +/* Native string is Unicode. */ +#define PJ_NATIVE_STRING_IS_UNICODE 1 + +/* If 1, use Read/Write mutex emulation for platforms that don't support it */ +#define PJ_EMULATE_RWMUTEX 1 + +/* If 1, pj_thread_create() should enforce the stack size when creating + * threads. + * Default: 0 (let OS decide the thread's stack size). + */ +#define PJ_THREAD_SET_STACK_SIZE 0 + +/* If 1, pj_thread_create() should allocate stack from the pool supplied. + * Default: 0 (let OS allocate memory for thread's stack). + */ +#define PJ_THREAD_ALLOCATE_STACK 0 + + +#endif /* __PJ_COMPAT_OS_UWP_H__ */ diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index c8a4ef72..064250c4 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -76,6 +76,31 @@ /* Also define Win32 */ # define PJ_WIN32 1 +#elif defined(PJ_WIN32_WINPHONE8) || defined(_WIN32_WINPHONE8) + /* + * Windows Phone 8 + */ +# undef PJ_WIN32_WINPHONE8 +# define PJ_WIN32_WINPHONE8 1 +# include + + /* Also define Win32 */ +# define PJ_WIN32 1 + +#elif defined(PJ_WIN32_UWP) || defined(_WIN32_UWP) + /* + * Windows UWP + */ +# undef PJ_WIN32_UWP +# define PJ_WIN32_UWP 1 +# include + + /* Define Windows phone */ +# define PJ_WIN32_WINPHONE8 1 + + /* Also define Win32 */ +# define PJ_WIN32 1 + #elif defined(PJ_WIN32) || defined(_WIN32) || defined(__WIN32__) || \ defined(WIN32) || defined(PJ_WIN64) || defined(_WIN64) || \ defined(WIN64) || defined(__TOS_WIN__) @@ -236,18 +261,23 @@ # define PJ_IS_LITTLE_ENDIAN 0 # define PJ_IS_BIG_ENDIAN 1 -#elif defined (PJ_M_ARMV4) || defined(ARM) || defined(_ARM_) || \ - defined(ARMV4) || defined(__arm__) +#elif defined(ARM) || defined(_ARM_) || defined(__arm__) || defined(_M_ARM) +# define PJ_HAS_PENTIUM 0 /* * ARM, bi-endian, so raise error if endianness is not configured */ -# undef PJ_M_ARMV4 -# define PJ_M_ARMV4 1 -# define PJ_M_NAME "armv4" -# define PJ_HAS_PENTIUM 0 # if !PJ_IS_LITTLE_ENDIAN && !PJ_IS_BIG_ENDIAN # error Endianness must be declared for this processor # endif +# if defined (PJ_M_ARMV7) || defined(ARMV7) +# undef PJ_M_ARMV7 +# define PJ_M_ARM7 1 +# define PJ_M_NAME "armv7" +# elif defined (PJ_M_ARMV4) || defined(ARMV4) +# undef PJ_M_ARMV4 +# define PJ_M_ARMV4 1 +# define PJ_M_NAME "armv4" +# endif #elif defined (PJ_M_POWERPC) || defined(__powerpc) || defined(__powerpc__) || \ defined(__POWERPC__) || defined(__ppc__) || defined(_M_PPC) || \ diff --git a/pjlib/src/pj/file_access_win32.c b/pjlib/src/pj/file_access_win32.c index 5cae386b..e93605f4 100644 --- a/pjlib/src/pj/file_access_win32.c +++ b/pjlib/src/pj/file_access_win32.c @@ -33,6 +33,57 @@ # define CONTROL_ACCESS READ_CONTROL #endif +static pj_status_t get_file_size(HANDLE hFile, pj_off_t *size) +{ +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + FILE_COMPRESSION_INFO fileInfo; + + if (GetFileInformationByHandleEx(hFile, FileCompressionInfo, &fileInfo, + sizeof(FILE_COMPRESSION_INFO))) + { + *size = fileInfo.CompressedFileSize.QuadPart; + } + else { + *size = -1; + return PJ_RETURN_OS_ERROR(GetLastError()); + } +#else + DWORD sizeLo, sizeHi; + + sizeLo = GetFileSize(hFile, &sizeHi); + if (sizeLo == INVALID_FILE_SIZE) { + DWORD dwStatus = GetLastError(); + if (dwStatus != NO_ERROR) { + *size = -1; + return PJ_RETURN_OS_ERROR(dwStatus); + } + } + *size = sizeHi; + *size = ((*size) << 32) + sizeLo; +#endif + return PJ_SUCCESS; +} + +static HANDLE WINAPI create_file(LPCTSTR filename, DWORD desired_access, + DWORD share_mode, + LPSECURITY_ATTRIBUTES security_attributes, + DWORD creation_disposition, + DWORD flags_and_attributes, + HANDLE template_file) +{ +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + PJ_UNUSED_ARG(security_attributes); + PJ_UNUSED_ARG(flags_and_attributes); + PJ_UNUSED_ARG(template_file); + + return CreateFile2(filename, desired_access, share_mode, + creation_disposition, NULL); +#else + return CreateFile(filename, desired_access, share_mode, + security_attributes, creation_disposition, + flags_and_attributes, template_file); +#endif +} /* * pj_file_exists() @@ -44,10 +95,11 @@ PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) PJ_ASSERT_RETURN(filename != NULL, 0); - hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), - CONTROL_ACCESS, - FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + hFile = create_file(PJ_STRING_TO_NATIVE(filename, + wfilename, sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; @@ -62,30 +114,20 @@ PJ_DEF(pj_bool_t) pj_file_exists(const char *filename) PJ_DEF(pj_off_t) pj_file_size(const char *filename) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) - HANDLE hFile; - DWORD sizeLo, sizeHi; + HANDLE hFile; pj_off_t size; PJ_ASSERT_RETURN(filename != NULL, -1); - hFile = CreateFile(PJ_STRING_TO_NATIVE(filename, wfilename,sizeof(wfilename)), - CONTROL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + hFile = create_file(PJ_STRING_TO_NATIVE(filename, + wfilename, sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return -1; - sizeLo = GetFileSize(hFile, &sizeHi); - if (sizeLo == INVALID_FILE_SIZE) { - DWORD dwStatus = GetLastError(); - if (dwStatus != NO_ERROR) { - CloseHandle(hFile); - return -1; - } - } - - size = sizeHi; - size = (size << 32) + sizeLo; + get_file_size(hFile, &size); CloseHandle(hFile); return size; @@ -138,12 +180,17 @@ PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname) static pj_status_t file_time_to_time_val(const FILETIME *file_time, pj_time_val *time_val) { +#if !defined(PJ_WIN32_WINPHONE8) || !PJ_WIN32_WINPHONE8 FILETIME local_file_time; +#endif + SYSTEMTIME localTime; pj_parsed_time pt; +#if !defined(PJ_WIN32_WINPHONE8) || !PJ_WIN32_WINPHONE8 if (!FileTimeToLocalFileTime(file_time, &local_file_time)) return PJ_RETURN_OS_ERROR(GetLastError()); +#endif if (!FileTimeToSystemTime(file_time, &localTime)) return PJ_RETURN_OS_ERROR(GetLastError()); @@ -172,35 +219,48 @@ PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat) { PJ_DECL_UNICODE_TEMP_BUF(wfilename,256) HANDLE hFile; - DWORD sizeLo, sizeHi; FILETIME creationTime, accessTime, writeTime; +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + FILE_BASIC_INFO fileInfo; +#endif PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL); - hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), - CONTROL_ACCESS, - FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + hFile = create_file(PJ_STRING_TO_NATIVE(filename, + wfilename, sizeof(wfilename)), + CONTROL_ACCESS, + FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return PJ_RETURN_OS_ERROR(GetLastError()); - sizeLo = GetFileSize(hFile, &sizeHi); - if (sizeLo == INVALID_FILE_SIZE) { - DWORD dwStatus = GetLastError(); - if (dwStatus != NO_ERROR) { - CloseHandle(hFile); - return PJ_RETURN_OS_ERROR(dwStatus); - } + if (get_file_size(hFile, &stat->size) != PJ_SUCCESS) { + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(GetLastError()); } - stat->size = sizeHi; - stat->size = (stat->size << 32) + sizeLo; - - if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) { - DWORD dwStatus = GetLastError(); - CloseHandle(hFile); - return PJ_RETURN_OS_ERROR(dwStatus); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + if (GetFileInformationByHandleEx(hFile, FileBasicInfo, &fileInfo, + sizeof(FILE_BASIC_INFO))) + { + creationTime.dwLowDateTime = fileInfo.CreationTime.LowPart; + creationTime.dwHighDateTime = fileInfo.CreationTime.HighPart; + accessTime.dwLowDateTime = fileInfo.LastAccessTime.LowPart; + accessTime.dwHighDateTime = fileInfo.LastAccessTime.HighPart; + writeTime.dwLowDateTime = fileInfo.LastWriteTime.LowPart; + writeTime.dwHighDateTime = fileInfo.LastWriteTime.HighPart; } + else { + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(GetLastError()); + } +#else + if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime) == FALSE) { + DWORD dwStatus = GetLastError(); + CloseHandle(hFile); + return PJ_RETURN_OS_ERROR(dwStatus); + } +#endif CloseHandle(hFile); diff --git a/pjlib/src/pj/file_io_win32.c b/pjlib/src/pj/file_io_win32.c index b43c3abe..4361868b 100644 --- a/pjlib/src/pj/file_io_win32.c +++ b/pjlib/src/pj/file_io_win32.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -28,6 +29,39 @@ # define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif +static pj_status_t set_file_pointer(pj_oshandle_t fd, + pj_off_t offset, + pj_off_t* newPos, + DWORD dwMoveMethod) +{ +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + LARGE_INTEGER liDistance, liNewPos; + + liDistance.QuadPart = offset; + if (!SetFilePointerEx(fd, liDistance, &liNewPos, dwMoveMethod)) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } + *newPos = liNewPos.QuadPart; +#else + DWORD dwNewPos; + LONG hi32; + + hi32 = (LONG)(offset >> 32); + + dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod); + if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) { + DWORD dwStatus = GetLastError(); + if (dwStatus != 0) + return PJ_RETURN_OS_ERROR(dwStatus); + /* dwNewPos actually is not an error. */ + } + *newPos = hi32; + *newPos = (*newPos << 32) + dwNewPos; +#endif + + return PJ_SUCCESS; +} + /** * Check for end-of-file condition on the specified descriptor. * @@ -45,7 +79,7 @@ PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, unsigned flags, pj_oshandle_t *fd) { - PJ_DECL_UNICODE_TEMP_BUF(wpathname,256) + PJ_DECL_UNICODE_TEMP_BUF(wpathname, 256) HANDLE hFile; DWORD dwDesiredAccess = 0; DWORD dwShareMode = 0; @@ -86,14 +120,25 @@ PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool, } dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,wpathname,sizeof(wpathname)), +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + hFile = CreateFile2(PJ_STRING_TO_NATIVE(pathname, + wpathname, sizeof(wpathname)), + dwDesiredAccess, dwShareMode, dwCreationDisposition, + NULL); +#else + hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname, + wpathname, sizeof(wpathname)), dwDesiredAccess, dwShareMode, NULL, - dwCreationDisposition, dwFlagsAndAttributes, NULL); + dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + if (hFile == INVALID_HANDLE_VALUE) { + DWORD lastErr = GetLastError(); *fd = 0; - return PJ_RETURN_OS_ERROR(GetLastError()); + return PJ_RETURN_OS_ERROR(lastErr); } if ((flags & PJ_O_APPEND) == PJ_O_APPEND) { @@ -180,8 +225,7 @@ PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, enum pj_file_seek_type whence) { DWORD dwMoveMethod; - DWORD dwNewPos; - LONG hi32; + pj_off_t newPos; if (whence == PJ_SEEK_SET) dwMoveMethod = FILE_BEGIN; @@ -194,13 +238,8 @@ PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, return PJ_EINVAL; } - hi32 = (LONG)(offset >> 32); - dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod); - if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) { - DWORD dwStatus = GetLastError(); - if (dwStatus != 0) - return PJ_RETURN_OS_ERROR(dwStatus); - /* dwNewPos actually is not an error. */ + if (set_file_pointer(fd, offset, &newPos, dwMoveMethod) != PJ_SUCCESS) { + return PJ_RETURN_OS_ERROR(GetLastError()); } return PJ_SUCCESS; @@ -209,18 +248,10 @@ PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd, PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd, pj_off_t *pos) { - LONG hi32 = 0; - DWORD lo32; - - lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT); - if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) { - DWORD dwStatus = GetLastError(); - if (dwStatus != 0) - return PJ_RETURN_OS_ERROR(dwStatus); + if (set_file_pointer(fd, 0, pos, FILE_CURRENT) != PJ_SUCCESS) { + return PJ_RETURN_OS_ERROR(GetLastError()); } - *pos = hi32; - *pos = (*pos << 32) + lo32; return PJ_SUCCESS; } diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c index 97fb12ba..edb3c027 100644 --- a/pjlib/src/pj/ioqueue_common_abs.c +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -115,10 +115,10 @@ static pj_status_t ioqueue_init_key( pj_pool_t *pool, /* Create mutex for the key. */ #if !PJ_IOQUEUE_HAS_SAFE_UNREG - rc = pj_lock_create_simple_mutex(poll, NULL, &key->lock); -#endif + rc = pj_lock_create_simple_mutex(pool, NULL, &key->lock); if (rc != PJ_SUCCESS) return rc; +#endif /* Group lock */ key->grp_lock = grp_lock; diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h index 3bdbb524..d5e36b4d 100644 --- a/pjlib/src/pj/ioqueue_common_abs.h +++ b/pjlib/src/pj/ioqueue_common_abs.h @@ -80,7 +80,7 @@ struct accept_operation union operation_key { - struct generic_operation generic; + struct generic_operation generic_op; struct read_operation read; struct write_operation write; #if PJ_HAS_TCP diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c index 8dd01720..438b37a2 100644 --- a/pjlib/src/pj/ioqueue_select.c +++ b/pjlib/src/pj/ioqueue_select.c @@ -894,8 +894,21 @@ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) /* Unlock ioqueue before select(). */ pj_lock_release(ioqueue->lock); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + count = 0; + __try { +#endif + count = pj_sock_select(nfds+1, &rfdset, &wfdset, &xfdset, timeout); + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + /* Ignore Invalid Handle Exception raised by select().*/ + } + __except (GetExceptionCode() == STATUS_INVALID_HANDLE ? + EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH) { + } +#endif if (count == 0) return 0; diff --git a/pjlib/src/pj/ioqueue_uwp.cpp b/pjlib/src/pj/ioqueue_uwp.cpp new file mode 100644 index 00000000..8dee694a --- /dev/null +++ b/pjlib/src/pj/ioqueue_uwp.cpp @@ -0,0 +1,336 @@ +/* $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 +#include +#include +#include +#include +#include + +#include +#include + +#define THIS_FILE "ioq_uwp" + +#include "sock_uwp.h" +#include "ioqueue_common_abs.h" + + /* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + DECLARE_COMMON_KEY +}; + +/* +* This describes the I/O queue itself. +*/ +struct pj_ioqueue_t +{ + DECLARE_COMMON_IOQUEUE + pj_thread_desc thread_desc[16]; + unsigned thread_cnt; +}; + + +#include "ioqueue_common_abs.c" + +static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type) +{ + PJ_UNUSED_ARG(ioqueue); + PJ_UNUSED_ARG(key); + PJ_UNUSED_ARG(event_type); +} + + +static void start_next_read(pj_ioqueue_key_t *key) +{ + if (key_has_pending_read(key)) { + PjUwpSocket *s = (PjUwpSocket*)key->fd; + struct read_operation *op; + op = (struct read_operation*)key->read_list.next; + + if (op->op == PJ_IOQUEUE_OP_RECV) + s->Recv(NULL, (pj_ssize_t*)&op->size); + else + s->RecvFrom(NULL, (pj_ssize_t*)&op->size, NULL); + } +} + + +static void start_next_write(pj_ioqueue_key_t *key) +{ + if (key_has_pending_write(key)) { + PjUwpSocket *s = (PjUwpSocket*)key->fd; + struct write_operation *op; + op = (struct write_operation*)key->write_list.next; + + if (op->op == PJ_IOQUEUE_OP_SEND) + s->Send(op->buf, (pj_ssize_t*)&op->size); + else + s->SendTo(op->buf, (pj_ssize_t*)&op->size, &op->rmt_addr); + } +} + + +static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + enum ioqueue_event_type event_type ) +{ + PJ_UNUSED_ARG(ioqueue); + + if (event_type == READABLE_EVENT) { + /* This is either recv, recvfrom, or accept, do nothing on accept */ + start_next_read(key); + } else if (event_type == WRITEABLE_EVENT) { + /* This is either send, sendto, or connect, do nothing on connect */ + //start_next_write(key); + } +} + + +static void check_thread(pj_ioqueue_t *ioq) { + if (pj_thread_is_registered()) + return; + + pj_thread_t *t; + char tmp[16]; + pj_ansi_snprintf(tmp, sizeof(tmp), "UwpThread%02d", ioq->thread_cnt); + pj_thread_register(tmp, ioq->thread_desc[ioq->thread_cnt++], &t); + pj_assert(ioq->thread_cnt < PJ_ARRAY_SIZE(ioq->thread_desc)); + ioq->thread_cnt %= PJ_ARRAY_SIZE(ioq->thread_desc); +} + +static void on_read(PjUwpSocket *s, int bytes_read) +{ + pj_ioqueue_key_t *key = (pj_ioqueue_key_t*)s->GetUserData(); + pj_ioqueue_t *ioq = key->ioqueue; + check_thread(ioq); + + ioqueue_dispatch_read_event(key->ioqueue, key); + + if (bytes_read > 0) + start_next_read(key); +} + +static void on_write(PjUwpSocket *s, int bytes_sent) +{ + PJ_UNUSED_ARG(bytes_sent); + pj_ioqueue_key_t *key = (pj_ioqueue_key_t*)s->GetUserData(); + pj_ioqueue_t *ioq = key->ioqueue; + check_thread(ioq); + + ioqueue_dispatch_write_event(key->ioqueue, key); + + //start_next_write(key); +} + +static void on_accept(PjUwpSocket *s) +{ + pj_ioqueue_key_t *key = (pj_ioqueue_key_t*)s->GetUserData(); + pj_ioqueue_t *ioq = key->ioqueue; + check_thread(ioq); + + ioqueue_dispatch_read_event(key->ioqueue, key); +} + +static void on_connect(PjUwpSocket *s, pj_status_t status) +{ + PJ_UNUSED_ARG(status); + pj_ioqueue_key_t *key = (pj_ioqueue_key_t*)s->GetUserData(); + pj_ioqueue_t *ioq = key->ioqueue; + check_thread(ioq); + + ioqueue_dispatch_write_event(key->ioqueue, key); +} + + +/* + * Return the name of the ioqueue implementation. + */ +PJ_DEF(const char*) pj_ioqueue_name(void) +{ + return "ioqueue-uwp"; +} + + +/* + * Create a new I/O Queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioq; + pj_lock_t *lock; + pj_status_t rc; + + PJ_UNUSED_ARG(max_fd); + + ioq = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_t); + + /* Create and init ioqueue mutex */ + rc = pj_lock_create_null_mutex(pool, "ioq%p", &lock); + if (rc != PJ_SUCCESS) + return rc; + + rc = pj_ioqueue_set_lock(ioq, lock, PJ_TRUE); + if (rc != PJ_SUCCESS) + return rc; + + PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioq)); + + *p_ioqueue = ioq; + return PJ_SUCCESS; +} + + +/* + * Destroy the I/O queue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioq ) +{ + return ioqueue_destroy(ioq); +} + + +/* + * Register a socket to the I/O queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key ) +{ + return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data, + cb, p_key); +} + +PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool, + pj_ioqueue_t *ioqueue, + pj_sock_t sock, + pj_grp_lock_t *grp_lock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + PjUwpSocketCallback uwp_cb = + { &on_read, &on_write, &on_accept, &on_connect }; + pj_ioqueue_key_t *key; + pj_status_t rc; + + pj_lock_acquire(ioqueue->lock); + + key = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_key_t); + rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + + /* Create ioqueue key lock, if not yet */ + if (!key->lock) { + rc = pj_lock_create_simple_mutex(pool, NULL, &key->lock); + if (rc != PJ_SUCCESS) { + key = NULL; + goto on_return; + } + } + + PjUwpSocket *s = (PjUwpSocket*)sock; + s->SetNonBlocking(&uwp_cb, key); + +on_return: + if (rc != PJ_SUCCESS) { + if (key && key->grp_lock) + pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0); + } + *p_key = key; + pj_lock_release(ioqueue->lock); + + return rc; + +} + +/* + * Unregister from the I/O Queue framework. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key ) +{ + pj_ioqueue_t *ioqueue; + + PJ_ASSERT_RETURN(key, PJ_EINVAL); + + ioqueue = key->ioqueue; + + /* Lock the key to make sure no callback is simultaneously modifying + * the key. We need to lock the key before ioqueue here to prevent + * deadlock. + */ + pj_ioqueue_lock_key(key); + + /* Also lock ioqueue */ + pj_lock_acquire(ioqueue->lock); + + /* Close socket. */ + pj_sock_close(key->fd); + + /* Clear callback */ + key->cb.on_accept_complete = NULL; + key->cb.on_connect_complete = NULL; + key->cb.on_read_complete = NULL; + key->cb.on_write_complete = NULL; + + pj_lock_release(ioqueue->lock); + + if (key->grp_lock) { + pj_grp_lock_t *grp_lock = key->grp_lock; + pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0); + pj_grp_lock_release(grp_lock); + } else { + pj_ioqueue_unlock_key(key); + } + + pj_lock_destroy(key->lock); + + return PJ_SUCCESS; +} + + +/* + * Poll the I/O Queue for completed events. + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioq, + const pj_time_val *timeout) +{ + /* Polling is not necessary on UWP, since each socket handles + * its events already. + */ + PJ_UNUSED_ARG(ioq); + + pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); + + return 0; +} + diff --git a/pjlib/src/pj/ip_helper_winphone8.c b/pjlib/src/pj/ip_helper_winphone8.c new file mode 100644 index 00000000..40ca2981 --- /dev/null +++ b/pjlib/src/pj/ip_helper_winphone8.c @@ -0,0 +1,55 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2011 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 + +#include +#include +#include +#include + +/* + * Enumerate the local IP interface currently active in the host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_interface(int af, + unsigned *p_cnt, + pj_sockaddr ifs[]) +{ + PJ_UNUSED_ARG(af); + PJ_UNUSED_ARG(ifs); + + PJ_ASSERT_RETURN(p_cnt, PJ_EINVAL); + + *p_cnt = 0; + + return PJ_ENOTSUP; +} + +/* + * Enumerate the IP routing table for this host. + */ +PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt, + pj_ip_route_entry routes[]) +{ + PJ_UNUSED_ARG(routes); + PJ_ASSERT_RETURN(p_cnt, PJ_EINVAL); + + *p_cnt = 0; + + return PJ_ENOTSUP; +} \ No newline at end of file diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index 531be6bf..1cb6004d 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -38,6 +38,10 @@ # include #endif +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 +# include "../../../third_party/threademulation/include/ThreadEmulation.h" +#endif + /* Activate mutex related logging if PJ_DEBUG_MUTEX is set, otherwise * use default level 6 logging. */ @@ -297,7 +301,12 @@ PJ_DEF(pj_bool_t) pj_thread_is_registered(void) */ PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread) { +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + PJ_UNUSED_ARG(thread); + return -1; +#else return GetThreadPriority(thread->hthread); +#endif } @@ -312,7 +321,11 @@ PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio) prio<=THREAD_PRIORITY_TIME_CRITICAL, PJ_EINVAL); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + if (SetThreadPriorityRT(thread->hthread, prio) == FALSE) +#else if (SetThreadPriority(thread->hthread, prio) == FALSE) +#endif return PJ_RETURN_OS_ERROR(GetLastError()); return PJ_SUCCESS; @@ -473,6 +486,10 @@ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, DWORD dwflags = 0; pj_thread_t *rec; +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + PJ_UNUSED_ARG(stack_size); +#endif + PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); @@ -506,9 +523,17 @@ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, /* Create the thread. */ rec->proc = proc; rec->arg = arg; - rec->hthread = CreateThread(NULL, stack_size, - thread_main, rec, - dwflags, &rec->idthread); + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + rec->hthread = CreateThreadRT(NULL, 0, + thread_main, rec, + dwflags, NULL); +#else + rec->hthread = CreateThread(NULL, stack_size, + thread_main, rec, + dwflags, &rec->idthread); +#endif + if (rec->hthread == NULL) return PJ_RETURN_OS_ERROR(GetLastError()); @@ -540,7 +565,11 @@ PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) PJ_CHECK_STACK(); PJ_ASSERT_RETURN(p, PJ_EINVAL); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + if (ResumeThreadRT(rec->hthread) == (DWORD)-1) +#else if (ResumeThread(rec->hthread) == (DWORD)-1) +#endif return PJ_RETURN_OS_ERROR(GetLastError()); else return PJ_SUCCESS; @@ -584,7 +613,11 @@ PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + rc = WaitForSingleObjectEx(rec->hthread, INFINITE, FALSE); +#else rc = WaitForSingleObject(rec->hthread, INFINITE); +#endif if (rc==WAIT_OBJECT_0) return PJ_SUCCESS; @@ -616,7 +649,13 @@ PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) { PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + SleepRT(msec); +#else Sleep(msec); +#endif + return PJ_SUCCESS; } @@ -810,7 +849,11 @@ PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) //beginning before main thread is initialized. //PJ_CHECK_STACK(); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + *index = TlsAllocRT(); +#else *index = TlsAlloc(); +#endif if (*index == TLS_OUT_OF_INDEXES) return PJ_RETURN_OS_ERROR(GetLastError()); @@ -824,7 +867,11 @@ PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) PJ_DEF(void) pj_thread_local_free(long index) { PJ_CHECK_STACK(); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + TlsFreeRT(index); +#else TlsFree(index); +#endif } /* @@ -837,7 +884,13 @@ PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) //Can't check stack because this function is called in the //beginning before main thread is initialized. //PJ_CHECK_STACK(); + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + rc = TlsSetValueRT(index, value); +#else rc = TlsSetValue(index, value); +#endif + return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); } @@ -849,7 +902,11 @@ PJ_DEF(void*) pj_thread_local_get(long index) //Can't check stack because this function is called //by PJ_CHECK_STACK() itself!!! //PJ_CHECK_STACK(); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + return TlsGetValueRT(index); +#else return TlsGetValue(index); +#endif } /////////////////////////////////////////////////////////////////////////////// @@ -858,7 +915,9 @@ static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) PJ_CHECK_STACK(); -#if PJ_WIN32_WINNT >= 0x0400 +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + InitializeCriticalSectionEx(&mutex->crit, 0, 0); +#elif PJ_WIN32_WINNT >= 0x0400 InitializeCriticalSection(&mutex->crit); #else mutex->hMutex = CreateMutex(NULL, FALSE, NULL); @@ -1112,7 +1171,15 @@ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); sem = pj_pool_alloc(pool, sizeof(*sem)); + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + /** SEMAPHORE_ALL_ACCESS **/ + sem->hSemaphore = CreateSemaphoreEx(NULL, initial, max, NULL, 0, + SEMAPHORE_ALL_ACCESS); +#else sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); +#endif + if (!sem->hSemaphore) return PJ_RETURN_OS_ERROR(GetLastError()); @@ -1142,8 +1209,13 @@ static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting", pj_thread_this()->obj_name)); - + +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + result = WaitForSingleObjectEx(sem->hSemaphore, timeout, FALSE); +#else result = WaitForSingleObject(sem->hSemaphore, timeout); +#endif + if (result == WAIT_OBJECT_0) { LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", pj_thread_this()->obj_name)); @@ -1240,8 +1312,14 @@ PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, if (!event) return PJ_ENOMEM; - event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, - initial?TRUE:FALSE, NULL); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + event->hEvent = CreateEventEx(NULL, NULL, + (manual_reset? 0x1:0x0) | (initial? 0x2:0x0), + EVENT_ALL_ACCESS); +#else + event->hEvent = CreateEvent(NULL, manual_reset ? TRUE : FALSE, + initial ? TRUE : FALSE, NULL); +#endif if (!event->hEvent) return PJ_RETURN_OS_ERROR(GetLastError()); @@ -1273,7 +1351,12 @@ static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", pj_thread_this()->obj_name)); +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + result = WaitForSingleObjectEx(event->hEvent, timeout, FALSE); +#else result = WaitForSingleObject(event->hEvent, timeout); +#endif + if (result == WAIT_OBJECT_0) { PJ_LOG(6, (event->obj_name, "Event: thread %s is released", pj_thread_this()->obj_name)); @@ -1331,15 +1414,21 @@ PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) */ PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) { +#if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + PJ_UNUSED_ARG(event); + pj_assert(!"pj_event_pulse() not supported!"); + return PJ_ENOTSUP; +#else PJ_CHECK_STACK(); PJ_ASSERT_RETURN(event, PJ_EINVAL); PJ_LOG(6, (event->obj_name, "Pulsing event")); if (PulseEvent(event->hEvent)) - return PJ_SUCCESS; + return PJ_SUCCESS; else - return PJ_RETURN_OS_ERROR(GetLastError()); + return PJ_RETURN_OS_ERROR(GetLastError()); +#endif } /* diff --git a/pjlib/src/pj/os_info.c b/pjlib/src/pj/os_info.c index 3bf11670..858deb08 100644 --- a/pjlib/src/pj/os_info.c +++ b/pjlib/src/pj/os_info.c @@ -196,6 +196,9 @@ PJ_DEF(const pj_sys_info*) pj_get_sys_info(void) #endif #elif defined(_MSC_VER) { + #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + si.os_name = pj_str("winphone"); + #else OSVERSIONINFO ovi; ovi.dwOSVersionInfoSize = sizeof(ovi); @@ -210,21 +213,28 @@ PJ_DEF(const pj_sys_info*) pj_get_sys_info(void) #else si.os_name = pj_str("win32"); #endif + #endif } { SYSTEM_INFO wsi; + #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + GetNativeSystemInfo(&wsi); + #else GetSystemInfo(&wsi); + #endif + switch (wsi.wProcessorArchitecture) { - #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE + #if (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE) || \ + (defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8) case PROCESSOR_ARCHITECTURE_ARM: si.machine = pj_str("arm"); break; case PROCESSOR_ARCHITECTURE_SHX: si.machine = pj_str("shx"); break; - #else + #else case PROCESSOR_ARCHITECTURE_AMD64: si.machine = pj_str("x86_64"); break; @@ -234,8 +244,12 @@ PJ_DEF(const pj_sys_info*) pj_get_sys_info(void) case PROCESSOR_ARCHITECTURE_INTEL: si.machine = pj_str("i386"); break; - #endif /* PJ_WIN32_WINCE */ + #endif /* PJ_WIN32_WINCE */ } + #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8 + /* Avoid compile warning. */ + goto get_sdk_info; + #endif } #elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0 { 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 +#include +#include +#include +#include + +#include +#include + +#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 + (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 + (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 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 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 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 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 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 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 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 (;icnt && ioptions[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 */ diff --git a/pjlib/src/pj/sock_uwp.h b/pjlib/src/pj/sock_uwp.h new file mode 100644 index 00000000..6647f3ac --- /dev/null +++ b/pjlib/src/pj/sock_uwp.h @@ -0,0 +1,311 @@ +/* $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 + */ +#pragma once + + +#include +#include +#include +#include + + +enum { + READ_TIMEOUT = 60 * 1000, + WRITE_TIMEOUT = 60 * 1000, + SEND_BUFFER_SIZE = 128 * 1024, +}; + +enum PjUwpSocketType { + SOCKTYPE_UNKNOWN, SOCKTYPE_LISTENER, + SOCKTYPE_STREAM, SOCKTYPE_DATAGRAM +}; + +enum PjUwpSocketState { + SOCKSTATE_NULL, SOCKSTATE_INITIALIZED, SOCKSTATE_CONNECTING, + SOCKSTATE_CONNECTED, SOCKSTATE_DISCONNECTED, SOCKSTATE_ERROR +}; + +ref class PjUwpSocketDatagramRecvHelper; +ref class PjUwpSocketListenerHelper; +class PjUwpSocket; + + +typedef struct PjUwpSocketCallback +{ + void (*on_read)(PjUwpSocket *s, int bytes_read); + void (*on_write)(PjUwpSocket *s, int bytes_sent); + void (*on_accept)(PjUwpSocket *s); + void (*on_connect)(PjUwpSocket *s, pj_status_t status); +} PjUwpSocketCallback; + + +/* + * UWP Socket Wrapper. + */ +class PjUwpSocket +{ +public: + PjUwpSocket(int af_, int type_, int proto_); + virtual ~PjUwpSocket(); + pj_status_t InitSocket(enum PjUwpSocketType sock_type_); + void DeinitSocket(); + + void* GetUserData() { return user_data; } + void SetNonBlocking(const PjUwpSocketCallback *cb_, void *user_data_) + { + is_blocking = PJ_FALSE; + cb=*cb_; + user_data = user_data_; + } + + enum PjUwpSocketType GetType() { return sock_type; } + enum PjUwpSocketState GetState() { return sock_state; } + + pj_sockaddr* GetLocalAddr() { return &local_addr; } + pj_sockaddr* GetRemoteAddr() { return &remote_addr; } + + + pj_status_t Bind(const pj_sockaddr_t *addr = NULL); + pj_status_t Send(const void *buf, pj_ssize_t *len); + pj_status_t SendTo(const void *buf, pj_ssize_t *len, const pj_sockaddr_t *to); + pj_status_t Recv(void *buf, pj_ssize_t *len); + pj_status_t RecvFrom(void *buf, pj_ssize_t *len, pj_sockaddr_t *from); + pj_status_t Connect(const pj_sockaddr_t *addr); + pj_status_t Listen(); + pj_status_t Accept(PjUwpSocket **new_sock); + + void (*on_read)(PjUwpSocket *s, int bytes_read); + void (*on_write)(PjUwpSocket *s, int bytes_sent); + void (*on_accept)(PjUwpSocket *s, pj_status_t status); + void (*on_connect)(PjUwpSocket *s, pj_status_t status); + +private: + PjUwpSocket* CreateAcceptSocket(Windows::Networking::Sockets::StreamSocket^ stream_sock_); + pj_status_t SendImp(const void *buf, pj_ssize_t *len); + int ConsumeReadBuffer(void *buf, int max_len); + + int af; + int type; + int proto; + pj_sockaddr local_addr; + pj_sockaddr remote_addr; + pj_bool_t is_blocking; + pj_bool_t has_pending_bind; + pj_bool_t has_pending_send; + pj_bool_t has_pending_recv; + void *user_data; + PjUwpSocketCallback cb; + + enum PjUwpSocketType sock_type; + enum PjUwpSocketState sock_state; + Windows::Networking::Sockets::DatagramSocket^ datagram_sock; + Windows::Networking::Sockets::StreamSocket^ stream_sock; + Windows::Networking::Sockets::StreamSocketListener^ listener_sock; + + /* Helper objects */ + PjUwpSocketDatagramRecvHelper^ dgram_recv_helper; + PjUwpSocketListenerHelper^ listener_helper; + + Windows::Storage::Streams::DataReader^ socket_reader; + Windows::Storage::Streams::DataWriter^ socket_writer; + Windows::Storage::Streams::IBuffer^ send_buffer; + + friend PjUwpSocketDatagramRecvHelper; + friend PjUwpSocketListenerHelper; +}; + + +////////////////////////////////// +// Misc + + +inline pj_status_t wstr_addr_to_sockaddr(const wchar_t *waddr, + const wchar_t *wport, + pj_sockaddr_t *sockaddr) +{ +#if 0 + char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1]; + pj_assert(wcslen(waddr) < sizeof(tmp_str_buf)); + pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf)); + pj_str_t remote_host; + pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf)); + pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &remote_host, (pj_sockaddr*)sockaddr); + pj_sockaddr_set_port((pj_sockaddr*)sockaddr, (pj_uint16_t)_wtoi(wport)); + + return PJ_SUCCESS; +#endif + char tmp_str_buf[PJ_INET6_ADDRSTRLEN+1]; + pj_assert(wcslen(waddr) < sizeof(tmp_str_buf)); + pj_unicode_to_ansi(waddr, wcslen(waddr), tmp_str_buf, sizeof(tmp_str_buf)); + pj_str_t remote_host; + pj_strset(&remote_host, tmp_str_buf, pj_ansi_strlen(tmp_str_buf)); + pj_sockaddr *addr = (pj_sockaddr*)sockaddr; + pj_bool_t got_addr = PJ_FALSE; + + if (pj_inet_pton(PJ_AF_INET, &remote_host, &addr->ipv4.sin_addr) + == PJ_SUCCESS) + { + addr->addr.sa_family = PJ_AF_INET; + got_addr = PJ_TRUE; + } else if (pj_inet_pton(PJ_AF_INET6, &remote_host, &addr->ipv6.sin6_addr) + == PJ_SUCCESS) + { + addr->addr.sa_family = PJ_AF_INET6; + got_addr = PJ_TRUE; + } + if (!got_addr) + return PJ_EINVAL; + + pj_sockaddr_set_port(addr, (pj_uint16_t)_wtoi(wport)); + return PJ_SUCCESS; +} + + +inline pj_status_t sockaddr_to_hostname_port(const pj_sockaddr_t *sockaddr, + Windows::Networking::HostName ^&hostname, + int *port) +{ + char tmp[PJ_INET6_ADDRSTRLEN]; + wchar_t wtmp[PJ_INET6_ADDRSTRLEN]; + pj_sockaddr_print(sockaddr, tmp, PJ_INET6_ADDRSTRLEN, 0); + pj_ansi_to_unicode(tmp, pj_ansi_strlen(tmp), wtmp, + PJ_INET6_ADDRSTRLEN); + hostname = ref new Windows::Networking::HostName(ref new Platform::String(wtmp)); + *port = pj_sockaddr_get_port(sockaddr); + + return PJ_SUCCESS; +} + + +/* Buffer helper */ + +#include +#include + +inline Microsoft::WRL::ComPtr GetBufferByteAccess(Windows::Storage::Streams::IBuffer^ buffer) +{ + auto pUnk = reinterpret_cast(buffer); + + Microsoft::WRL::ComPtr comBuff; + pUnk->QueryInterface(__uuidof(Windows::Storage::Streams::IBufferByteAccess), (void**)comBuff.ReleaseAndGetAddressOf()); + + return comBuff; +} + + +inline void GetRawBufferFromIBuffer(Windows::Storage::Streams::IBuffer^ buffer, unsigned char** pbuffer) +{ + Platform::Object^ obj = buffer; + Microsoft::WRL::ComPtr insp(reinterpret_cast(obj)); + Microsoft::WRL::ComPtr bufferByteAccess; + insp.As(&bufferByteAccess); + bufferByteAccess->Buffer(pbuffer); +} + +inline void CopyToIBuffer(unsigned char* buffSource, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int writeStartPos = 0) +{ + auto bufferLen = buffer->Capacity; + assert(copyByteCount <= bufferLen); + + unsigned char* pBuffer; + + GetRawBufferFromIBuffer(buffer, &pBuffer); + + memcpy(pBuffer + writeStartPos, buffSource, copyByteCount); +} + +inline void CopyFromIBuffer(unsigned char* buffDestination, unsigned int copyByteCount, Windows::Storage::Streams::IBuffer^ buffer, unsigned int readStartPos = 0) +{ + assert(copyByteCount <= buffer->Capacity); + + unsigned char* pBuffer; + + GetRawBufferFromIBuffer(buffer, &pBuffer); + + memcpy(buffDestination, pBuffer + readStartPos, copyByteCount); +} + + +/* PPL helper */ + +#include +#include + +// Creates a task that completes after the specified delay, in ms. +inline concurrency::task complete_after(unsigned int timeout) +{ + // A task completion event that is set when a timer fires. + concurrency::task_completion_event tce; + + // Create a non-repeating timer. + auto fire_once = new concurrency::timer(timeout, 0, nullptr, false); + // Create a call object that sets the completion event after the timer fires. + auto callback = new concurrency::call([tce](int) + { + tce.set(); + }); + + // Connect the timer to the callback and start the timer. + fire_once->link_target(callback); + fire_once->start(); + + // Create a task that completes after the completion event is set. + concurrency::task event_set(tce); + + // Create a continuation task that cleans up resources and + // and return that continuation task. + return event_set.then([callback, fire_once]() + { + delete callback; + delete fire_once; + }); +} + +// Cancels the provided task after the specifed delay, if the task +// did not complete. +template +inline concurrency::task cancel_after_timeout(concurrency::task t, concurrency::cancellation_token_source cts, unsigned int timeout) +{ + // Create a task that returns true after the specified task completes. + concurrency::task success_task = t.then([](T) + { + return true; + }); + // Create a task that returns false after the specified timeout. + concurrency::task failure_task = complete_after(timeout).then([] + { + return false; + }); + + // Create a continuation task that cancels the overall task + // if the timeout task finishes first. + return (failure_task || success_task).then([t, cts](bool success) + { + if (!success) + { + // Set the cancellation token. The task that is passed as the + // t parameter should respond to the cancellation and stop + // as soon as it can. + cts.cancel(); + } + + // Return the original task. + return t; + }); +} -- cgit v1.2.3