Click here to Skip to main content
Click here to Skip to main content
Go to top

C# REST Client for Amazon Route 53

, 3 Apr 2011
Rate this:
Please Sign up or sign in to vote.
This example implements GET and POST requests for an Amazon Route 53 client in C#.

Introduction

This example implements GET and POST requests for an Amazon Route 53 client in C#. The AWS SDK for .NET does not currently support Route 53, and the Perl script tool, called DNSCurl.pl, doesn't work on Windows without considerable adaptation. This code runs the API methods described in the AWS Route 53 API documentation with XML post data, result data, and error messages.

To use any of the Route 53 API methods, you have to create an authentication signature. The Route 53 signature is based on the date and the user’s access key. The following method encrypts the signature using the HMACSHA1 algorithm. The method gets the formatted date as an input parameter from a method described with those following this method. An instance of the System.Security.Cryptography.HMACSHA1 class does the work as a variable named MySigner. After the signature is created, it is base 64 encoded using a static method of the Convert class.

public static string GetAWSR53_SHA1AuthorizationValue(string AWSAccessKeyId, 
              string AWSSecretAccessKey, string AmzDate) 
{ 
    System.Security.Cryptography.HMACSHA1 MySigner = 
       new System.Security.Cryptography.HMACSHA1(
       System.Text.Encoding.UTF8.GetBytes(AWSSecretAccessKey)); 

    string SignatureValue = Convert.ToBase64String(
      MySigner.ComputeHash(System.Text.Encoding.UTF8.GetBytes(AmzDate))); 

    string AuthorizationValue =  "AWS3-HTTPS AWSAccessKeyId=" + 
       System.Uri.EscapeDataString(AWSAccessKeyId) + 
       ",Algorithm=HmacSHA1,Signature=" + SignatureValue; 

    return AuthorizationValue;
}

The date is obtained from the Amazon Route 53 API by the following unsecured method:

public static string GetRoute53Date() 
{ 
    string url = "https://route53.amazonaws.com/date"; 
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
    request.Method = "GET"; 
    HttpWebResponse response; 
    response = request.GetResponse() as HttpWebResponse; 
    return response.Headers["Date"]; 
}

The code for a complete client project is shown below. Each method is a REST implementation that interacts with an AWS Route 53 API method described in the Amazon Web Services documentation. The most interesting in the group is the method ChangeResourceRecordSet, which requires POST data as described in the API documentation topic. To use this method, or for that matter any of these methods, some research into the Domain Name System (DNS) will be helpful. Amazon’s implementation of ChangeResourceRecordSet uses XML data that is identified by the last parameter of this method, postFile. The data is sent as an HttpWebRequest POST. One thing that can create problems in this procedure is that the request may be initialized automatically to expect a 100 response before it sends the data. This code works around this by inserting this line of code:

request.ServicePoint.Expect100Continue = false;

Beyond that, the POST sets up the headers, including the Route 53 date and authentication signature from the methods described above. The POST data is read into a byte array and then into the request stream by the following code segment:

Stream streamPostData = request.GetRequestStream(); 
streamPostData.Write(data, 0, data.Length); 
streamPostData.Close();

The request is attempted and response obtained, if successful, inside the try block. The response or an exception is displayed by the console.

public static void ChangeResourceRecordSet(string accessKeyId, 
       string secretKey, string zoneId, string postFile) 
{ 
    string xmlText = System.IO.File.ReadAllText(postFile); 
    UTF8Encoding encoder = new UTF8Encoding(); 
    byte[] data = encoder.GetBytes(xmlText); 

    string url = "https://route53.amazonaws.com/2010-10-01/hostedzone/" + 
                 zoneId + "/rrset"; 

    Uri uri = new Uri(url); 
    HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest; 
    request.ServicePoint.Expect100Continue = false; 

    request.Method = "POST"; 
    request.ContentType = "text/xml"; 
    request.ContentLength = data.Length; 

    WebHeaderCollection headers = request.Headers; 
    string httpDate = GetRoute53Date(); 
    headers.Add("x-amz-date", httpDate); 
    string authenticationSig = 
    GetAWSR53_SHA1AuthorizationValue(accessKeyId, secretKey, httpDate); 
    headers.Add("X-Amzn-Authorization", authenticationSig); 

    Stream streamPostData = request.GetRequestStream(); 
    streamPostData.Write(data, 0, data.Length); 
    streamPostData.Close(); 

    try 
    { 
        WebResponse response = request.GetResponse(); 
        Stream stream = response.GetResponseStream() as Stream; 
        StreamReader streamReader = new StreamReader(stream); 
        string responseString = streamReader.ReadToEnd(); 
        System.Console.Write(responseString); 

        stream.Close(); 

        response.Close();
    } 
    catch (WebException ex) 
    { 
        Stream stream = ex.Response.GetResponseStream(); 
        byte[] errorBuffer = new byte[stream.Length]; 
        stream.Read(errorBuffer, 0, (Int32)stream.Length); 

        UTF8Encoding encoding = new UTF8Encoding(); 
        Console.Write(encoding.GetString(errorBuffer)); 
    } 
}

