2 /// \file brawchannel_win32.cc
3 /// Implements OS support for STDIN/STDOUT and TCP
7 Copyright (C) 2010-2012, RealVNC Ltd.
9 Some parts are inspired from bjavaloader.cc
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "brawchannel.h"
26 #include <barry/barry.h>
32 #define INVALID_HANDLE ((HANDLE)NULL)
33 // This is a name of a public semaphore to signal when the listen socket is opened
34 #define LISTEN_SEMAPHORE_NAME _T("Barry_brawchannel_%s_%d_startup_rendezvous")
35 #define LISTEN_SEMAPHORE_MAX_LEN 255
36 #define LISTEN_ADDRESS_MAX 128
39 using namespace Barry
;
44 // FIXME - this should be a std::string if ever moved to the library
45 const char * mListenAddress
;
53 ssize_t
StdOutStream::write(const unsigned char* ptr
, size_t size
)
55 size_t written
= fwrite(ptr
, 1, size
, stderr
);
57 ( ferror(stderr
) != 0 || feof(stderr
) != 0 ) ) {
60 return static_cast<ssize_t
>(written
);
63 /* Windows terminal input class implementation */
64 ssize_t
StdInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
66 /* Windows CE can't do non-blocking IO, so just always fail to read anything*/
67 Sleep(timeout
* 1000);
71 TcpStream::TcpStream(const char * addr
, long port
)
73 mImpl
.reset(new TcpStreamImpl
);
74 mImpl
->mListenAddress
= addr
;
76 mImpl
->mListenSocket
= INVALID_SOCKET
;
77 mImpl
->mSocket
= INVALID_SOCKET
;
78 mImpl
->mEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
80 mImpl
->mLastError
= WSAStartup(MAKEWORD(2, 2), &wsaData
);
81 if( mImpl
->mLastError
!= 0 ) {
82 cerr
<< _("Failed to startup WSA: ") << mImpl
->mLastError
<< endl
;
84 mImpl
->mListenSocket
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
85 if( mImpl
->mListenSocket
== INVALID_SOCKET
) {
86 cerr
<< _("Failed to create listening socket: ") <<
87 WSAGetLastError() << endl
;
89 if( mImpl
->mListenAddress
== NULL
) {
90 mImpl
->mHostAddress
.s_addr
= INADDR_ANY
;
92 mImpl
->mHostAddress
.s_addr
= inet_addr(mImpl
->mListenAddress
);
96 TcpStream::~TcpStream()
98 if( mImpl
->mSocket
!= INVALID_SOCKET
) {
99 shutdown(mImpl
->mSocket
, SD_SEND
);
100 closesocket(mImpl
->mSocket
);
101 mImpl
->mSocket
= INVALID_SOCKET
;
103 if( mImpl
->mListenSocket
!= INVALID_SOCKET
) {
104 shutdown(mImpl
->mListenSocket
, SD_SEND
);
105 closesocket(mImpl
->mListenSocket
);
106 mImpl
->mListenSocket
= INVALID_SOCKET
;
108 if( mImpl
->mEvent
!= INVALID_HANDLE
) {
109 CloseHandle(mImpl
->mEvent
);
110 mImpl
->mEvent
= INVALID_HANDLE
;
115 bool TcpStream::accept()
117 if( mImpl
->mListenSocket
== INVALID_SOCKET
||
118 mImpl
->mLastError
!= 0 ||
119 mImpl
->mHostAddress
.s_addr
== INADDR_NONE
) {
122 struct sockaddr_in serverAddr
;
123 memset(&serverAddr
, 0, sizeof(serverAddr
));
124 serverAddr
.sin_family
= AF_INET
;
125 serverAddr
.sin_addr
= mImpl
->mHostAddress
;
126 serverAddr
.sin_port
= htons(static_cast<u_short
>(mImpl
->mPort
));
127 if( ::bind(mImpl
->mListenSocket
, (sockaddr
*) & serverAddr
, sizeof(serverAddr
)) < 0 ) {
128 cerr
<< _("Failed to bind to listening address") << endl
;
132 // Set the socket options
134 if( setsockopt(mImpl
->mListenSocket
, SOL_SOCKET
, SO_REUSEADDR
,
135 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
136 cerr
<< _("Failed to enable reuse of address") << endl
;
141 if( ioctlsocket(mImpl
->mListenSocket
, FIONBIO
, &longOne
) == INVALID_SOCKET
) {
142 cerr
<< _("Failed to set non-blocking listening socket") << endl
;
146 if( ::listen(mImpl
->mListenSocket
, 5) == INVALID_SOCKET
) {
147 cerr
<< _("Failed to listen to listening address") << endl
;
151 struct sockaddr_in clientAddr
;
152 socklen_t len
= sizeof(clientAddr
);
153 cout
<< string_vprintf(_("Listening for connection on %s:%d"),
154 ( mImpl
->mListenAddress
== NULL
? "*" : mImpl
->mListenAddress
),
155 mImpl
->mPort
) << endl
;
157 /* Signal to a public semaphore that the listen socket is up */
158 TCHAR wListenAddress
[LISTEN_ADDRESS_MAX
];
159 if( MultiByteToWideChar(CP_ACP
, 0,
160 (mImpl
->mListenAddress
== NULL
? "*" : mImpl
->mListenAddress
), -1,
161 wListenAddress
, LISTEN_ADDRESS_MAX
) > 0 ) {
162 TCHAR semName
[LISTEN_SEMAPHORE_MAX_LEN
];
163 _snwprintf(semName
, LISTEN_SEMAPHORE_MAX_LEN
, LISTEN_SEMAPHORE_NAME
, wListenAddress
, mImpl
->mPort
);
164 semName
[LISTEN_SEMAPHORE_MAX_LEN
- 1] = 0;
165 HANDLE sem
= CreateSemaphore(NULL
, 0, 1, semName
);
167 ReleaseSemaphore(sem
, 1, NULL
);
172 int ret
= WSAEventSelect(mImpl
->mListenSocket
, mImpl
->mEvent
, FD_ACCEPT
);
174 cerr
<< _("WSAEventSelect failed with error: ") << ret
<< endl
;
177 DWORD signalledObj
= WaitForSingleObject(mImpl
->mEvent
, INFINITE
);
178 if( signalledObj
!= WAIT_OBJECT_0
) {
179 cerr
<< _("Failed to wait for new connection: ") << signalledObj
<< endl
;
183 mImpl
->mSocket
= ::accept(mImpl
->mListenSocket
, (struct sockaddr
*) &clientAddr
, &len
);
184 shutdown(mImpl
->mListenSocket
, SD_SEND
);
185 closesocket(mImpl
->mListenSocket
);
186 mImpl
->mListenSocket
= INVALID_SOCKET
;
187 if( mImpl
->mSocket
== INVALID_SOCKET
) {
188 cerr
<< _("Failed to accept on listening socket") << endl
;
192 if( setsockopt(mImpl
->mSocket
, IPPROTO_TCP
, TCP_NODELAY
,
193 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
194 cerr
<< _("Failed to set no delay") << endl
;
201 ssize_t
TcpInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
203 int ret
= WSAEventSelect(mStream
.mImpl
->mSocket
, mStream
.mImpl
->mEvent
, FD_READ
);
205 cerr
<< _("WSAEventSelect failed with error: ") << ret
<< endl
;
208 switch( WaitForSingleObject(mStream
.mImpl
->mEvent
, timeout
* 1000) ) {
213 ResetEvent(mStream
.mImpl
->mEvent
);
214 ret
= ::recv(mStream
.mImpl
->mSocket
, reinterpret_cast<char *>(ptr
), size
, 0);
215 if( ret
== SOCKET_ERROR
) {
216 int wsaErr
= WSAGetLastError();
228 cerr
<< _("WaitForSingleObject failed with error: ") << GetLastError() << endl
;
234 ssize_t
TcpOutStream::write(const unsigned char* ptr
, size_t size
)
236 return ::send(mStream
.mImpl
->mSocket
, reinterpret_cast<const char*>(ptr
), size
, 0);