// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_BASE_SSL_FALSE_START_BLACKLIST_H_
#define NET_BASE_SSL_FALSE_START_BLACKLIST_H_

#include "base/basictypes.h"
#include "net/base/net_api.h"

namespace net {

// SSLFalseStartBlacklist is a set of domains which we believe to be intolerant
// to TLS False Start. Because this set is several hundred long, it's
// precompiled by the code in ssl_false_start_blacklist_process.cc into a hash
// table for fast lookups.
class NET_TEST SSLFalseStartBlacklist {
 public:
  // IsMember returns true if the given host is in the blacklist.
  //   host: a DNS name in dotted form (i.e. "www.example.com")
  static bool IsMember(const char* host);

  // Hash returns the modified djb2 hash of the given string.
  static unsigned Hash(const char* str) {
    // This is inline because the code which generates the hash table needs to
    // use it. However, the generating code cannot link against
    // ssl_false_start_blacklist.cc because that needs the tables which it
    // generates.
    const unsigned char* in = reinterpret_cast<const unsigned char*>(str);
    unsigned hash = 5381;
    unsigned char c;

    while ((c = *in++))
      hash = ((hash << 5) + hash) ^ c;
    return hash;
  }

  // LastTwoLabels returns a pointer within |host| to the last two labels of
  // |host|. For example, if |host| is "a.b.c.d" then LastTwoLabels will return
  // "c.d".
  //   host: a DNS name in dotted form.
  //   returns: NULL on error, otherwise a pointer inside |host|.
  static const char* LastTwoLabels(const char* host) {
    // See comment in |Hash| for why this function is inline.
    const size_t len = strlen(host);
    if (len == 0)
      return NULL;

    unsigned dots_found = 0;
    size_t i;
    for (i = len - 1; i < len; i--) {
      if (host[i] == '.') {
        dots_found++;
        if (dots_found == 2) {
          i++;
          break;
        }
      }
    }

    if (i > len)
      i = 0;

    if (dots_found == 0)
      return NULL;  // no names with less than two labels are in the blacklist.
    if (dots_found == 1) {
      if (host[0] == '.')
        return NULL;  // ditto
    }

    return &host[i];
  }

  // This is the number of buckets in the blacklist hash table. (Must be a
  // power of two).
  static const unsigned kBuckets = 128;

 private:
  // The following two members are defined in
  // ssl_false_start_blacklist_data.cc, which is generated by
  // ssl_false_start_blacklist_process.cc

  // kHashTable contains an offset into |kHashData| for each bucket. The
  // additional element at the end contains the length of |kHashData|.
  static const uint32 kHashTable[kBuckets + 1];
  // kHashData contains the contents of the hash table. |kHashTable| indexes
  // into this array. Each bucket consists of zero or more, 8-bit length
  // prefixed strings. Each string is a DNS name in dotted form. For a given
  // string x, x and *.x are considered to be in the blacklist. In order to
  // assign a string to a hash bucket, the last two labels (not including the
  // root label) are hashed. Thus, the bucket for "www.example.com" is
  // Hash("example.com"). No names that are less than two labels long are
  // included in the blacklist.
  static const char kHashData[];
};

}  // namespace net

#endif  // NET_BASE_SSL_FALSE_START_BLACKLIST_H_
