15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NB: Modelled after Mozilla's code (originally written by Pamela Greene, 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// later modified by others), but almost entirely rewritten for Chrome. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (netwerk/dns/src/nsEffectiveTLDService.cpp) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* ***** BEGIN LICENSE BLOCK ***** 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Version: MPL 1.1/GPL 2.0/LGPL 2.1 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The contents of this file are subject to the Mozilla Public License Version 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.1 (the "License"); you may not use this file except in compliance with 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the License. You may obtain a copy of the License at 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * http://www.mozilla.org/MPL/ 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Software distributed under the License is distributed on an "AS IS" basis, 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for the specific language governing rights and limitations under the 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * License. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Original Code is Mozilla Effective-TLD Service 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Initial Developer of the Original Code is 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Google Inc. 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Portions created by the Initial Developer are Copyright (C) 2006 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the Initial Developer. All Rights Reserved. 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Contributor(s): 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Pamela Greene <pamg.bugs@gmail.com> (original author) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Daniel Witte <dwitte@stanford.edu> 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Alternatively, the contents of this file may be used under the terms of 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * either the GNU General Public License Version 2 or later (the "GPL"), or 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in which case the provisions of the GPL or the LGPL are applicable instead 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of those above. If you wish to allow use of your version of this file only 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * under the terms of either the GPL or the LGPL, and not to allow others to 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * use your version of this file under the terms of the MPL, indicate your 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * decision by deleting the provisions above and replace them with the notice 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * and other provisions required by the GPL or the LGPL. If you do not delete 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the provisions above, a recipient may use your version of this file under 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the terms of any one of the MPL, the GPL or the LGPL. 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ***** END LICENSE BLOCK ***** */ 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/registry_controlled_domains/registry_controlled_domain.h" 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 495e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h" 50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_module.h" 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h" 537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h" 547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/url_parse.h" 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 57a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)namespace registry_controlled_domains { 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/base/registry_controlled_domains/effective_tld_names-inc.cc" 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// See make_dafsa.py for documentation of the generated dafsa byte array. 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const unsigned char* g_graph = kDafsa; 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)size_t g_graph_length = sizeof(kDafsa); 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const int kNotFound = -1; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kExceptionRule = 1; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kWildcardRule = 2; 707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const int kPrivateRule = 4; 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Read next offset from pos. 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Returns true if an offset could be read, false otherwise. 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool GetNextOffset(const unsigned char** pos, const unsigned char* end, 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const unsigned char** offset) { 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (*pos == end) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // When reading an offset the byte array must always contain at least 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // three more bytes to consume. First the offset to read, then a node 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // to skip over and finally a destination node. No object can be smaller 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // than one byte. 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_LT(*pos + 2, end); 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) size_t bytes_consumed; 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) switch (**pos & 0x60) { 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case 0x60: // Read three byte offset 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *offset += (((*pos)[0] & 0x1F) << 16) | ((*pos)[1] << 8) | (*pos)[2]; 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bytes_consumed = 3; 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) case 0x40: // Read two byte offset 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *offset += (((*pos)[0] & 0x1F) << 8) | (*pos)[1]; 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bytes_consumed = 2; 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) break; 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) default: 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *offset += (*pos)[0] & 0x3F; 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bytes_consumed = 1; 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((**pos & 0x80) != 0) { 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *pos = end; 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *pos += bytes_consumed; 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Check if byte at offset is last in label. 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool IsEOL(const unsigned char* offset, const unsigned char* end) { 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_LT(offset, end); 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return (*offset & 0x80) != 0; 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Check if byte at offset matches first character in key. 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// This version matches characters not last in label. 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool IsMatch(const unsigned char* offset, const unsigned char* end, 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const char* key) { 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_LT(offset, end); 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return *offset == *key; 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Check if byte at offset matches first character in key. 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// This version matches characters last in label. 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool IsEndCharMatch(const unsigned char* offset, const unsigned char* end, 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const char* key) { 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_LT(offset, end); 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return *offset == (*key | 0x80); 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 1277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Read return value at offset. 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Returns true if a return value could be read, false otherwise. 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool GetReturnValue(const unsigned char* offset, const unsigned char* end, 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int* return_value) { 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_LT(offset, end); 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if ((*offset & 0xE0) == 0x80) { 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *return_value = *offset & 0x0F; 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return true; 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 1397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Lookup a domain key in a byte array generated by make_dafsa.py. 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// The rule type is returned if key is found, otherwise kNotFound is returned. 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int LookupString(const unsigned char* graph, size_t length, const char* key, 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) size_t key_length) { 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const unsigned char* pos = graph; 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const unsigned char* end = graph + length; 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const unsigned char* offset = pos; 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const char* key_end = key + key_length; 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (GetNextOffset(&pos, end, &offset)) { 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // char <char>+ end_char offsets 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // char <char>+ return value 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // char end_char offsets 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // char return value 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // end_char offsets 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // return_value 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool did_consume = false; 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (key != key_end && !IsEOL(offset, end)) { 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Leading <char> is not a match. Don't dive into this child 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!IsMatch(offset, end, key)) 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) continue; 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_consume = true; 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++offset; 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++key; 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Possible matches at this point: 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // <char>+ end_char offsets 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // <char>+ return value 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // end_char offsets 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // return value 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Remove all remaining <char> nodes possible 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) while (!IsEOL(offset, end) && key != key_end) { 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!IsMatch(offset, end, key)) 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return kNotFound; 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++key; 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++offset; 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Possible matches at this point: 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // end_char offsets 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // return_value 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If one or more <char> elements were consumed, a failure 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // to match is terminal. Otherwise, try the next node. 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (key == key_end) { 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int return_value; 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (GetReturnValue(offset, end, &return_value)) 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return return_value; 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // The DAFSA guarantees that if the first char is a match, all 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // remaining char elements MUST match if the key is truly present. 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (did_consume) 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return kNotFound; 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) continue; 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!IsEndCharMatch(offset, end, key)) { 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (did_consume) 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return kNotFound; // Unexpected 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) continue; 195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ++key; 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pos = ++offset; // Dive into child 198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return kNotFound; // No match 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 202a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)size_t GetRegistryLengthImpl( 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& host, 204a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) UnknownRegistryFilter unknown_filter, 205a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter private_filter) { 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!host.empty()); 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Skip leading dots. 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const size_t host_check_begin = host.find_first_not_of('.'); 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (host_check_begin == std::string::npos) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; // Host is only dots. 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // A single trailing dot isn't relevant in this determination, but does need 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to be included in the final returned length. 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t host_check_len = host.length(); 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (host[host_check_len - 1] == '.') { 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) --host_check_len; 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(host_check_len > 0); // If this weren't true, the host would be ".", 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and we'd have already returned above. 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (host[host_check_len - 1] == '.') 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; // Multiple trailing dots. 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Walk up the domain tree, most specific to least specific, 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // looking for matches at each level. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t prev_start = std::string::npos; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t curr_start = host_check_begin; 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t next_dot = host.find('.', curr_start); 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (next_dot >= host_check_len) // Catches std::string::npos as well. 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; // This can't have a registry + domain. 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (1) { 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char* domain_str = host.data() + curr_start; 233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) size_t domain_length = host_check_len - curr_start; 234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int type = LookupString(g_graph, g_graph_length, domain_str, domain_length); 235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool do_check = 236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) type != kNotFound && (!(type & kPrivateRule) || 237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) private_filter == INCLUDE_PRIVATE_REGISTRIES); 238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If the apparent match is a private registry and we're not including 240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // those, it can't be an actual match. 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (do_check) { 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Exception rules override wildcard rules when the domain is an exact 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // match, but wildcards take precedence when there's a subdomain. 244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (type & kWildcardRule && (prev_start != std::string::npos)) { 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If prev_start == host_check_begin, then the host is the registry 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // itself, so return 0. 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return (prev_start == host_check_begin) ? 0 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : (host.length() - prev_start); 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (type & kExceptionRule) { 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (next_dot == std::string::npos) { 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If we get here, we had an exception rule with no dots (e.g. 254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // "!foo"). This would only be valid if we had a corresponding 255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // wildcard rule, which would have to be "*". But we explicitly 256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // disallow that case, so this kind of rule is invalid. 257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NOTREACHED() << "Invalid exception rule"; 258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return 0; 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return host.length() - next_dot - 1; 2617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch } 262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If curr_start == host_check_begin, then the host is the registry 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // itself, so return 0. 265cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return (curr_start == host_check_begin) ? 0 266cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : (host.length() - curr_start); 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (next_dot >= host_check_len) // Catches std::string::npos as well. 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev_start = curr_start; 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) curr_start = next_dot + 1; 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_dot = host.find('.', curr_start); 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No rule found in the registry. curr_start now points to the first 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // character of the last subcomponent of the host, so if we allow unknown 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // registries, return the length of this subcomponent. 280a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return unknown_filter == INCLUDE_UNKNOWN_REGISTRIES ? 281a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) (host.length() - curr_start) : 0; 282a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 283a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 284a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)std::string GetDomainAndRegistryImpl( 285a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string& host, PrivateRegistryFilter private_filter) { 286a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) DCHECK(!host.empty()); 287a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 288a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // Find the length of the registry for this host. 289a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const size_t registry_length = 290a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) GetRegistryLengthImpl(host, INCLUDE_UNKNOWN_REGISTRIES, private_filter); 291a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if ((registry_length == std::string::npos) || (registry_length == 0)) 292a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string(); // No registry. 293a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // The "2" in this next line is 1 for the dot, plus a 1-char minimum preceding 294a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // subcomponent length. 295a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) DCHECK(host.length() >= 2); 296a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (registry_length > (host.length() - 2)) { 297a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) NOTREACHED() << 298a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) "Host does not have at least one subcomponent before registry!"; 299a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string(); 300a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) } 301a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 302a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // Move past the dot preceding the registry, and search for the next previous 303a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // dot. Return the host from after that dot, or the whole host when there is 304a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // no dot. 305a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const size_t dot = host.rfind('.', host.length() - registry_length - 2); 306a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (dot == std::string::npos) 307a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return host; 308a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return host.substr(dot + 1); 309a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 310a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 311a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} // namespace 312a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 313a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)std::string GetDomainAndRegistry( 314a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const GURL& gurl, 315a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter filter) { 3165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu const url::Component host = gurl.parsed_for_possibly_invalid_spec().host; 317a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if ((host.len <= 0) || gurl.HostIsIPAddress()) 318a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string(); 319a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetDomainAndRegistryImpl(std::string( 320a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) gurl.possibly_invalid_spec().data() + host.begin, host.len), filter); 321a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 322a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 323a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)std::string GetDomainAndRegistry( 324a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string& host, 325a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter filter) { 3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::CanonHostInfo host_info; 327a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string canon_host(CanonicalizeHost(host, &host_info)); 328a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (canon_host.empty() || host_info.IsIPAddress()) 329a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string(); 330a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetDomainAndRegistryImpl(canon_host, filter); 331a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 332a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 333a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)bool SameDomainOrHost( 334a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const GURL& gurl1, 335a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const GURL& gurl2, 336a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter filter) { 337a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // See if both URLs have a known domain + registry, and those values are the 338a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // same. 339a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string domain1(GetDomainAndRegistry(gurl1, filter)); 340a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string domain2(GetDomainAndRegistry(gurl2, filter)); 341a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (!domain1.empty() || !domain2.empty()) 342a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return domain1 == domain2; 343a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 344a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // No domains. See if the hosts are identical. 3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu const url::Component host1 = gurl1.parsed_for_possibly_invalid_spec().host; 3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu const url::Component host2 = gurl2.parsed_for_possibly_invalid_spec().host; 347a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if ((host1.len <= 0) || (host1.len != host2.len)) 348a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return false; 349a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return !strncmp(gurl1.possibly_invalid_spec().data() + host1.begin, 350a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) gurl2.possibly_invalid_spec().data() + host2.begin, 351a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) host1.len); 352a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 353a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 354a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)size_t GetRegistryLength( 355a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const GURL& gurl, 356a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) UnknownRegistryFilter unknown_filter, 357a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter private_filter) { 3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu const url::Component host = gurl.parsed_for_possibly_invalid_spec().host; 359a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (host.len <= 0) 360a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string::npos; 361a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (gurl.HostIsIPAddress()) 362a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return 0; 363a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetRegistryLengthImpl( 364a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) std::string(gurl.possibly_invalid_spec().data() + host.begin, host.len), 365a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) unknown_filter, 366a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) private_filter); 367a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 368a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 369a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)size_t GetRegistryLength( 370a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string& host, 371a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) UnknownRegistryFilter unknown_filter, 372a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) PrivateRegistryFilter private_filter) { 3735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu url::CanonHostInfo host_info; 374a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const std::string canon_host(CanonicalizeHost(host, &host_info)); 375a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (canon_host.empty()) 376a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return std::string::npos; 377a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) if (host_info.IsIPAddress()) 378a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return 0; 379a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) return GetRegistryLengthImpl(canon_host, unknown_filter, private_filter); 380a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} 381a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 382cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SetFindDomainGraph() { 383cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) g_graph = kDafsa; 384cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) g_graph_length = sizeof(kDafsa); 385cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 386cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 387cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SetFindDomainGraph(const unsigned char* domains, size_t length) { 388cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK(domains); 389cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) CHECK_NE(length, 0u); 390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) g_graph = domains; 391cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) g_graph_length = length; 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 394a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)} // namespace registry_controlled_domains 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace net 396