Programming guide for Smart Subdomains

I'll just assume you already created a smart subdomain called www.example.com and you want to write code for it. Smart subdomains can only return A, AAAA and CNAME records (for now).

Note: If you want to redirect visitors based on their country of origin, network name, uptime information, or extra information, please don't forget to select the “share” checkboxes in the Edit smart subdomain page.

Simple Script

The simplest script that you can write is:

$output[] = array("A", "127.0.0.1");

As I said in the Overview, smart subdomains run limited PHP scripts. This code returns 127.0.0.1 to anybody that requests the IP address of the www.example.com subdomain.

The $output array represents the only output that your script can send to the DNS server. Once the script has finished running, GeoScaling searches for the $output variable, and if it is well formatted, it generated the reply to the user. If the scrpt fails to run or it has invalid output, DNS2 uses the IP address which you put in the “Failover IP” section when you defined the smart subdomain.

Country Redirection

This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/.

Next, let's add redirection based on the country. Let's say I want to return 127.0.0.1 for everybody from inside Romania, and 127.0.0.2 for anybody else:

if($country == "ro")
  $output[] = array("A", "127.0.0.1");
else
  $output[] = array("A", "127.0.0.2");

All country names are lower-case. You can find a list of possible countries, including codes for Satellite Providers and Anonymous Proxies, here.

Please note: You need to select “Share country name” in the smart subdomains form for the country information to be available to the script!

Return serveral IPs

As the $output variable is an array, you can return more than one IP address:

  $output[] = array("A", "127.0.0.3");
  $output[] = array("A", "127.0.0.4");

This would return both ip addresses.

Set the TTL

The default TTL (time-to-live) in GeoScaling is 300 (5 minutes). You cannot define a record with a TTL lower than 300 (well, unless you contact to us). If you want to output an IP address with a custom TTL in a smart subdomain, you can do it like this:

  $output[] = array("A", "127.0.0.5", "400");

This returns a ttl of 400 seconds for 127.0.0.5.

Fail without returning failure ip

If you don't want to return any ip address, you can just define $output like this:

$output[] = array("fail");

Returning CNAMEs

You can return a CNAME is a similar way you return an IP address:

$output[] = array("CNAME", "ghs.google.com");

This returns the CNAME of ghs.google.com (pleasae note the lack of an ending dot after ghs.google.com).

Redirecting based on the Network Name (AS/ASN)

GeoScaling DNS2 can redirect based on the AS number of the network the visitor comes from.

If it is defined, the $as variable contains the AS/ASN network id in lower case. You can make a simple redirect:

if( $as == "as6128" )
  $output[] = array("A", "127.0.0.1");
else
  $output[] = array("A", "127.0.0.2");

Or you can check for lists of network names, like this:

if( in_array ( $as, array("as30890", "as24745", "as6128") ) )
  $output[] = array("A", "127.0.0.1");
else
  $output[] = array("A", "127.0.0.2");

Please note: You need to select “Share network name / AS number” in the smart subdomains form for the network information to be available to the script!

Redirecting on uptime information

Redirecting based on uptime is also easy. All the results of your uptime checks are made available in the $uptime array. Let's say you set up an uptime check named “simpliq”. If simpliq is down, you can redirect visitors to another mirror like this:

if($uptime['simpliq']==1)
 $output[] = array("A", "127.0.0.1");
else
 $output[] = array("A", "127.0.0.2");

Here's another real-life example that you can base your code on:

if($uptime['fisierulmeu-teentelecom']==1)
  $output[] = array("A", "193.138.195.114");
 
if($uptime['fisierulmeu-evolva']==1)
  $output[] = array("A", "86.55.16.126");
 
if(!isset($output) || !sizeof($output))
  $output[] = array("A", "96.9.130.197"); // backup mirror

Please note: You need to select “Share uptime info” in the smart subdomains form for the uptime information to be available to the script!

Redirecting based on extra information

