void RunEnum(bool cmdClient, int version, int suite, int curve, int hs) { IDictionary <string, object> d = new SortedDictionary <string, object>( StringComparer.Ordinal); d["name"] = TEnumToString(version, suite, curve, hs); d["versionMin"] = SSL.VersionName(version); d["versionMax"] = SSL.VersionName(version); d["cipherSuites"] = new string[] { SSL.CipherSuiteName(suite) }; if (curve >= 0) { d["curves"] = new string[] { SSL.CurveName(curve) }; d["hashAndSigns"] = new string[] { SSL.HashAndSignName(hs) }; } else { d["curves"] = new string[0]; d["hashAndSigns"] = new string[0]; } if (SSL.IsRSA(suite) || SSL.IsECDHE_RSA(suite)) { d["serverCertType"] = "RSA"; } if (SSL.IsECDH(suite) || SSL.IsECDHE_ECDSA(suite)) { d["serverCertType"] = "EC"; } RunTest(cmdClient, d); }
void Run(string[] args) { bool verbose = true; bool trace = false; string host = null; string sni = null; List <string> csNames = null; List <string> hsNames = null; int vmin = 0; int vmax = 0; for (int i = 0; i < args.Length; i++) { string a = args[i]; if (!a.StartsWith("-")) { if (host != null) { throw new Exception( "duplicate host name"); } host = a; continue; } a = a.Substring(1).ToLowerInvariant(); switch (a) { case "v": verbose = true; break; case "q": verbose = false; break; case "trace": trace = true; break; case "sni": if (sni != null) { throw new Exception( "duplicate SNI"); } if (++i >= args.Length) { throw new Exception( "no SNI provided"); } sni = args[i]; break; case "nosni": if (sni != null) { throw new Exception( "duplicate SNI"); } sni = ""; break; case "cs": if (++i >= args.Length) { throw new Exception( "no cipher names provided"); } if (csNames == null) { csNames = new List <string>(); } AddNames(csNames, args[i]); break; case "hs": if (++i >= args.Length) { throw new Exception( "no hash-and-sign provided"); } if (hsNames == null) { hsNames = new List <string>(); } AddNames(hsNames, args[i]); break; case "vmin": if (vmin != 0) { throw new Exception( "duplicate minimum version"); } if (++i >= args.Length) { throw new Exception( "no minimum version provided"); } vmin = SSL.GetVersionByName(args[i]); break; case "vmax": if (vmax != 0) { throw new Exception( "duplicate maximum version"); } if (++i >= args.Length) { throw new Exception( "no maximum version provided"); } vmax = SSL.GetVersionByName(args[i]); break; default: throw new Exception(string.Format( "Unknown option: '-{0}'", a)); } } if (host == null) { throw new Exception("no host name provided"); } int j = host.LastIndexOf(':'); int port; if (j < 0) { port = 443; } else { if (!Int32.TryParse(host.Substring(j + 1), out port) || port <= 0 || port > 65535) { throw new Exception("invalid port number"); } host = host.Substring(0, j); } if (sni == null) { sni = host; } int[] css = null; if (csNames != null) { css = new int[csNames.Count]; for (int i = 0; i < css.Length; i++) { css[i] = SSL.GetSuiteByName(csNames[i]); } } int[] hss = null; if (hsNames != null) { hss = new int[hsNames.Count]; for (int i = 0; i < hss.Length; i++) { hss[i] = SSL.GetHashAndSignByName(hsNames[i]); } } if (vmin != 0 && vmax != 0 && vmin > vmax) { throw new Exception("invalid version range"); } /* * Connect to the designated server. */ TcpClient tc = new TcpClient(host, port); Socket sock = tc.Client; Stream ns = tc.GetStream(); if (trace) { MergeStream ms = new MergeStream(ns, ns); ms.Debug = Console.Out; ns = ms; } SSLClient ssl = new SSLClient(ns); if (sni != "") { ssl.ServerName = sni; } if (css != null) { ssl.SupportedCipherSuites = css; } if (hss != null) { ssl.SupportedHashAndSign = hss; } if (vmin != 0) { ssl.VersionMin = vmin; } if (vmax != 0) { ssl.VersionMax = vmax; } /* * This is a debug tool; we accept the server certificate * without validation. */ ssl.ServerCertValidator = SSLClient.InsecureCertValidator; /* * Force a Flush. There is no application data to flush * at this point, but as a side-effect it forces the * handshake to complete. */ ssl.Flush(); if (verbose) { Console.WriteLine("Handshake completed:"); Console.WriteLine(" Version = {0}", SSL.VersionName(ssl.Version)); Console.WriteLine(" Cipher suite = {0}", SSL.CipherSuiteName(ssl.CipherSuite)); } /* * Now relay data back and forth between the connection * and the console. Since the underlying SSL stream does * not support simultaneous reads and writes, we use * the following approximation: * * - We poll on the socket for incoming data. When there * is some activity, we assume that some application * data (or closure) follows, and we read it. It is * then immediately written out (synchronously) on * standard output. * * - When waiting for read activity on the socket, we * regularly (every 200 ms) check for data to read on * standard input. If there is, we read it, and send * it synchronously on the SSL stream. * * - The data reading from console is performed by * another thread. * * Since SSL records are read one by one, we know that, * by using a buffer larger than 16 kB, a single Read() * call cannot leave any buffered application data. */ ssl.CloseSub = false; Thread t = new Thread(new ThreadStart(CRThread)); t.IsBackground = true; t.Start(); byte[] buf = new byte[16384]; Stream stdout = Console.OpenStandardOutput(); for (;;) { if (sock.Poll(200000, SelectMode.SelectRead)) { int rlen = ssl.Read(buf, 0, buf.Length); if (rlen < 0) { Console.WriteLine( "Connection closed.\n"); break; } stdout.Write(buf, 0, rlen); } else { while (CRHasData()) { int rlen = CRRead(buf, 0, buf.Length); if (rlen < 0) { ssl.Close(); break; } if (rlen > 0) { ssl.Write(buf, 0, rlen); } } } } sock.Close(); }