• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.services.s3.internal;
17 
18 import java.util.regex.Pattern;
19 import software.amazon.awssdk.annotations.SdkInternalApi;
20 
21 /**
22  * Utilities for working with Amazon S3 bucket names, such as validation and
23  * checked to see if they are compatible with DNS addressing.
24  */
25 @SdkInternalApi
26 public final class BucketUtils {
27 
28     private static final int MIN_BUCKET_NAME_LENGTH = 3;
29     private static final int MAX_BUCKET_NAME_LENGTH = 63;
30 
31     private static final Pattern IP_ADDRESS_PATTERN = Pattern.compile("(\\d+\\.){3}\\d+");
32 
BucketUtils()33     private BucketUtils() {
34     }
35 
36     /**
37      * Validates that the specified bucket name is valid for Amazon S3 V2 naming
38      * (i.e. DNS addressable in virtual host style). Throws an
39      * IllegalArgumentException if the bucket name is not valid.
40      * <p>
41      * S3 bucket naming guidelines are specified in
42      * <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?BucketRestrictions.html"
43      * > http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?
44      * BucketRestrictions.html</a>
45      *
46      * @param bucketName The bucket name to validate.
47      * @throws IllegalArgumentException If the specified bucket name doesn't follow Amazon S3's
48      * guidelines.
49      */
isValidDnsBucketName(final String bucketName, final boolean throwOnError)50     public static boolean isValidDnsBucketName(final String bucketName,
51                                                final boolean throwOnError) {
52 
53         if (bucketName == null) {
54             return exception(throwOnError, "Bucket name cannot be null");
55         }
56 
57         if (bucketName.length() < MIN_BUCKET_NAME_LENGTH ||
58             bucketName.length() > MAX_BUCKET_NAME_LENGTH) {
59 
60             return exception(
61                 throwOnError,
62                 "Bucket name should be between " + MIN_BUCKET_NAME_LENGTH + " and " + MAX_BUCKET_NAME_LENGTH + " characters long"
63             );
64         }
65 
66         if (IP_ADDRESS_PATTERN.matcher(bucketName).matches()) {
67             return exception(
68                 throwOnError,
69                 "Bucket name must not be formatted as an IP Address"
70             );
71         }
72 
73         char previous = '\0';
74 
75         for (int i = 0; i < bucketName.length(); ++i) {
76             char next = bucketName.charAt(i);
77 
78             if (next >= 'A' && next <= 'Z') {
79                 return exception(
80                     throwOnError,
81                     "Bucket name should not contain uppercase characters"
82                 );
83             }
84 
85             if (next == ' ' || next == '\t' || next == '\r' || next == '\n') {
86                 return exception(
87                     throwOnError,
88                     "Bucket name should not contain white space"
89                 );
90             }
91 
92             if (next == '.') {
93                 if (previous == '\0') {
94                     return exception(
95                         throwOnError,
96                         "Bucket name should not begin with a period"
97                     );
98                 }
99                 if (previous == '.') {
100                     return exception(
101                         throwOnError,
102                         "Bucket name should not contain two adjacent periods"
103                     );
104                 }
105                 if (previous == '-') {
106                     return exception(
107                         throwOnError,
108                         "Bucket name should not contain dashes next to periods"
109                     );
110                 }
111             } else if (next == '-') {
112                 if (previous == '.') {
113                     return exception(
114                         throwOnError,
115                         "Bucket name should not contain dashes next to periods"
116                     );
117                 }
118                 if (previous == '\0') {
119                     return exception(
120                         throwOnError,
121                         "Bucket name should not begin with a '-'"
122                     );
123                 }
124             } else if ((next < '0')
125                        || (next > '9' && next < 'a')
126                        || (next > 'z')) {
127 
128                 return exception(
129                     throwOnError,
130                     "Bucket name should not contain '" + next + "'"
131                 );
132             }
133 
134             previous = next;
135         }
136 
137         if (previous == '.' || previous == '-') {
138             return exception(
139                 throwOnError,
140                 "Bucket name should not end with '-' or '.'"
141             );
142         }
143 
144         return true;
145     }
146 
147     /**
148      * Validates if the given bucket name follows naming guidelines that are acceptable for using
149      * virtual host style addressing.
150      *
151      * @param bucketName The bucket name to validate.
152      * @param throwOnError boolean to decide if an error should be thrown if the bucket name doesn't follow the naming convention
153      */
isVirtualAddressingCompatibleBucketName(final String bucketName, final boolean throwOnError)154     public static boolean isVirtualAddressingCompatibleBucketName(final String bucketName,
155                                                                   final boolean throwOnError) {
156         return isValidDnsBucketName(bucketName, throwOnError) && !bucketName.contains(".");
157     }
158 
159     /**
160      * If 'exception' is true, throw an IllegalArgumentException with the given
161      * message. Otherwise, silently return false.
162      *
163      * @param exception true to throw an exception
164      * @param message the message for the exception
165      * @return false if 'exception' is false
166      */
exception(final boolean exception, final String message)167     private static boolean exception(final boolean exception, final String message) {
168         if (exception) {
169             throw new IllegalArgumentException(message);
170         }
171         return false;
172     }
173 }
174