As described here, you can send extra information in the form of a string that can be accessed by your scripts. The following script returns reuturns 4.4.4.4 when the variable datacenter3 is smaller than 80. This could be useful to redirect users to mirrors based on the load of your servers.

if(isset($extra_info) && $extra_info!=false )
{
  $extra_info = unserialize($extra_info);
  if($extra_info['datacenter3']<80)
    $output[] = array("A", "4.4.4.4");
  else
    $output[] = array("A", "5.5.5.5");
}
else
{
  $output[] = array("A", "3.3.3.3");
}

This particular example uses data sent by the script given as example in the Sending extra information to DNS2 with the XML-RPC API page:

$payload['datacenter1'] = "43";
$payload['datacenter2'] = "22";
$payload['datacenter3'] = "87";

Please note: You need to select “Share extra info (like server load)” in the smart subdomains form for the extra information to be available to the script!

Other useful variables sent to the script

Real-life example that combines two types of redirects

 
/*
as30890 - Evolva
as8708 - RDS
as34304 - Teen Telecom
as24745 - Romtelecom
*/
 
if($country=="ro") // if our user comes from Romania
{
  if( in_array( $as, array("as30890", "as24745") ) ) // if the network of the visitor is either evolva or romtelecom,
  {
    $output[] = array("A", "86.55.16.126"); // give them this ip
  }
  else if( in_array( $as, array("as34304", "as8708") ) ) // else if their network is teen telecom or rds
  {
    $output[] = array("A", "193.138.195.114"); // give them this ip
  }
  else // if he/she comes from another network, just send both ips
  {
    $output[] = array("A", "86.55.16.126");
    $output[] = array("A", "193.138.195.114");
  }
}
else // if our user comes from outside Romania
{
  $output[] = array("A", "91.121.158.100", "400"); // return IP 91.121.158.100 with a TTL of 400
}

Return the closest server to the user

$new_server['lat'] = 41.0186111;
$new_server['lon'] = 28.9647222;
$new_server['loc'] = "Istanbul TR";
$new_server['ip'] = "1.2.3.4"; // IP1
$servers[] = $new_server;
 
$new_server['lat'] = 47.6063889;
$new_server['lon'] = -122.3308333;
$new_server['loc'] = "Seattle, Wa";
$new_server['ip'] = "1.2.3.5"; // IP2
$servers[] = $new_server;
 
$new_server['lat'] = 32.7833333;
$new_server['lon'] = -96.8;
$new_server['loc'] = "Dallas, TX";
$new_server['ip'] = "1.2.3.6"; // IP3
$servers[] = $new_server;
 
$new_server['lat'] = 38.895;
$new_server['lon'] = -77.0366667;
$new_server['loc'] = "Washington D.C.";
$new_server['ip'] = "1.2.3.7"; // IP4
$servers[] = $new_server;
 
$current_lat = $city_info['latitude'];
$current_lon = $city_info['longitude'];
 
$minimum_distance = PHP_INT_MAX;
$minimum_distance_server_id = 0;
 
for($i=0 ; $i<sizeof($servers); $i++)
{
    $server = $servers[$i];
    $distance_to_user = @distance($current_lat, $current_lon, $server['lat'], $server['lon'], "k");
    if($distance_to_user<$minimum_distance)
    {
	$minimum_distance = $distance_to_user;
	$minimum_distance_server_id = $i;
    }
}
 