The complete client code is shown below. All the methods are explained further in the AWS Route 53 API documentation.

using System; 
using System.Text; 
using System.Net; 
using System.IO; 
using System.Security.Cryptography; 
using System.Configuration; 
using System.Collections.Generic; 
using System.Collections.Specialized; 
using System.Xml; 
using System.Web; 

namespace Route53REST 
{ 

    class Program 
    { 
        static void Main(string[] args) 
        { 
            NameValueCollection appConfig = ConfigurationManager.AppSettings; 
            string accessKey = appConfig["gmdAccessKey"]; 
            string secretKey = appConfig["gmdSecretKey"]; 

            //GetHostedZoneData(accessKey, secretKey, "Z3RBMCKQL808BN"); 
            //ListResourceRecordSets(accessKey, secretKey,
            //           "Z34VXO1XXCAVM9", "mcle-carl.com"); 
            //GetResourceRecordSet(accessKey, secretKey,
            //           "Z3IJVPDYHW4PIQ", "integral-design.net"); 
            //GetResourceRecordSet(accessKey, secretKey,
            //           " Z3RBMCKQL808BN ", "mcle-grant.net"); 
            ChangeResourceRecordSet(accessKey, secretKey,
                         "Z34VXCAVO1XXM9", "ChangeData3.xml"); 
            //CreateHostedZone(accessKey, secretKey,
            //           "AddHostedZoneMcleGrantNet.xml"); 
            //System.Console.Write(GetChangeStatus(accessKey,
            //           secretKey, "C3P9ATRH5BN74U")); 
            //System.Console.Write(GetChangeStatus(accessKey, 
            //           secretKey, "C15YPPE6CYFTRV")); 
            Console.Read(); 
        } 

        public static void ChangeResourceRecordSet(string accessKeyId, 
                           string secretKey, string zoneId, string postFile) 
        { 
            string xmlText = System.IO.File.ReadAllText(postFile); 
            UTF8Encoding encoder = new UTF8Encoding(); 
            byte[] data = encoder.GetBytes(xmlText); 
            string url = "https://route53.amazonaws.com/2010-10-01/hostedzone/" + 
                         zoneId + "/rrset"; 
            Uri uri = new Uri(url); 
            HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest; 
            request.ServicePoint.Expect100Continue = false; 
            request.Method = "POST"; 
            request.ContentType = "text/xml"; 
            request.ContentLength = data.Length; 
            WebHeaderCollection headers = request.Headers; 
            string httpDate = GetRoute53Date(); 
            headers.Add("x-amz-date", httpDate); 
            string authenticationSig = GetAWSR53_SHA1AuthorizationValue(
                                          accessKeyId, secretKey, httpDate); 
            headers.Add("X-Amzn-Authorization", authenticationSig); 
            Stream streamPostData = request.GetRequestStream(); 
            streamPostData.Write(data, 0, data.Length); 
            streamPostData.Close(); 
            
            try 
            { 
                WebResponse response = request.GetResponse(); 
                Stream stream = response.GetResponseStream() as Stream; 
                StreamReader streamReader = new StreamReader(stream); 
                string responseString = streamReader.ReadToEnd(); 
                System.Console.Write(responseString); 
                stream.Close(); 
                response.Close(); 
            } 
            catch (WebException ex) 
            { 
                Stream stream = ex.Response.GetResponseStream(); 
                byte[] errorBuffer = new byte[stream.Length]; 
                stream.Read(errorBuffer, 0, (Int32)stream.Length); 
                UTF8Encoding encoding = new UTF8Encoding(); 
                Console.Write(encoding.GetString(errorBuffer)); 
            } 
        } 

