com.sshtools.forker.common.Cookie Maven / Gradle / Ivy
Show all versions of forker-common Show documentation
/**
* Copyright © 2015 - ${year} SSHTOOLS Limited ([email protected])
*
* 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.
*/
package com.sshtools.forker.common;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.Socket;
/**
* Because Forker Daemon may run as a privileged process (or another
* user), it is of course sensible to have some kind of security to prevent
* malicious processes making use of its facilities.
*
* This protection is provided by this class which represents a 'Cookie'. When
* clients want to connect to the daemon, they must first present the cookie.
*
* How the client gets the cookie in the first place depends on how the daemon
* was started.
*
Isolated Mode
*
* This is the safest method and recommended for most uses. In this case, the
* client application is usually responsible for launching the daemon (possibly
* requesting administrator credentials to do so). Once launched, the daemon
* will return it's cookie on stdout, allowing the client to read it an
* make a connection back to the daemons server socket. No other processes will
* be able to see this, so the cookie remains secret between the daemon and it's
* client. Once launched, stdout can be used as normal.
*
* This is all completely transparent to your code, you just need to tell Forker
* to use isolated mode for the daemon, make the following call early in your
* applications lifecylce. Either
*
*
* Forker.loadDaemon()
*
*
* to run as the same user as you are currently running as, or
*
*
* Forker.loadDaemon(true)
*
*
* to run the daemon as an administrator.
*/
public class Cookie {
private static Cookie cookie = new Cookie();
private Instance isolatedCookieInstance;
/**
* Represents a single running instance of the daemon, containing its cookie
* and the port it is available on
*
*/
public static class Instance {
private String cookie;
private int port;
/**
* Create a new instance given the actual cookie and the port the daemon
* is running on
*
* @param cookie
* cookie
* @param port
* port
*/
public Instance(String cookie, int port) {
this.cookie = cookie;
this.port = port;
}
/**
* Create a new instance given a cookie spec string consisten of
* [cookie]:[port].
*
* @param cookieText
* cookie text
*/
public Instance(String cookieText) {
String[] a = cookieText.split(":");
cookie = a[0];
port = Integer.parseInt(a[1]);
}
/**
* Get the cookie
*
* @return cookie
*/
public String getCookie() {
return cookie;
}
/**
* Get the port the daemon is running on
*
* @return port
*/
public int getPort() {
return port;
}
@Override
public String toString() {
return String.format("%s:%d", cookie, port);
}
/**
* Test whether daemon represented by this cookie instance is actually
* running.
*
* @return daemon running
*/
public boolean isRunning() {
try {
final Socket s = new Socket("127.0.0.1", port);
try {
s.setSoTimeout(1000);
DataOutputStream dout = new DataOutputStream(s.getOutputStream());
dout.writeUTF(cookie);
dout.write(0);
dout.flush();
DataInputStream din = new DataInputStream(s.getInputStream());
int state = din.readInt();
if (state == States.OK)
return true;
} finally {
s.close();
}
} catch (IOException ioe) {
}
return false;
}
}
/**
* Set the cookie.
*
* @param cookie
* fixed cookie
*/
public static void set(Cookie cookie) {
Cookie.cookie = cookie;
}
/**
* Get the cookie. This is NOT the actual cookie instance (see
* {@link #load()} for that).
*
* @return cookie
*/
public static Cookie get() {
return cookie;
}
/**
* Set the fixed cookie instance. Used when daemon has been loaded by the
* client runtime in isolated mode.
*
* @param isolatedCookieInstance
* cookie instance
*/
public void set(Instance isolatedCookieInstance) {
this.isolatedCookieInstance = isolatedCookieInstance;
}
/**
* Load the cookie to use to connect to the daemon. If a fixed instance has
* been set using {@link #set(Instance)}, that will be returned, otherwise
* an attempt will be made to locate the cookie if possible. If no cookie
* can be found, null
will be returned.
*
* @return cookie instance
* @throws IOException
* on any error
*/
public Instance load() throws IOException {
if (isolatedCookieInstance != null)
return isolatedCookieInstance;
try {
BufferedReader r = new BufferedReader(new FileReader(getCookieFile()));
try {
return new Instance(r.readLine());
} finally {
r.close();
}
} catch (FileNotFoundException fnfe) {
return null;
}
}
/**
* Get the file used to store the cookie. The daemon should write to the
* same file.
*
* @return cookie file
*/
public File getCookieFile() {
String cookieFile = System.getProperty("forker.cookie.file");
if (cookieFile != null) {
return new File(cookieFile);
}
File dir = new File(new File(System.getProperty("user.home", ".")), ".forker");
if (!dir.exists() && !dir.mkdirs())
throw new RuntimeException("Could not create cookie directory " + dir);
return new File(dir, "cookie");
}
}