/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import cx.ath.matthew.debug.Debug;
import cx.ath.matthew.unix.UnixSocket;
import cx.ath.matthew.unix.UnixSocketAddress;
import cx.ath.matthew.utils.Hexdump;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Random;
import java.util.Vector;
import static org.freedesktop.dbus.Gettext.getString;
public class Transport {
public static class SASL {
public static class Command {
private int command;
private int mechs;
private String data;
private String response;
public Command() {
}
public Command(String s) throws IOException {
String[] ss = s.split(" ");
if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating command from: " + Arrays.toString(ss));
if (0 == col.compare(ss[0], "OK")) {
command = COMMAND_OK;
data = ss[1];
} else if (0 == col.compare(ss[0], "AUTH")) {
command = COMMAND_AUTH;
if (ss.length > 1) {
if (0 == col.compare(ss[1], "EXTERNAL"))
mechs = AUTH_EXTERNAL;
else if (0 == col.compare(ss[1], "DBUS_COOKIE_SHA1"))
mechs = AUTH_SHA;
else if (0 == col.compare(ss[1], "ANONYMOUS"))
mechs = AUTH_ANON;
}
if (ss.length > 2)
data = ss[2];
} else if (0 == col.compare(ss[0], "DATA")) {
command = COMMAND_DATA;
data = ss[1];
} else if (0 == col.compare(ss[0], "REJECTED")) {
command = COMMAND_REJECTED;
for (int i = 1; i < ss.length; i++)
if (0 == col.compare(ss[i], "EXTERNAL"))
mechs |= AUTH_EXTERNAL;
else if (0 == col.compare(ss[i], "DBUS_COOKIE_SHA1"))
mechs |= AUTH_SHA;
else if (0 == col.compare(ss[i], "ANONYMOUS"))
mechs |= AUTH_ANON;
} else if (0 == col.compare(ss[0], "BEGIN")) {
command = COMMAND_BEGIN;
} else if (0 == col.compare(ss[0], "CANCEL")) {
command = COMMAND_CANCEL;
} else if (0 == col.compare(ss[0], "ERROR")) {
command = COMMAND_ERROR;
data = ss[1];
} else {
throw new IOException(getString("invalidCommand") + ss[0]);
}
if (Debug.debug) Debug.print(Debug.VERBOSE, "Created command: " + this);
}
public int getCommand() {
return command;
}
public int getMechs() {
return mechs;
}
public String getData() {
return data;
}
public String getResponse() {
return response;
}
public void setResponse(String s) {
response = s;
}
public String toString() {
return "Command(" + command + ", " + mechs + ", " + data + ", " + null + ")";
}
}
private static Collator col = Collator.getInstance();
static {
col.setDecomposition(Collator.FULL_DECOMPOSITION);
col.setStrength(Collator.PRIMARY);
}
public static final int LOCK_TIMEOUT = 1000;
public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5;
public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2);
public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5;
public static final int COOKIE_TIMEOUT = 240;
public static final String COOKIE_CONTEXT = "org_freedesktop_java";
private String findCookie(String context, String ID) throws IOException {
String homedir = System.getProperty("user.home");
File f = new File(homedir + "/.dbus-keyrings/" + context);
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
String s = null;
String cookie = null;
long now = System.currentTimeMillis() / 1000;
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long timestamp = Long.parseLong(line[1]);
if (line[0].equals(ID) && (!(timestamp < 0 ||
(now + MAX_TIME_TRAVEL_SECONDS) < timestamp ||
(now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp))) {
cookie = line[2];
break;
}
}
r.close();
return cookie;
}
private void addCookie(String context, String ID, long timestamp, String cookie) throws IOException {
String homedir = System.getProperty("user.home");
File keydir = new File(homedir + "/.dbus-keyrings/");
File cookiefile = new File(homedir + "/.dbus-keyrings/" + context);
File lock = new File(homedir + "/.dbus-keyrings/" + context + ".lock");
File temp = new File(homedir + "/.dbus-keyrings/" + context + ".temp");
// ensure directory exists
if (!keydir.exists()) keydir.mkdirs();
// acquire lock
long start = System.currentTimeMillis();
while (!lock.createNewFile() && LOCK_TIMEOUT > (System.currentTimeMillis() - start)) ;
// read old file
Vector<String> lines = new Vector<String>();
if (cookiefile.exists()) {
BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile)));
String s = null;
while (null != (s = r.readLine())) {
String[] line = s.split(" ");
long time = Long.parseLong(line[1]);
// expire stale cookies
if ((timestamp - time) < COOKIE_TIMEOUT)
lines.add(s);
}
r.close();
}
// add cookie
lines.add(ID + " " + timestamp + " " + cookie);
// write temp file
PrintWriter w = new PrintWriter(new FileOutputStream(temp));
for (String l : lines)
w.println(l);
w.close();
// atomically move to old file
if (!temp.renameTo(cookiefile)) {
cookiefile.delete();
temp.renameTo(cookiefile);
}
// remove lock
lock.delete();
}
/**
* Takes the string, encodes it as hex and then turns it into a string again.
* No, I don't know why either.
*/
private String stupidlyEncode(String data) {
return Hexdump.toHex(data.getBytes()).replaceAll(" ", "");
}
private String stupidlyEncode(byte[] data) {
return Hexdump.toHex(data).replaceAll(" ", "");
}
private byte getNibble(char c) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (byte) (c - '0');
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return (byte) (c - 'A' + 10);
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return (byte) (c - 'a' + 10);
default:
return 0;
}
}
private String stupidlyDecode(String data) {
char[] cs = new char[data.length()];
char[] res = new char[cs.length / 2];
data.getChars(0, data.length(), cs, 0);
for (int i = 0, j = 0; j < res.length; i += 2, j++) {
int b = 0;
b |= getNibble(cs[i]) << 4;
b |= getNibble(cs[i + 1]);
res[j] = (char) b;
}
return new String(res);
}
public static final int MODE_SERVER = 1;
public static final int MODE_CLIENT = 2;
public static final int AUTH_NONE = 0;
public static final int AUTH_EXTERNAL = 1;
public static final int AUTH_SHA = 2;
public static final int AUTH_ANON = 4;
public static final int COMMAND_AUTH = 1;
public static final int COMMAND_DATA = 2;
public static final int COMMAND_REJECTED = 3;
public static final int COMMAND_OK = 4;
public static final int COMMAND_BEGIN = 5;
public static final int COMMAND_CANCEL = 6;
public static final int COMMAND_ERROR = 7;
public static final int INITIAL_STATE = 0;
public static final int WAIT_DATA = 1;
public static final int WAIT_OK = 2;
public static final int WAIT_REJECT = 3;
public static final int WAIT_AUTH = 4;
public static final int WAIT_BEGIN = 5;
public static final int AUTHENTICATED = 6;
public static final int FAILED = 7;
public static final int OK = 1;
public static final int CONTINUE = 2;
public static final int ERROR = 3;
public static final int REJECT = 4;
public Command receive(InputStream s) throws IOException {
StringBuffer sb = new StringBuffer();
top:
while (true) {
int c = s.read();
switch (c) {
case -1:
throw new IOException("Stream unexpectedly short (broken pipe)");
case 0:
case '\r':
continue;
case '\n':
break top;
default:
sb.append((char) c);
}
}
if (Debug.debug) Debug.print(Debug.VERBOSE, "received: " + sb);
try {
return new Command(sb.toString());
} catch (Exception e) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, e);
return new Command();
}
}
public void send(OutputStream out, int command, String... data) throws IOException {
StringBuffer sb = new StringBuffer();
switch (command) {
case COMMAND_AUTH:
sb.append("AUTH");
break;
case COMMAND_DATA:
sb.append("DATA");
break;
case COMMAND_REJECTED:
sb.append("REJECTED");
break;
case COMMAND_OK:
sb.append("OK");
break;
case COMMAND_BEGIN:
sb.append("BEGIN");
break;
case COMMAND_CANCEL:
sb.append("CANCEL");
break;
case COMMAND_ERROR:
sb.append("ERROR");
break;
default:
return;
}
for (String s : data) {
sb.append(' ');
sb.append(s);
}
sb.append('\r');
sb.append('\n');
if (Debug.debug) Debug.print(Debug.VERBOSE, "sending: " + sb);
out.write(sb.toString().getBytes());
}
public int do_challenge(int auth, Command c) throws IOException {
switch (auth) {
case AUTH_SHA:
String[] reply = stupidlyDecode(c.getData()).split(" ");
if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.toString(reply));
if (3 != reply.length) {
if (Debug.debug) Debug.print(Debug.DEBUG, "Reply is not length 3");
return ERROR;
}
String context = reply[0];
String ID = reply[1];
String serverchallenge = reply[2];
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException NSAe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
return ERROR;
}
byte[] buf = new byte[8];
Message.marshallintBig(System.currentTimeMillis(), buf, 0, 8);
String clientchallenge = stupidlyEncode(md.digest(buf));
md.reset();
long start = System.currentTimeMillis();
String cookie = null;
while (null == cookie && (System.currentTimeMillis() - start) < LOCK_TIMEOUT)
cookie = findCookie(context, ID);
if (null == cookie) {
if (Debug.debug)
Debug.print(Debug.DEBUG, "Did not find a cookie in context " + context + " with ID " + ID);
return ERROR;
}
String response = serverchallenge + ":" + clientchallenge + ":" + cookie;
buf = md.digest(response.getBytes());
if (Debug.debug)
Debug.print(Debug.VERBOSE, "Response: " + response + " hash: " + Hexdump.format(buf));
response = stupidlyEncode(buf);
c.setResponse(stupidlyEncode(clientchallenge + " " + response));
return OK;
default:
if (Debug.debug) Debug.print(Debug.DEBUG, "Not DBUS_COOKIE_SHA1 authtype.");
return ERROR;
}
}
public String challenge = "";
public String cookie = "";
public int do_response(int auth, String Uid, String kernelUid, Command c) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException NSAe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
return ERROR;
}
switch (auth) {
case AUTH_NONE:
switch (c.getMechs()) {
case AUTH_ANON:
return OK;
case AUTH_EXTERNAL:
if (0 == col.compare(Uid, c.getData()) &&
(null == kernelUid || 0 == col.compare(Uid, kernelUid)))
return OK;
else
return ERROR;
case AUTH_SHA:
String context = COOKIE_CONTEXT;
long id = System.currentTimeMillis();
byte[] buf = new byte[8];
Message.marshallintBig(id, buf, 0, 8);
challenge = stupidlyEncode(md.digest(buf));
Random r = new Random();
r.nextBytes(buf);
cookie = stupidlyEncode(md.digest(buf));
try {
addCookie(context, "" + id, id / 1000, cookie);
} catch (IOException IOe) {
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe);
}
if (Debug.debug)
Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge);
c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge));
return CONTINUE;
default:
return ERROR;
}
case AUTH_SHA:
String[] response = stupidlyDecode(c.getData()).split(" ");
if (response.length < 2) return ERROR;
String cchal = response[0];
String hash = response[1];
String prehash = challenge + ":" + cchal + ":" + cookie;
byte[] buf = md.digest(prehash.getBytes());
String posthash = stupidlyEncode(buf);
if (Debug.debug)
Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash);
if (0 == col.compare(posthash, hash))
return OK;
else
return ERROR;
default:
return ERROR;
}
}
public String[] getTypes(int types) {
switch (types) {
case AUTH_EXTERNAL:
return new String[]{"EXTERNAL"};
case AUTH_SHA:
return new String[]{"DBUS_COOKIE_SHA1"};
case AUTH_ANON:
return new String[]{"ANONYMOUS"};
case AUTH_SHA + AUTH_EXTERNAL:
return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"};
case AUTH_SHA + AUTH_ANON:
return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"};
case AUTH_EXTERNAL + AUTH_ANON:
return new String[]{"ANONYMOUS", "EXTERNAL"};
case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"};
default:
return new String[]{};
}
}
/**
* performs SASL auth on the given streams.
* Mode selects whether to run as a SASL server or client.
* Types is a bitmask of the available auth types.
* Returns true if the auth was successful and false if it failed.
*/
@SuppressWarnings("unchecked")
public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException {
String username = System.getProperty("user.name");
String Uid = null;
String kernelUid = null;
try {
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
Method m = c.getMethod("getUid");
Object o = c.newInstance();
long uid = (Long) m.invoke(o);
Uid = stupidlyEncode("" + uid);
} catch (Exception e) {
Uid = stupidlyEncode(username);
}
Command c;
int failed = 0;
int current = 0;
int state = INITIAL_STATE;
while (state != AUTHENTICATED && state != FAILED) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH state: " + state);
switch (mode) {
case MODE_CLIENT:
switch (state) {
case INITIAL_STATE:
if (null == us)
out.write(new byte[]{0});
else
us.sendCredentialByte((byte) 0);
send(out, COMMAND_AUTH);
state = WAIT_DATA;
break;
case WAIT_DATA:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_DATA:
switch (do_challenge(current, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
break;
case OK:
send(out, COMMAND_DATA, c.getResponse());
state = WAIT_OK;
break;
case ERROR:
send(out, COMMAND_ERROR, c.getResponse());
break;
}
break;
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
case COMMAND_ERROR:
send(out, COMMAND_CANCEL);
state = WAIT_REJECT;
break;
case COMMAND_OK:
send(out, COMMAND_BEGIN);
state = AUTHENTICATED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_OK:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_OK:
send(out, COMMAND_BEGIN);
state = AUTHENTICATED;
break;
case COMMAND_ERROR:
case COMMAND_DATA:
send(out, COMMAND_CANCEL);
state = WAIT_REJECT;
break;
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
state = WAIT_DATA;
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_REJECT:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_REJECTED:
failed |= current;
int available = c.getMechs() & (~failed);
if (0 != (available & AUTH_EXTERNAL)) {
send(out, COMMAND_AUTH, "EXTERNAL", Uid);
current = AUTH_EXTERNAL;
} else if (0 != (available & AUTH_SHA)) {
send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid);
current = AUTH_SHA;
} else if (0 != (available & AUTH_ANON)) {
send(out, COMMAND_AUTH, "ANONYMOUS");
current = AUTH_ANON;
} else state = FAILED;
break;
default:
state = FAILED;
break;
}
break;
default:
state = FAILED;
}
break;
case MODE_SERVER:
switch (state) {
case INITIAL_STATE:
byte[] buf = new byte[1];
if (null == us) {
in.read(buf);
} else {
buf[0] = us.recvCredentialByte();
int kuid = us.getPeerUID();
if (kuid >= 0)
kernelUid = stupidlyEncode("" + kuid);
}
if (0 != buf[0]) state = FAILED;
else state = WAIT_AUTH;
break;
case WAIT_AUTH:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_AUTH:
if (null == c.getData()) {
send(out, COMMAND_REJECTED, getTypes(types));
} else {
switch (do_response(current, Uid, kernelUid, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
current = c.getMechs();
state = WAIT_DATA;
break;
case OK:
send(out, COMMAND_OK, guid);
state = WAIT_BEGIN;
current = 0;
break;
case REJECT:
send(out, COMMAND_REJECTED, getTypes(types));
current = 0;
break;
}
}
break;
case COMMAND_ERROR:
send(out, COMMAND_REJECTED, getTypes(types));
break;
case COMMAND_BEGIN:
state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_DATA:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_DATA:
switch (do_response(current, Uid, kernelUid, c)) {
case CONTINUE:
send(out, COMMAND_DATA, c.getResponse());
state = WAIT_DATA;
break;
case OK:
send(out, COMMAND_OK, guid);
state = WAIT_BEGIN;
current = 0;
break;
case REJECT:
send(out, COMMAND_REJECTED, getTypes(types));
current = 0;
break;
}
break;
case COMMAND_ERROR:
case COMMAND_CANCEL:
send(out, COMMAND_REJECTED, getTypes(types));
state = WAIT_AUTH;
break;
case COMMAND_BEGIN:
state = FAILED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
case WAIT_BEGIN:
c = receive(in);
switch (c.getCommand()) {
case COMMAND_ERROR:
case COMMAND_CANCEL:
send(out, COMMAND_REJECTED, getTypes(types));
state = WAIT_AUTH;
break;
case COMMAND_BEGIN:
state = AUTHENTICATED;
break;
default:
send(out, COMMAND_ERROR, "Got invalid command");
break;
}
break;
default:
state = FAILED;
}
break;
default:
return false;
}
}
return state == AUTHENTICATED;
}
}
public MessageReader min;
public MessageWriter mout;
public Transport() {
}
public static String genGUID() {
Random r = new Random();
byte[] buf = new byte[16];
r.nextBytes(buf);
String guid = Hexdump.toHex(buf);
return guid.replaceAll(" ", "");
}
public Transport(BusAddress address) throws IOException {
connect(address);
}
public Transport(String address) throws IOException, ParseException {
connect(new BusAddress(address));
}
public Transport(String address, int timeout) throws IOException, ParseException {
connect(new BusAddress(address), timeout);
}
public void connect(String address) throws IOException, ParseException {
connect(new BusAddress(address), 0);
}
public void connect(String address, int timeout) throws IOException, ParseException {
connect(new BusAddress(address), timeout);
}
public void connect(BusAddress address) throws IOException {
connect(address, 0);
}
public void connect(BusAddress address, int timeout) throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Connecting to " + address);
OutputStream out = null;
InputStream in = null;
UnixSocket us = null;
Socket s = null;
int mode = 0;
int types = 0;
if ("unix".equals(address.getType())) {
types = SASL.AUTH_EXTERNAL;
mode = SASL.MODE_CLIENT;
us = new UnixSocket();
if (null != address.getParameter("abstract"))
us.connect(new UnixSocketAddress(address.getParameter("abstract"), true));
else if (null != address.getParameter("path"))
us.connect(new UnixSocketAddress(address.getParameter("path"), false));
us.setPassCred(true);
in = us.getInputStream();
out = us.getOutputStream();
} else if ("tcp".equals(address.getType())) {
types = SASL.AUTH_SHA;
if (null != address.getParameter("listen")) {
mode = SASL.MODE_SERVER;
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
s = ss.accept();
} else {
mode = SASL.MODE_CLIENT;
s = new Socket();
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
}
in = s.getInputStream();
out = s.getOutputStream();
} else {
throw new IOException(getString("unknownAddress") + address.getType());
}
if (!(new SASL()).auth(mode, types, address.getParameter("guid"), out, in, us)) {
out.close();
throw new IOException(getString("errorAuth"));
}
if (null != us) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket");
if (timeout == 1)
us.setBlocking(false);
else
us.setSoTimeout(timeout);
}
if (null != s) {
if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket");
s.setSoTimeout(timeout);
}
mout = new MessageWriter(out);
min = new MessageReader(in);
}
public void disconnect() throws IOException {
if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Transport");
min.close();
mout.close();
}
}