        public static string GetChangeStatus(string accessKeyId, 
               string secretKey, string changeId) 
        { 
            string url = "https://route53.amazonaws.com/2010-10-01/change/" + changeId; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
            request.Method = "GET"; 
            WebHeaderCollection headers = (request as HttpWebRequest).Headers; 

            string httpDate = GetRoute53Date(); 
            headers.Add("x-amz-date", httpDate); 
            string authenticationSig = GetAWSR53_SHA1AuthorizationValue(
                   accessKeyId, secretKey, httpDate); 
            headers.Add("X-Amzn-Authorization", authenticationSig); 
            HttpWebResponse response = request.GetResponse() as HttpWebResponse; 

            // read the response stream and put it into a byte array 
            Stream stream = response.GetResponseStream() as Stream; 
            byte[] buffer = new byte[32 * 1024]; 
            int nRead = 0; 

            MemoryStream ms = new MemoryStream(); 
            do 
            { 
                nRead = stream.Read(buffer, 0, buffer.Length); 
                ms.Write(buffer, 0, nRead); 
            } while (nRead > 0); 

             // Convert bytes to string. 
             ASCIIEncoding encoding = new ASCIIEncoding(); 
             string responseString = encoding.GetString(ms.ToArray()); 
             return responseString; 
        } 

        public static void CreateHostedZone(string accessKeyId, 
                      string secretKey, string postFile) 
        { 
            StringBuilder strBuilder = new StringBuilder(); 
            foreach (string line in File.ReadAllLines(postFile, Encoding.Default)) 
                strBuilder.Append(line); 

             string hostedZoneXml = strBuilder.ToString(); 
             XmlDocument xmlPostData = new XmlDocument(); 
             xmlPostData.LoadXml(hostedZoneXml); 

             // Get post data as byte[]. 
             byte[] buffer = Encoding.ASCII.GetBytes(xmlPostData.ToString()); 

            string url = "https://route53.amazonaws.com/2010-10-01/hostedzone"; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
            request.Method = "POST"; 
            request.ContentType = "text/xml"; 
            request.ContentLength = buffer.Length; 

            Stream postData = request.GetRequestStream(); 
            // Write the stream. 
            postData.Write(buffer, 0, buffer.Length); 

            WebHeaderCollection headers = (request as HttpWebRequest).Headers; 
            string httpDate = GetRoute53Date(); 
            headers.Add("x-amz-date", httpDate); 

            string authenticationSig = 
              GetAWSR53AuthorizationValue(accessKeyId, secretKey, httpDate); 
            headers.Add("X-Amzn-Authorization", authenticationSig); 
            HttpWebResponse response = request.GetResponse() as HttpWebResponse; 

            // Read the response stream and put it into a byte array 
            Stream stream = response.GetResponseStream() as Stream; 
            byte[] bufferResponse = new byte[32 * 1024]; 
            int nRead = 0; 

             MemoryStream ms = new MemoryStream(); 
             do 
             { 
                 nRead = stream.Read(bufferResponse, 0, bufferResponse.Length); 
                 ms.Write(bufferResponse, 0, nRead); 
             } while (nRead > 0); 

            // Convert bytes to string. 
            ASCIIEncoding encoding = new ASCIIEncoding(); 
            string responseString = encoding.GetString(ms.ToArray()); 
            System.Console.Write(responseString); 

            postData.Close(); 
            stream.Close(); 
        } 

        public static string GetAWSR53AuthorizationValue(string AWSAccessKeyId, 
               string AWSSecretAccessKey, string AmzDate) 
        {
            System.Security.Cryptography.HMACSHA256 MySigner = 
              new System.Security.Cryptography.HMACSHA256(
              System.Text.Encoding.UTF8.GetBytes(AWSSecretAccessKey)); 

            string SignatureValue = Convert.ToBase64String(
              MySigner.ComputeHash(System.Text.Encoding.UTF8.GetBytes(AmzDate))); 

            string AuthorizationValue =  "AWS3-HTTPS AWSAccessKeyId=" + 
              System.Uri.EscapeDataString(AWSAccessKeyId) + 
              ",Algorithm=HmacSHA256,Signature=" + 
              SignatureValue; 
            return AuthorizationValue; 
        } 

        public static void ListResourceRecordSets(string awsKeyId, string secretKey, 
               string zoneId, string domainName) 
        { 
            string url = "https://route53.amazonaws.com/2010-10-01/hostedzone/" + 
                         zoneId + "/rrset?type=A&name=" + domainName + "&maxitems=15"; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 

            request.Method = "GET"; 
            WebHeaderCollection headers = (request as HttpWebRequest).Headers; 

            // DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss ") + "GMT"; 
            string httpDate = GetRoute53Date();
            headers.Add("x-amz-date", httpDate); 
            string authenticationSig = 
              GetAWSR53_SHA1AuthorizationValue(awsKeyId, secretKey, httpDate); 
            headers.Add("X-Amzn-Authorization", authenticationSig); 
            HttpWebResponse response = request.GetResponse() as HttpWebResponse; 

             // read the response stream and put it into a byte array 
            Stream stream = response.GetResponseStream() as Stream; 
            byte[] buffer = new byte[32 * 1024]; 
            int nRead = 0; 

            MemoryStream ms = new MemoryStream(); 
            do 
            { 
                nRead = stream.Read(buffer, 0, buffer.Length); 
                ms.Write(buffer, 0, nRead); 
            } while (nRead > 0); 

            // Convert bytes to string. 
            ASCIIEncoding encoding = new ASCIIEncoding(); 
            string responseString = encoding.GetString(ms.ToArray()); 
            System.Console.Write(responseString); 
        } 

