Skip to content

Commit

Permalink
Validate DNS when using S3 virtual-addressing mode
Browse files Browse the repository at this point in the history
Needed to validate S3 bucket names when used in virtual-addressing mode.
Valid DNS hostnames are composed from valid DNS labels. Valid DNS labels
follow the following rules:

1. Have a maximum length of 63 octets (the entirety of the hostname cannot exceed 255 octets)
2. Composed of just alphanumeric characters and dashes
3. Does not start or end with a dash '-'

https://www.ietf.org/rfc/rfc1035.txt
  • Loading branch information
marcomagdy authored May 5, 2017
1 parent 0876431 commit 44b6404
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Release
*~
*#
*.iml
tags

#compiled python files
*.pyc
Expand Down
46 changes: 46 additions & 0 deletions aws-cpp-sdk-core-tests/utils/DnsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#include <aws/external/gtest.h>
#include <aws/core/utils/Dns.h>

using namespace Aws::Utils;

TEST(DnsTest, TestValidDnsLabels)
{
ASSERT_TRUE(IsValidDnsLabel("a"));
ASSERT_TRUE(IsValidDnsLabel("ab"));
ASSERT_TRUE(IsValidDnsLabel("abc"));
ASSERT_TRUE(IsValidDnsLabel("contains-dashes-in-the-middle"));
ASSERT_TRUE(IsValidDnsLabel("123StartsWithNumbers"));
ASSERT_TRUE(IsValidDnsLabel("Ends-With-Numbers123"));
ASSERT_TRUE(IsValidDnsLabel("123StartsAndEndsWithNumbers000"));
ASSERT_TRUE(IsValidDnsLabel("42"));
ASSERT_TRUE(IsValidDnsLabel("012345678901234567890123456789012345678901234567890123456789012")); // 63 characters
}

TEST(DnsTest, TestInvalidDnsLabels)
{
ASSERT_FALSE(IsValidDnsLabel(""));
ASSERT_FALSE(IsValidDnsLabel("has_underscore"));
ASSERT_FALSE(IsValidDnsLabel("trailing-dash-"));
ASSERT_FALSE(IsValidDnsLabel("-starts-with-dash"));
ASSERT_FALSE(IsValidDnsLabel("ends-with-dash-"));
ASSERT_FALSE(IsValidDnsLabel("-starts-and-ends-with-dash-"));
ASSERT_FALSE(IsValidDnsLabel("-"));
ASSERT_FALSE(IsValidDnsLabel("c0nt@in$-$ymb01$"));
ASSERT_FALSE(IsValidDnsLabel("0123456789012345678901234567890123456789012345678901234567890123")); // 64 characters
}
26 changes: 26 additions & 0 deletions aws-cpp-sdk-core/include/aws/core/utils/Dns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

#pragma once

#include <aws/core/utils/memory/stl/AWSString.h>

namespace Aws
{
namespace Utils
{
AWS_CORE_API bool IsValidDnsLabel(const Aws::String& label);
}
}
39 changes: 39 additions & 0 deletions aws-cpp-sdk-core/source/utils/Dns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/




#include <aws/core/utils/DNS.h>
#include <regex>

namespace Aws
{
namespace Utils
{
bool IsValidDnsLabel(const Aws::String& label)
{
// Valid DNS hostnames are composed of valid DNS labels separated by a period.
// Valid DNS labels are characterized by the following:
// 1- Only contains alphanumeric characters and/or dashes
// 2- Cannot start or end with a dash
// 3- Have a maximum length of 63 characters (the entirety of the domain name should be less than 255 bytes)

//TODO: consider making this regex static and passing std::regex_constants::optimize flag
const std::regex dnsLabel("^[[:alnum:]](?:[[:alnum:]-]{0,61}[[:alnum:]])?$");
return regex_search(label, dnsLabel);
}
}
}
38 changes: 36 additions & 2 deletions aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ namespace

static const char* ALLOCATION_TAG = "BucketAndObjectOperationTest";
static const char* BASE_CREATE_BUCKET_TEST_NAME = "awsnativesdkcreatebuckettestbucketa";
static const char* BASE_DNS_UNFRIENDLY_TEST_NAME = "aws.native.sdk.create.bucket.test.bucketa";
static const char* BASE_LOCATION_BUCKET_TEST_NAME = "awsnativesdklocbuckettesta";
static const char* BASE_PUT_OBJECTS_BUCKET_NAME = "awsnativesdkputobjectstestbucketa";
static const char* BASE_PUT_WEIRD_CHARSETS_OBJECTS_BUCKET_NAME = "awsnativesdkcharsetstestbucketa";
Expand All @@ -79,6 +80,7 @@ namespace
static const char* BASE_INTERRUPT_TESTING_BUCKET = "awsnativesdkinterruptbucketa";
static const char* TEST_OBJ_KEY = "TestObjectKey";
static const char* TEST_NOT_MODIFIED_OBJ_KEY = "TestNotModifiedObjectKey";
static const char* TEST_DNS_UNFRIENDLY_OBJ_KEY = "WhySoHostile";
//windows won't let you hard code unicode strings in a source file and assign them to a char*. Every other compiler does and I need to test this.
//to get around this, this string is url encoded version of "TestUnicode中国Key". At test time, we'll convert it to the unicode string
static const char* URLENCODED_UNICODE_KEY = "TestUnicode%E4%B8%AD%E5%9B%BDKey";
Expand Down Expand Up @@ -118,16 +120,21 @@ namespace
config.proxyPort = PROXY_PORT;
}