$output[] = array("A", $servers[$minimum_distance_server_id]['ip']);
 
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
/*::                                                                         :*/
/*::  this routine calculates the distance between two points (given the     :*/
/*::  latitude/longitude of those points). it is being used to calculate     :*/
/*::  the distance between two zip codes or postal codes using our           :*/
/*::  zipcodeworld(tm) and postalcodeworld(tm) products.                     :*/
/*::                                                                         :*/
/*::  definitions:                                                           :*/
/*::    south latitudes are negative, east longitudes are positive           :*/
/*::                                                                         :*/
/*::  passed to function:                                                    :*/
/*::    lat1, lon1 = latitude and longitude of point 1 (in decimal degrees)  :*/
/*::    lat2, lon2 = latitude and longitude of point 2 (in decimal degrees)  :*/
/*::    unit = the unit you desire for results                               :*/
/*::           where: 'm' is statute miles                                   :*/
/*::                  'k' is kilometers (default)                            :*/
/*::                  'n' is nautical miles                                  :*/
/*::  united states zip code/ canadian postal code databases with latitude & :*/
/*::  longitude are available at http://www.zipcodeworld.com                 :*/
/*::                                                                         :*/
/*::  For enquiries, please contact sales@zipcodeworld.com                   :*/
/*::                                                                         :*/
/*::  official web site: http://www.zipcodeworld.com                         :*/
/*::                                                                         :*/
/*::  hexa software development center © all rights reserved 2004            :*/
/*::                                                                         :*/
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
 
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "m") . " miles<br>";
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "k") . " kilometers<br>";
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "n") . " nautical miles<br>";
 
 
function distance($lat1, $lon1, $lat2, $lon2, $unit)
{ 
 
  $theta = $lon1 - $lon2; 
  $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); 
  $dist = acos($dist); 
  $dist = rad2deg($dist); 
  $miles = $dist * 60 * 1.1515;
  $unit = strtoupper($unit);
 
  if ($unit == "K") {
    return ($miles * 1.609344); 
  } else if ($unit == "N") {
      return ($miles * 0.8684);
    } else {
        return $miles;
      }
}

Output debug information in TXT records

This simple script generates TXT records with the information GeoScaling sends to your script:

$output[] = array("TXT", "City Info: ".var_export($city_info, true));
$output[] = array("TXT", "Network info: ".var_export($as, true));
$output[] = array("TXT", "Country: ".$country);
$output[] = array("TXT", "Subdomain: ".$subdomain);
$output[] = array("TXT", "Remote IP: ".$remote_ip);

Now, if you make a DNS request for the TXT records of your subdomain:

dig -t TXT geo.nyuu.org @ns1.geoscaling.com

you will get something like this:

...
;; ANSWER SECTION:
geo.nyuu.org.		300	IN	TXT	"Remote IP: 67.82.29.100"
geo.nyuu.org.		300	IN	TXT	"Network info: 'as6128'"
geo.nyuu.org.		300	IN	TXT	"City Info: array (   'city' => 'newark',   'country' => 'us',   'region' => 'nj',   'latitude' => 40.734,   'longitude' => -74.1868, )"
geo.nyuu.org.		300	IN	TXT	"Country: us"
geo.nyuu.org.		300	IN	TXT	"Subdomain: geo.nyuu.org"
...

User contribution that combines, Closest, plus uptime / take server offline, plus send extra info to take server offline during a deployment See send extra_info wiki part, to see the php code that sends the associated extra_info via a URL

/*
GeoScaling "smart subdomain script"
        PURPOSE:
        1/ associate url/IP with the physically closest server, monitoring with www.monitis.com reveals minimum of 2x improvement in latency if we do this
        2/ take a server out of the list of possible servers if GeoScaling monitoring shows it as down
        3/ take a sever out of the list (immediate effect) if we are about to deploy an upgrade of the app software by sending "extra_info"
 
        Contributed BY: keitht@sasimedia.net
        AVAILABLE INFRASTRUCTURE CONSULTING ON
        1/ automated build and deploy the N servers in the cloud
        2/ Unix or Windows cloud server setup (linode / Rackspace / other)
        3/ Techniques for achieving fastest initial page load
        4/ No SQL keystore for database replication across cloud servers (Hazelcast + Voldemort + Oracle BDB)
        5/ HA / 100 % uptime
        6/ Smart browser clients (Flex / GWT), with service calls automatically tied to the fastest responding server (not necessarily the closest)
*/
 
$info = unserialize($extra_info);
 
if($uptime['Linode-s1.info']==1 && strcmp($info['linode-s1-offline'],'true') != 0){
$new_server['lat'] = 37.4919627;
$new_server['lon'] = -121.93811;
$new_server['loc'] = "Fremont, California";
$new_server['ip'] = "1.1.1.1"; //linode-s1 CA
$servers[] = $new_server;
}
 