        public static void GetHostedZoneData(string awsId, string secretId, string zoneId) 
        { 
            string url = "https://route53.amazonaws.com/2010-10-01/hostedzone/" + zoneId ; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
            request.Method = "GET"; 
            WebHeaderCollection headers = (request as HttpWebRequest).Headers; 

            // DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss ") + "GMT"; 
            string httpDate = GetRoute53Date();
            headers.Add("x-amz-date", httpDate); 
            string authenticationSig = 
              GetAWSR53_SHA1AuthorizationValue(awsId, secretId, httpDate); 

            headers.Add("X-Amzn-Authorization", authenticationSig); 
            HttpWebResponse response = request.GetResponse() as HttpWebResponse; 

            // read the response stream 
            Stream stream = response.GetResponseStream() as Stream; 
            string responseString = new StreamReader(stream).ReadToEnd(); 
            System.Console.Write(responseString); 
        } 

        public static void ListHostedZones(string awsId, string secretId) 
        { 
            string url = "https://route53.amazonaws.com/2010-10-01/hostedzone"; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
            request.Method = "GET"; 
            WebHeaderCollection headers = (request as HttpWebRequest).Headers; 

            // the canonical string is the date string 
            string httpDate = GetRoute53Date();
            // DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss ") + "GMT"; 

            headers.Add("x-amz-date", httpDate); 

            // Both the following methods work! 
            string authenticationSig = 
                GetAWSR53_SHA1AuthorizationValue(awsId, secretId, httpDate); 
            //string authenticationSig = 
            //  GetAWSR53AuthorizationValue(awsId, secretId, httpDate); 
            headers.Add("X-Amzn-Authorization", authenticationSig); 

            HttpWebResponse response = request.GetResponse() as HttpWebResponse; 

            // read the response stream and put it into a byte array 
            Stream stream = response.GetResponseStream() as Stream; 
            byte[] buffer = new byte[32 * 1024]; 
            int nRead = 0; 
 
            MemoryStream ms = new MemoryStream(); 
            do 
            { 
                nRead = stream.Read(buffer, 0, buffer.Length); 
                ms.Write(buffer, 0, nRead); 
            } while (nRead > 0); 

            // Convert bytes to string. 
            ASCIIEncoding encoding = new ASCIIEncoding(); 
            string responseString = encoding.GetString(ms.ToArray()); 
            System.Console.Write(responseString); 
        } 

         public static string GetAWSR53_SHA1AuthorizationValue(string AWSAccessKeyId, 
                       string AWSSecretAccessKey, string AmzDate) 
        { 
            System.Security.Cryptography.HMACSHA1 MySigner = 
              new System.Security.Cryptography.HMACSHA1(
              System.Text.Encoding.UTF8.GetBytes(AWSSecretAccessKey)); 

            string SignatureValue = Convert.ToBase64String(
              MySigner.ComputeHash(System.Text.Encoding.UTF8.GetBytes(AmzDate))); 

            string AuthorizationValue =  "AWS3-HTTPS AWSAccessKeyId=" + 
              System.Uri.EscapeDataString(AWSAccessKeyId) + 
              ",Algorithm=HmacSHA1,Signature=" + SignatureValue; 

            return AuthorizationValue; 
        } 

        public static string GetRoute53Date() 
        { 
            string url = "https://route53.amazonaws.com/date"; 
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
            request.Method = "GET"; 
            HttpWebResponse response; 
            response = request.GetResponse() as HttpWebResponse; 
            return response.Headers["Date"]; 
        } 
    } 
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael Dodaro
Technical Writer Steyer Associates at Microsoft Research
United States United States
http://www.iis.net/community/default.aspx?tabid=320&g=6&CreatedBy=mike%20dodaro
 
http://mikedodaro.net
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmvpKanasz Robert27-Sep-12 8:29 
QuestionHTTP Requests PinmemberCorwinTomkinson25-Jan-12 1:46 
AnswerRe: HTTP Requests PinmemberMichael Dodaro25-Jan-12 6:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 3 Apr 2011
Article Copyright 2011 by Michael Dodaro
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid