I'm trying to connect to smtp.live.com using the TcpClient class. There is a wonderful example of connecting to Gmail here: Testing SMTP server is running via C#
Unfortunately, when updating this to work with smtp.live.com, I'm getting an "IOException: The handshake failed due to an unexpected packet format" when the AuthenticateAsClient method is called.
How can I work around this?
class Program
{
static void Main(string[] args)
{
using (var client = new TcpClient())
{
var server = "smtp.live.com";
var port = 25;
client.Connect(server, port);
using (var stream = client.GetStream())
using (var sslStream = new SslStream(stream))
{
// Getting an IOException here
sslStream.AuthenticateAsClient(server);
using (var writer = new StreamWriter(sslStream))
using (var reader = new StreamReader(sslStream))
{
writer.WriteLine("EHLO " + server);
writer.Flush();
Console.WriteLine(reader.ReadLine());
}
}
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
I tried specifying the SslProtocol in AuthenticateAsClient. Neither Tls or Ssl3 worked.
Also tried providing a callback for RemoteCertificateValidation that alway returned true just in case the server cert was invalid. That didn't work either.
NOTE: Please do not suggest that I use SmtpClient; I need more control than it provides.
Thanks to nos for putting me on the right track. The smtp.live.com servers require the following sequence of events:
Connect
HELO - will not accept the STARTTLS until this has been sent
STARTTLS - apparently this sets up the server to accept an encrypted connection
SslStream.AuthenticateAsClient() - this seems to let the C# framework and the SMTP server come to an "understanding" :)
Now that we have an encrypted connection, the usual SMTP commands work
Anyway, this code works for both smtp.live.com AND smtp.gmail.com on port 587:
class Program
{
static void Main(string[] args)
{
const string server = "smtp.live.com";
const int port = 587;
using (var client = new TcpClient(server, port))
{
using (var stream = client.GetStream())
using (var clearTextReader = new StreamReader(stream))
using (var clearTextWriter = new StreamWriter(stream) { AutoFlush = true })
using (var sslStream = new SslStream(stream))
{
var connectResponse = clearTextReader.ReadLine();
if (!connectResponse.StartsWith("220"))
throw new InvalidOperationException("SMTP Server did not respond to connection request");
clearTextWriter.WriteLine("HELO");
var helloResponse = clearTextReader.ReadLine();
if (!helloResponse.StartsWith("250"))
throw new InvalidOperationException("SMTP Server did not respond to HELO request");
clearTextWriter.WriteLine("STARTTLS");
var startTlsResponse = clearTextReader.ReadLine();
if (!startTlsResponse.StartsWith("220"))
throw new InvalidOperationException("SMTP Server did not respond to STARTTLS request");
sslStream.AuthenticateAsClient(server);
using (var reader = new StreamReader(sslStream))
using (var writer = new StreamWriter(sslStream) { AutoFlush = true })
{
writer.WriteLine("EHLO " + server);
Console.WriteLine(reader.ReadLine());
}
}
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
If you want to use ssl on the connection. You should use port 465 for ssl or 587 for tls.
Tip: Try it first without ssl, and add it after you've got things working.
Link: http://www.checktls.com/tests.html to see some starttls examples.
Related
I am trying to gain some traction on secure socket implementation in c#. In my pursuit, I got a feeling about how TLS handshake works by going through Wikipedia and other sources.
I also thought to review some sample code and stumbled on the following https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslstream?redirectedfrom=MSDN&view=netframework-4.7.2
There are two programs(console applications) basically one for client and other for the server, to be able to run the program Main method requires to be fed with some arguments i.e. MachineName, and ServerCertificateName.
Please suggest how to get a hold of a certificate to be able to fulfill the certificate name field.
Here is simple example with 2 threads. You can separate these threads between 2 applications.
static void Main(string[] args)
{
var serverThread = new Thread(() =>
{
var server = new TcpListener(IPAddress.Any, 12345);
server.Start();
var client = server.AcceptTcpClient();
var ssl = new SslStream(client.GetStream());
// certificate file with private key and password
var cert = new X509Certificate2(#"rsa-4096.pfx", "hh87$-Jqo");
ssl.AuthenticateAsServer(cert);
ssl.Write(Encoding.ASCII.GetBytes("Hello world"), 0, 11);
ssl.Flush();
ssl.Close();
server.Stop();
});
var clientThread = new Thread(() =>
{
var client = new TcpClient();
client.Connect(IPAddress.Loopback, 12345);
// last parameter disables certificate validation
var ssl = new SslStream(client.GetStream(), false, (a, b, c, d) => true);
ssl.AuthenticateAsClient("localhost");
using (var sr = new StreamReader(ssl, Encoding.ASCII))
{
string recivedText = sr.ReadToEnd();
Console.WriteLine(recivedText);
}
});
serverThread.Start();
clientThread.Start();
serverThread.Join();
clientThread.Join();
}
You can download example certificate here: https://github.com/Zergatul/ZergatulLib/blob/master/ConsoleTest/rsa-4096.pfx
Disclaimer: this is my first foray into anything directly tcp/socket related. I've read -the- -following- -resources- and am trying to come up with a very simple test application.
I'm trying to develop a local server running with a TcpListener object. I can instantiate it fine and run netstat to see the port in the LISTENING state. However, I can't telnet or create a test client to connect manually. Telnet says simply that it could not open a connection. Trying a test client application throws the exception
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
Additional information: Only one usage of each socket address (protocol/network address/port) is normally permitted
In this SO question, the asker ultimately resolved it via a faulty NIC. How might I look for that?
This leaves me puzzled. How do I ever test or connect to the server? Here's my code.
For the server:
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace TcpConsole
{
class Program
{
static void Main(string[] args)
{
var localPort = ConfigurationManager.AppSettings["localPort"];
var portNumber = int.Parse(localPort);
var maxConnections = ConfigurationManager.AppSettings["maxConnections"];
var maxConnectionsNumber = int.Parse(maxConnections);
Console.WriteLine("Preparing to start server on port {0}", portNumber);
Console.WriteLine("Max connections: {0}", maxConnectionsNumber);
var ipHostInfo = Dns.GetHostEntry("localhost");
var ipAddress = ipHostInfo.AddressList[0];
var localEndPoint = new IPEndPoint(ipAddress, portNumber);
Console.WriteLine("Starting server with local IP {0}", ipAddress);
var listener = new TcpListener(localEndPoint);
listener.Start(maxConnectionsNumber);
Console.WriteLine();
Console.WriteLine("Server started...");
Console.WriteLine();
while (true)
{
var socket = listener.AcceptSocket();
Task.Factory.StartNew(() =>
{
ProcessSocket(socket);
});
}
}
private static async void ProcessSocket(Socket socket)
{
try
{
using (var stream = new NetworkStream(socket))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
var received = await reader.ReadToEndAsync();
Console.WriteLine("Received: " + received);
}
socket.Close();
socket.Dispose();
}
catch (Exception exception)
{
Console.WriteLine("There was an error processing a message.");
Console.WriteLine(exception);
}
}
}
}
Seeing the above code running:
For the test application:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace TcpConsoleClient
{
class Program
{
static void Main(string[] args)
{
var ipHostInfo = Dns.GetHostEntry("localhost");
var ipAddress = ipHostInfo.AddressList[0];
var remoteEndPoint = new IPEndPoint(ipAddress, 3245);
var client = new TcpClient(remoteEndPoint);
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
string input;
while((input = Console.ReadLine()) != "exit")
{
writer.WriteLine(input);
}
}
}
}
}
A TCP/IP connection has a local IP address and port, as well as a remote IP address and port.
Generally, a port number can only be assigned to one socket at a time. The error message indicates that an attempt was made to associate more than one socket with the same port number.
The reason why can be found in the client code:
var client = new TcpClient(remoteEndPoint);
This overload of the TcpClient constructor accepts an IPEndPoint specifying the local endpoint. That is, when the TcpClient is constructed, it will get bound to the port number specified by remoteEndPoint, which fails, because that port number is already in use by the server application.
To fix this, use the parameter-less TcpClient constructor, and instead pass remoteEndPoint to the Connect call:
var client = new TcpClient();
client.Connect(remoteEndPoint);
I am using Visual Studio 2010, .NET 4.0 and I am trying to test the SMTPClient.TimeOut property and actually get it to throw a SmtpException. However, even when I set the TimeOut property to 1 millisecond, it still sends the email which seems like it is sending under a millisecond but what I find interesting is that when I inspect the object I can see a private member variable called timedOut set to true, indicating that it in fact timed out.
Here is my code:
try
{
MailMessage emailMsg = new MailMessage(this.EmailFrom, this.EmailTo, this.EmailSubject, msg);
//SmtpClient emailClient = new SmtpClient(this.EmailServer);
SmtpClient emailClient = new SmtpClient();
emailClient.Credentials = new System.Net.NetworkCredential("username", "password");
emailMsg.IsBodyHtml = true;
emailClient.Timeout = Properties.Settings.Default.SMTPTimeOut;
emailClient.Timeout = 1;
emailClient.Send(emailMsg);
sendingEmail = true;
return sendingEmail;
}
catch (Exception ex)
{
// Handle time out exception here.
}
Has anyone ever seen this or know a better way to test this? Right now I am hitting gmail's smtp.
I believe that you can use telnet. Turn on telnet and use that ip as your smtp server. It would not connect so it should timeout. Have not tested this.
http://technet.microsoft.com/en-us/library/aa995718(v=exchg.65).aspx
To finally test this was working I wrote a very simple TCP server console app on my local machine. I used the following tutorial on how to do this: http://bit.ly/XoHWPC
By creating this console app I was able to take my service which sends email and point it to my local machine (127.0.0.1,25). The TCP Server console app accepted the connection but then I never send a response back.
My service sending the email successfully timed out as expected so I could finally verify it was working properly. Here is a snippet of the TCP Server Console app from the tutorial for a guide. I recommend reading the whole tutorial though if you have time.
Program.cs
using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace TCPServer
{
class Program
{
static void Main(string[] args)
{
Server server = new Server();
}
}
}
Server.cs
using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace TCPServer
{
public class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Server()
{
this.tcpListener = new TcpListener(IPAddress.Any, 25);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
}
tcpClient.Close();
}
}
}
ther is a server and client sockets. I want to learn a way how can I send a List<T>, IEnumerable<T> object to client to server or server to client.
i want to send server-side TaskManager Threads List to client-side. this code send line by line, I want to send complate list. How can I do it?
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
....
....
....
while (true)
{
Socket socket = Listener.AcceptSocket();
try
{
//open stream
Stream stream = new NetworkStream(socket);
StreamReader sr = new StreamReader(stream);
StreamWriter sw = new StreamWriter(stream);
sw.AutoFlush = true;
while (true)
{
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
}
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Disconnected: {0}", socket.RemoteEndPoint);
socket.Close();
socket.Dispose();
}
You can use BinaryFormatter to serialize and deserialze a List.
using System.Runtime.Serialization.Formatters.Binary;
// Server side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
bin.Serialize(stream, TaskManager());
// Client side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
var list = (List<string>)bin.Deserialize(stream);
You could implement all aspects of a solution yourself, i.e. serialisation, compression, sending, receiving etc. Most things can be found by reading about WCF or the System.Net namespace. I can however give you a very concise solution using the network library NetworkComms.Net here.
For the client:
using System;
using NetworkCommsDotNet;
namespace Client
{
class Program
{
static void Main(string[] args)
{
//Create a connection
Connection connection = TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000));
//Make a request for the threadIds and get the answer in one statement.
string[] taskManagerThreadIds = connection.SendReceiveObject<string[]>("ThreadIdRequest", "ThreadIds", 2000);
Console.WriteLine("Server provided an array containing {0} ids", taskManagerThreadIds.Length);
Console.WriteLine("Send completed. Press any key to exit client.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
For the server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using NetworkCommsDotNet;
namespace Server
{
class Program
{
static void Main(string[] args)
{
NetworkComms.AppendGlobalIncomingPacketHandler<object>("ThreadIdRequest", (packetHeader, connection, incomingPlayer) =>
{
//Reply with the requested threadIds
Console.WriteLine("Received thread ID request from {0}.", connection.ToString());
connection.SendObject("ThreadIds", TaskManager().ToArray());
});
//Start listening for incoming TCP Connections
TCPConnection.StartListening(true);
Console.WriteLine("Server ready. Press any key to shutdown server.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
}
}
You will obviously need to download the NetworkComms.Net DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.
Disclaimer: I'm one of the developers for this library.
If you want a reliable and robust solution, use WCF instead of implementing serialization yourself.
Also, writing to stream from parallel threads would not work correctly. Parts of lines from different threads would mix together to some intangible garbage:
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
I am trying to use named pipes to communicate between a server and a client process on the same machine. server sends a message to client, client does something with it and returns a result, and server is supposed to get the result.
here is the code for server:
using System;
using System.IO;
using System.IO.Pipes;
class PipeServer
{
static void Main()
{
using (NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut))
{
Console.WriteLine("NamedPipeServerStream object created.");
// Wait for a client to connect
Console.Write("Waiting for client connection...");
pipeServer.WaitForConnection();
Console.WriteLine("Client connected.");
try
{
// Read user input and send that to the client process.
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
Console.Write("Enter text: ");
sw.WriteLine(Console.ReadLine());
}
pipeServer.WaitForPipeDrain();
using (StreamReader sr = new StreamReader(pipeServer))
{
// Display the read text to the console
string temp;
// Wait for result from the client.
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("[CLIENT] Echo: " + temp);
}
}
}
// Catch the IOException that is raised if the pipe is
// broken or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
}
}
}
and here is the code for client:
using System;
using System.IO;
using System.IO.Pipes;
class PipeClient
{
static void Main(string[] args)
{
using (NamedPipeClientStream pipeClient =
new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut))
{
// Connect to the pipe or wait until the pipe is available.
Console.Write("Attempting to connect to pipe...");
pipeClient.Connect();
Console.WriteLine("Connected to pipe.");
Console.WriteLine("There are currently {0} pipe server instances open.",
pipeClient.NumberOfServerInstances);
using (StreamReader sr = new StreamReader(pipeClient))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
// send the "result" back to the Parent process.
using (StreamWriter sw = new StreamWriter(pipeClient))
{
sw.AutoFlush = true;
sw.WriteLine("Result");
}
pipeClient.WaitForPipeDrain();
}
Console.Write("Press Enter to continue...");
Console.ReadLine();
}
}
But in the server code, on line pipeServer.WaitForPipeDrain(); I get an ObjectDisposedException and it says "cannot access a closed pipe."
I also get the same error in the client code on when setting sw.AutoFlush to true.
Basically I couldn't find an example of duplex named pipe in c#. I either need that, or an example of anonynous pipe, with two pipes one for reading and one for writting between a parent and a child process.
Thanks in Advance.
The Problem is the using block of the StreamWriter, which will close the underlying Stream (which is your pipe here). If you don't use that block it should work.
You could do the following:
using (var pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut))
using (var streamReader = new StreamReader(pipeServer))
using (var streamWriter = new StreamWriter(pipeServer))
{
// ... Your code ..
}
As Johannes Egger pointed out, the StreamWriter flushes the stream on Dispose(), so the StreamWriter should be disposed first and thus be the inner-most object to dispose.