Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

TCP code update

  • Loading branch information...
commit 4a0cb16699953900990e46d6fca5f41a2b8c2c80 1 parent 2b83b5e
@jselbie authored
View
1  .gitignore
@@ -4,6 +4,7 @@ nbproject/
*.gch
*.a
*.txtcode
+callgrind.out.*
stunclient
stunserver
View
3  Makefile
@@ -20,6 +20,9 @@ copybin: everything
debug: T := debug
debug: all
+profile: T := profile
+profile: all
+
clean: T := clean
clean: everything
rm -f stunserver stunclient stuntestcode
View
32 README
@@ -1,21 +1,22 @@
-StunServer version 1.0.0
-September 7, 2011
+StunServer version 1.1.0
+January 22, 2012
---------------------------------------------------------
+
Features:
Compliant with the latest RFCs including 5389, 5769, and 5780. Also includes
backwards compatibility for RFC 3489.
- IPv4 and IPv6 support
+ Supports both UDP and TCP on both IPv4 and IPv6.
- Client test app provided
+ Client test app provided.
Stun server can operate in "full" mode as well as "basic" mode. Basic mode
configures the server to listen on one port and respond to STUN binding
requests. Full mode configures the service to listen on two different IP
address interfaces (if available) and provide NAT behavior and filtering
- detection support for clients
+ detection support for clients.
Open source Apache license. See LICENSE file fore more details.
---------------------------------------------------------
@@ -23,8 +24,7 @@ Features:
Known issues:
- UDP only. Command line options for working in TCP or TLS modes have yet to
- be implemented.
+ TLS mode has yet to be implemented.
Server does not honor the stun padding attribute. If someone really wants
this support, let me know and I will consider adding it.
@@ -38,7 +38,7 @@ Known issues:
hooks are provided for implementors to write their own code to validate a
username, fetch a password, and allow/deny a request. Details of writing
your own authentication provider code are described in the file
- "server/sampleauthprovider.h"
+ "server/sampleauthprovider.h".
Dependency checking is not implemented in the Makefile. So if you need to
recompile, I recommend "make clean" from the root to preceed any subsequent
@@ -67,16 +67,15 @@ Testing:
Prerequisites before compiling and running.
+ perl. Just have any old version installed. It is needed for one particular
+ build script.
+
Boost header files. (Actual boost runtime not required) www.boost.org (sudo
yum install boost-devel)
OpenSSL development files and runtime. www.boost.org (sudo yum install
openssl-devel)
- /usr/bin/xxd (this is a tool for converting the help text into resources. It
- is usually universally installed. If not, then "sudo yum install
- vim-common")
-
pthreads header and libs (I haven't seen a distribution where this wasn't
already installed)
---------------------------------------------------------
@@ -113,17 +112,18 @@ Firewall
Feature roadmap (the features I want to implement in a subsequent release)
- TCP and TLS support
+ Host a full server across two separate machines (such that two ip addresses
+ on a single machine will not be required for full mode).
+
+ Cleanup Makefile and add "configure" and autotools support
Finish Windows port and able to run as a Windows service
Scale across more than one CPU (for multi-core and multi-proc machines). The
threading code has already been written, just needs some finish work.
- Host a full server across two separate machines (such that two ip addresses
- on a single machine will not be required for full mode).
+ TLS support
- Cleanup Makefile and add "configure" and autotools support
---------------------------------------------------------
View
187 client/clientmain.cpp
@@ -97,12 +97,14 @@ HRESULT CreateConfigFromCommandLine(ClientCmdLineArgs& args, StunClientLogicConf
uint16_t remoteport = 0;
int nPort = 0;
char szIP[100];
+ bool fTCP = false;
config.fBehaviorTest = false;
config.fFilteringTest = false;
- config.timeoutSeconds = 5;
- config.uMaxAttempts = 3;
+ config.fTimeoutIsInstant = false;
+ config.timeoutSeconds = 0; // use default
+ config.uMaxAttempts = 0;
socketconfig.family = AF_INET;
socketconfig.socktype = SOCK_DGRAM;
@@ -132,11 +134,18 @@ HRESULT CreateConfigFromCommandLine(ClientCmdLineArgs& args, StunClientLogicConf
StringHelper::ToLower(args.strProtocol);
if (StringHelper::IsNullOrEmpty(args.strProtocol.c_str()) == false)
{
- if (args.strProtocol != "udp")
+ if ((args.strProtocol != "udp") && (args.strProtocol != "tcp"))
{
- Logging::LogMsg(LL_ALWAYS, "Only udp is supported as a protocol option in this version");
+ Logging::LogMsg(LL_ALWAYS, "Only udp and tcp are supported protocol versions");
Chk(E_INVALIDARG);
}
+
+ if (args.strProtocol == "tcp")
+ {
+ fTCP = true;
+ socketconfig.socktype = SOCK_STREAM;
+ config.uMaxAttempts = 1;
+ }
}
// remote port ---------------------------------------------
@@ -224,7 +233,7 @@ HRESULT CreateConfigFromCommandLine(ClientCmdLineArgs& args, StunClientLogicConf
else if (args.strMode == "full")
{
config.fBehaviorTest = true;
- config.fFilteringTest = true;
+ config.fFilteringTest = (fTCP == false); // impossible to to a filtering test in TCP
}
else
{
@@ -241,7 +250,6 @@ HRESULT CreateConfigFromCommandLine(ClientCmdLineArgs& args, StunClientLogicConf
-
void NatBehaviorToString(NatBehavior behavior, std::string* pStr)
{
std::string& str = *pStr;
@@ -308,13 +316,161 @@ void DumpResults(StunClientLogicConfig& config, StunClientResults& results)
Logging::LogMsg(LL_ALWAYS, "Nat filtering: %s", strResult.c_str());
}
}
-
}
+void TcpClientLoop(StunClientLogicConfig& config, ClientSocketConfig& socketconfig)
+{
+
+ HRESULT hr = S_OK;
+ CStunSocket stunsocket;
+ CStunClientLogic clientlogic;
+ int sock;
+ CRefCountedBuffer spMsg(new CBuffer(1500));
+ CRefCountedBuffer spMsgReader(new CBuffer(1500));
+ CSocketAddress addrDest, addrLocal;
+ HRESULT hrRet, hrResult;
+ int ret;
+ size_t bytes_sent, bytes_recv;
+ size_t bytes_to_send, max_bytes_recv, remaining;
+ uint8_t* pData=NULL;
+ size_t readsize;
+ CStunMessageReader reader;
+ StunClientResults results;
+
+
+ hr= clientlogic.Initialize(config);
+ if (FAILED(hr))
+ {
+ Logging::LogMsg(LL_ALWAYS, "clientlogic.Initialize failed (hr == %x)", hr);
+ Chk(hr);
+ }
+
+
+ while (true)
+ {
+
+ stunsocket.Close();
+ hr = stunsocket.TCPInit(socketconfig.addrLocal, RolePP, true);
+ if (FAILED(hr))
+ {
+ Logging::LogMsg(LL_ALWAYS, "Unable to create local socket for TCP connection (hr == %x)", hr);
+ Chk(hr);
+ }
+
+ hrRet = clientlogic.GetNextMessage(spMsg, &addrDest, ::GetMillisecondCounter());
+
+ if (hrRet == E_STUNCLIENT_RESULTS_READY)
+ {
+ // clean exit
+ break;
+ }
+ // we should never get a "still waiting" return with TCP, because config.timeout is 0
+ ASSERT(hrRet != E_STUNCLIENT_STILL_WAITING);
+
+ if (FAILED(hrRet))
+ {
+ Chk(hrRet);
+ }
+
+ // connect to server
+ sock = stunsocket.GetSocketHandle();
+
+ ret = ::connect(sock, addrDest.GetSockAddr(), addrDest.GetSockAddrLength());
+
+ if (ret == -1)
+ {
+ hrResult = ERRNOHR;
+ Logging::LogMsg(LL_ALWAYS, "Can't connect to server (hr == %x)", hrResult);
+ Chk(hrResult);
+ }
+
+ Logging::LogMsg(LL_DEBUG, "Connected to server");
+
+ bytes_to_send = (int)(spMsg->GetSize());
+
+ bytes_sent = 0;
+ pData = spMsg->GetData();
+ while (bytes_sent < bytes_to_send)
+ {
+ ret = ::send(sock, pData+bytes_sent, bytes_to_send-bytes_sent, 0);
+ if (ret < 0)
+ {
+ hrResult = ERRNOHR;
+ Logging::LogMsg(LL_ALWAYS, "Send failed (hr == %x)", hrResult);
+ Chk(hrResult);
+ }
+ bytes_sent += ret;
+ }
+
+ Logging::LogMsg(LL_DEBUG, "Request sent - waiting for response");
+
+
+ // consume the response
+ reader.Reset();
+ reader.GetStream().Attach(spMsgReader, true);
+ pData = spMsg->GetData();
+ bytes_recv = 0;
+ max_bytes_recv = spMsg->GetAllocatedSize();
+ remaining = max_bytes_recv;
+
+ while (remaining > 0)
+ {
+ readsize = reader.HowManyBytesNeeded();
+
+ if (readsize == 0)
+ {
+ break;
+ }
+
+ if (readsize > remaining)
+ {
+ // technically an error, but the client logic will figure it out
+ ASSERT(false);
+ break;
+ }
+
+ ret = ::recv(sock, pData+bytes_recv, readsize, 0);
+ if (ret == 0)
+ {
+ // server cut us off before we got all the bytes we thought we were supposed to get?
+ ASSERT(false);
+ break;
+ }
+ if (ret < 0)
+ {
+ hrResult = ERRNOHR;
+ Logging::LogMsg(LL_ALWAYS, "Recv failed (hr == %x)", hrResult);
+ Chk(hrResult);
+ }
+
+ reader.AddBytes(pData+bytes_recv, ret);
+ bytes_recv += ret;
+ remaining = max_bytes_recv - bytes_recv;
+ spMsg->SetSize(bytes_recv);
+ }
+
+
+ // now feed the response into the client logic
+ stunsocket.UpdateAddresses();
+ addrLocal = stunsocket.GetLocalAddress();
+ clientlogic.ProcessResponse(spMsg, addrDest, addrLocal);
+ }
+
+ stunsocket.Close();
-HRESULT ClientLoop(StunClientLogicConfig& config, const ClientSocketConfig& socketconfig)
+ results.Init();
+ clientlogic.GetResults(&results);
+ ::DumpResults(config, results);
+
+Cleanup:
+ return;
+
+}
+
+
+HRESULT UdpClientLoop(StunClientLogicConfig& config, const ClientSocketConfig& socketconfig)
{
HRESULT hr = S_OK;
CRefCountedStunSocket spStunSocket;
@@ -431,6 +587,11 @@ HRESULT ClientLoop(StunClientLogicConfig& config, const ClientSocketConfig& sock
}
+
+
+
+
+
int main(int argc, char** argv)
{
CCmdLineParser cmdline;
@@ -495,7 +656,15 @@ int main(int argc, char** argv)
}
DumpConfig(config, socketconfig);
- ClientLoop(config, socketconfig);
+
+ if (socketconfig.socktype == SOCK_STREAM)
+ {
+ TcpClientLoop(config, socketconfig);
+ }
+ else
+ {
+ UdpClientLoop(config, socketconfig);
+ }
return 0;
}
View
28 client/usage.txt
@@ -4,30 +4,30 @@ Perform a binding test with a remote STUN server and optionally perform a full b
Parameters:
"server" is the IP address or FQDN of the remote server to befrom the binding tests with. It is the only required paramter
- "port" is an optional paramter that can follow the server paramter. The default is 3478 for UDP and TCP. And 5349 for TLS.
+ "port" is an optional paramter that can follow the server paramter. The default is 3478 for UDP and TCP.
Available options:
- --localaddr = INTERFACE OR IPADDRESS
- The value for this option may the name of an interface (such as "eth0" or "lo"). Or it may be one of the available IP addresses assigned to a network interface present on the host (such as "128.23.45.67"). The interface chosen will be the preferred address for sending and receiving responses with the remote server. The default is to let the system decide which address to send on and to listen on all addresses (INADDR_ANY).
+ --localaddr = INTERFACE OR IPADDRESS
+ The value for this option may the name of an interface (such as "eth0" or "lo"). Or it may be one of the available IP addresses assigned to a network interface present on the host (such as "128.23.45.67"). The interface chosen will be the preferred address for sending and receiving responses with the remote server. The default is to let the system decide which address to send on and to listen on all addresses (INADDR_ANY).
- --localport=PORTNUM
- PORTNUM is a value between 1 to 65535. This is the UDP or TCP port that the primary and alternate interfaces listen on as the primary port for binding requests. If not specified, a randomly avaialbe port chosed by the system is used.
+ --localport=PORTNUM
+ PORTNUM is a value between 1 to 65535. This is the UDP or TCP port that the primary and alternate interfaces listen on as the primary port for binding requests. If not specified, a randomly avaialbe port chosed by the system is used.
- --mode=MODE
- Where MODE is either "basic" or "full". "basic" mode is the default and indicates that the client should perform a STUN binding test only. "full" mode indicates that the client should attempt to diagnose NAT behavior and filtering methodologies if the server supports this mode.
+ --mode=MODE
+ Where MODE is either "basic" or "full". "basic" mode is the default and indicates that the client should perform a STUN binding test only. "full" mode indicates that the client should attempt to diagnose NAT behavior and filtering methodologies if the server supports this mode. The NAT filtering test is only supported for UDP.
- --family=IPVERSION
- IPVERSION is either "4" or "6" to specify the usage of IPV4 or IPV6. If not specified, the default value is "4".
+ --family=IPVERSION
+ IPVERSION is either "4" or "6" to specify the usage of IPV4 or IPV6. If not specified, the default value is "4".
--protocol=PROTO
- PROTO is either "udp", "tcp", or "tls". Where "udp" is the default. "tcp" and "tls" modes are only available when the --mode option is "basic".
+ PROTO is either "udp" or "tcp". "udp" is the default if this parameter is not specified
--verbosity=LOGLEVEL
- Sets the verbosity of the logging level. 0 is the default (minimal output and logging). 1 shows slightly more. 2 and higher shows even more.
+ Sets the verbosity of the logging level. 0 is the default (minimal output and logging). 1 shows slightly more. 2 and higher shows even more.
--help
- Prints this help page
+ Prints this help page
Examples:
@@ -37,4 +37,8 @@ stunclient stunserver.org 3478
stunclient --mode full --localport 9999 12.34.56.78
Performs a full set of UDP NAT behavior tests from local port 9999 to the server listening at IP Address 12.34.56.78 (port 3478)
+stunclient --protocol tcp stun.selbie.com
+ Performs a simple binding test using TCP to server listening on the default port of 3478 at stun.selbie.com
+
+
View
5 common.inc
@@ -1,4 +1,4 @@
-BOOST_INCLUDE := -I/home/jselbie/boost_1_48_0
+# BOOST_INCLUDE := -I/home/jselbie/boost_1_48_0
# OPENSSL_INCLUDE := -I/home/jselbie/lib/openssl
DEFINES := -DNDEBUG
@@ -7,6 +7,7 @@ STANDARD_FLAGS := -Wall -Wuninitialized
RELEASE_FLAGS := -O2
DEBUG_FLAGS := -g
+PROFILE_FLAGS := -O2 -g
FLAVOR_FLAGS = $(RELEASE_FLAGS)
.PHONY: all clean debug
@@ -26,5 +27,7 @@ debug: DEFINES = -DDEBUG
debug: all
+profile: FLAVOR_FLAGS = $(PROFILE_FLAGS)
+profile: all
View
11 common/commonincludes.h
@@ -19,6 +19,11 @@
#define STUNSERVER_COMMON_COMMONINCLUDES_H
+
+#if __linux || __linux__ || __gnu_linux__ || linux
+#define IS_LINUX
+#endif
+
// standard system includes
#include <sys/types.h>
#include <sys/socket.h>
@@ -48,10 +53,14 @@
#include <list>
#include <string>
-#ifndef _bsd
+
+#ifdef IS_LINUX
+#define HAS_EPOLL
#include <sys/epoll.h>
#endif
+#include <poll.h>
+
#include <pthread.h>
View
10 common/fasthash.h
@@ -257,6 +257,16 @@ class FastHashBase
return _size;
}
+ size_t GetMaxCapacity()
+ {
+ return _fsize;
+ }
+
+ size_t GetTableWidth()
+ {
+ return _tsize;
+ }
+
int Insert(const K& key, V& value)
{
size_t hashindex = FastHash_Hash(key) % _tsize;
View
2  networkutils/Makefile
@@ -1,7 +1,7 @@
include ../common.inc
PROJECT_TARGET := libnetworkutils.a
-PROJECT_OBJS := adapters.o recvfromex.o resolvehostname.o stunsocket.o
+PROJECT_OBJS := adapters.o polling.o recvfromex.o resolvehostname.o stunsocket.o
INCLUDES := $(BOOST_INCLUDE) -I../common -I../stuncore
View
490 networkutils/polling.cpp
@@ -0,0 +1,490 @@
+#include "commonincludes.h"
+#include "polling.h"
+#include "fasthash.h"
+
+#ifdef __GNUC__
+ #ifdef HAS_EPOLL
+ #pragma message "polling.cpp: EPOLL is available"
+ #else
+ #pragma message "polling.cpp: no kernel polling api available!"
+ #endif
+#endif
+
+
+
+// --------------------------------------------------------------------------
+
+#ifdef HAS_EPOLL
+
+class CEpoll :
+ public CBasicRefCount,
+ public CObjectFactory<CEpoll>,
+ public IPolling
+{
+private:
+ int _epollfd;
+
+ uint32_t ToNativeFlags(uint32_t eventflags);
+ uint32_t FromNativeFlags(uint32_t eventflags);
+
+public:
+ virtual HRESULT Initialize();
+ virtual HRESULT Close();
+ virtual HRESULT Add(int fd, uint32_t eventflags);
+ virtual HRESULT Remove(int fd);
+ virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags);
+ virtual HRESULT WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds);
+
+ CEpoll();
+ ~CEpoll();
+
+ ADDREF_AND_RELEASE_IMPL();
+};
+
+
+
+CEpoll::CEpoll() :
+_epollfd(-1)
+{
+
+}
+
+CEpoll::~CEpoll()
+{
+ Close();
+}
+
+uint32_t CEpoll::ToNativeFlags(uint32_t eventflags)
+{
+ uint32_t result = 0;
+
+ if (eventflags & IPOLLING_READ) result |= EPOLLIN;
+ if (eventflags & IPOLLING_WRITE) result |= EPOLLOUT;
+ if (eventflags & IPOLLING_EDGETRIGGER) result |= EPOLLET;
+ if (eventflags & IPOLLING_RDHUP) result |= EPOLLRDHUP;
+ if (eventflags & IPOLLING_HUP) result |= EPOLLHUP;
+ if (eventflags & IPOLLING_PRI) result |= EPOLLPRI;
+ if (eventflags & IPOLLING_ERROR) result |= EPOLLERR;
+
+ return result;
+}
+
+
+uint32_t CEpoll::FromNativeFlags(uint32_t eventflags)
+{
+ uint32_t result = 0;
+
+ if (eventflags & EPOLLIN) result |= IPOLLING_READ;
+ if (eventflags & EPOLLOUT) result |= IPOLLING_WRITE;
+ if (eventflags & EPOLLET) result |= IPOLLING_EDGETRIGGER;
+ if (eventflags & EPOLLRDHUP) result |= IPOLLING_RDHUP;
+ if (eventflags & EPOLLHUP) result |= IPOLLING_HUP;
+ if (eventflags & EPOLLPRI) result |= IPOLLING_PRI;
+ if (eventflags & EPOLLERR) result |= IPOLLING_ERROR;
+
+ return result;
+}
+
+
+HRESULT CEpoll::Initialize()
+{
+ ASSERT(_epollfd == -1);
+
+ Close();
+
+ _epollfd = epoll_create(1000);
+ if (_epollfd == -1)
+ {
+ return ERRNOHR;
+ }
+ return S_OK;
+}
+
+HRESULT CEpoll::Close()
+{
+ if (_epollfd != -1)
+ {
+ close(_epollfd);
+
+ }
+ return S_OK;
+}
+
+
+HRESULT CEpoll::Add(int fd, uint32_t eventflags)
+{
+ epoll_event ev = {};
+ HRESULT hr = S_OK;
+
+ ChkIfA(fd == -1, E_INVALIDARG);
+ ChkIfA(_epollfd==-1, E_UNEXPECTED);
+
+
+ ev.data.fd = fd;
+ ev.events = ToNativeFlags(eventflags);
+
+ ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1, ERRNOHR);
+Cleanup:
+ return hr;
+}
+
+HRESULT CEpoll::Remove(int fd)
+{
+ HRESULT hr = S_OK;
+ epoll_event ev={}; // pass empty ev, because some implementations of epoll_ctl can't handle a NULL event struct
+
+ ChkIfA(fd == -1, E_INVALIDARG);
+ ChkIfA(_epollfd==-1, E_UNEXPECTED);
+
+ ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ev) == -1, ERRNOHR);
+Cleanup:
+ return hr;
+}
+
+HRESULT CEpoll::ChangeEventSet(int fd, uint32_t eventflags)
+{
+ HRESULT hr = S_OK;
+ epoll_event ev = {};
+
+ ChkIfA(fd == -1, E_INVALIDARG);
+ ChkIfA(_epollfd==-1, E_UNEXPECTED);
+
+ ev.data.fd = fd;
+ ev.events = ToNativeFlags(eventflags);
+
+ ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev) == -1, ERRNOHR);
+
+Cleanup:
+ return hr;
+}
+
+HRESULT CEpoll::WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds)
+{
+ HRESULT hr = S_OK;
+ epoll_event ev = {};
+ int ret;
+
+ ChkIfA(_epollfd==-1, E_UNEXPECTED);
+
+ ret = ::epoll_wait(_epollfd, &ev, 1, timeoutMilliseconds);
+ ChkIf(ret == -1, ERRNOHR);
+
+ ChkIf(ret == 0, S_FALSE);
+
+ pPollEvent->fd = ev.data.fd;
+ pPollEvent->eventflags = FromNativeFlags(ev.events);
+
+Cleanup:
+ return hr;
+}
+
+#endif // HAS_EPOLL
+
+// ------------------------------------------------------------------------------
+
+class CPoll :
+ public CBasicRefCount,
+ public CObjectFactory<CPoll>,
+ public IPolling
+{
+private:
+ std::vector<pollfd> _fds;
+ uint32_t _rotation;
+ uint32_t _unreadcount;
+ bool _fInitialized;
+
+ FastHash<int, size_t, 100, 101> _hashtable; // maps socket to position in fds
+
+ void Reindex();
+
+ uint32_t ToNativeFlags(uint32_t eventflags);
+ uint32_t FromNativeFlags(uint32_t eventflags);
+
+ bool FindNextEvent(PollEvent* pEvent);
+
+public:
+ virtual HRESULT Initialize();
+ virtual HRESULT Close();
+ virtual HRESULT Add(int fd, uint32_t eventflags);
+ virtual HRESULT Remove(int fd);
+ virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags);
+ virtual HRESULT WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds);
+
+ CPoll();
+ ~CPoll();
+
+ ADDREF_AND_RELEASE_IMPL();
+};
+
+CPoll::CPoll() :
+_rotation (0),
+_unreadcount(0),
+_fInitialized(false)
+{
+
+}
+
+CPoll::~CPoll()
+{
+ Close();
+}
+
+uint32_t CPoll::ToNativeFlags(uint32_t eventflags)
+{
+ uint32_t result = 0;
+
+ if (eventflags & IPOLLING_READ) result |= POLLIN;
+ if (eventflags & IPOLLING_WRITE) result |= POLLOUT;
+ if (eventflags & IPOLLING_RDHUP) result |= POLLRDHUP;
+ if (eventflags & IPOLLING_HUP) result |= POLLHUP;
+ if (eventflags & IPOLLING_PRI) result |= POLLPRI;
+ if (eventflags & IPOLLING_ERROR) result |= POLLERR;
+
+ return result;
+}
+
+
+uint32_t CPoll::FromNativeFlags(uint32_t eventflags)
+{
+ uint32_t result = 0;
+
+ if (eventflags & POLLIN) result |= IPOLLING_READ;
+ if (eventflags & POLLOUT) result |= IPOLLING_WRITE;
+ if (eventflags & POLLRDHUP) result |= IPOLLING_RDHUP;
+ if (eventflags & POLLHUP) result |= IPOLLING_HUP;
+ if (eventflags & POLLPRI) result |= IPOLLING_PRI;
+ if (eventflags & POLLERR) result |= IPOLLING_ERROR;
+
+ return result;
+}
+
+
+HRESULT CPoll::Initialize()
+{
+ pollfd pfd = {};
+ pfd.fd = -1;
+
+
+ _fds.reserve(1000);
+ _rotation = 0;
+ _unreadcount = 0;
+ _fInitialized = true;
+
+ return S_OK;
+}
+
+HRESULT CPoll::Close()
+{
+ _fds.clear();
+ _fInitialized = false;
+
+ return S_OK;
+}
+
+HRESULT CPoll::Add(int fd, uint32_t eventflags)
+{
+ HRESULT hr = S_OK;
+ size_t pos = _fds.size();
+ pollfd pfd;
+
+ ChkIfA(_fInitialized == false, E_FAIL);
+
+ ChkIfA(_hashtable.Lookup(fd)!=NULL, E_UNEXPECTED);
+
+ pfd.events = ToNativeFlags(eventflags);
+ pfd.fd = fd;
+ pfd.revents = 0;
+ _fds.push_back(pfd);
+ _hashtable.Insert(fd, pos);
+Cleanup:
+ return hr;
+}
+
+HRESULT CPoll::Remove(int fd)
+{
+ size_t* pPos = NULL;
+ size_t size = _fds.size();
+ size_t pos;
+ HRESULT hr = S_OK;
+
+ ChkIfA(_fInitialized == false, E_FAIL);
+
+ ASSERT(_hashtable.Size() == size);
+
+ ChkIf(size == 0, E_FAIL);
+
+ pPos = _hashtable.Lookup(fd);
+
+ ChkIfA(pPos == NULL, E_FAIL);
+
+ pos = *pPos;
+
+ ChkIfA(pos >= size, E_FAIL);
+
+ ChkIfA(_fds[pos].fd != fd, E_FAIL);
+
+ if (pos != (size-1))
+ {
+ _fds[pos] = _fds[size-1];
+ pPos = _hashtable.Lookup(_fds[pos].fd);
+ ASSERT(pPos);
+ *pPos = pos;
+ }
+
+ _hashtable.Remove(fd);
+ _fds.pop_back();
+
+Cleanup:
+ return hr;
+}
+
+HRESULT CPoll::ChangeEventSet(int fd, uint32_t eventflags)
+{
+ size_t* pPos = NULL;
+ size_t pos;
+ HRESULT hr = S_OK;
+ size_t size = _fds.size();
+
+ ChkIfA(_fInitialized == false, E_FAIL);
+
+ ASSERT(_hashtable.Size() == size);
+ ChkIf(size == 0, E_FAIL);
+
+ pPos = _hashtable.Lookup(fd);
+ ChkIfA(pPos == NULL, E_FAIL);
+ pos = *pPos;
+ ChkIfA(pos >= size, E_FAIL);
+ ChkIfA(_fds[pos].fd != fd, E_FAIL);
+ _fds[pos].events = ToNativeFlags(eventflags);
+
+Cleanup:
+ return hr;
+}
+
+HRESULT CPoll::WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds)
+{
+ HRESULT hr = S_OK;
+ int ret;
+ size_t size = _fds.size();
+ pollfd* list = NULL;
+ bool fFound = false;
+
+ ChkIfA(_fInitialized == false, E_FAIL);
+
+ ChkIfA(pPollEvent == NULL, E_INVALIDARG);
+ pPollEvent->eventflags = 0;
+
+ ChkIfA(size == 0, S_FALSE);
+
+ // check first to see if there is a pending event from the last poll() call
+ fFound = FindNextEvent(pPollEvent);
+
+ if (fFound == false)
+ {
+ ASSERT(_unreadcount == 0);
+
+ list = _fds.data();
+
+ ret = poll(list, size, timeoutMilliseconds);
+
+ ChkIfA(ret < 0, ERRNOHR); // error
+ ChkIf(ret == 0, S_FALSE); // no data, we timed out
+
+ _unreadcount = (uint32_t)ret;
+
+ fFound = FindNextEvent(pPollEvent);
+ ASSERT(fFound); // poll returned a positive value, but we didn't find anything?
+ }
+
+ hr = fFound ? S_OK : S_FALSE;
+
+Cleanup:
+ return hr;
+}
+
+bool CPoll::FindNextEvent(PollEvent* pEvent)
+{
+ size_t size = _fds.size();
+ ASSERT(size > 0);
+ pollfd* list = _fds.data();
+ bool fFound = false;
+
+ if (_unreadcount == 0)
+ {
+ return false;
+ }
+
+ if (_rotation >= size)
+ {
+ _rotation = 0;
+ }
+
+ for (size_t index = 0; index < size; index++)
+ {
+ size_t slotindex = (index + _rotation) % size;
+
+ if (list[slotindex].revents)
+ {
+ pEvent->fd = list[slotindex].fd;
+ pEvent->eventflags = FromNativeFlags(list[slotindex].revents);
+ list[slotindex].revents = 0;
+ fFound = true;
+ break;
+ }
+ }
+
+ if (fFound)
+ {
+ _rotation++;
+ }
+ else
+ {
+ _unreadcount = _unreadcount - 1;
+ // don't increment rotation if we didn't find anything
+ }
+
+ return fFound;
+}
+
+
+
+HRESULT CreatePollingInstance(uint32_t type, IPolling** ppPolling)
+{
+ HRESULT hr = S_OK;
+
+ ChkIfA(ppPolling == NULL, E_INVALIDARG);
+
+#ifdef HAS_EPOLL
+ if (type == IPOLLING_TYPE_BEST)
+ {
+ type = IPOLLING_TYPE_EPOLL;
+ }
+#else
+ if (type == IPOLLING_TYPE_BEST)
+ {
+ type = IPOLLING_TYPE_POLL;
+ }
+#endif
+
+
+ if (type == IPOLLING_TYPE_EPOLL)
+ {
+#ifndef HAS_EPOLL
+ ChkA(E_FAIL);
+#else
+ ChkA(CEpoll::CreateInstance(ppPolling));
+#endif
+ }
+ else if (type == IPOLLING_TYPE_POLL)
+ {
+ ChkA(CPoll::CreateInstance(ppPolling));
+ }
+ else
+ {
+ ChkA(E_FAIL); // unknown type
+ }
+
+Cleanup:
+ return hr;
+}
+
View
50 networkutils/polling.h
@@ -0,0 +1,50 @@
+/*
+ * File: polling.h
+ * Author: jselbie
+ *
+ * Created on January 12, 2012, 5:58 PM
+ */
+
+#ifndef POLLING_H
+#define POLLING_H
+
+
+struct PollEvent
+{
+ int fd;
+ uint32_t eventflags;
+};
+
+
+// event flags
+const uint32_t IPOLLING_READ = 0x01 << 0;
+const uint32_t IPOLLING_WRITE = 0x01 << 1;
+const uint32_t IPOLLING_EDGETRIGGER = 0x01 << 2;
+const uint32_t IPOLLING_RDHUP = 0x01 << 3;
+const uint32_t IPOLLING_HUP = 0x01 << 4;
+const uint32_t IPOLLING_PRI = 0x01 << 5;
+const uint32_t IPOLLING_ERROR = 0x01 << 6;
+
+
+class IPolling : public IRefCounted
+{
+public:
+ virtual HRESULT Initialize() = 0;
+ virtual HRESULT Close() = 0;
+ virtual HRESULT Add(int fd, uint32_t eventflags) = 0;
+ virtual HRESULT Remove(int fd) = 0;
+ virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags) = 0;
+ virtual HRESULT WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds) = 0;
+};
+
+
+const uint32_t IPOLLING_TYPE_BEST = 0x01 << 0;
+const uint32_t IPOLLING_TYPE_EPOLL = 0x01 << 1;
+const uint32_t IPOLLING_TYPE_POLL = 0x01 << 2;
+
+HRESULT CreatePollingInstance(uint32_t type, IPolling** ppPolling);
+
+
+
+#endif /* POLLING_H */
+
View
17 networkutils/stunsocket.cpp
@@ -185,7 +185,7 @@ void CStunSocket::UpdateAddresses()
-HRESULT CStunSocket::InitCommon(int socktype, const CSocketAddress& addrlocal, SocketRole role)
+HRESULT CStunSocket::InitCommon(int socktype, const CSocketAddress& addrlocal, SocketRole role, bool fSetReuseFlag)
{
int sock = -1;
int ret;
@@ -196,10 +196,15 @@ HRESULT CStunSocket::InitCommon(int socktype, const CSocketAddress& addrlocal, S
sock = socket(addrlocal.GetFamily(), socktype, 0);
ChkIf(sock < 0, ERRNOHR);
-
+ if (fSetReuseFlag)
+ {
+ int fAllow = 1;
+ ret = ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &fAllow, sizeof(fAllow));
+ ChkIf(ret == -1, ERRNOHR);
+ }
ret = bind(sock, addrlocal.GetSockAddr(), addrlocal.GetSockAddrLength());
- ChkIf(ret < 0, ERRNOHR);
+ ChkIf(ret == -1, ERRNOHR);
Attach(sock);
sock = -1;
@@ -219,12 +224,12 @@ HRESULT CStunSocket::InitCommon(int socktype, const CSocketAddress& addrlocal, S
HRESULT CStunSocket::UDPInit(const CSocketAddress& local, SocketRole role)
{
- return InitCommon(SOCK_DGRAM, local, role);
+ return InitCommon(SOCK_DGRAM, local, role, false);
}
-HRESULT CStunSocket::TCPInit(const CSocketAddress& local, SocketRole role)
+HRESULT CStunSocket::TCPInit(const CSocketAddress& local, SocketRole role, bool fSetReuseFlag)
{
- return InitCommon(SOCK_STREAM, local, role);
+ return InitCommon(SOCK_STREAM, local, role, fSetReuseFlag);
}
View
4 networkutils/stunsocket.h
@@ -30,7 +30,7 @@ class CStunSocket
CStunSocket(const CStunSocket&) {;}
void operator=(const CStunSocket&) {;}
- HRESULT InitCommon(int socktype, const CSocketAddress& addrlocal, SocketRole role);
+ HRESULT InitCommon(int socktype, const CSocketAddress& addrlocal, SocketRole role, bool fSetReuseFlag);
void Reset();
@@ -59,7 +59,7 @@ class CStunSocket
void UpdateAddresses();
HRESULT UDPInit(const CSocketAddress& local, SocketRole role);
- HRESULT TCPInit(const CSocketAddress& local, SocketRole role);
+ HRESULT TCPInit(const CSocketAddress& local, SocketRole role, bool fSetReuseFlag);
};
typedef boost::shared_ptr<CStunSocket> CRefCountedStunSocket;
View
33 server/main.cpp
@@ -97,6 +97,7 @@ struct StartupArgs
std::string strProtocol;
std::string strHelp;
std::string strVerbosity;
+ std::string strMaxConnections;
};
#define PRINTARG(member) Logging::LogMsg(LL_DEBUG, "%s = %s", #member, args.member.length() ? args.member.c_str() : "<empty>");
@@ -113,6 +114,7 @@ void DumpStartupArgs(StartupArgs& args)
PRINTARG(strProtocol);
PRINTARG(strHelp);
PRINTARG(strVerbosity);
+ PRINTARG(strMaxConnections);
Logging::LogMsg(LL_DEBUG, "--------------------------\n");
}
@@ -145,7 +147,10 @@ void DumpConfig(CStunServerConfig &config)
}
Logging::LogMsg(LL_DEBUG, "Protocol = %s", config.fTCP ? "TCP" : "UDP");
-
+ if (config.fTCP && (config.nMaxConnections>0))
+ {
+ Logging::LogMsg(LL_DEBUG, "Max TCP Connections per thread: %d", config.nMaxConnections);
+ }
}
@@ -189,6 +194,7 @@ HRESULT BuildServerConfigurationFromArgs(StartupArgs& argsIn, CStunServerConfig*
int nAltPort = DEFAULT_STUN_PORT + 1;
bool fHasAtLeastTwoAdapters = false;
CStunServerConfig config;
+ int nMaxConnections = 0;
enum ServerMode
{
@@ -272,6 +278,28 @@ HRESULT BuildServerConfigurationFromArgs(StartupArgs& argsIn, CStunServerConfig*
config.fTCP = (args.strProtocol == "tcp");
}
+
+
+ // ---- MAX Connections -----------------------------------------------------
+ nMaxConnections = 0;
+ if (args.strMaxConnections.length() > 0)
+ {
+ if (config.fTCP == false)
+ {
+ Logging::LogMsg(LL_ALWAYS, "Max connections parameter has no meaning in UDP mode. Did you mean to specify \"--protocol=tcp ?\"");
+ }
+ else
+ {
+ hr = StringHelper::ValidateNumberString(args.strMaxConnections.c_str(), 1, 100000, &nMaxConnections);
+ if (FAILED(hr))
+ {
+ Logging::LogMsg(LL_ALWAYS, "Max connections must be between 1-100000");
+ Chk(hr);
+ }
+ }
+ config.nMaxConnections = nMaxConnections;
+ }
+
// ---- PRIMARY PORT --------------------------------------------------------
nPrimaryPort = DEFAULT_STUN_PORT;
@@ -376,6 +404,7 @@ HRESULT BuildServerConfigurationFromArgs(StartupArgs& argsIn, CStunServerConfig*
Logging::LogMsg(LL_ALWAYS, "Error - Primary interface and Alternate Interface appear to have the same IP address. Full mode requires two IP addresses that are unique");
Chk(E_INVALIDARG);
}
+
config.addrPP = addrPrimary;
config.addrPP.SetPort(portPrimary);
@@ -392,7 +421,6 @@ HRESULT BuildServerConfigurationFromArgs(StartupArgs& argsIn, CStunServerConfig*
config.addrAA = addrAlternate;
config.addrAA.SetPort(portAlternate);
config.fHasAA = true;
-
}
@@ -417,6 +445,7 @@ HRESULT ParseCommandLineArgs(int argc, char** argv, int startindex, StartupArgs*
cmdline.AddOption("altport", required_argument, &pStartupArgs->strAltPort);
cmdline.AddOption("family", required_argument, &pStartupArgs->strFamily);
cmdline.AddOption("protocol", required_argument, &pStartupArgs->strProtocol);
+ cmdline.AddOption("maxconn", required_argument, &pStartupArgs->strMaxConnections);
cmdline.AddOption("help", no_argument, &pStartupArgs->strHelp);
cmdline.AddOption("verbosity", required_argument, &pStartupArgs->strVerbosity);
View
4 server/sampleauthprovider.h
@@ -124,7 +124,7 @@ as appropriate:
To have the server host an instance of an IStunAuth implementation, modify
- CStunServer::Initialize to create an instance of your class and initialize
+ CStunServer::Initialize and CTCPServer::Initialize to create an instance of your class and initialize
_spAuth as appropriate.
#endif
@@ -138,6 +138,7 @@ class CShortTermAuth :
{
public:
virtual HRESULT DoAuthCheck(AuthAttributes* pAuthAttributes, AuthResponse* pResponse);
+ ADDREF_AND_RELEASE_IMPL();
};
@@ -154,6 +155,7 @@ class CLongTermAuth :
public:
virtual HRESULT DoAuthCheck(AuthAttributes* pAuthAttributes, AuthResponse* pResponse);
+ ADDREF_AND_RELEASE_IMPL();
};
View
3  server/server.cpp
@@ -30,7 +30,8 @@ fHasPA(false),
fHasAP(false),
fHasAA(false),
fMultiThreadedMode(false),
-fTCP(false)
+fTCP(false),
+nMaxConnections(0) // zero means default
{
;
}
View
2  server/server.h
@@ -28,6 +28,7 @@
class CStunServerConfig
{
public:
+
bool fHasPP; // PP: Primary ip, Primary port
bool fHasPA; // PA: Primary ip, Alternate port
bool fHasAP; // AP: Alternate ip, Primary port
@@ -36,6 +37,7 @@ class CStunServerConfig
bool fMultiThreadedMode; // if true, one thread for each socket
bool fTCP; // if true, then use TCP instead of UDP
+ uint32_t nMaxConnections; // only valid for TCP (on a per-thread basis)
CSocketAddress addrPP; // address for PP
CSocketAddress addrPA; // address for PA
View
148 server/tcpserver.cpp
@@ -26,6 +26,8 @@
+
+
#define IS_DIVISIBLE_BY(x, y) ((x % y)==0)
static bool IsPrime(unsigned int val)
@@ -79,23 +81,23 @@ static size_t GetHashTableWidth(unsigned int maxConnections)
}
// client sockets are edge triggered
-const uint32_t EPOLL_CLIENT_READ_EVENT_SET = EPOLLET | EPOLLIN | EPOLLRDHUP;
-const uint32_t EPOLL_CLIENT_WRITE_EVENT_SET = EPOLLET | EPOLLOUT;
+const uint32_t EPOLL_CLIENT_READ_EVENT_SET = IPOLLING_EDGETRIGGER | IPOLLING_READ | IPOLLING_RDHUP;
+
+const uint32_t EPOLL_CLIENT_WRITE_EVENT_SET = IPOLLING_EDGETRIGGER | IPOLLING_WRITE;
// listen sockets are always level triggered (that way, when we recover from
// hitting a max connections condition, we don't have to worry about
// missing a notification
-const uint32_t EPOLL_LISTEN_SOCKET_EVENT_SET = EPOLLIN;
+const uint32_t EPOLL_LISTEN_SOCKET_EVENT_SET = IPOLLING_READ;
// notification pipe could go either way
-const uint32_t EPOLL_PIPE_EVENT_SET = EPOLLIN;
+const uint32_t EPOLL_PIPE_EVENT_SET = IPOLLING_READ;
-const int c_MaxNumberOfConnectionsDefault = 10000;
+const int c_MaxNumberOfConnectionsDefault = 1000;
CTCPStunThread::CTCPStunThread()
{
- _epoll = -1;
_pipe[0] = _pipe[1] = -1;
_pthread = (pthread_t)-1;
Reset();
@@ -103,7 +105,8 @@ CTCPStunThread::CTCPStunThread()
void CTCPStunThread::Reset()
{
- CloseEpoll();
+ _spPolling.ReleaseAndClear();
+
CloseListenSockets();
ClosePipes();
@@ -189,75 +192,7 @@ HRESULT CTCPStunThread::NotifyThreadViaPipe()
}
-HRESULT CTCPStunThread::CreateEpoll()
-{
- ASSERT(_epoll == -1);
- _epoll = epoll_create(1000); // todo change this parameter to "max connections" (although it's likely an ignored parameter)
- if (_epoll == -1)
- {
- return ERRNOHR;
- }
- return S_OK;
-}
-void CTCPStunThread::CloseEpoll()
-{
- if (_epoll != -1)
- {
- close(_epoll);
- _epoll = -1;
- }
-}
-
-HRESULT CTCPStunThread::AddSocketToEpoll(int sock, uint32_t events)
-{
- HRESULT hr = S_OK;
- epoll_event ev = {};
-
- ASSERT(sock != -1);
-
- ev.data.fd = sock;
- ev.events = events;
- ChkIfA(epoll_ctl(_epoll, EPOLL_CTL_ADD, sock, &ev) == -1, ERRNOHR);
-Cleanup:
- return hr;
-}
-
-HRESULT CTCPStunThread::AddClientSocketToEpoll(int sock)
-{
- return AddSocketToEpoll(sock, EPOLL_CLIENT_READ_EVENT_SET);
-}
-
-HRESULT CTCPStunThread::DetachFromEpoll(int sock)
-{
- HRESULT hr = S_OK;
- epoll_event ev={}; // pass empty ev, because some implementations of epoll_ctl can't handle a NULL event struct
-
- if (sock == -1)
- {
- return S_FALSE;
- }
-
- ChkIfA(epoll_ctl(_epoll, EPOLL_CTL_DEL, sock, &ev) == -1, ERRNOHR);
-Cleanup:
- return hr;
-}
-
-
-HRESULT CTCPStunThread::EpollCtrl(int sock, uint32_t events)
-{
- HRESULT hr = S_OK;
-
- ASSERT(sock != -1);
-
- epoll_event ev = {};
- ev.data.fd = sock;
- ev.events = events;
-
- ChkIfA(epoll_ctl(_epoll, EPOLL_CTL_MOD, sock, &ev) == -1, ERRNOHR);
-Cleanup:
- return hr;
-}
HRESULT CTCPStunThread::SetListenSocketsOnEpoll(bool fEnable)
{
@@ -276,11 +211,11 @@ HRESULT CTCPStunThread::SetListenSocketsOnEpoll(bool fEnable)
if (fEnable)
{
- ChkA(AddSocketToEpoll(sock, EPOLL_LISTEN_SOCKET_EVENT_SET));
+ ChkA(_spPolling->Add(sock, EPOLL_LISTEN_SOCKET_EVENT_SET));
}
else
{
- ChkA(DetachFromEpoll(sock));
+ ChkA(_spPolling->Remove(sock));
}
}
@@ -302,7 +237,7 @@ HRESULT CTCPStunThread::CreateListenSockets()
{
if (_tsaListen.set[r].fValid)
{
- ChkA(_socketListenArray[r].TCPInit(_tsaListen.set[r].addr, (SocketRole)r));
+ ChkA(_socketListenArray[r].TCPInit(_tsaListen.set[r].addr, (SocketRole)r, true));
_socketTable[r] = _socketListenArray[r].GetSocketHandle();
ChkA(_socketListenArray[r].SetNonBlocking(true));
ret = listen(_socketTable[r], 128); // todo - figure out the right value to pass to listen
@@ -385,7 +320,7 @@ HRESULT CTCPStunThread::Init(const TransportAddressSet& tsaListen, const Transpo
ChkA(CreatePipes());
- ChkA(CreateEpoll());
+ ChkA(CreatePollingInstance(IPOLLING_TYPE_BEST, _spPolling.GetPointerPointer()));
// add listen socket to epoll
ASSERT(_fListenSocketsOnEpoll == false);
@@ -393,7 +328,7 @@ HRESULT CTCPStunThread::Init(const TransportAddressSet& tsaListen, const Transpo
// add read end of pipe to epoll so we can get notified of when a signal to exit has occurred
- ChkA(AddSocketToEpoll(_pipe[0], EPOLL_PIPE_EVENT_SET));
+ ChkA(_spPolling->Add(_pipe[0], EPOLL_PIPE_EVENT_SET));
_maxConnections = (maxConnections > 0) ? maxConnections : c_MaxNumberOfConnectionsDefault;
@@ -485,6 +420,7 @@ bool CTCPStunThread::IsConnectionCountAtMax()
void CTCPStunThread::Run()
{
+ HRESULT hrPoll;
Logging::LogMsg(LL_DEBUG, "Starting TCP listening thread (%d sockets)\n", _countSocks);
@@ -492,13 +428,11 @@ void CTCPStunThread::Run()
while (_fNeedToExit == false)
{
+ PollEvent pollevent = {};
// wait for a notification
- epoll_event ev = {};
int timeout = -1; // wait forever
CStunSocket* pListenSocket = NULL;
- int ret;
-
if (IsTimeoutNeeded())
{
timeout = CTCPStunThread::c_sweepTimeoutMilliseconds;
@@ -508,27 +442,30 @@ void CTCPStunThread::Run()
// otherwise, make sure it is enabled.
SetListenSocketsOnEpoll(IsConnectionCountAtMax() == false);
- ret = ::epoll_wait(_epoll, &ev, 1, timeout);
+ hrPoll = _spPolling->WaitForNextEvent(&pollevent, timeout);
- if ( _fNeedToExit || (ev.data.fd == _pipe[0]) )
+ if ( _fNeedToExit || (pollevent.fd == _pipe[0]) )
{
break;
}
- if (ret > 0)
+ // hrPoll will be S_OK if there was an event. S_FALSE otherwise
+ ASSERT(SUCCEEDED(hrPoll));
+
+ if (hrPoll == S_OK)
{
if (Logging::GetLogLevel() >= LL_VERBOSE)
{
- Logging::LogMsg(LL_VERBOSE, "socket %d: %x (%s%s%s%s%s%s)", ev.data.fd, ev.events,
- (ev.events&EPOLLIN) ? " EPOLLIN " : "",
- (ev.events&EPOLLOUT) ? " EPOLLOUT " : "",
- (ev.events&EPOLLRDHUP) ? " EPOLLRDHUP " : "",
- (ev.events&EPOLLHUP) ? " EPOLLHUP " : "",
- (ev.events&EPOLLERR) ? " EPOLLERR " : "",
- (ev.events&EPOLLPRI) ? " EPOLLPRI " : "");
+ Logging::LogMsg(LL_VERBOSE, "socket %d: %x (%s%s%s%s%s%s)", pollevent.fd, pollevent.eventflags,
+ (pollevent.eventflags&IPOLLING_READ) ? " IPOLLING_READ " : "",
+ (pollevent.eventflags&IPOLLING_WRITE) ? " IPOLLING_WRITE " : "",
+ (pollevent.eventflags&IPOLLING_RDHUP) ? " IPOLLING_RDHUP " : "",
+ (pollevent.eventflags&IPOLLING_HUP) ? " IPOLLING_HUP " : "",
+ (pollevent.eventflags&IPOLLING_ERROR) ? " IPOLLING_ERROR " : "",
+ (pollevent.eventflags&IPOLLING_PRI) ? " IPOLLING_PRI " : "");
}
- pListenSocket = GetListenSocket(ev.data.fd);
+ pListenSocket = GetListenSocket(pollevent.fd);
if (pListenSocket)
{
StunConnection* pConn = AcceptConnection(pListenSocket);
@@ -541,7 +478,7 @@ void CTCPStunThread::Run()
}
else
{
- ProcessConnectionEvent(ev.data.fd, ev.events);
+ ProcessConnectionEvent(pollevent.fd, pollevent.eventflags);
}
}
@@ -623,7 +560,8 @@ StunConnection* CTCPStunThread::AcceptConnection(CStunSocket* pListenSocket)
socktmp = -1;
ChkA(pConn->_stunsocket.SetNonBlocking(true));
- ChkA(AddClientSocketToEpoll(clientsock));
+
+ ChkA(_spPolling->Add(clientsock, EPOLL_CLIENT_READ_EVENT_SET));
// add connection to our tracking tables
pConn->_idHashTable = (_pNewConnList == &_hashConnections1) ? 1 : 2;
@@ -718,7 +656,7 @@ HRESULT CTCPStunThread::ReceiveBytesForConnection(StunConnection* pConn)
pConn->_state = ConnectionState_Transmitting;
// change the socket such that we only listen for "write events"
- Chk(EpollCtrl(sock, EPOLL_CLIENT_WRITE_EVENT_SET));
+ Chk(_spPolling->ChangeEventSet(sock, EPOLL_CLIENT_WRITE_EVENT_SET));
// optimization - go ahead and try to send the response
WriteBytesForConnection(pConn);
@@ -795,7 +733,8 @@ HRESULT CTCPStunThread::WriteBytesForConnection(StunConnection* pConn)
shutdown(sock, SHUT_WR);
// go back to listening for read events
- ChkA(EpollCtrl(sock, EPOLL_CLIENT_READ_EVENT_SET));
+ ChkA(_spPolling->ChangeEventSet(sock, EPOLL_CLIENT_READ_EVENT_SET));
+
ConsumeRemoteClose(pConn);
@@ -867,7 +806,7 @@ void CTCPStunThread::CloseConnection(StunConnection* pConn)
Logging::LogMsg(LL_VERBOSE, "Closing socket %d\n", sock);
- DetachFromEpoll(pConn->_stunsocket.GetSocketHandle());
+ _spPolling->Remove(pConn->_stunsocket.GetSocketHandle());
pConn->_stunsocket.Close();
// now figure out which hash table we were in
@@ -960,6 +899,9 @@ HRESULT CTCPServer::Initialize(const CStunServerConfig& config)
ChkIfA(_threads[0] != NULL, E_UNEXPECTED); // we can't already be initialized, right?
+ // optional code: create an authentication provider and initialize it here (if you want authentication)
+ // set the _spAuth member to reference it
+ // Chk(CYourAuthProvider::CreateInstanceNoInit(&_spAuth));
// tsaHandler is sort of a hack for TCP. It's really just a glorified indication to the the
// CStunRequestHandler code to figure out if can offer a CHANGED-ADDRESS attribute.
@@ -983,7 +925,7 @@ HRESULT CTCPServer::Initialize(const CStunServerConfig& config)
// todo - max connections needs to be a config param!
// todo - create auth
- ChkA(_threads[0]->Init(tsaListen, tsaHandler, NULL, 1000));
+ ChkA(_threads[0]->Init(tsaListen, tsaHandler, _spAuth, config.nMaxConnections));
}
else
{
@@ -999,7 +941,7 @@ HRESULT CTCPServer::Initialize(const CStunServerConfig& config)
// todo - max connections needs to be a config param!
// todo - create auth
- Chk(_threads[threadindex]->Init(tsaListen, tsaHandler, NULL, 1000));
+ Chk(_threads[threadindex]->Init(tsaListen, tsaHandler, NULL, config.nMaxConnections));
}
}
}
@@ -1016,9 +958,13 @@ HRESULT CTCPServer::Shutdown()
{
for (int role = (int)RolePP; role <= (int)RoleAA; role++)
{
+ // destructor of each TCP thread will stop the thread before returning
delete _threads[role];
_threads[role] = NULL;
}
+
+ _spAuth.ReleaseAndClear();
+
return S_OK;
}
View
15 server/tcpserver.h
@@ -24,7 +24,7 @@
#include "fasthash.h"
#include "messagehandler.h"
#include "stunconnection.h"
-
+#include "polling.h"
@@ -37,17 +37,11 @@ class CTCPStunThread
HRESULT CreatePipes();
HRESULT NotifyThreadViaPipe();
void ClosePipes();
-
- int _epoll;
+
+ CRefCountedPtr<IPolling> _spPolling;
bool _fListenSocketsOnEpoll;
- HRESULT CreateEpoll();
- void CloseEpoll();
// epoll helpers
- HRESULT AddSocketToEpoll(int sock, uint32_t events);
- HRESULT AddClientSocketToEpoll(int sock);
- HRESULT DetachFromEpoll(int sock);
- HRESULT EpollCtrl(int sock, uint32_t events);
HRESULT SetListenSocketsOnEpoll(bool fEnable);
TransportAddressSet _tsaListen; // this is not what gets passed to CStunRequestHandler, see _tsa below
@@ -131,7 +125,8 @@ class CTCPServer :
private:
CTCPStunThread* _threads[4];
-
+
+ CRefCountedPtr<IStunAuth> _spAuth;
public:
View
11 server/usage.txt
@@ -21,13 +21,16 @@ Available options:
--family=IPVERSION
IPVERSION is either "4" or "6" to specify the usage of IPV4 or IPV6. If not specified, the default value is "4".
- --protocol=PROTO
- PROTO is either "udp", "tcp", or "tls". Where "udp" is the default. "tcp" and "tls" modes are only available when the --mode option is "basic". (Note: tcp and tls are not yet available in this version)
+ --protocol=PROTO
+ PROTO is either "udp" or "tcp". "udp" is the default.
- --verbosity=LOGLEVEL
+ --maxconn=MAXCONN
+ MAXCONN is a value between 1 and 100000. The default is 1000. This value specifies the maximum number of simultaneous TCP connections allowed (per thread). It's an ignored parameter when the PROTOCOL option is UDP.
+
+ --verbosity=LOGLEVEL
Sets the verbosity of the logging level. 0 is the default (minimal output and logging). 1 shows slightly more. 2 and higher shows even more.
- --help
+ --help
Prints this help page
Examples:
View
2  stuncore/buffer.h
@@ -51,7 +51,7 @@ class CBuffer
HRESULT InitWithAllocAndCopy(uint8_t* pByteArray, size_t nByteArraySize);
HRESULT InitNoAlloc(uint8_t* pByteArray, size_t nByteArraySize);
- size_t GetSize() {return _size;}
+ inline size_t GetSize() {return _size;}
inline size_t GetAllocatedSize() {return _allocatedSize;}
HRESULT SetSize(size_t size);
View
17 stuncore/stunclientlogic.cpp
@@ -83,15 +83,20 @@ HRESULT CStunClientLogic::Initialize(StunClientLogicConfig& config)
_fInitialized = true;
-
- if (config.timeoutSeconds == 0)
+
+ if (_config.fTimeoutIsInstant)
+ {
+ _config.timeoutSeconds = 0;
+ }
+ else if (_config.timeoutSeconds == 0)
{
- config.timeoutSeconds = 5;
+ _config.timeoutSeconds = 3; // default to waiting for 3 seconds
}
+
- if (config.uMaxAttempts <= 0)
+ if (_config.uMaxAttempts <= 0)
{
- config.uMaxAttempts = 2;
+ _config.uMaxAttempts = 2;
}
_nTestIndex = 0;
@@ -183,7 +188,7 @@ HRESULT CStunClientLogic::GetNextMessage(CRefCountedBuffer& spMsg, CSocketAddres
break;
}
- // have we exceed the retry count
+ // have we exceeded the retry count
if (_sendCount >= _config.uMaxAttempts)
{
// notify the test that it has timed out
View
6 stuncore/stunclientlogic.h
@@ -21,10 +21,14 @@
#include "stunclienttests.h"
+
+
struct StunClientLogicConfig
{
CSocketAddress addrServer;
- uint32_t timeoutSeconds;
+
+ bool fTimeoutIsInstant; // if true, then timeoutSeconds is ignored and assumed to be zero
+ uint32_t timeoutSeconds; // if fTimeoutIsInstant is false, then "0" implies to use the default
uint32_t uMaxAttempts;
bool fBehaviorTest;
View
4 stuncore/stunreader.cpp
@@ -75,8 +75,8 @@ uint16_t CStunMessageReader::HowManyBytesNeeded()
BOOST_ASSERT(STUN_HEADER_SIZE > currentSize);
return STUN_HEADER_SIZE - currentSize;
case HeaderValidated:
- BOOST_ASSERT(_msgLength > currentSize);
- return _msgLength - currentSize;
+ BOOST_ASSERT((_msgLength+STUN_HEADER_SIZE) > currentSize);
+ return (_msgLength+STUN_HEADER_SIZE) - currentSize;
default:
return 0;
}
Please sign in to comment.
Something went wrong with that request. Please try again.