Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nx-libs
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
dimbor
nx-libs
Commits
c6e95651
Commit
c6e95651
authored
Jun 15, 2016
by
Mike Gabriel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nxcomp/Loop.cpp: Add Unix file socket support for proxy <-> proxy connection.
parent
b23dcd10
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
607 additions
and
222 deletions
+607
-222
ChannelEndPoint.cpp
nxcomp/ChannelEndPoint.cpp
+120
-23
ChannelEndPoint.h
nxcomp/ChannelEndPoint.h
+9
-2
Loop.cpp
nxcomp/Loop.cpp
+478
-197
No files found.
nxcomp/ChannelEndPoint.cpp
View file @
c6e95651
...
...
@@ -24,6 +24,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include "ChannelEndPoint.h"
...
...
@@ -31,8 +32,21 @@
ChannelEndPoint
::
ChannelEndPoint
(
const
char
*
spec
)
:
defaultTCPPort_
(
0
),
defaultTCPInterface_
(
0
),
defaultUnixPath_
(
NULL
)
{
spec_
=
(
spec
?
strdup
(
spec
)
:
NULL
);
defaultUnixPath_
(
NULL
),
spec_
(
NULL
)
{
setSpec
(
spec
);
}
ChannelEndPoint
::~
ChannelEndPoint
()
{
char
*
unixPath
=
NULL
;
if
(
getUnixPath
(
&
unixPath
))
{
struct
stat
st
;
lstat
(
unixPath
,
&
st
);
if
(
S_ISSOCK
(
st
.
st_mode
))
unlink
(
unixPath
);
}
}
void
...
...
@@ -40,21 +54,92 @@ ChannelEndPoint::setSpec(const char *spec) {
if
(
spec_
)
free
(
spec_
);
if
(
spec
&&
strlen
(
spec
))
{
spec_
=
strdup
(
spec
);
isUnix_
=
getUnixPath
();
isTCP_
=
getTCPHostAndPort
();
}
else
{
spec_
=
NULL
;
isUnix_
=
false
;
isTCP_
=
false
;
}
}
void
ChannelEndPoint
::
setSpec
(
int
port
)
{
ChannelEndPoint
::
setSpec
(
long
port
)
{
if
(
port
>=
0
)
{
char
tmp
[
20
];
sprintf
(
tmp
,
"%d"
,
port
);
sprintf
(
tmp
,
"%
l
d"
,
port
);
setSpec
(
tmp
);
}
else
{
disable
();
}
}
void
ChannelEndPoint
::
setSpec
(
const
char
*
hostName
,
long
port
)
{
int
length
;
if
(
spec_
)
free
(
spec_
);
isUnix_
=
false
;
isTCP_
=
false
;
if
(
hostName
&&
strlen
(
hostName
)
&&
port
>=
1
)
{
length
=
snprintf
(
NULL
,
0
,
"tcp:%s:%ld"
,
hostName
,
port
);
spec_
=
(
char
*
)
calloc
(
length
+
1
,
sizeof
(
char
));
snprintf
(
spec_
,
length
+
1
,
"tcp:%s:%ld"
,
hostName
,
port
);
isTCP_
=
true
;
}
else
setSpec
((
char
*
)
NULL
);
}
bool
ChannelEndPoint
::
getSpec
(
char
**
socketUri
)
const
{
if
(
socketUri
)
*
socketUri
=
NULL
;
char
*
unixPath
=
NULL
;
char
*
hostName
=
NULL
;
long
port
=
-
1
;
char
*
newSocketUri
=
NULL
;
int
length
=
-
1
;
if
(
getUnixPath
(
&
unixPath
))
{
length
=
snprintf
(
NULL
,
0
,
"unix:%s"
,
unixPath
);
}
else
if
(
getTCPHostAndPort
(
&
hostName
,
&
port
))
{
length
=
snprintf
(
NULL
,
0
,
"tcp:%s:%ld"
,
hostName
,
port
);
}
if
(
length
>
0
)
{
newSocketUri
=
(
char
*
)
calloc
(
length
+
1
,
sizeof
(
char
));
if
(
isUnixSocket
())
snprintf
(
newSocketUri
,
length
+
1
,
"unix:%s"
,
unixPath
);
else
snprintf
(
newSocketUri
,
length
+
1
,
"tcp:%s:%ld"
,
hostName
,
port
);
if
(
socketUri
)
*
socketUri
=
strdup
(
newSocketUri
);
}
free
(
newSocketUri
);
free
(
unixPath
);
free
(
hostName
);
if
(
*
socketUri
!=
'\0'
)
return
true
;
return
false
;
}
void
ChannelEndPoint
::
setDefaultTCPPort
(
long
port
)
{
defaultTCPPort_
=
port
;
...
...
@@ -76,10 +161,12 @@ ChannelEndPoint::setDefaultUnixPath(char *path) {
}
void
ChannelEndPoint
::
disable
()
{
setSpec
(
"0"
);
}
ChannelEndPoint
::
disable
()
{
setSpec
(
"0"
);
}
bool
ChannelEndPoint
::
specIs
Port
(
long
*
port
)
const
{
ChannelEndPoint
::
get
Port
(
long
*
port
)
const
{
if
(
port
)
*
port
=
0
;
long
p
=
-
1
;
if
(
spec_
)
{
...
...
@@ -101,7 +188,7 @@ ChannelEndPoint::getUnixPath(char **unixPath) const {
long
p
;
char
*
path
=
NULL
;
if
(
specIs
Port
(
&
p
))
{
if
(
get
Port
(
&
p
))
{
if
(
p
!=
1
)
return
false
;
}
else
if
(
spec_
&&
(
strncmp
(
"unix:"
,
spec_
,
5
)
==
0
))
{
...
...
@@ -122,6 +209,11 @@ ChannelEndPoint::getUnixPath(char **unixPath) const {
return
true
;
}
bool
ChannelEndPoint
::
isUnixSocket
()
const
{
return
isUnix_
;
}
// FIXME!!!
static
const
char
*
getComputerName
()
{
...
...
@@ -158,7 +250,7 @@ ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const {
if
(
host
)
*
host
=
NULL
;
if
(
port
)
*
port
=
0
;
if
(
specIs
Port
(
&
p
))
{
if
(
get
Port
(
&
p
))
{
h_len
=
0
;
}
else
if
(
spec_
&&
(
strncmp
(
"tcp:"
,
spec_
,
4
)
==
0
))
{
...
...
@@ -194,8 +286,8 @@ ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const {
}
bool
ChannelEndPoint
::
enabled
()
const
{
return
(
getUnixPath
()
||
getTCPHostAndPort
())
;
ChannelEndPoint
::
isTCPSocket
()
const
{
return
isTCP_
;
}
long
ChannelEndPoint
::
getTCPPort
()
const
{
...
...
@@ -205,8 +297,15 @@ long ChannelEndPoint::getTCPPort() const {
}
bool
ChannelEndPoint
::
enabled
()
const
{
return
(
isUnixSocket
()
||
isTCPSocket
());
}
bool
ChannelEndPoint
::
validateSpec
()
{
return
(
specIsPort
()
||
getUnixPath
()
||
getTCPHostAndPort
());
isTCP_
=
getTCPHostAndPort
();
isUnix_
=
getUnixPath
();
return
(
getPort
()
||
isUnix_
||
isTCP_
);
}
ChannelEndPoint
&
ChannelEndPoint
::
operator
=
(
const
ChannelEndPoint
&
other
)
{
...
...
@@ -219,26 +318,24 @@ ChannelEndPoint &ChannelEndPoint::operator=(const ChannelEndPoint &other) {
old
=
spec_
;
spec_
=
(
other
.
spec_
?
strdup
(
other
.
spec_
)
:
NULL
);
free
(
old
);
isUnix_
=
getUnixPath
();
isTCP_
=
getTCPHostAndPort
();
return
*
this
;
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
ChannelEndPoint
&
endPoint
)
{
if
(
endPoint
.
enabled
())
{
char
*
unixPath
,
*
host
;
long
port
;
if
(
endPoint
.
getUnixPath
(
&
unixPath
))
{
os
<<
"unix:"
<<
unixPath
;
free
(
unixPath
);
char
*
endPointSpec
=
NULL
;
if
(
endPoint
.
getSpec
(
&
endPointSpec
))
{
os
<<
endPointSpec
;
free
(
endPointSpec
);
}
else
if
(
endPoint
.
getTCPHostAndPort
(
&
host
,
&
port
))
{
os
<<
"tcp:"
<<
host
<<
":"
<<
port
;
free
(
host
);
}
else
{
else
os
<<
"(invalid)"
;
}
}
else
{
else
{
os
<<
"(disabled)"
;
}
return
os
;
...
...
nxcomp/ChannelEndPoint.h
View file @
c6e95651
...
...
@@ -33,25 +33,32 @@ class ChannelEndPoint
int
defaultTCPInterface_
;
// 0=localhost, otherwise IP of public interface.
char
*
defaultUnixPath_
;
char
*
spec_
;
bool
isUnix_
;
bool
isTCP_
;
bool
specIs
Port
(
long
*
port
=
NULL
)
const
;
bool
get
Port
(
long
*
port
=
NULL
)
const
;
public
:
ChannelEndPoint
(
const
char
*
spec
=
NULL
);
~
ChannelEndPoint
();
ChannelEndPoint
&
operator
=
(
const
ChannelEndPoint
&
other
);
bool
enabled
()
const
;
bool
disabled
()
{
return
!
enabled
();
}
void
disable
();
void
setSpec
(
const
char
*
spec
);
void
setSpec
(
int
port
);
void
setSpec
(
long
port
);
void
setSpec
(
const
char
*
hostName
,
long
port
);
bool
getSpec
(
char
**
socketUri
)
const
;
void
setDefaultTCPPort
(
long
port
);
void
setDefaultTCPInterface
(
int
publicInterface
);
void
setDefaultUnixPath
(
char
*
path
);
bool
getUnixPath
(
char
**
path
=
NULL
)
const
;
bool
isUnixSocket
()
const
;
bool
getTCPHostAndPort
(
char
**
hostname
=
NULL
,
long
*
port
=
NULL
)
const
;
long
getTCPPort
()
const
;
bool
isTCPSocket
()
const
;
bool
validateSpec
();
};
...
...
nxcomp/Loop.cpp
View file @
c6e95651
...
...
@@ -239,7 +239,7 @@ struct sockaddr_un
// should connect to remote.
//
#define WE_INITIATE_CONNECTION (
*connectHost != '\0'
)
#define WE_INITIATE_CONNECTION (
connectSocket.enabled()
)
//
// Is true if we must provide our credentials
...
...
@@ -255,7 +255,7 @@ struct sockaddr_un
//
#define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \
listen
Port != -1
)
listen
Socket.enabled()
)
//
// You must define FLUSH in Misc.h if
...
...
@@ -440,7 +440,7 @@ static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr,
static
int
ListenConnection
(
ChannelEndPoint
&
endPoint
,
const
char
*
label
);
static
int
ListenConnectionTCP
(
const
char
*
host
,
long
port
,
const
char
*
label
);
static
int
ListenConnectionUnix
(
const
char
*
unixP
ath
,
const
char
*
label
);
static
int
ListenConnectionUnix
(
const
char
*
p
ath
,
const
char
*
label
);
static
int
ListenConnectionAny
(
sockaddr
*
addr
,
socklen_t
addrlen
,
const
char
*
label
);
static
int
AcceptConnection
(
int
fd
,
int
domain
,
const
char
*
label
);
...
...
@@ -448,8 +448,11 @@ static int AcceptConnection(int fd, int domain, const char *label);
// Other convenience functions.
//
static
int
WaitForRemote
(
int
portNum
);
static
int
ConnectToRemote
(
const
char
*
const
hostName
,
int
portNum
);
static
int
PrepareProxyConnectionTCP
(
char
**
hostName
,
long
int
*
portNum
,
int
*
timeout
,
int
*
proxyFD
,
int
*
reason
);
static
int
PrepareProxyConnectionUnix
(
char
**
path
,
int
*
timeout
,
int
*
proxyFD
,
int
*
reason
);
static
int
WaitForRemote
(
ChannelEndPoint
&
socketAddress
);
static
int
ConnectToRemote
(
ChannelEndPoint
&
socketAddress
);
static
int
SendProxyOptions
(
int
fd
);
static
int
SendProxyCaches
(
int
fd
);
...
...
@@ -514,7 +517,7 @@ static int ParsePackOption(const char *opt);
// given on the command line.
//
static
int
ParseHostOption
(
const
char
*
opt
,
char
*
host
,
int
&
port
);
static
int
ParseHostOption
(
const
char
*
opt
,
char
*
host
,
long
&
port
);
//
// Translate a font server port specification
...
...
@@ -944,7 +947,6 @@ static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 };
// Other parameters.
//
static
char
connectHost
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
static
char
acceptHost
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
static
char
displayHost
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
static
char
authCookie
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
...
...
@@ -963,13 +965,19 @@ static sockaddr *xServerAddr = NULL;
static
unsigned
int
xServerAddrLength
=
0
;
//
// The port where the local proxy will await
// the peer connection or where the remote
// proxy will be contacted.
// The representation of a Unix socket path or
// a bind address, denoting where the local proxy
// will await the peer connection.
//
static
ChannelEndPoint
listenSocket
;
//
// The TCP host and port or Unix file socket where
// the remote proxy will be contacted.
//
static
int
listenPort
=
-
1
;
static
int
connectPort
=
-
1
;
static
ChannelEndPoint
connectSocket
;
//
// Helper channels are disabled by default.
...
...
@@ -3355,58 +3363,88 @@ int InitBeforeNegotiation()
int
SetupProxyConnection
()
{
if
(
proxyFD
==
-
1
)
{
char
*
socketUri
=
NULL
;
// Let's make sure, the default value for listenSocket is properly set. Doing this
// here, because we have to make sure that we call it after the connectSocket
// declaration is really really complete.
if
(
listenSocket
.
disabled
()
&&
connectSocket
.
disabled
())
{
char
listenPortValue
[
20
]
=
{
0
};
sprintf
(
listenPortValue
,
"%ld"
,
(
long
)(
proxyPort
+
DEFAULT_NX_PROXY_PORT_OFFSET
));
SetAndValidateChannelEndPointArg
(
"local"
,
"listen"
,
listenPortValue
,
listenSocket
);
}
#ifdef TEST
connectSocket
.
getSpec
(
&
socketUri
);
*
logofs
<<
"Loop: connectSocket is "
<<
(
connectSocket
.
enabled
()
?
"enabled"
:
"disabled"
)
<<
". "
<<
"The socket URI is '"
<<
(
socketUri
!=
NULL
?
socketUri
:
"<unset>"
)
<<
"'.
\n
"
<<
logofs_flush
;
listenSocket
.
getSpec
(
&
socketUri
);
*
logofs
<<
"Loop: listenSocket is "
<<
(
listenSocket
.
enabled
()
?
"enabled"
:
"disabled"
)
<<
". "
<<
"The socket URI is '"
<<
(
socketUri
!=
NULL
?
socketUri
:
"<unset>"
)
<<
"'.
\n
"
<<
logofs_flush
;
free
(
socketUri
);
socketUri
=
NULL
;
#endif
if
(
WE_INITIATE_CONNECTION
)
{
if
(
connect
Port
<
0
)
if
(
connect
Socket
.
getSpec
(
&
socketUri
)
)
{
connectPort
=
DEFAULT_NX_PROXY_PORT_OFFSET
+
proxyPort
;
}
#ifdef TEST
*
logofs
<<
"Loop: Going to connect to "
<<
connectHost
<<
":"
<<
connectPort
<<
".
\n
"
<<
logofs_flush
;
#endif
#ifdef TEST
*
logofs
<<
"Loop: Going to connect to '"
<<
socketUri
<<
"'.
\n
"
<<
logofs_flush
;
#endif
free
(
socketUri
);
proxyFD
=
ConnectToRemote
(
connectHost
,
connectPor
t
);
proxyFD
=
ConnectToRemote
(
connectSocke
t
);
#ifdef TEST
*
logofs
<<
"Loop: Connected to remote proxy on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
#endif
#ifdef TEST
*
logofs
<<
"Loop: Connected to remote proxy on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Info"
<<
": Connection to remote proxy '"
<<
connectHost
<<
":"
<<
connectPort
<<
"' established.
\n
"
;
cerr
<<
"Info"
<<
": Connected to remote proxy on FD#"
<<
proxyFD
<<
".
\n
"
;
}
}
else
{
if
(
listenPort
<
0
)
if
(
listenSocket
.
isTCPSocket
()
&&
(
listenSocket
.
getTCPPort
()
<
0
))
{
listen
Port
=
DEFAULT_NX_PROXY_PORT_OFFSET
+
proxyPort
;
listen
Socket
.
setSpec
(
DEFAULT_NX_PROXY_PORT_OFFSET
+
proxyPort
)
;
}
#ifdef TEST
*
logofs
<<
"Loop: Going to wait for connection on port "
<<
listenPort
<<
".
\n
"
<<
logofs_flush
;
#endif
if
(
listenSocket
.
getSpec
(
&
socketUri
))
{
#ifdef TEST
*
logofs
<<
"Loop: Going to wait for connection at '"
<<
socketUri
<<
"'.
\n
"
<<
logofs_flush
;
#endif
free
(
socketUri
);
proxyFD
=
WaitForRemote
(
listenPor
t
);
proxyFD
=
WaitForRemote
(
listenSocke
t
);
#ifdef TEST
#ifdef TEST
if
(
WE_LISTEN_FORWARDER
)
{
*
logofs
<<
"Loop: Connected to remote forwarder on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
}
else
{
*
logofs
<<
"Loop: Connected to remote proxy on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
}
#endif
if
(
WE_LISTEN_FORWARDER
)
{
*
logofs
<<
"Loop: Connected to remote forwarder on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
}
else
{
*
logofs
<<
"Loop: Connected to remote proxy on FD#"
<<
proxyFD
<<
".
\n
"
<<
logofs_flush
;
}
#endif
}
}
#ifdef TEST
...
...
@@ -3422,8 +3460,11 @@ int SetupProxyConnection()
// to reduce startup time. Option will
// later be disabled if needed.
//
// either listenSocket or connectSocket is used here...
if
(
listenSocket
.
isTCPSocket
()
||
connectSocket
.
isTCPSocket
())
SetNoDelay
(
proxyFD
,
1
);
SetNoDelay
(
proxyFD
,
1
);
//
// We need non-blocking input since the
...
...
@@ -4404,11 +4445,13 @@ int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label)
goto
SetupSocketError
;
}
if
(
SetReuseAddress
(
newFD
)
<
0
)
{
// SetReuseAddress already warns with an error
goto
SetupSocketError
;
}
if
(
addr
->
sa_family
==
AF_INET
)
if
(
SetReuseAddress
(
newFD
)
<
0
)
{
// SetReuseAddress already warns with an error
goto
SetupSocketError
;
}
if
(
bind
(
newFD
,
addr
,
addrlen
)
==
-
1
)
{
...
...
@@ -4427,12 +4470,12 @@ int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label)
if
(
listen
(
newFD
,
8
)
==
-
1
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Call to
bind
failed for "
<<
label
*
logofs
<<
"Loop: PANIC! Call to
listen
failed for "
<<
label
<<
". Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Call to
bind
failed for "
<<
label
cerr
<<
"Error"
<<
": Call to
listen
failed for "
<<
label
<<
". Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
...
...
@@ -5445,7 +5488,6 @@ void CleanupLocal()
*
unixSocketName
=
'\0'
;
*
connectHost
=
'\0'
;
*
acceptHost
=
'\0'
;
*
displayHost
=
'\0'
;
*
authCookie
=
'\0'
;
...
...
@@ -5460,8 +5502,8 @@ void CleanupLocal()
xServerAddr
=
NULL
;
listen
Port
=
-
1
;
connect
Port
=
-
1
;
listen
Socket
.
disable
()
;
connect
Socket
.
disable
()
;
cupsPort
.
disable
();
auxPort
.
disable
();
...
...
@@ -6592,62 +6634,79 @@ void ResetTimer()
}
//
// Open TCP
socket to listen for remote proxy and
// block until remote connects. If successful close
// the listening socket and return FD on which the
//
other
party is connected.
// Open TCP
or UNIX file socket to listen for remote proxy
//
and
block until remote connects. If successful close
// the listening socket and return FD on which the
other
// party is connected.
//
int
WaitForRemote
(
int
portNum
)
int
WaitForRemote
(
ChannelEndPoint
&
socketAddress
)
{
char
hostLabel
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
char
*
socketUri
=
NULL
;
int
retryAccept
=
-
1
;
int
proxyFD
=
-
1
;
int
newFD
=
-
1
;
//
// Get IP address of host to be awaited.
//
int
acceptIPAddr
=
0
;
if
(
*
acceptHost
!=
'\0'
)
if
(
socketAddress
.
isTCPSocket
()
)
{
acceptIPAddr
=
GetHostAddress
(
acceptHost
);
if
(
acceptIPAddr
==
0
)
//
// Get IP address of host to be awaited.
//
if
(
*
acceptHost
!=
'\0'
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Cannot accept connections from unknown host '"
<<
acceptHost
<<
"'.
\n
"
<<
logofs_flush
;
#endif
acceptIPAddr
=
GetHostAddress
(
acceptHost
);
cerr
<<
"Error"
<<
": Cannot accept connections from unknown host '"
<<
acceptHost
<<
"'.
\n
"
;
if
(
acceptIPAddr
==
0
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Cannot accept connections from unknown host '"
<<
acceptHost
<<
"'.
\n
"
<<
logofs_flush
;
#endif
goto
WaitForRemoteError
;
cerr
<<
"Error"
<<
": Cannot accept connections from unknown host '"
<<
acceptHost
<<
"'.
\n
"
;
goto
WaitForRemoteError
;
}
strcpy
(
hostLabel
,
"any host"
);
}
else
{
snprintf
(
hostLabel
,
sizeof
(
hostLabel
),
"'%s'"
,
acceptHost
);
}
if
(
loopbackBind
)
{
long
bindPort
;
if
(
socketAddress
.
getTCPHostAndPort
(
NULL
,
&
bindPort
))
socketAddress
.
setSpec
(
"localhost"
,
bindPort
);
}
strcpy
(
hostLabel
,
"any host"
);
}
else
if
(
socketAddress
.
isUnixSocket
())
strcpy
(
hostLabel
,
"this host"
);
else
{
snprintf
(
hostLabel
,
sizeof
(
hostLabel
),
"'%s'"
,
acceptHost
);
}
strcpy
(
hostLabel
,
"unknown origin (something went wrong!!!)"
);
proxyFD
=
ListenConnectionTCP
(((
loopbackBind
||
(
control
->
ProxyMode
==
proxy_server
))
?
"localhost"
:
"*"
),
portNum
,
"NX"
);
proxyFD
=
ListenConnection
(
socketAddress
,
"NX"
);
socketAddress
.
getSpec
(
&
socketUri
);
#ifdef TEST
*
logofs
<<
"Loop: Waiting for connection from "
<<
hostLabel
<<
" on
port '"
<<
portNum
<<
hostLabel
<<
" on
socket '"
<<
socketUri
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Info"
<<
": Waiting for connection from "
<<
hostLabel
<<
" on
port '"
<<
portNum
<<
hostLabel
<<
" on
socket '"
<<
socketUri
<<
"'.
\n
"
;
free
(
socketUri
);
//
// How many times to loop waiting for connections
...
...
@@ -6702,12 +6761,19 @@ int WaitForRemote(int portNum)
}
else
if
(
result
>
0
&&
FD_ISSET
(
proxyFD
,
&
readSet
))
{
sockaddr_in
newAddr
;
socklen_t
addrLen
=
sizeof
(
sockaddr_in
);
newFD
=
accept
(
proxyFD
,
(
sockaddr
*
)
&
newAddr
,
&
addrLen
)
;
sockaddr_in
newAddrINET
;
if
(
socketAddress
.
isUnixSocket
())
{
socklen_t
addrLen
=
sizeof
(
sockaddr_un
);
newFD
=
accept
(
proxyFD
,
NULL
,
&
addrLen
);
}
else
if
(
socketAddress
.
isTCPSocket
())
{
socklen_t
addrLen
=
sizeof
(
sockaddr_in
);
newFD
=
accept
(
proxyFD
,
(
sockaddr
*
)
&
newAddrINET
,
&
addrLen
);
}
if
(
newFD
==
-
1
)
{
#ifdef PANIC
...
...
@@ -6722,47 +6788,82 @@ int WaitForRemote(int portNum)
goto
WaitForRemoteError
;
}
char
*
connectedHost
=
inet_ntoa
(
newAddr
.
sin_addr
);
if
(
*
acceptHost
==
'\0'
||
(
int
)
newAddr
.
sin_addr
.
s_addr
==
acceptIPAddr
)
if
(
socketAddress
.
isUnixSocket
())
{
#ifdef TEST
unsigned
int
connectedPort
=
ntohs
(
newAddr
.
sin_port
);
*
logofs
<<
"Loop: Accepted connection from '"
<<
connectedHost
<<
"' with port '"
<<
connectedPort
<<
"'.
\n
"
char
*
unixPath
=
NULL
;
socketAddress
.
getUnixPath
(
&
unixPath
);
#ifdef TEST
*
logofs
<<
"Loop: Accepted connection from this host on Unix file socket '"
<<
unixPath
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Info"
<<
": Accepted connection from '"
<<
connectedHost
<<
"'.
\n
"
;
cerr
<<
"Info"
<<
": Accepted connection from this host on Unix file socket '"
<<
unixPath
<<
"'.
\n
"
;
free
(
unixPath
);
break
;
}
else
else
if
(
socketAddress
.
isTCPSocket
())
{
#ifdef PANIC
*
logofs
<<
"Loop: WARNING! Refusing connection from '"
<<
connectedHost
<<
"' on port '"
<<
portNum
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Warning"
<<
": Refusing connection from '"
<<
connectedHost
<<
"'.
\n
"
;
}
char
*
connectedHost
=
inet_ntoa
(
newAddrINET
.
sin_addr
);
//
// Not the best way to elude a DOS attack...
//
if
(
*
acceptHost
==
'\0'
||
(
int
)
newAddrINET
.
sin_addr
.
s_addr
==
acceptIPAddr
)
{
#ifdef TEST
sleep
(
5
);
unsigned
int
connectedPort
=
ntohs
(
newAddrINET
.
sin_port
);
*
logofs
<<
"Loop: Accepted connection from '"
<<
connectedHost
<<
"' with port '"
<<
connectedPort
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Info"
<<
": Accepted connection from '"
<<
connectedHost
<<
"'.
\n
"
;
break
;
}
else
{
#ifdef PANIC
*
logofs
<<
"Loop: WARNING! Refusing connection from '"
<<
connectedHost
<<
"' on port '"
<<
socketAddress
.
getTCPPort
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Warning"
<<
": Refusing connection from '"
<<
connectedHost
<<
"'.
\n
"
;
}
//
// Not the best way to elude a DOS attack...
//
sleep
(
5
);
close
(
newFD
);
}
close
(
newFD
);
}
if
(
--
retryAccept
==
0
)
{
if
(
*
acceptHost
==
'\0'
)
if
(
socketAddress
.
isUnixSocket
())
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Connection via Unix file socket from this host "
<<
"could not be established.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Connection via Unix file socket from this host "
<<
"could not be established.
\n
"
;
}
else
if
(
*
acceptHost
==
'\0'
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Connection with remote host "
...
...
@@ -6804,43 +6905,194 @@ WaitForRemoteError:
HandleCleanup
();
}
//
// Connect to remote proxy. If successful
// return FD of connection, else return -1.
//
int
ConnectToRemote
(
const
char
*
const
hostName
,
int
portNum
)
int
PrepareProxyConnectionTCP
(
char
**
hostName
,
long
int
*
portNum
,
int
*
timeout
,
int
*
proxyFD
,
int
*
reason
)
{
int
proxyFD
=
-
1
;
int
remoteIPAddr
=
GetHostAddress
(
hostName
);
if
(
!
proxyFD
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). "
<<
"'proxyFD' must not be a NULL pointer.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Implementation error (PrepareProxyConnectionTCP). "
<<
"'proxyFD' must not be a NULL pointer.
\n
"
;
return
-
1
;
}
if
(
!
reason
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). "
<<
"'reason' must not be a NULL pointer.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Implementation error (PrepareProxyConnectionTCP). "
<<
"'reason' must not be a NULL pointer.
\n
"
;
return
-
1
;
}
int
remoteIPAddr
=
GetHostAddress
(
*
hostName
);
if
(
remoteIPAddr
==
0
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Unknown remote host '"
<<
hostName
<<
"'.
\n
"
<<
logofs_flush
;
<<
*
hostName
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Unknown remote host '"
<<
hostName
<<
"'.
\n
"
;
cerr
<<
"Error"
<<
": Unknown remote host '"
<<
*
hostName
<<
"'.
\n
"
;
HandleCleanup
();
}
#ifdef TEST
*
logofs
<<
"Loop: Connecting to remote host '"
<<
hostName
<<
":"
<<
portNum
<<
"'.
\n
"
<<
*
hostName
<<
":"
<<
*
portNum
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Info"
<<
": Connecting to remote host '"
<<
hostName
<<
":"
<<
portNum
<<
"'.
\n
"
<<
*
hostName
<<
":"
<<
*
portNum
<<
"'.
\n
"
<<
logofs_flush
;
*
proxyFD
=
-
1
;
*
reason
=
-
1
;
sockaddr_in
addr
;
addr
.
sin_family
=
AF_INET
;
addr
.
sin_port
=
htons
(
*
portNum
);
addr
.
sin_addr
.
s_addr
=
remoteIPAddr
;
*
proxyFD
=
socket
(
AF_INET
,
SOCK_STREAM
,
PF_UNSPEC
);
*
reason
=
EGET
();
if
(
*
proxyFD
==
-
1
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Call to socket failed. "
<<
"Error is "
<<
*
reason
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Call to socket failed. "
<<
"Error is "
<<
*
reason
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
return
-
1
;
}
else
if
(
SetReuseAddress
(
*
proxyFD
)
<
0
)
{
return
-
1
;
}
//
// Ensure operation is timed out
// if there is a network problem.
//
if
(
timeout
)
SetTimer
(
*
timeout
);
else
SetTimer
(
20000
);
int
result
=
connect
(
*
proxyFD
,
(
sockaddr
*
)
&
addr
,
sizeof
(
sockaddr_in
));
*
reason
=
EGET
();
ResetTimer
();
return
result
;
}
int
PrepareProxyConnectionUnix
(
char
**
path
,
int
*
timeout
,
int
*
proxyFD
,
int
*
reason
)
{
if
(
!
proxyFD
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). "
<<
"proxyFD must not be a NULL pointer.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Implementation error (PrepareProxyConnectionUnix). "
<<
"proxyFD must not be a NULL pointer.
\n
"
;
return
-
1
;
}
if
(
!
reason
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). "
<<
"'reason' must not be a NULL pointer.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Implementation error (PrepareProxyConnectionUnix). "
<<
"'reason' must not be a NULL pointer.
\n
"
;
return
-
1
;
}
/* FIXME: Add socket file existence and permission checks */
*
proxyFD
=
-
1
;
*
reason
=
-
1
;
sockaddr_un
addr
;
addr
.
sun_family
=
AF_UNIX
;
strncpy
(
addr
.
sun_path
,
*
path
,
108
-
1
);
*
proxyFD
=
socket
(
AF_UNIX
,
SOCK_STREAM
,
PF_UNSPEC
);
*
reason
=
EGET
();
if
(
*
proxyFD
==
-
1
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Call to socket failed. "
<<
"Error is "
<<
*
reason
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Call to socket failed. "
<<
"Error is "
<<
*
reason
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
return
-
1
;
}
//
// Ensure operation is timed out
// if there is a network problem.
//
if
(
timeout
)
SetTimer
(
*
timeout
);
else
SetTimer
(
20000
);
int
result
=
connect
(
*
proxyFD
,
(
sockaddr
*
)
&
addr
,
sizeof
(
sockaddr_un
));
*
reason
=
EGET
();
ResetTimer
();
return
result
;
}
//
// Connect to remote proxy. If successful
// return FD of connection, else return -1.
//
int
ConnectToRemote
(
ChannelEndPoint
&
socketAddress
)
{
//
// How many times we retry to connect to remote
// host in case of failure?
// host
/ Unix domain socket
in case of failure?
//
int
retryConnect
=
control
->
OptionProxyRetryConnect
;
...
...
@@ -6858,39 +7110,16 @@ int ConnectToRemote(const char *const hostName, int portNum)
T_timestamp
lastRetry
=
getNewTimestamp
();
sockaddr_in
addr
;
int
result
=
-
1
;
int
reason
=
-
1
;
int
proxyFD
=
-
1
;
addr
.
sin_family
=
AF_INET
;
addr
.
sin_port
=
htons
(
portNum
)
;
addr
.
sin_addr
.
s_addr
=
remoteIPAddr
;
char
*
hostName
=
NULL
;
long
int
portNum
=
-
1
;
char
*
unixPath
=
NULL
;
for
(;;)
{
proxyFD
=
socket
(
AF_INET
,
SOCK_STREAM
,
PF_UNSPEC
);
if
(
proxyFD
==
-
1
)
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Call to socket failed. "
<<
"Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Call to socket failed. "
<<
"Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
goto
ConnectToRemoteError
;
}
else
if
(
SetReuseAddress
(
proxyFD
)
<
0
)
{
goto
ConnectToRemoteError
;
}
//
// Ensure operation is timed out
// if there is a network problem.
//
#ifdef DEBUG
*
logofs
<<
"Loop: Timer set to "
<<
connectTimeout
/
1000
...
...
@@ -6899,13 +7128,10 @@ int ConnectToRemote(const char *const hostName, int portNum)
<<
"'.
\n
"
<<
logofs_flush
;
#endif
SetTimer
(
connectTimeout
);
int
result
=
connect
(
proxyFD
,
(
sockaddr
*
)
&
addr
,
sizeof
(
sockaddr_in
));
int
reason
=
EGET
();
ResetTimer
();
if
(
socketAddress
.
getUnixPath
(
&
unixPath
))
result
=
PrepareProxyConnectionUnix
(
&
unixPath
,
&
connectTimeout
,
&
proxyFD
,
&
reason
);
else
if
(
socketAddress
.
getTCPHostAndPort
(
&
hostName
,
&
portNum
))
result
=
PrepareProxyConnectionTCP
(
&
hostName
,
&
portNum
,
&
connectTimeout
,
&
proxyFD
,
&
reason
);
if
(
result
<
0
)
{
...
...
@@ -6919,24 +7145,40 @@ int ConnectToRemote(const char *const hostName, int portNum)
{
ESET
(
reason
);
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
if
(
socketAddress
.
isUnixSocket
())
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Connection to Unix file socket '"
<<
unixPath
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
cerr
<<
"Error"
<<
": Connection to Unix file socket '"
<<
unixPath
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
}
else
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed. Error is "
<<
EGET
()
<<
" '"
<<
ESTR
()
<<
"'.
\n
"
;
}
goto
ConnectToRemoteError
;
}
else
{
#ifdef TEST
*
logofs
<<
"Loop: Sleeping "
<<
retryTimeout
<<
"
M
s before retrying.
\n
"
*
logofs
<<
"Loop: Sleeping "
<<
retryTimeout
<<
"
m
s before retrying.
\n
"
<<
logofs_flush
;
#endif
...
...
@@ -7005,10 +7247,20 @@ int ConnectToRemote(const char *const hostName, int portNum)
ESET
(
reason
);
#ifdef TEST
*
logofs
<<
"Loop: Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed with error '"
<<
ESTR
()
<<
"'. Retrying.
\n
"
<<
logofs_flush
;
if
(
unixPath
[
0
]
!=
'\0'
)
{
*
logofs
<<
"Loop: Connection to Unix socket file '"
<<
unixPath
<<
"' failed with error '"
<<
ESTR
()
<<
"'. Retrying.
\n
"
<<
logofs_flush
;
}
else
{
*
logofs
<<
"Loop: Connection to '"
<<
hostName
<<
":"
<<
portNum
<<
"' failed with error '"
<<
ESTR
()
<<
"'. Retrying.
\n
"
<<
logofs_flush
;
}
#endif
}
else
...
...
@@ -8234,6 +8486,9 @@ int ParseEnvironmentOptions(const char *env, int force)
name
=
strtok
(
nextOpts
,
"="
);
char
connectHost
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
long
connectPort
=
-
1
;
while
(
name
)
{
value
=
strtok
(
NULL
,
","
);
...
...
@@ -8254,6 +8509,7 @@ int ParseEnvironmentOptions(const char *env, int force)
}
else
if
(
strcasecmp
(
name
,
"link"
)
==
0
)
{
if
(
control
->
ProxyMode
==
proxy_server
)
{
PrintOptionIgnored
(
"local"
,
name
,
value
);
...
...
@@ -8315,26 +8571,29 @@ int ParseEnvironmentOptions(const char *env, int force)
}
else
if
(
strcasecmp
(
name
,
"listen"
)
==
0
)
{
if
(
*
connectHost
!=
'\0'
)
char
*
socketUri
=
NULL
;
if
(
connectSocket
.
getSpec
(
&
socketUri
))
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Can't handle 'listen' and 'connect' parameters "
<<
"at the same time.
\n
"
<<
logofs_flush
;
*
logofs
<<
"Loop: PANIC! Refusing 'listen' parameter with 'connect' being '"
<<
connectHost
<<
"'.
\n
"
<<
logofs_flush
;
<<
socketUri
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Can't handle 'listen' and 'connect' parameters "
<<
"at the same time.
\n
"
;
cerr
<<
"Error"
<<
": Refusing 'listen' parameter with 'connect' being '"
<<
connectHost
<<
"'.
\n
"
;
<<
socketUri
<<
"'.
\n
"
;
free
(
socketUri
);
return
-
1
;
}
listenPort
=
ValidateArg
(
"local"
,
name
,
value
);
SetAndValidateChannelEndPointArg
(
"local"
,
name
,
value
,
listenSocket
);
}
else
if
(
strcasecmp
(
name
,
"loopback"
)
==
0
)
{
...
...
@@ -8342,22 +8601,24 @@ int ParseEnvironmentOptions(const char *env, int force)
}
else
if
(
strcasecmp
(
name
,
"accept"
)
==
0
)
{
if
(
*
connectHost
!=
'\0'
)
char
*
socketUri
=
NULL
;
if
(
connectSocket
.
getSpec
(
&
socketUri
))
{
#ifdef PANIC
*
logofs
<<
"Loop: PANIC! Can't handle 'accept' and 'connect' parameters "
<<
"at the same time.
\n
"
<<
logofs_flush
;
*
logofs
<<
"Loop: PANIC! Refusing 'accept' parameter with 'connect' being '"
<<
connectHost
<<
"'.
\n
"
<<
logofs_flush
;
<<
socketUri
<<
"'.
\n
"
<<
logofs_flush
;
#endif
cerr
<<
"Error"
<<
": Can't handle 'accept' and 'connect' parameters "
<<
"at the same time.
\n
"
;
cerr
<<
"Error"
<<
": Refusing 'accept' parameter with 'connect' being '"
<<
connectHost
<<
"'.
\n
"
;
<<
socketUri
<<
"'.
\n
"
;
free
(
socketUri
);
return
-
1
;
}
...
...
@@ -8383,8 +8644,12 @@ int ParseEnvironmentOptions(const char *env, int force)
return
-
1
;
}
strncpy
(
connectHost
,
value
,
DEFAULT_STRING_LENGTH
-
1
);
if
((
strncmp
(
value
,
"tcp:"
,
4
)
==
0
)
||
(
strncmp
(
value
,
"unix:"
,
5
)
==
0
))
SetAndValidateChannelEndPointArg
(
"local"
,
name
,
value
,
connectSocket
);
else
// if the "connect" parameter does not start with "unix:" or "tcp:" assume
// old parameter usage style (providing hostname string only).
strcpy
(
connectHost
,
value
);
}
else
if
(
strcasecmp
(
name
,
"port"
)
==
0
)
{
...
...
@@ -8842,6 +9107,17 @@ int ParseEnvironmentOptions(const char *env, int force)
}
// End of while (name) ...
// Assemble the connectSocket channel end point if parameter values have been old-school...
if
(
connectSocket
.
disabled
()
&&
(
connectHost
[
0
]
!=
'\0'
)
&&
(
proxyPort
>
0
||
connectPort
>
0
))
{
if
(
connectPort
<
0
)
connectPort
=
proxyPort
+
DEFAULT_NX_PROXY_PORT_OFFSET
;
char
tcpHostAndPort
[
DEFAULT_STRING_LENGTH
]
=
{
0
};
sprintf
(
tcpHostAndPort
,
"tcp:%s:%ld"
,
connectHost
,
connectPort
);
SetAndValidateChannelEndPointArg
(
"local"
,
name
,
tcpHostAndPort
,
connectSocket
);
}
#ifdef TEST
*
logofs
<<
"Loop: Completed parsing of string '"
<<
env
<<
"'.
\n
"
<<
logofs_flush
;
...
...
@@ -9082,16 +9358,21 @@ int ParseCommandLineOptions(int argc, const char **argv)
// command line at the connecting side.
//
if
(
ParseHostOption
(
nextArg
,
connectHost
,
connectPort
)
>
0
)
{
//
// Assume port is at a proxied display offset.
//
char
*
cHost
;
long
cPort
;
proxyPort
=
connectPort
;
if
(
connectSocket
.
getTCPHostAndPort
(
&
cHost
,
&
cPort
)
&&
(
ParseHostOption
(
nextArg
,
cHost
,
cPort
)
>
0
))
{
//
// Assume port is at a proxied display offset.
//
connectPort
+=
DEFAULT_NX_PROXY_PORT_OFFSET
;
}
proxyPort
=
cPort
;
cPort
+=
DEFAULT_NX_PROXY_PORT_OFFSET
;
connectSocket
.
setSpec
(
cHost
,
cPort
);
}
else
if
(
ParseEnvironmentOptions
(
nextArg
,
1
)
<
0
)
{
return
-
1
;
...
...
@@ -13195,7 +13476,7 @@ int ParseBitrateOption(const char *opt)
return
1
;
}
int
ParseHostOption
(
const
char
*
opt
,
char
*
host
,
int
&
port
)
int
ParseHostOption
(
const
char
*
opt
,
char
*
host
,
long
&
port
)
{
#ifdef TEST
*
logofs
<<
"Loop: Trying to parse options string '"
<<
opt
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment