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