if($uptime['rackspace-s1.info']==1 && strcmp($info['rackspace-s1-offline'],'true') != 0){
$new_server['lat'] = 32.7833333;
$new_server['lon'] = -96.8;
$new_server['loc'] = "Dallas, TX";
$new_server['ip'] = "1.1.1.1"; //rackspace-s1 DALLAS
$servers[] = $new_server;
}
 
if($uptime['rackspace-s1.info']==1 && strcmp($info['rackspace-s2-offline'],'true') != 0){
$new_server['lat'] = 41.85;
$new_server['lon'] = -87.65;
$new_server['loc'] = "Chicago, IL";
$new_server['ip'] = "1.1.1.1";  // rackspace-s2 CHICAGO
$servers[] = $new_server;
}
 
if($uptime['Linode-s2.info']==1 && strcmp($info['linode-s2-offline'],'true') != 0){
$new_server['lat'] = 33.73;
$new_server['lon'] = -84.38;
$new_server['loc'] = "Atlanta, GA.";
$new_server['ip'] = "11.1.1.1";  // linode-s2 ATLANTA
$servers[] = $new_server;
}
 
$current_lat = $city_info['latitude'];
$current_lon = $city_info['longitude'];
 
$minimum_distance = PHP_INT_MAX;
$minimum_distance_server_id = 0;
 
for($i=0 ; $i<sizeof($servers); $i++)
{
    $server = $servers[$i];
    $distance_to_user = @distance($current_lat, $current_lon, $server['lat'], $server['lon'], "k");
    if($distance_to_user<$minimum_distance)
    {
	$minimum_distance = $distance_to_user;
	$minimum_distance_server_id = $i;
    }
}
 
$output[] = array("A", $servers[$minimum_distance_server_id]['ip']);
 
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
/*::                                                                         :*/
/*::  this routine calculates the distance between two points (given the     :*/
/*::  latitude/longitude of those points). it is being used to calculate     :*/
/*::  the distance between two zip codes or postal codes using our           :*/
/*::  zipcodeworld(tm) and postalcodeworld(tm) products.                     :*/
/*::                                                                         :*/
/*::  definitions:                                                           :*/
/*::    south latitudes are negative, east longitudes are positive           :*/
/*::                                                                         :*/
/*::  passed to function:                                                    :*/
/*::    lat1, lon1 = latitude and longitude of point 1 (in decimal degrees)  :*/
/*::    lat2, lon2 = latitude and longitude of point 2 (in decimal degrees)  :*/
/*::    unit = the unit you desire for results                               :*/
/*::           where: 'm' is statute miles                                   :*/
/*::                  'k' is kilometers (default)                            :*/
/*::                  'n' is nautical miles                                  :*/
/*::  united states zip code/ canadian postal code databases with latitude & :*/
/*::  longitude are available at http://www.zipcodeworld.com                 :*/
/*::                                                                         :*/
/*::  For enquiries, please contact sales@zipcodeworld.com                   :*/
/*::                                                                         :*/
/*::  official web site: http://www.zipcodeworld.com                         :*/
/*::                                                                         :*/
/*::  hexa software development center © all rights reserved 2004            :*/
/*::                                                                         :*/
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
 
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "m") . " miles<br>";
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "k") . " kilometers<br>";
//echo distance(32.9697, -96.80322, 29.46786, -98.53506, "n") . " nautical miles<br>";
 
 
function distance($lat1, $lon1, $lat2, $lon2, $unit)
{
 
  $theta = $lon1 - $lon2;
  $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
  $dist = acos($dist);
  $dist = rad2deg($dist);
  $miles = $dist * 60 * 1.1515;
  $unit = strtoupper($unit);
 
  if ($unit == "K") {
    return ($miles * 1.609344);
  } else if ($unit == "N") {
      return ($miles * 0.8684);
    } else {
        return $miles;
      }
}