Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listen on an unencrypted and on an encrypted port #22

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion SSL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ The purpose of this document is to provide a summary of how to configuration SSL

## Configuring a SMPP server with SSL transport

### Example:
There are two ways to use SSL on the server side: Either SSL only or non-SSL and SSL on separate ports (added in version 5.0.x).

### Example for SSL only:

// Configure the server as you normally would:
SmppServerConfiguration configuration = new SmppServerConfiguration();
Expand All @@ -24,6 +26,27 @@ The purpose of this document is to provide a summary of how to configuration SSL
configuration.setUseSsl(true);
configuration.setSslConfiguration(sslConfig);

### Example for non-SSL and SSL:

// Configure the server as you normally would:
SmppServerConfiguration configuration = new SmppServerConfiguration();
configuration.setPort(2776); // 2776 serves unencrypted traffic
...

// Then create a SSL configuration:
SslConfiguration sslConfig = new SslConfiguration();
sslConfig.setKeyStorePath("path/to/keystore");
sslConfig.setKeyStorePassword("changeit");
sslConfig.setKeyManagerPassword("changeit");
sslConfig.setTrustStorePath("path/to/keystore");
sslConfig.setTrustStorePassword("changeit");
...

// And add it to the server configuration:
configuration.setUseSsl(true);
configuration.setSslConfiguration(sslConfig);
configuration.setSslPort(2777); // 2777 serves SSL-encrypted traffic


