Ysgrifennodd Jon Skeet [C# MVP]:
>
Hi Jon. Thanks for replying. My comments below ...
>
Is there a public server we can easily contact to try this out?
Yes. At least for this part of the operation it can be considered
public. What I'll do is append all my code below this email. It's
fairly tidy apart from the business I'm currently stuck with. The
details about the server are contained in the App.config file.
Note that in RFC 4934 I can't see anything suggesting it will be in
ASCII - just that it's XML. Might it be UTF-16 encoding?
I don't *think* so. RFC4939 says:
"All XML instances SHOULD begin with an <?xml?declarati on to
identify the version of XML that is being used, optionally identify
use of the character encoding used, and optionally provide a hint to
an XML parser that an external schema file is needed to validate the
XML instance. Conformant XML parsers recognize both UTF-8 (defined
in RFC 3629 [RFC3629]) and UTF-16 (defined in RFC 2781 [RFC2781]);
per RFC 2277 [RFC2277] UTF-8 is the RECOMMENDED character encodingfor
use with EPP."
So there is just a chance it's utf-16. I'll check with the registry
(centralnic).
Using BitConverter directly is definitely a bad idea - the RFC says it
will be in big endian format. I have an EndianBitConver ter which lets
you specify endianness as part of MiscUtil:
http://pobox.com/~skeet/csharp/miscutil
But yes, that number looks rather large.
Thanks. I'll look at your converter and see if I can understand it.
If you want me to post the entire solution to you privately, I can do
so, but I doubt you need that to see what's going on. It would just
allow you to immediately compile and run the code.
Many thanks
Peter
Here's the code:
///// this is the EppClient class that does all the work
/*
* This file is part of EPP.NET.
*
* EppClient is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EppClient 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EppClient. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Peter Bradley, 2008
*/
using System;
using System.Collecti ons.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sock ets;
using System.Net.Secu rity;
using System.Security .Cryptography.X 509Certificates ;
using System.Configur ation;
using System.Resource s;
using System.Reflecti on;
using System.Security .Authentication ;
namespace uk.co.special.E pp
{
/// <summary>
/// EppClient is a class to interact with an EPP server
/// The EPP protocol is set out in IETF RFC4930
/// </summary>
public class EppClient
{
// Private instance variables
string serverName = null;
int portNumber = 0;
X509Certificate serverCertifica te = null;
X509Certificate Collection certificates = new
X509Certificate Collection();
//---------- Constructors --------------//
/// <summary>
/// Default constructor for EppClient
/// </summary>
public EppClient() : this(null, 700, null)
{
// Default constructor
}
/// <summary>
/// Constructor with all necessary parameters to build an
EppClient object
/// </summary>
/// <param name="serverNam e">
/// The name of the EPP server
/// </param>
/// <param name="portNumbe r">
/// The port number on which to connect (default = 700)
/// </param>
/// <param name="serverCer tificate">
/// The server certificate to use for authentication
/// </param>
public EppClient(strin g serverName, int portNumber,
X509Certificate serverCertifica te)
{
this.serverName = serverName;
this.portNumber = portNumber;
this.serverCert ificate = serverCertifica te;
certificates.Ad d(this.serverCe rtificate);
}
//---------------- Properties ----------------//
/// <summary>
/// Get or set the name of the EPP server
/// </summary>
public string ServerName
{
get
{
return serverName;
}
set
{
serverName = value;
}
}
/// <summary>
/// Get or set the port number on which to connect to the EPP
server
/// </summary>
public int PortNumber
{
get
{
return portNumber;
}
set
{
portNumber = value;
}
}
/// <summary>
/// Get or set the X509 certificate to use for authentication
/// </summary>
public X509Certificate ServerCertifica te
{
get
{
return serverCertifica te;
}
set
{
serverCertifica te = value;
certificates.Ad d(value);
}
}
//-------------- public methods ----------------//
/// <summary>
/// Connect and authenticate to an EPP server
/// </summary>
public void Connect()
{
if (String.IsNullO rEmpty(serverNa me))
throw new EppException("T he server name has not been set");
if (portNumber == 0)
throw new EppException("T he port number has not been set");
if (certificates.C ount == 0)
throw new EppException("N o X509 certificate has been
supplied for authentication" );
TcpClient client = null;
try
{
client = new TcpClient(serve rName, portNumber);
SslStream sslStream = new SslStream(clien t.GetStream(),
false,
new
RemoteCertifica teValidationCal lback(Certifica teValidationCal lback),
null);
sslStream.Authe nticateAsClient (serverName,
certificates,
SslProtocols.De fault,
false);
ReadResponse(cl ient);
}
catch (ArgumentNullEx ception ane)
{
throw new EppException( "Server name or SslStream inner
stream " +
" is a null reference. " +
"\nExceptio n message is: " +
ane.Message);
}
catch (ArgumentOutOfR angeException aoore)
{
throw new EppException( "Port number is not between
MinPort and MaxPort. " +
"\nExceptio n message is: " +
aoore.Message);
}
catch (SocketExceptio n se)
{
throw new EppException( "An error occurred when
accessing the socket. " +
"See the Remarks section for
more information. " +
"\nExceptio ns message is: " +
se.Message);
}
catch (ArgumentExcept ion ae)
{
throw new EppException( "Inner SslStream stream is
either not readable " +
"or not writable." +
"\nExceptio n message is: " +
ae.Message);
}
} //End of Connect()
//------------- private methods -----------------//
private bool CertificateVali dationCallback( object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors
sslPolicyErrors )
{
if (sslPolicyError s == SslPolicyErrors .None)
{
return true;
}
else
{
if (sslPolicyError s ==
SslPolicyErrors .RemoteCertific ateChainErrors)
{
throw new EppException("T he X509Chain.Chain Status
returned an array " +
"of X509ChainStatus objects containing error
information.");
}
else if (sslPolicyError s ==
SslPolicyErrors .RemoteCertific ateNameMismatch )
{
throw new EppException("T here was a mismatch of the
name " +
"on a certificate.");
}
else if (sslPolicyError s ==
SslPolicyErrors .RemoteCertific ateNotAvailable )
{
throw new EppException("N o certificate was
available.");
}
else
{
throw new EppException("S SL Certificate Validation
Error!");
}
}
} // End of CertificateVali dationCallback( )
private string ReadResponse(Tc pClient client)
{
NetworkStream stream = client.GetStrea m();
if (stream.CanRead )
{
byte[] buffer = new byte[4]; // Big enough for the size
int read = stream.Read(buf fer, 0, buffer.Length);
int length = BitConverter.To Int32(buffer, 0);
StringBuilder response = new StringBuilder() ;
buffer = new byte[1024];
do
{
read = stream.Read(buf fer, 0, buffer.Length);
response.Append Format("{0}",
Encoding.ASCII. GetString(buffe r, 0, read));
}
while (stream.DataAva ilable);
return response.ToStri ng();
}
else
{
throw new EppException("U nable to read the network
stream");
}
} // End of ReadResponse()
} // End of EppClient class
} // End of uk.org.special. Epp namespace
/////--------------------------------------------------------
//// this is the UI class that just calls the EppClient
//// methods. It does deal with the response because
//// I'm just in the process of starting to deal with it
/*
* This file is part of EPP.NET.
*
* EppClient is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EppClient 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EppClient. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Peter Bradley, 2008
*/
using System;
using System.Collecti ons.Generic;
using System.Linq;
using System.Text;
using System.Configur ation;
using System.Security .Cryptography;
using System.Security .Cryptography.X 509Certificates ;
namespace uk.co.special.E pp
{
class EppClientConsol eUI
{
static void Main(string[] args)
{
EppClientConsol eUI ui = new EppClientConsol eUI();
ui.Run();
}
/// <summary>
/// Run the UI. The UI creates an instance of an EppClient and
exercises its methods.
/// </summary>
public void Run()
{
try
{
EppClient eppClient = new EppClient(
ConfigurationMa nager.AppSettin gs.Get("server-name"),
Convert.ToInt32 (ConfigurationM anager.AppSetti ngs.Get("port-number")),
X509Certificate .CreateFromCert File(Configurat ionManager.AppS ettings.Get("ce rt-file-name")));
eppClient.Conne ct();
Console.WriteLi ne("Connected and authenticated
successfully ...");
Console.Write(" \nPress [Return] to continue ... ");
Console.ReadLin e();
}
catch (EppException ee)
{
Console.WriteLi ne(ee.Message);
Console.Write(" \nPress [Return] to continue ... ");
Console.ReadLin e();
}
catch (CryptographicE xception ce)
{
Console.WriteLi ne(ce.Message);
Console.Write(" \nPress [Return] to continue ... ");
Console.ReadLin e();
}
} // End Run()
}
}
////---------------------------------------------------------
//// This is the App.config file ...
<?xml version="1.0" encoding="utf-8" ?>
<configuratio n>
<appSettings>
<add key="server-name" value="epp-ote.centralnic. com" />
<add key="port-number" value="700" />
<add key="cert-file-name" value="Thawte_S SL_Domain_CA.ce r"/>
</appSettings>
</configuration>
/////---------------------------------------------------------
//// And here's the custom exception class for completeness
/*
* This file is part of EPP.NET.
*
* EppClient is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EppClient 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EppClient. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Peter Bradley, 2008
*/
using System;
using System.Collecti ons.Generic;
using System.Linq;
using System.Text;
using System.Runtime. Serialization;
namespace uk.co.special.E pp
{
/// <summary>
/// Custom EppException class
/// </summary>
public class EppException : Exception
{
/// <summary>
/// Create a new instance of a EppException
/// </summary>
public EppException()
{
}
/// <summary>
/// Create a new instance of a EppException, specifying the message
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
public EppException(st ring message) : base (message)
{
}
/// <summary>
/// Create a new instance of a EppException, specifying the message
/// and the inner Exception
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="innerExce ption">
/// The exception that is the cause of the current exception.
/// If the innerException parameter is not a null reference,
/// the current exception is raised in a catch block that handles
/// the inner exception.
/// </param>
public EppException(st ring message, Exception innerException)
: base (message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the EppException class, with
/// serialized data.
/// </summary>
/// <param name="info">
/// The SerializationIn fo that holds the serialized object data
about
/// the exception being thrown.
/// </param>
/// <param name="context">
/// The StreamingContex t that contains contextual information
about the
/// source or destination.
/// </param>
protected EppException(Se rializationInfo info, StreamingContex t
context)
: base(info, context)
{
}
}
}