/* * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.test.integration.domain.suites; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.ServerSocket; import java.net.Socket; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.xnio.IoUtils; /** * @author: Kabir Khan */ public abstract class SimpleSyslogServer implements Runnable { protected final AtomicBoolean closed = new AtomicBoolean(false); protected final BlockingQueue<byte[]> receivedData = new LinkedBlockingQueue<byte[]>(); SimpleSyslogServer(){ } static SimpleSyslogServer createUdp(int port) throws IOException { SimpleSyslogServer server = new Udp(port); Thread t = new Thread(server); t.start(); return server; } static SimpleSyslogServer createTcp(int port, boolean octetCounting) throws IOException { SimpleSyslogServer server = new Tcp(new ServerSocket(port), octetCounting); Thread t = new Thread(server); t.start(); return server; } static SimpleSyslogServer createTls(int port, boolean octetCounting, File keystorePath, String keystorePassword, File clientCertPath, String clientCertPwd) throws IOException, GeneralSecurityException { KeyManager[] keyManagers; final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); final FileInputStream in = new FileInputStream(keystorePath); try { final KeyStore ks = KeyStore.getInstance("JKS"); ks.load(in, keystorePassword.toCharArray()); kmf.init(ks, keystorePassword.toCharArray()); keyManagers = kmf.getKeyManagers(); } finally { IoUtils.safeClose(in); } boolean requireClientAuth = false; TrustManager[] trustManagers = null; if (clientCertPath != null) { requireClientAuth = true; final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); final FileInputStream clientIn = new FileInputStream(clientCertPath); try { final KeyStore ks = KeyStore.getInstance("JKS"); ks.load(clientIn, clientCertPwd.toCharArray()); tmf.init(ks); trustManagers = tmf.getTrustManagers(); } finally { IoUtils.safeClose(in); } } final SSLContext context = SSLContext.getInstance("TLS"); context.init(keyManagers, trustManagers, null); SSLServerSocket socket = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(port); socket.setNeedClientAuth(requireClientAuth); SimpleSyslogServer server = new Tcp(socket, octetCounting); Thread t = new Thread(server); t.start(); return server; } abstract void close(); byte[] receiveData(int seconds, TimeUnit unit) throws InterruptedException { return receivedData.poll(seconds, unit); } byte[] pollData() { return receivedData.poll(); } private static class Udp extends SimpleSyslogServer { private final DatagramSocket socket; Udp(int port) throws IOException { socket = new DatagramSocket(port); } @Override void close() { closed.set(true); socket.close(); } @Override public void run(){ while (!closed.get()){ try { DatagramPacket packet = new DatagramPacket(new byte[2048], 2048); socket.receive(packet); byte[] bytes = new byte[packet.getLength()]; System.arraycopy(packet.getData(), 0, bytes, 0, packet.getLength()); receivedData.add(bytes); } catch (IOException e) { if (!closed.get()){ e.printStackTrace(); close(); } } } } } private static class Tcp extends SimpleSyslogServer { private final boolean octetCounting; private final ServerSocket serverSocket; private volatile Socket socket; Tcp(ServerSocket servetSocket, boolean octetCounting){ this.serverSocket = servetSocket; this.octetCounting = octetCounting; } @Override void close() { closed.set(true); IoUtils.safeClose(serverSocket); IoUtils.safeClose(socket); } Socket accept(ServerSocket serverSocket) throws IOException { return serverSocket.accept(); } @Override public void run() { try { socket = accept(serverSocket); } catch (IOException e) { e.printStackTrace(); return; } try { InputStream in = new BufferedInputStream(socket.getInputStream()); while (!closed.get()){ ByteArrayOutputStream out = new ByteArrayOutputStream(); if (octetCounting) { //There will be some bytes indicating the length of the message, a space and then the message int i = in.read(); int length = -1; int count = 0; while (i != -1 && (length == -1 || count++ <= length)){ if (length == -1){ if ((char)i == ' '){ byte[] bytes = out.toByteArray(); length = 0; for (int j = 0 ; j < bytes.length ; j++) { length = length * 10 + bytes[j] - (int)Character.valueOf('0'); } out.reset(); continue; } } out.write(i); if (length != -1 && count > length) { break; } i = in.read(); } } else { //Here the message is terminated by a '\n'. This means that multiline messages will appear as separate //messags in syslog. For the purposes of this test, since we are using json as the output format the //message will look something like // 2013-05-30T23:11:52.950+01:00 Kabirs-MacBook-Pro.local WildFly 615 - - 2013-05-30 23:11:52 - {\n....}\n. //So we count the curly braces to figure out the full message int braceCount = 0; int i = in.read(); while (i != -1) { out.write(i); if (((char)i) == '{' ) { braceCount++; break; } i = in.read(); } i = in.read(); while (i != - 1) { char c = (char)i; if (c == '{') { braceCount++; } if (c == '}') { braceCount--; } out.write(i); if (c == '\n' && braceCount == 0) { break; } i = in.read(); } } if (out.toByteArray() != null && out.toByteArray().length > 0) { receivedData.add(out.toByteArray()); } } } catch (IOException e) { if (!closed.get()){ e.printStackTrace(); close(); } } } } // // private static class Tls extends Tcp { // private final boolean requireClientAuth; // Tls(ServerSocket servetSocket, boolean octetCounting, boolean requireClientAuth) throws IOException { // super(servetSocket, octetCounting); // this.requireClientAuth = requireClientAuth; // } // // Socket accept(ServerSocket serverSocket) throws IOException { // SSLSocket socket = (SSLSocket)serverSocket.accept(); // socket.setNeedClientAuth(requireClientAuth); // return socket; // } // // } }