Client = Aws::MakeShared<S3Client>(ALLOCATION_TAG, Aws::MakeShared<DefaultAWSCredentialsProviderChain>(ALLOCATION_TAG), config, false);
Client = Aws::MakeShared<S3Client>(ALLOCATION_TAG,
Aws::MakeShared<DefaultAWSCredentialsProviderChain>(ALLOCATION_TAG), config,
false /*signPayloads*/, true /*useVirtualAddressing*/);
config.region = Aws::Region::US_WEST_2;
config.useDualStack = true;
oregonClient = Aws::MakeShared<S3Client>(ALLOCATION_TAG, Aws::MakeShared<DefaultAWSCredentialsProviderChain>(ALLOCATION_TAG), config, false);
oregonClient = Aws::MakeShared<S3Client>(ALLOCATION_TAG,
Aws::MakeShared<DefaultAWSCredentialsProviderChain>(ALLOCATION_TAG), config,
false /*signPayloads*/, true /*useVirtualAddressing*/);
m_HttpClient = Aws::Http::CreateHttpClient(config);
}

static void TearDownTestCase()
{
DeleteBucket(CalculateBucketName(BASE_CREATE_BUCKET_TEST_NAME));
DeleteBucket(CalculateBucketName(BASE_DNS_UNFRIENDLY_TEST_NAME));
DeleteBucket(CalculateBucketName(BASE_LOCATION_BUCKET_TEST_NAME));
DeleteBucket(CalculateBucketName(BASE_PUT_OBJECTS_BUCKET_NAME));
DeleteBucket(CalculateBucketName(BASE_PUT_OBJECTS_PRESIGNED_URLS_BUCKET_NAME));
Expand Down Expand Up @@ -949,6 +956,33 @@ namespace
ASSERT_FALSE(getObjectOutcome.IsSuccess());
ASSERT_EQ(Aws::Http::HttpResponseCode::NOT_MODIFIED, getObjectOutcome.GetError().GetResponseCode());
}

TEST_F(BucketAndObjectOperationTest, TestVirtualAddressingWithUnfriendlyBucketName)
{
Aws::String fullBucketName = CalculateBucketName(BASE_DNS_UNFRIENDLY_TEST_NAME);
CreateBucketRequest createBucketRequest;
createBucketRequest.SetBucket(fullBucketName);
createBucketRequest.SetACL(BucketCannedACL::private_);
CreateBucketOutcome createBucketOutcome = Client->CreateBucket(createBucketRequest);
ASSERT_TRUE(createBucketOutcome.IsSuccess());

PutObjectRequest putObjectRequest;
putObjectRequest.SetBucket(fullBucketName);

std::shared_ptr<Aws::IOStream> objectStream = Aws::MakeShared<Aws::StringStream>("BucketAndObjectOperationTest");
*objectStream << "'A program that has not been tested does not work'-- Bjarne Stroustrup";
objectStream->flush();
putObjectRequest.SetBody(objectStream);
putObjectRequest.SetContentLength(static_cast<long>(putObjectRequest.GetBody()->tellp()));
putObjectRequest.SetContentType("text/plain");
putObjectRequest.WithKey(TEST_DNS_UNFRIENDLY_OBJ_KEY);

PutObjectOutcome putObjectOutcome = Client->PutObject(putObjectRequest);
ASSERT_TRUE(putObjectOutcome.IsSuccess());

Aws::String presignedUrlPut = Client->GeneratePresignedUrl(fullBucketName, TEST_DNS_UNFRIENDLY_OBJ_KEY, HttpMethod::HTTP_PUT);
ASSERT_EQ(0ul, presignedUrlPut.find("https://s3.amazonaws.com/" + fullBucketName + "/" + TEST_DNS_UNFRIENDLY_OBJ_KEY));
}
}


Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
Expand All @@ -11,4 +11,4 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
*/
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
\#include <aws/core/client/AWSClient.h>
\#include <aws/core/utils/memory/stl/AWSString.h>
\#include <aws/core/utils/xml/XmlSerializer.h>
\#include <aws/core/utils/DNS.h>
#parse("com/amazonaws/util/awsclientgenerator/velocity/cpp/ServiceClientHeaderModelIncludes.vm")
\#include <aws/core/client/AsyncCallerContext.h>
\#include <aws/core/http/HttpTypes.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Aws::String ${className}::GeneratePresignedUrl(const Aws::String& bucketName, co
Aws::String ${className}::ComputeEndpointString(const Aws::String& bucket) const
{
Aws::StringStream ss;
if(m_useVirtualAdressing)
if(m_useVirtualAdressing && Aws::Utils::IsValidDnsLabel(bucket))
{
ss << m_scheme << "://" << bucket << "." << m_baseUri;
}
Expand Down

0 comments on commit 44b6404

Please sign in to comment.