### Require client auth

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/cloudhopper/smpp/SmppServerConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class SmppServerConfiguration extends SmppConnectionConfiguration {
private long defaultRequestExpiryTimeout = SmppConstants.DEFAULT_REQUEST_EXPIRY_TIMEOUT;
private long defaultWindowMonitorInterval = SmppConstants.DEFAULT_WINDOW_MONITOR_INTERVAL;
private boolean defaultSessionCountersEnabled = false;
private Integer sslPort;

public SmppServerConfiguration() {
super("0.0.0.0", 2775, 5000l);
Expand Down Expand Up @@ -250,4 +251,19 @@ public void setDefaultSessionCountersEnabled(boolean defaultSessionCountersEnabl
this.defaultSessionCountersEnabled = defaultSessionCountersEnabled;
}

/**
* @return the SSL Port, might be null
*/
public Integer getSslPort() {
return sslPort;
}

/**
* Sets the SSL port. If you just set the normal port and use SSL, then the server only supports SSL on the normal port. If you specify the SSL
* port, then the server will listen to unencrypted connections on the normal and the SSL connections on the SSL port.
* @param sslPort
*/
public void setSslPort(Integer sslPort) {
this.sslPort = sslPort;
}
}
30 changes: 19 additions & 11 deletions src/main/java/com/cloudhopper/smpp/channel/SmppServerConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/


import com.cloudhopper.smpp.SmppServerConfiguration;
import com.cloudhopper.smpp.impl.DefaultSmppServer;
import com.cloudhopper.smpp.impl.UnboundSmppSession;
import com.cloudhopper.smpp.ssl.SslConfiguration;
Expand All @@ -36,6 +37,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.InetSocketAddress;

/**
* Channel handler for server SMPP sessions.
*
Expand Down Expand Up @@ -66,23 +70,27 @@ public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) thr
// create a default "unbound" thread name for the thread processing the channel
// this will create a name of "RemoteIPAddress.RemotePort"
String channelName = ChannelUtil.createChannelName(channel);
String threadName = server.getConfiguration().getName() + ".UnboundSession." + channelName;
SmppServerConfiguration serverConfig = server.getConfiguration();
String threadName = serverConfig.getName() + ".UnboundSession." + channelName;

// rename the current thread for logging, then rename it back
String currentThreadName = Thread.currentThread().getName();
Thread.currentThread().setName(server.getConfiguration().getName());
Thread.currentThread().setName(serverConfig.getName());
logger.info("New channel from [{}]", channelName);
Thread.currentThread().setName(currentThreadName);

// add SSL handler
if (server.getConfiguration().isUseSsl()) {
SslConfiguration sslConfig = server.getConfiguration().getSslConfiguration();
if (sslConfig == null) throw new IllegalStateException("sslConfiguration must be set");
SslContextFactory factory = new SslContextFactory(sslConfig);
SSLEngine sslEngine = factory.newSslEngine();
sslEngine.setUseClientMode(false);
channel.getPipeline().addLast(SmppChannelConstants.PIPELINE_SESSION_SSL_NAME, new SslHandler(sslEngine));
}
// add SSL handler
if (serverConfig.isUseSsl()
&& ((serverConfig.getSslPort() != null
&& channel.getLocalAddress() instanceof InetSocketAddress
&& ((InetSocketAddress)channel.getLocalAddress()).getPort() == serverConfig.getSslPort()) || serverConfig.getSslPort() == null)) {
SslConfiguration sslConfig = serverConfig.getSslConfiguration();
if (sslConfig == null) throw new IllegalStateException("sslConfiguration must be set");
SslContextFactory factory = new SslContextFactory(sslConfig);
SSLEngine sslEngine = factory.newSslEngine();
sslEngine.setUseClientMode(false);
channel.getPipeline().addLast(SmppChannelConstants.PIPELINE_SESSION_SSL_NAME, new SslHandler(sslEngine));
}

// add a new instance of a thread renamer
channel.getPipeline().addLast(SmppChannelConstants.PIPELINE_SESSION_THREAD_RENAMER_NAME, new SmppSessionThreadRenamer(threadName));
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/cloudhopper/smpp/impl/DefaultSmppServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class DefaultSmppServer implements SmppServer, DefaultSmppServerMXBean {
private ExecutorService bossThreadPool;
private ChannelFactory channelFactory;
private ServerBootstrap serverBootstrap;
private Channel serverChannel;
private ChannelGroup serverChannel;
// shared instance of a timer for session writeTimeout timing
private final org.jboss.netty.util.Timer writeTimeoutTimer;
// shared instance of a timer background thread to close unbound channels
Expand Down Expand Up @@ -223,7 +223,14 @@ public Timer getBindTimer() {

@Override
public boolean isStarted() {
return (this.serverChannel != null && this.serverChannel.isBound());
if (serverChannel == null) {
return false;
}
boolean allStarted = true;
for(Channel channel : serverChannel) {
allStarted = allStarted && channel.isBound();
}
return allStarted;
}

@Override
Expand All @@ -242,8 +249,14 @@ public void start() throws SmppChannelException {
throw new SmppChannelException("Unable to start: server is destroyed");
}
try {
serverChannel = this.serverBootstrap.bind(new InetSocketAddress(configuration.getHost(), configuration.getPort()));
serverChannel = new DefaultChannelGroup();
serverChannel.add(this.serverBootstrap.bind(new InetSocketAddress(configuration.getHost(), configuration.getPort())));
logger.info("{} started at {}:{}", configuration.getName(), configuration.getHost(), configuration.getPort());
if (configuration.isUseSsl() && configuration.getSslPort() != null) {
serverChannel.add(this.serverBootstrap.bind(new InetSocketAddress(configuration.getHost(), configuration.getSslPort())));
logger.info("{} started at {}:{} (SSL)", configuration.getName(), configuration.getHost(), configuration.getSslPort());
}

} catch (ChannelException e) {
throw new SmppChannelException(e.getMessage(), e);
}
Expand Down
145 changes: 145 additions & 0 deletions src/test/java/com/cloudhopper/smpp/demo/TwoPortsServerMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.cloudhopper.smpp.demo;

/*
* #%L
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import com.cloudhopper.smpp.*;
import com.cloudhopper.smpp.impl.DefaultSmppServer;
import com.cloudhopper.smpp.impl.DefaultSmppSessionHandler;
import com.cloudhopper.smpp.pdu.BaseBind;
import com.cloudhopper.smpp.pdu.BaseBindResp;
import com.cloudhopper.smpp.pdu.PduRequest;
import com.cloudhopper.smpp.pdu.PduResponse;
import com.cloudhopper.smpp.ssl.SslConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Demonstration of a simple SMPP server listening on a two ports: one unencrypted and one SSL-encrypted
*
* @author ruwen
*/
public class TwoPortsServerMain {
private static final Logger LOGGER = LoggerFactory.getLogger(TwoPortsServerMain.class);

static public void main(String[] args) throws Exception {
//
// setup 3 things required for a server
//

// for monitoring thread use, it's preferable to create your own instance
// of an executor and cast it to a ThreadPoolExecutor from Executors.newCachedThreadPool()
// this permits exposing things like executor.getActiveCount() via JMX possible
// no point renaming the threads in a factory since underlying Netty
// framework does not easily allow you to customize your thread names
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();

// to enable automatic expiration of requests, a second scheduled executor
// is required which is what a monitor task will be executed with - this
// is probably a thread pool that can be shared with between all client bootstraps
ScheduledThreadPoolExecutor monitorExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, new ThreadFactory() {
private AtomicInteger sequence = new AtomicInteger(0);

@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("SmppServerSessionWindowMonitorPool-" + sequence.getAndIncrement());
return t;
}
});

// create a server configuration
SmppServerConfiguration configuration = new SmppServerConfiguration();
configuration.setPort(2776);
configuration.setMaxConnectionSize(10);
configuration.setNonBlockingSocketsEnabled(true);
configuration.setDefaultRequestExpiryTimeout(30000);
configuration.setDefaultWindowMonitorInterval(15000);
configuration.setDefaultWindowSize(5);
configuration.setDefaultWindowWaitTimeout(configuration.getDefaultRequestExpiryTimeout());
configuration.setDefaultSessionCountersEnabled(true);
configuration.setJmxEnabled(true);

//ssl
SslConfiguration sslConfig = new SslConfiguration();
sslConfig.setKeyStorePath("src/test/resources/keystore");
sslConfig.setKeyStorePassword("changeit");
sslConfig.setKeyManagerPassword("changeit");
sslConfig.setTrustStorePath("src/test/resources/keystore");
sslConfig.setTrustStorePassword("changeit");
configuration.setUseSsl(true);
configuration.setSslConfiguration(sslConfig);
configuration.setSslPort(2777);

// create a server, start it up
DefaultSmppServer smppServer = new DefaultSmppServer(configuration, new DefaultSmppServerHandler(), executor, monitorExecutor);

LOGGER.info("Starting SMPP server...");
smppServer.start();
LOGGER.info("SMPP server started");

System.out.println("Press any key to stop server");
System.in.read();

LOGGER.info("Stopping SMPP server...");
smppServer.stop();
LOGGER.info("SMPP server stopped");

LOGGER.info("Server counters: {}", smppServer.getCounters());
}

public static class DefaultSmppServerHandler implements SmppServerHandler {

@Override
public void sessionBindRequested(Long sessionId, SmppSessionConfiguration sessionConfiguration, final BaseBind bindRequest) {
// test name change of sessions
// this name actually shows up as thread context....
sessionConfiguration.setName("Application.SMPP." + sessionConfiguration.getSystemId());
}

@Override
public void sessionCreated(Long sessionId, SmppServerSession session, BaseBindResp preparedBindResponse) {
LOGGER.info("Session created: {}", session);
// need to do something it now (flag we're ready)
session.serverReady(new TestSmppSessionHandler());
}

@Override
public void sessionDestroyed(Long sessionId, SmppServerSession session) {
LOGGER.info("Session destroyed: {}", session);
// print out final stats
if (session.hasCounters()) {
LOGGER.info(" final session rx-submitSM: {}", session.getCounters().getRxSubmitSM());
}

// make sure it's really shutdown
session.destroy();
}
}

public static class TestSmppSessionHandler extends DefaultSmppSessionHandler {
@Override
public PduResponse firePduRequestReceived(PduRequest pduRequest) {
return pduRequest.createResponse();
}
}
}
Loading