2 /// \file brawchannel_unix.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 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <arpa/inet.h>
41 #define SD_SEND SHUT_WR
42 #define INVALID_SOCKET -1
43 #define INVALID_HANDLE -1
46 using namespace Barry
;
51 std::string mListenAddress
;
58 ssize_t
StdOutStream::write(const unsigned char* ptr
, size_t size
)
60 size_t written
= ::write(STDOUT_FILENO
, ptr
, size
);
62 ( ferror(stdout
) != 0 || feof(stdout
) != 0 ) ) {
65 return static_cast<ssize_t
>(written
);
68 ssize_t
StdInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
73 FD_SET(STDIN_FILENO
, &rfds
);
74 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
76 int ret
= select(STDIN_FILENO
+ 1, &rfds
, NULL
, NULL
, &tv
);
78 cerr
<< _("Select failed with errno: ") << errno
<< endl
;
80 } else if ( ret
&& FD_ISSET(STDIN_FILENO
, &rfds
) ) {
81 return ::read(STDIN_FILENO
, ptr
, size
);
87 TcpStream::TcpStream(const std::string
& addr
, long port
)
89 mImpl
.reset(new TcpStreamImpl
);
90 mImpl
->mListenAddress
= addr
;
92 mImpl
->mSocket
= INVALID_SOCKET
;
93 mImpl
->mListenSocket
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
94 if( mImpl
->mListenSocket
== INVALID_SOCKET
) {
95 cerr
<< _("Failed to create listening socket: ") <<
98 if( mImpl
->mListenAddress
.length() == 0 ) {
99 mImpl
->mHostAddress
.s_addr
= INADDR_ANY
;
100 mImpl
->mListenAddress
= "*";
102 mImpl
->mHostAddress
.s_addr
= inet_addr(mImpl
->mListenAddress
.c_str());
106 TcpStream::~TcpStream()
108 if( mImpl
->mSocket
!= INVALID_SOCKET
) {
109 shutdown(mImpl
->mSocket
, SD_SEND
);
110 close(mImpl
->mSocket
);
111 mImpl
->mSocket
= INVALID_SOCKET
;
113 if( mImpl
->mListenSocket
!= INVALID_SOCKET
) {
114 shutdown(mImpl
->mListenSocket
, SD_SEND
);
115 close(mImpl
->mListenSocket
);
116 mImpl
->mListenSocket
= INVALID_SOCKET
;
120 bool TcpStream::accept()
122 if( mImpl
->mListenSocket
== INVALID_SOCKET
||
123 mImpl
->mHostAddress
.s_addr
== INADDR_NONE
) {
126 struct sockaddr_in serverAddr
;
127 memset(&serverAddr
, 0, sizeof(serverAddr
));
128 serverAddr
.sin_family
= AF_INET
;
129 serverAddr
.sin_addr
= mImpl
->mHostAddress
;
130 serverAddr
.sin_port
= htons(static_cast<u_short
>(mImpl
->mPort
));
131 if( ::bind(mImpl
->mListenSocket
, (sockaddr
*) & serverAddr
, sizeof(serverAddr
)) < 0 ) {
132 cerr
<< _("Failed to bind to listening address") << endl
;
136 // Set the socket options
138 if( setsockopt(mImpl
->mListenSocket
, SOL_SOCKET
, SO_REUSEADDR
,
139 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
140 cerr
<< _("Failed to enable reuse of address") << endl
;
144 if( ::listen(mImpl
->mListenSocket
, 5) == INVALID_SOCKET
) {
145 cerr
<< _("Failed to listen to listening address") << endl
;
149 struct sockaddr_in clientAddr
;
150 socklen_t len
= sizeof(clientAddr
);
151 cout
<< string_vprintf(_("Listening for connection on %s:%ld"),
152 mImpl
->mListenAddress
.c_str(),
153 mImpl
->mPort
) << endl
;
155 mImpl
->mSocket
= ::accept(mImpl
->mListenSocket
, (struct sockaddr
*) &clientAddr
, &len
);
156 shutdown(mImpl
->mListenSocket
, SD_SEND
);
157 close(mImpl
->mListenSocket
);
158 mImpl
->mListenSocket
= INVALID_SOCKET
;
159 if( mImpl
->mSocket
== INVALID_SOCKET
) {
160 cerr
<< _("Failed to accept on listening socket") << endl
;
164 if( setsockopt(mImpl
->mSocket
, IPPROTO_TCP
, TCP_NODELAY
,
165 reinterpret_cast<const char *> (&one
), sizeof(one
)) < 0 ) {
166 cerr
<< _("Failed to set no delay") << endl
;
170 if( fcntl(mImpl
->mSocket
, F_SETFL
,
171 fcntl(mImpl
->mSocket
, F_GETFL
, 0) | O_NONBLOCK
) < 0 ) {
172 cerr
<< _("Failed to set non-blocking on socket") << endl
;
179 ssize_t
TcpInStream::read(unsigned char* ptr
, size_t size
, int timeout
)
184 FD_SET(mStream
.mImpl
->mSocket
, &rfds
);
185 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
187 int ret
= select(mStream
.mImpl
->mSocket
+ 1, &rfds
, NULL
, NULL
, &tv
);
189 cerr
<< _("Select failed with errno: ") << errno
<< endl
;
191 } else if ( ret
&& FD_ISSET(mStream
.mImpl
->mSocket
, &rfds
) ) {
192 return ::recv(mStream
.mImpl
->mSocket
, reinterpret_cast<char *>(ptr
), size
, 0);
198 ssize_t
TcpOutStream::write(const unsigned char* ptr
, size_t size
)
200 return ::send(mStream
.mImpl
->mSocket
, reinterpret_cast<const char*>(ptr
), size
, 0);