BIND 10 trac1462, updated. ceeb87f6d539c413ebdc66e4cf718e7eb8559c45 Merge branch 'master' into trac1462

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Jan 4 03:20:02 UTC 2012


The branch, trac1462 has been updated
       via  ceeb87f6d539c413ebdc66e4cf718e7eb8559c45 (commit)
       via  707480d9966bc95151de93b2071ee9674c37a3b4 (commit)
       via  4bfc81c495ca08f5e4bed03807de0aa0006d0c19 (commit)
       via  d4aed53c3b2fedcda8d944678b15d89b501b604f (commit)
       via  c520591d322056a69bdf1d0c803030ee550b6db3 (commit)
       via  f8a705c023c49b646420b047da00b08ec67b1667 (commit)
       via  1293abbaa78190c5d464f220ff4b29d753e2e9df (commit)
       via  3f9c1cb430c81ce8b373d48af7fb1610a046f562 (commit)
       via  2cba8cb83cde4f34842898a848c0b1182bc20597 (commit)
       via  1bc918fff46c6c812f73be52c107c8e394a5f0c5 (commit)
       via  b35797ba1a49c78246abc8f2387901f9690b328d (commit)
       via  c4c5bf470a4c13282c684efcec6ddea2cf2fde1f (commit)
       via  3758ab360efe1cdf616636b76f2e0fb41f2a62a0 (commit)
       via  f08d444ae2f66c1465705bd06fa1bad7beea047c (commit)
       via  cef5f3257a2b45d09134e750a76e99313f4fca28 (commit)
       via  57b06f8cb6681f591fa63f25a053eb6f422896ef (commit)
       via  be309bf010ba993fb13e79bb4ce8802d8dacf0a4 (commit)
       via  aac05f566c49daad4d3de35550cfaff31c124513 (commit)
       via  55aa7ec8abaae5ddf3e16d613ef7f63395a1849c (commit)
       via  eee7e884390d87b3416747f093a2b6431e6bae91 (commit)
       via  77c469b9c3e483f495072fbec06bd4b9db055d56 (commit)
       via  0cfe92d0076a6b8108cb232a5fbfddddd9197c0b (commit)
       via  8f3f6eeff43f670d0c0f71216b87483b73d33607 (commit)
       via  81f5f15727cde4a4c98971fa09bf5d82fce1b3e2 (commit)
       via  56f15f53c14593d5a6c777cee0567e92c8cab23f (commit)
       via  bc24ccba8ce6cca78548a2ff6a0db7cf26831f3a (commit)
       via  315edfd3b032c8d2b5683212635b81c2bc5f7dbf (commit)
       via  126ab0b66824ee084ee1e03cdcf3a2e73d4bca04 (commit)
       via  33595d0d554cf7208472d31929cae013cebdc485 (commit)
       via  4881a52f6541d200ad2755ce4b159525466498db (commit)
       via  7e0fdfb412969cc6ba46aa53dd6d7af6200c7e24 (commit)
       via  de6f226ae7bc45d144b6e012884bfd28ca74e350 (commit)
       via  3344e8b8d949fa9b4bcd9cf007f6a234f0fa5b1c (commit)
       via  5280be98d09b95b5edb25ab85753070ba4b400fc (commit)
       via  0cc0fe82ac433701d239a5ca1e8307cfcb9c7825 (commit)
       via  50ef0ada77267ba465c6e35489f435a42ac9d39f (commit)
       via  a9f48f11eda70e641b4c4624eef4dbada03828ff (commit)
       via  5a18f692351fe3631ee0749583b6ffb09f20b9c7 (commit)
       via  7f8da8e4b1bbafac7156e8cd2f39330467c2233d (commit)
       via  dc49f886943c9f66a1f5bce734c7e434af88686d (commit)
       via  156282cb2989360f6d329049f58e60917859f61e (commit)
       via  d82df10e192238851799070520e23f753db6c2ee (commit)
       via  8a040737426aece7cc92a795f2b712d7c3407513 (commit)
       via  b362cbe3a2eaeabbeebaa720bb4aaab04f667044 (commit)
       via  f382050248b5b7ed1881b086d89be2d9dd8fe385 (commit)
       via  dc2ae81fef04a1d3ec94ea5e75009393266107fa (commit)
       via  a7341f3e3306c19df1b8d17ffbc30d1cf03e9562 (commit)
       via  f4e31621d8dbfe3742cc62c8d5435147f7f1b96a (commit)
       via  1cd357e3b4d3940d7d0f7b40fc33b03d0bb58eee (commit)
       via  365c733b967c7b0a53122f62931d459d1ec63b35 (commit)
       via  2818dab13b61cdbbee27076e820e4069e3dd14d4 (commit)
       via  67ebd76fc05f3eee15bdcbd8e8be58d9e0ce74fe (commit)
       via  760720cb251366fe37bae4a06ce6e0e45ae02a33 (commit)
       via  cc48c3a82c718f490fc7029eb08a7fb6441c80c5 (commit)
       via  f0246bfbe7e40e2e7897911797f910520209812b (commit)
       via  7c6a56c81ba347d181584c47b0fb681510837efb (commit)
       via  7017367c1118709bb9d793814493df4dcab28221 (commit)
       via  1cd2de193c2e6b350caa9b643c568c2a5ac55304 (commit)
       via  90f4e987cf3477a8ecc361399e268175be3a5ce8 (commit)
       via  c5b51bb8881dd5c7950143033d507f27b1fc4422 (commit)
       via  235a2af717e4eed8599d5b4051d1b62e27888a4b (commit)
       via  6718ccfa5f7ca499473590aa0a8618ec7adca46e (commit)
       via  d2c410cd67c37b1f27bb7121ecc7aa273df02015 (commit)
       via  3663624dd0efb7d2864117c430b6b9c1efa41e11 (commit)
       via  a2120b04405184479b27b52d30cab38903b6f83c (commit)
       via  102355de8ca47e80fcf3bc2a884f36907b7b7922 (commit)
       via  dd6f8bdc0f67c83aea2f91cd56c21a4b822d735b (commit)
       via  d12104290629bd4f3c2e9edbc1b4139d337afeeb (commit)
       via  01a1b2f4d210c62fc1d7a9b53e7967057e93d331 (commit)
       via  9a2f395450e0241bc0f56bca2f7a3d3ff24aad81 (commit)
       via  e303d479811d063f0094cceac5e586ea69c63454 (commit)
       via  b0482e786ffa236075d3cfde2e2a970a95698142 (commit)
       via  85083a76107ba2236732b45524ce7018eefbaf90 (commit)
       via  b396c483833fbca9aa93a0c33dc9a3c8ab7888c0 (commit)
       via  4efd3d6cab93018b850da07a8f51433c248d615f (commit)
       via  0020456f8d118c9f3fd6fc585757c822b79a96f6 (commit)
       via  6969f47777586b17c909b5f75f4f79699bc8ce1d (commit)
       via  2858ffa9471778539dabf7cbf5204296d464351d (commit)
       via  2d357b48add9d446e96488a48135aec7a12eb0a0 (commit)
       via  6030d51fe670fb00e6831263be61a7cc4fe97f1b (commit)
       via  0b0d1a0d3a54a75941fe245a781df36e34194911 (commit)
       via  b86fdfc8326c74838f92e8752d8f0421e6b03a36 (commit)
       via  3dfa82c4bae8bac305a4781eadc80ecfa6608534 (commit)
       via  c6fd1d3c3c57b9273c7598119e2654e35beebccc (commit)
       via  f59e0c5bab84c830f6922c5982c0ae61f028597e (commit)
       via  d907907c7dfaeca2ace318cb67d70c4445974b6f (commit)
       via  a1b7e0e81d17c607fdf1f3c4c9b3bc578b9269eb (commit)
       via  4acc56d22891578b8a0885fc0e4e3d8637d7ff76 (commit)
       via  ad1a5ee64e4647e39cf1a242f0323366b1f838e6 (commit)
       via  3e851b72d4869bd4e7ede881aac0470da39aef3a (commit)
       via  7bf0432cb1f1b7777a25bfd8c0e3c1c8def9bf18 (commit)
       via  6c8cde4eb79b1aa8d5b924bcddd17e4219cea67d (commit)
       via  f60bf56ebed7189ad71d6c4ed4044d97b5e5c7a5 (commit)
       via  e945e57855e4252349635d4b1f90f11626da7508 (commit)
       via  e1252b9d0a5a8f23dad207911923326f05d0db4c (commit)
       via  a47f01b6c860c565516456d329a4aebe63d346a0 (commit)
       via  3e3ba9302689de42e4e475f98436b096d112d371 (commit)
       via  b0d0bf39fbdc29a7879315f9b8e6d602ef3afb1b (commit)
       via  05c1eb516b8633dce55e3b528e71242dbc8a28a5 (commit)
       via  99ad0292af284a246fff20b3702fbd7902c45418 (commit)
       via  9814abbcd475b08ccfcc67e887f37d5e6bd873fd (commit)
       via  35a74de7bc1ccc9336d697b30f80a8b91c68553e (commit)
       via  ec48e1a92ff03492f7eca063a8a9e243d8fb2574 (commit)
       via  9d392668cddf7bc1e90e1fc6047a0804d525d4e3 (commit)
       via  f709af9e07ce8a0b700862fbc086e72c8f46f784 (commit)
       via  267a466b3ecac6a2ec07d7c873a3cbb9041f67cb (commit)
       via  838a74457c825f003f6786fff1ea7f28519b00cd (commit)
       via  d2f1ad06d2beb7ab5f51357bf191a9aaf56ddb2f (commit)
       via  5050c9607584178e27d9caf196c25df01b3cd651 (commit)
       via  137405d030cfdcdec5b1a247e42dd8c4ec83e538 (commit)
       via  87a72f002abc5c6db9baeb3b3e2ac20c0a1e24ce (commit)
       via  ff3a2e359264e4089a989eb60b1832e753663878 (commit)
       via  ee21a81dbcfc6dc3587a9ca8ae95175c3b52bfea (commit)
       via  321a37a11088881f57df5088571f9ac7c919daf7 (commit)
       via  0938b2fb80685e2b91fafcc3bf75dba718c72de0 (commit)
       via  5e6c9f89116f5710e6c2b88d134e787d069ce51b (commit)
       via  f3559fa1f97da649602b3804bc1fab809a628666 (commit)
       via  855f42784d0e9e5f86a3dfb6988da3987cda2200 (commit)
       via  7d90bbbc9310e7e539e2e9898e6334993e66bd1f (commit)
       via  0bc14376a9c39fbadba551de7426a8404fa351bb (commit)
       via  7576e362b61f1b6049e8be7a0fd5b29e53032e59 (commit)
       via  43df0d5564a5363deaeb8e1a3b76eff3426adf4c (commit)
       via  2b4c9db386e884ba4ef5113bb52cf0f1ada97c46 (commit)
       via  8fda3f02eafeb955b965d9cc04f5a703dbab5f02 (commit)
       via  a27ddbc73f2fbf66172a2f3f47f1d4d4a11a027c (commit)
       via  1a05959b2741d390294f50652765e125931fcc16 (commit)
       via  39db78f60268fb6cf001307f2ddb3243926ef6b2 (commit)
       via  ea85f834c3e36f1ad5b3818a0558e8378eef9c23 (commit)
       via  73601f240d4b3aa72b3498b71235b949f645a326 (commit)
       via  5ec569cd8c5e56ec6e4f4f8df05a44485489c38a (commit)
       via  ae488cc48d35ca3a96c42ba6a7fd1d33c4b2a1c7 (commit)
       via  84ef9da129de44dfa74933f83db60bdaf2dce51e (commit)
       via  8177f30e22454cf9bc2561c16695f3c58c4a59c5 (commit)
       via  4feacbc10b4f22b6d8b82230ce88c95a391b82da (commit)
       via  0bcc177400198c0ea2ede57651853302a6d37490 (commit)
       via  2f0aa20b44604b671e6bde78815db39381e563bf (commit)
       via  ad1fd67bc72d8a050b58496fab79c4aeeeec1f44 (commit)
       via  424113eeebb77e6edac02231a64595729bc04bbc (commit)
       via  30b1cc45e2b536acdf0216a02b88de39c4fce4b3 (commit)
       via  05afc74542cdfaeeeece255f7a8174199d36a53b (commit)
       via  8c96b066c55f216096107b51f3d634505dd3f8f0 (commit)
       via  8cabb13b990bd2536af98d5a73edaa31b0928f9d (commit)
       via  7b122af8489acf0f28f935a19eca2c5509a3677f (commit)
       via  b76b851a6abf6758102abd6280867d388daf7cb2 (commit)
       via  f3db2f5329ae957c463e486d7fe4ab09aa0a30f0 (commit)
       via  a0a07a1b5ee1442f1bcbbb455d83bd8f3c229813 (commit)
       via  884644953f7c356cc53ca975a61502ef77d6dcbe (commit)
       via  196b58b907f9069c004fbf2ac140bcfd59f960bc (commit)
       via  4d6719f99efecca28f65c792ac9933c5424c963a (commit)
       via  11b8542e2d8e8a2ffd03fcb10a4278b399e9353b (commit)
       via  a593cb487feceef36fc0d8f3670927c2f4e82cdc (commit)
       via  2c4dcf9b5974e5b1e23b8670b984e0b4b4e6a625 (commit)
       via  2103ad2196d78f1a7b4105c38e976d9c20e4a12e (commit)
       via  86b08bc45b6e3c8934971625b8b189012ef51357 (commit)
       via  702631dc5f42c288ebb701eb5a8b67074f675de5 (commit)
       via  740d33cd94f12b8c88e5dcb1e1640e87a97a499f (commit)
       via  0d0b9b0ebea1a12b35cdd646700f332c9f5a7cb5 (commit)
       via  d254b329691d43307184e78b33c2704eafa39f77 (commit)
       via  3525f88d298f4e877c8fe6e59f5ce3f2f1ff9b47 (commit)
       via  fc05832813ba340a93bb8abab10cb769f03da486 (commit)
       via  bdd00552a920331dc3cf1f2e6a7644112caa4b72 (commit)
       via  227a3a7be884c74c4a1189f0f3cc6ac77d7adaa7 (commit)
       via  aac28766882753daf7f8a80c75fdd93e388a3ba4 (commit)
       via  9176c84be6b46b48cf31a21764db0ada5d0fda26 (commit)
       via  ed5fa95326dc7233e4e289acda77d7a1fb562f89 (commit)
       via  eb591a94c560e7ac7067141ee451b7d693a2bae1 (commit)
       via  502cc85e6d101d89210cf8a1a41f06ea8b2412dd (commit)
       via  4d5f96b4d083f8ba171bc90e5767ea89e2dc98c6 (commit)
       via  1e4d796212bc7c91def18e9edd838c92b042e6b1 (commit)
       via  47256b986057875cb535aff393bd604b1d8cd620 (commit)
       via  787a439b41c71c78a7de585d3603f477474426cc (commit)
       via  12649b6a44db756795a3160d1a70c7fe3a6fc9b1 (commit)
       via  3f83fac07d34fc709aebe528028f041fc3637bab (commit)
       via  2d0aacaedbdc31e8afa5075ed6d77ea9a1db8920 (commit)
       via  aaea8925c243f6ead71f368de54ad27cfd19ef6b (commit)
       via  3839f74b09d70e852cf1329d55628f6ae712140e (commit)
       via  920ee68644189ee382135cfcf774e31ca8d9d530 (commit)
       via  0b13e5293e643ab9098ab32579b5435beeaff9a1 (commit)
       via  6bbffedb003004dbbbc1b05b7c909e5cf03c529c (commit)
       via  e3c35e75141468ab84fc07ea16d6665ef3fab784 (commit)
       via  4b226d86ea74b839e6066750de85c6247de691a5 (commit)
       via  697a02295a8d2a7579aaa7dd6b599dcdab14bd2c (commit)
       via  7ffef24a4823b63694628ee9fdab0b196d7caa06 (commit)
       via  b2da8d97a3f24f2f80af7f408f6b8d461f3dccd4 (commit)
       via  f1104654a25beae9f1b6fcd7e09c5128346a150a (commit)
       via  f1f4ce3e3014366d4916f924655c27761327c681 (commit)
       via  23350205fb9b09ed188be0898b3f95efb34f74b1 (commit)
       via  ebfebb1be1968d33fb0054fe6df65aa90a085612 (commit)
       via  28f5b7df990e502695635c112d61ce5bbe088efd (commit)
       via  8cd3a3f50336eff26b80af13326daf3df337a234 (commit)
       via  f37822dcf51b014d5fa9f93f2e9ac85dec0d0ede (commit)
       via  1007c575bbedf9bd07fef24de28d5c744b4c2293 (commit)
       via  f942fb1a8c2475bac2fd73e5fbf979fcacbd6f2f (commit)
       via  6328a99430a833f72c7faa3515cde78e49b5de12 (commit)
       via  0fd58479c441c0ce5584df6ab898594345e69ef5 (commit)
       via  c066e58bd521a107d027818f47268c5183ec5b18 (commit)
       via  ae4e8ba0c81e273b515147e916e28924c2804c14 (commit)
       via  b5f53a509974185553f40022b947c9c515992493 (commit)
       via  f15d8831381af8a56c62e5e8a762166fcd053422 (commit)
       via  0a5f810dd86794befad38590f68d3725828379eb (commit)
       via  d1a22e481da930ca3151ce704305d7995f9d8fcc (commit)
       via  cd4c9eb0868200fbd53beef6eebc35ba6982a798 (commit)
       via  5dbdcfa0a3fdeab7b8757a8a22ff6d5b9cf87cbb (commit)
       via  c4ca960f3139817c9d3baf35c2b975a240c8acd2 (commit)
       via  09f2de75e96261cc6bf0709cd30877ed3d0b8a20 (commit)
       via  5cdcda0439416cbe1610a5990d30441c20e571eb (commit)
       via  f18114000f75a0615ae97e36c54881754cc84be9 (commit)
       via  0213d987ac8b4fb30bc1aa1ee6bd67cdfde02ce0 (commit)
       via  62463d1d0236e7fb6c3bfc94b3b66e46c875ca93 (commit)
       via  bf2e60ca2a5bd14d057c5ad292b752e980d9637b (commit)
       via  936855eb6fd5304128b737b72ca464747061a476 (commit)
       via  29003e06a65e9cbd18f83d44e197e70542a2a6a2 (commit)
       via  0daa6e10d2b7446069b3e43b8cbf80691270431f (commit)
       via  0910b0f180332e46fd0bac7867c646f76e040615 (commit)
       via  c254f7fcb4fac6b47cc880221aa5d28a0772b641 (commit)
       via  6c60425c85fceb901e5f71844ad0f4786c4db4f1 (commit)
       via  0bddf9ffefd543c9a67ec8d096a88290fc8a5dc3 (commit)
       via  bf289fc63b181e980ef0781b7515802279ee51d9 (commit)
       via  3dbb7c077ba74aa03c2652d39392661fe7ba1e70 (commit)
       via  d7a5e79e8182c50780860ffcd340595904f89516 (commit)
       via  0431e1b28adec802d93725fc0d388a379f925873 (commit)
       via  90c77658e79a21c3a60da992f22eb2f2660db667 (commit)
       via  07d2c46d78d35556a3e83f490eaa1aee9563e494 (commit)
       via  e0c434fbb5e15c98af42b86248b3de1472a4a7b0 (commit)
       via  b5e5d9e912722ff8ab9c2cf5e16ca913b64733aa (commit)
       via  09ed7a24bf40675619e01ffdc00068c1560f6f31 (commit)
       via  8e93f114a93671c4eda46f420c81e1101fc16e05 (commit)
       via  118d0d9c047879133c412390d67dda70004da589 (commit)
       via  cbddeb5ad8ecb4d3ef403943e588fa2c858b0d3b (commit)
       via  2611ea78dac67e4a116e1dc9894431131b3cc0c4 (commit)
       via  bdaee9411ed47bb02470eb05c45486553f0c7778 (commit)
       via  6f615f4d947042151a35f8e8184564a0cd08052c (commit)
       via  5be5e6a639a6a1c74761cae55a97f1fa46de5c6d (commit)
       via  e722aee2fc22e1b94c49451c35bc5f51c78854d2 (commit)
       via  f38b0d742bf1b93f64c49c42de68d7ebc9935ec0 (commit)
       via  1f27698e244f53266e09d7d115395b1235e15a71 (commit)
       via  6c7201e6875c30ef5097126b0b57c9d818301a05 (commit)
       via  1c36f89dd234c05d3af662a33063169f85b27ef4 (commit)
       via  254c4c3be3c6b18f11474c0762a9e0318cf6d48b (commit)
       via  59a5275869f315677720ae0deac7d01bc8e586bc (commit)
       via  e96109294ea4e6f164c0abcc39f5a2e78a26a950 (commit)
       via  be3e7b88a02dee29777eef96021001dfab64bee4 (commit)
       via  7a05a543c3c8a9d8a338c3a25f42764783b2a9f0 (commit)
       via  084c9790ef595d433ad9caf620897d8a3a84da4b (commit)
       via  1ee6d8e7f892d929321b32e5577eb3a3be65a15e (commit)
       via  0ca7dca47750297e7477223093da8159d7f48f6c (commit)
       via  7132a806926e9dcb03f97094b982d837cd27b55b (commit)
       via  bd08f4f7126f420dc102cf2c234391b4bcd87054 (commit)
      from  0fa94211a98fbfbd15bde8929c9317eeba2888d7 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ceeb87f6d539c413ebdc66e4cf718e7eb8559c45
Merge: 0fa94211a98fbfbd15bde8929c9317eeba2888d7 707480d9966bc95151de93b2071ee9674c37a3b4
Author: Xie Jiagui <xiejiagui at cnnic.cn>
Date:   Wed Jan 4 11:18:28 2012 +0800

    Merge branch 'master' into trac1462

commit 707480d9966bc95151de93b2071ee9674c37a3b4
Author: Jeremy C. Reed <jreed at ISC.org>
Date:   Tue Jan 3 10:23:20 2012 -0600

    [master] ChangeLog fixes
    
    Use two tabs before the committer username.
    
    Lines too long.

commit 4bfc81c495ca08f5e4bed03807de0aa0006d0c19
Author: Jeremy C. Reed <jreed at ISC.org>
Date:   Tue Jan 3 10:16:50 2012 -0600

    [master] mention Linux again for the interface detection
    
    and mention it is planned for other systems.

commit d4aed53c3b2fedcda8d944678b15d89b501b604f
Author: Jeremy C. Reed <jreed at ISC.org>
Date:   Tue Jan 3 10:15:18 2012 -0600

    [master] fix typo

commit c520591d322056a69bdf1d0c803030ee550b6db3
Author: Jeremy C. Reed <jreed at ISC.org>
Date:   Tue Jan 3 10:14:39 2012 -0600

    [master] refer to ticket of task to e revisited later

commit f8a705c023c49b646420b047da00b08ec67b1667
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Mon Jan 2 06:28:09 2012 +0000

    [master] use a different TCP port for tests than that used in some other
    tests.  using the same port could make it fail on some build bots due to
    failure in bind() with a conflicting socket in the time wait state.
    this is a fragile workaround, but I expect we'll revist these test scenarios
    with the introduction of socket creator anyway, so I chose to work this around
    with a simplest fix.

commit 1293abbaa78190c5d464f220ff4b29d753e2e9df
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Sun Jan 1 18:07:15 2012 +0000

    [master] use TCP socket to emulate the scenario of duplicate bind();
    on Solaris SO_REUSEADDR allow multiple UDP sockets bound to the same port
    (seemingly for the old multicast extension).
    committing it at my discretion as it's simple and the problem made buildbot
    fail.

commit 3f9c1cb430c81ce8b373d48af7fb1610a046f562
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 18:04:39 2011 -0800

    [master] changelog for #1424

commit 2cba8cb83cde4f34842898a848c0b1182bc20597
Merge: 1bc918fff46c6c812f73be52c107c8e394a5f0c5 365c733b967c7b0a53122f62931d459d1ec63b35
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 18:00:55 2011 -0800

    [master] Merge branch 'trac1424'

commit 1bc918fff46c6c812f73be52c107c8e394a5f0c5
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 15:25:44 2011 -0800

    [master] changelog for #1430

commit b35797ba1a49c78246abc8f2387901f9690b328d
Merge: c4c5bf470a4c13282c684efcec6ddea2cf2fde1f 4acc56d22891578b8a0885fc0e4e3d8637d7ff76
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 15:18:31 2011 -0800

    [master] Merge branch 'trac1430'

commit c4c5bf470a4c13282c684efcec6ddea2cf2fde1f
Merge: cef5f3257a2b45d09134e750a76e99313f4fca28 3758ab360efe1cdf616636b76f2e0fb41f2a62a0
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Fri Dec 30 16:29:05 2011 +0100

    Merge branch 'trac1367'
    
    Conflicts:
    	ChangeLog

commit cef5f3257a2b45d09134e750a76e99313f4fca28
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 00:22:08 2011 -0800

    [master] added changelog entry for #1502.  also fixed entry number gaps
    introduced at entry "359" (which should have been 350).

commit 57b06f8cb6681f591fa63f25a053eb6f422896ef
Merge: be309bf010ba993fb13e79bb4ce8802d8dacf0a4 8f3f6eeff43f670d0c0f71216b87483b73d33607
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 30 00:18:54 2011 -0800

    [master] Merge branch 'trac1502'

commit be309bf010ba993fb13e79bb4ce8802d8dacf0a4
Merge: 55aa7ec8abaae5ddf3e16d613ef7f63395a1849c aac05f566c49daad4d3de35550cfaff31c124513
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Dec 29 23:26:40 2011 +0100

    Merge branch 'trac1230'
    
    Conflicts:
    	ChangeLog
    	src/lib/dhcp/iface_mgr.h

commit 55aa7ec8abaae5ddf3e16d613ef7f63395a1849c
Merge: 0cfe92d0076a6b8108cb232a5fbfddddd9197c0b 5a18f692351fe3631ee0749583b6ffb09f20b9c7
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Dec 29 18:13:19 2011 +0100

    Merge remote-tracking branch 'origin/trac1527'

commit 8f3f6eeff43f670d0c0f71216b87483b73d33607
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Dec 28 11:37:47 2011 -0800

    [1502] revised the logic of checking equality of RR types as suggested in review

commit 365c733b967c7b0a53122f62931d459d1ec63b35
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 23 00:28:58 2011 -0800

    [1424] added some more comments

commit 2818dab13b61cdbbee27076e820e4069e3dd14d4
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Fri Dec 23 00:19:58 2011 -0800

    [1424] made sure any installable initial configs are installed on startup
    even if listen_on failed.  this fix is a kind of an ugly hack, but the symptom
    is quite counter intuitive, so it's probably better than not-fixing.
    I guess the introducing of socket creator will situation a bit, and in
    any event I believe we should refactor the configuration mechanism later.
    I also removed setConfigure() method and configured_ member as they are
    effectively unused in the code anymore.

commit 760720cb251366fe37bae4a06ce6e0e45ae02a33
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Dec 22 20:16:29 2011 -0800

    [1502] fixed log parameters for LIBXFRIN_DIFFERENT_TTL.  this will solve #1471.

commit cc48c3a82c718f490fc7029eb08a7fb6441c80c5
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Dec 22 20:10:05 2011 -0800

    [1502] made sure comparing RRSIG type covered in compact() to avoid incorrect
    compaction of non compatible RRSIGs.

commit 4acc56d22891578b8a0885fc0e4e3d8637d7ff76
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Dec 21 00:27:02 2011 -0800

    [1430] (unrelated cleanup) avoid calling getLabelCount() multiple times
    unnecessarily.

commit ad1a5ee64e4647e39cf1a242f0323366b1f838e6
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Dec 21 00:23:20 2011 -0800

    [1430] make sure that out-of-zone qname results in NXDOMAIN for find()
    and findAll() (compatible with inmemory data source).

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |   87 +
 configure.ac                                       |   55 +-
 doc/Doxyfile                                       |    2 +-
 doc/guide/Makefile.am                              |    2 +
 doc/guide/bind10-guide.html                        |  964 ++++--
 doc/guide/bind10-guide.xml                         | 1039 +++++--
 src/bin/Makefile.am                                |    4 +-
 src/bin/auth/auth_config.cc                        |   14 +-
 src/bin/auth/auth_srv.cc                           |   18 +-
 src/bin/auth/command.cc                            |    5 +-
 src/bin/auth/query.cc                              |  284 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |    9 +-
 src/bin/auth/tests/query_unittest.cc               |   38 +-
 src/bin/bind10/run_bind10.sh.in                    |    2 +-
 src/bin/cfgmgr/plugins/logging.spec                |    2 +-
 src/bin/ddns/Makefile.am                           |   42 +
 src/bin/ddns/b10-ddns.8                            |   97 +
 src/bin/ddns/b10-ddns.xml                          |  161 +
 src/bin/ddns/ddns.py.in                            |  209 ++
 src/bin/ddns/ddns.spec                             |   42 +
 src/bin/ddns/ddns_messages.mes                     |   66 +
 src/bin/ddns/tests/Makefile.am                     |   28 +
 src/bin/ddns/tests/ddns_test.py                    |  142 +
 src/bin/dhcp4/dhcp4_srv.cc                         |  156 +-
 src/bin/dhcp4/dhcp4_srv.h                          |   39 +
 src/bin/dhcp4/tests/Makefile.am                    |    1 +
 src/bin/dhcp4/tests/dhcp4_srv_unittest.cc          |  135 +-
 src/bin/dhcp6/Makefile.am                          |    4 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |  133 +-
 src/bin/dhcp6/dhcp6_srv.h                          |   60 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |   17 +-
 src/bin/resolver/main.cc                           |    5 +-
 src/bin/resolver/resolver.cc                       |   36 +-
 src/bin/resolver/resolver.h                        |   20 +-
 src/bin/resolver/tests/resolver_config_unittest.cc |  124 +-
 src/bin/resolver/tests/resolver_unittest.cc        |    1 -
 .../resolver/tests/response_scrubber_unittest.cc   |    4 +-
 src/bin/xfrin/tests/xfrin_test.py                  |    2 +-
 src/bin/xfrin/xfrin.py.in                          |    3 +-
 src/bin/xfrout/b10-xfrout.8                        |   21 +-
 src/bin/xfrout/b10-xfrout.xml                      |   22 +-
 src/bin/xfrout/tests/xfrout_test.py.in             |    2 +-
 src/bin/xfrout/xfrout.py.in                        |    5 +-
 src/bin/zonemgr/b10-zonemgr.8                      |    8 +-
 src/bin/zonemgr/tests/Makefile.am                  |    1 +
 src/bin/zonemgr/tests/zonemgr_test.py              |  136 +-
 src/bin/zonemgr/zonemgr.py.in                      |   51 +-
 src/lib/acl/dns.cc                                 |   18 +-
 src/lib/acl/tests/acl_test.cc                      |    5 +-
 src/lib/acl/tests/ip_check_unittest.cc             |    8 +-
 src/lib/acl/tests/loader_test.cc                   |   51 +-
 src/lib/acl/tests/logic_check_test.cc              |   21 +-
 src/lib/asiodns/dns_lookup.h                       |    4 +-
 src/lib/asiodns/dns_server.h                       |    4 +-
 src/lib/asiodns/dns_service.cc                     |    4 +-
 src/lib/asiodns/io_fetch.cc                        |   18 +-
 src/lib/asiodns/io_fetch.h                         |   10 +-
 src/lib/asiodns/tcp_server.cc                      |    2 +-
 src/lib/asiodns/tests/dns_server_unittest.cc       |    2 +-
 src/lib/asiodns/tests/io_fetch_unittest.cc         |   15 +-
 src/lib/asiodns/udp_server.cc                      |    2 +-
 src/lib/asiodns/udp_server.h                       |    2 +-
 src/lib/asiolink/io_address.cc                     |    2 +-
 src/lib/asiolink/io_service.cc                     |    4 +-
 src/lib/asiolink/simple_callback.h                 |    4 +-
 src/lib/asiolink/tcp_socket.h                      |    2 +-
 src/lib/asiolink/tests/io_endpoint_unittest.cc     |    3 +-
 src/lib/bench/benchmark.h                          |    2 +-
 src/lib/bench/benchmark_util.cc                    |    2 +-
 src/lib/cache/resolver_cache.cc                    |    9 +-
 src/lib/cc/data.cc                                 |   14 +-
 src/lib/cc/session.cc                              |    2 +-
 src/lib/cc/tests/data_unittests.cc                 |   11 +
 src/lib/cryptolink/tests/crypto_unittests.cc       |    3 +-
 src/lib/datasrc/database.cc                        |  110 +-
 src/lib/datasrc/database.h                         |  174 +-
 src/lib/datasrc/datasrc_messages.mes               |    9 +
 src/lib/datasrc/memory_datasrc.cc                  |   19 +-
 src/lib/datasrc/memory_datasrc.h                   |   14 +-
 src/lib/datasrc/rbtree.h                           |   13 +-
 src/lib/datasrc/tests/Makefile.am                  |   39 +-
 src/lib/datasrc/tests/database_unittest.cc         |  319 ++-
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |  146 +-
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |    5 +-
 src/lib/datasrc/zone.h                             |   40 +-
 src/lib/dhcp/Makefile.am                           |    2 +
 src/lib/dhcp/dhcp4.h                               |    2 +-
 src/lib/dhcp/iface_mgr.cc                          |  521 +++-
 src/lib/dhcp/iface_mgr.h                           |   67 +-
 src/lib/dhcp/iface_mgr_bsd.cc                      |   38 +
 src/lib/dhcp/iface_mgr_linux.cc                    |  381 +++
 src/lib/dhcp/libdhcp++.cc                          |   16 +-
 src/lib/dhcp/option.cc                             |   40 +
 src/lib/dhcp/option.h                              |   45 +-
 src/lib/dhcp/pkt4.cc                               |   76 +-
 src/lib/dhcp/pkt4.h                                |   26 +-
 src/lib/dhcp/tests/Makefile.am                     |    3 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |  375 ++-
 src/lib/dhcp/tests/option_unittest.cc              |   83 +-
 src/lib/dhcp/tests/pkt4_unittest.cc                |   50 +-
 src/lib/dns/masterload.cc                          |    5 +-
 src/lib/dns/messagerenderer.cc                     |    2 +-
 src/lib/dns/python/pydnspp_common.cc               |    1 -
 src/lib/dns/rdata.cc                               |   10 +-
 src/lib/dns/rdata/generic/dnskey_48.cc             |    2 +-
 src/lib/dns/rdata/generic/nsec3_50.cc              |    2 +-
 src/lib/dns/rdata/generic/nsec_47.cc               |    2 +-
 src/lib/dns/rdatafields.cc                         |    4 +-
 src/lib/dns/rrparamregistry-placeholder.cc         |    6 +-
 src/lib/dns/tests/message_unittest.cc              |    2 +-
 src/lib/dns/tests/unittest_util.cc                 |    4 +-
 src/lib/log/compiler/message.cc                    |    2 +-
 src/lib/log/logger_manager.cc                      |    2 +-
 src/lib/log/logger_manager_impl.h                  |    2 +-
 src/lib/log/logger_specification.h                 |    2 +-
 src/lib/log/output_option.h                        |    2 +-
 src/lib/log/tests/logger_manager_unittest.cc       |    2 +-
 src/lib/log/tests/output_option_unittest.cc        |    2 +-
 src/lib/nsas/hash_key.cc                           |    2 +-
 src/lib/nsas/tests/hash_table_unittest.cc          |    7 +-
 .../tests/nameserver_address_store_unittest.cc     |    4 +-
 src/lib/python/isc/acl/dns_requestloader_python.cc |    3 +-
 src/lib/python/isc/datasrc/finder_inc.cc           |   36 +-
 src/lib/python/isc/datasrc/finder_python.cc        |   84 +-
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |  121 +-
 src/lib/python/isc/datasrc/updater_python.cc       |   34 +-
 src/lib/python/isc/log/log.cc                      |    4 +-
 src/lib/python/isc/log_messages/Makefile.am        |    2 +
 src/lib/python/isc/log_messages/ddns_messages.py   |    1 +
 src/lib/python/isc/util/Makefile.am                |    2 +-
 src/lib/python/isc/util/io/Makefile.am             |   41 +
 src/lib/python/isc/util/io/__init__.py             |    3 +
 src/lib/python/isc/util/io/socketsession.py        |   26 +
 src/lib/python/isc/util/io/socketsession_inc.cc    |  122 +
 src/lib/python/isc/util/io/socketsession_python.cc |   79 +
 src/lib/python/isc/util/io/socketsession_python.h  |   35 +
 .../isc/util/io/socketsessionforwarder_inc.cc      |  136 +
 .../isc/util/io/socketsessionforwarder_python.cc   |  309 ++
 .../isc/util/io/socketsessionforwarder_python.h    |   45 +
 .../isc/util/io/socketsessionreceiver_inc.cc       |   89 +
 .../isc/util/io/socketsessionreceiver_python.cc    |  327 ++
 .../isc/util/io/socketsessionreceiver_python.h     |   46 +
 src/lib/python/isc/util/io/tests/Makefile.am       |   36 +
 .../python/isc/util/io/tests/socketsession_test.py |  253 ++
 src/lib/python/isc/xfrin/diff.py                   |   18 +-
 src/lib/python/isc/xfrin/libxfrin_messages.mes     |    4 +-
 src/lib/python/isc/xfrin/tests/diff_tests.py       |   30 +-
 src/lib/resolve/recursive_query.cc                 |   49 +-
 src/lib/resolve/response_classifier.cc             |    9 +-
 src/lib/resolve/tests/Makefile.am                  |    1 +
 .../resolve/tests/recursive_query_unittest_2.cc    |   21 +-
 .../resolve/tests/recursive_query_unittest_3.cc    |  564 ++++
 src/lib/server_common/portconfig.cc                |    2 +-
 src/lib/util/io/Makefile.am                        |    6 +-
 src/lib/util/io/fd.cc                              |   71 +-
 src/lib/util/io/fd_share.cc                        |   17 +-
 src/lib/util/io/fd_share.h                         |   14 +-
 src/lib/util/io/fdshare_python.cc                  |   11 +-
 src/lib/util/io/sockaddr_util.h                    |   69 +
 src/lib/util/io/socketsession.cc                   |  434 +++
 src/lib/util/io/socketsession.h                    |  466 +++
 src/lib/util/python/wrapper_template.cc            |  101 +-
 src/lib/util/strutil.cc                            |    2 +-
 src/lib/util/strutil.h                             |    2 +-
 src/lib/util/tests/Makefile.am                     |    6 +
 src/lib/util/tests/base32hex_unittest.cc           |    2 +-
 src/lib/util/tests/hex_unittest.cc                 |    2 +-
 src/lib/util/tests/socketsession_unittest.cc       |  846 +++++
 src/lib/util/time_utilities.cc                     |    2 +-
 tests/lettuce/setup_intree_bind10.sh.in            |    2 +-
 tests/tools/Makefile.am                            |    6 +-
 tests/tools/perfdhcp/Makefile.am                   |   12 +
 tests/tools/perfdhcp/perfdhcp.c                    | 3523 ++++++++++++++++++++
 tools/reorder_message_file.py                      |    2 +-
 174 files changed, 13506 insertions(+), 1824 deletions(-)
 create mode 100644 src/bin/ddns/Makefile.am
 create mode 100644 src/bin/ddns/b10-ddns.8
 create mode 100644 src/bin/ddns/b10-ddns.xml
 create mode 100755 src/bin/ddns/ddns.py.in
 create mode 100644 src/bin/ddns/ddns.spec
 create mode 100644 src/bin/ddns/ddns_messages.mes
 create mode 100644 src/bin/ddns/tests/Makefile.am
 create mode 100755 src/bin/ddns/tests/ddns_test.py
 create mode 100644 src/lib/dhcp/iface_mgr_bsd.cc
 create mode 100644 src/lib/dhcp/iface_mgr_linux.cc
 create mode 100644 src/lib/python/isc/log_messages/ddns_messages.py
 create mode 100644 src/lib/python/isc/util/io/Makefile.am
 create mode 100644 src/lib/python/isc/util/io/__init__.py
 create mode 100644 src/lib/python/isc/util/io/socketsession.py
 create mode 100644 src/lib/python/isc/util/io/socketsession_inc.cc
 create mode 100644 src/lib/python/isc/util/io/socketsession_python.cc
 create mode 100644 src/lib/python/isc/util/io/socketsession_python.h
 create mode 100644 src/lib/python/isc/util/io/socketsessionforwarder_inc.cc
 create mode 100644 src/lib/python/isc/util/io/socketsessionforwarder_python.cc
 create mode 100644 src/lib/python/isc/util/io/socketsessionforwarder_python.h
 create mode 100644 src/lib/python/isc/util/io/socketsessionreceiver_inc.cc
 create mode 100644 src/lib/python/isc/util/io/socketsessionreceiver_python.cc
 create mode 100644 src/lib/python/isc/util/io/socketsessionreceiver_python.h
 create mode 100644 src/lib/python/isc/util/io/tests/Makefile.am
 create mode 100644 src/lib/python/isc/util/io/tests/socketsession_test.py
 create mode 100644 src/lib/resolve/tests/recursive_query_unittest_3.cc
 create mode 100644 src/lib/util/io/sockaddr_util.h
 create mode 100644 src/lib/util/io/socketsession.cc
 create mode 100644 src/lib/util/io/socketsession.h
 create mode 100644 src/lib/util/tests/socketsession_unittest.cc
 create mode 100644 tests/tools/perfdhcp/Makefile.am
 create mode 100644 tests/tools/perfdhcp/perfdhcp.c

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 936cff7..cb16f99 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,90 @@
+358.	[bug]		jinmei
+	b10-resolver ignored default configuration parameters if listen_on
+	failed (this can easily happen especially for a test environment
+	where the run time user doesn't have root privilege), and even if
+	listen_on was updated later the resolver wouldn't work correctly
+	unless it's fully restarted (for example, all queries would be
+	rejected due to an empty ACL).
+	(Trac #1424, git 2cba8cb83cde4f34842898a848c0b1182bc20597)
+
+357.	[bug]		jinmei
+	ZoneFinder::find() for database based data sources didn't
+	correctly identify out-of-zone query name and could return a
+	confusing result such as NXRRSET.  It now returns NXDOMAIN with an
+	empty RRset.  Note: we should rather throw an exception in such a
+	case, which should be revisited later (see Trac #1536).
+	(Trac #1430, git b35797ba1a49c78246abc8f2387901f9690b328d)
+
+356.	[doc]		tomek
+	BIND10 Guide updated. It now describes DHCPv4 and DHCPv6
+	components, including their overview, usage, supported standard
+	and limitations. libdhcp++ is also described.
+	(Trac #1367, git 3758ab360efe1cdf616636b76f2e0fb41f2a62a0)
+
+355.	[bug]		jinmei
+	Python xfrin.diff module incorrectly combined RRSIGs of different
+	type covered, possibly merging different TTLs.  As a result a
+	secondary server could store different RRSIGs than those at the
+	primary server if it gets these records via IXFR.
+	(Trac #1502, git 57b06f8cb6681f591fa63f25a053eb6f422896ef)
+
+354.	[func]		tomek
+	dhcp4: Support for DISCOVER and OFFER implemented. b10-dhcp4 is
+	now able to offer hardcoded leases to DHCPv4 clients.
+	dhcp6: Code refactored to use the same approach as dhcp4.
+	(Trac #1230, git aac05f566c49daad4d3de35550cfaff31c124513)
+
+353.	[func]		tomek
+	libdhcp++: Interface detection in Linux implemented. libdhcp++
+	is now able (on Linux systems) to detect available network
+	interfaces, its link-layer addresses, flags and configured
+	IPv4 and IPv6 addresses. Interface detection on other
+	systems is planned.
+	(Trac #1237, git 8a040737426aece7cc92a795f2b712d7c3407513)
+
+352.	[func]		tomek
+	libdhcp++: Transmission and reception of DHCPv4 packets is now
+	implemented. Low-level hacks are not implemented for transmission
+	to hosts that don't have IPv4 address yet, so currently the code
+	is usable for communication with relays only, not hosts on the
+	same link.
+	(Trac #1239, #1240, git f382050248b5b7ed1881b086d89be2d9dd8fe385)
+
+351.	[func]		fdupont
+	Alpha version of DHCP benchmarking tool added.  "perfdhcp" is able to
+	test both IPv4 and IPv6 servers: it can time the four-packet exchange
+	(DORA and SARR) as well as time the initial two-packet exchange (DO
+	and SA).  More information can be obtained by invoking the utility
+	(in tests/tools/perfdhcp) with the "-h" flag.
+	(Trac #1450, git 85083a76107ba2236732b45524ce7018eefbaf90)
+
+350.	[func]*		vorner
+	The target parameter of ZoneFinder::find is no longer present, as the
+	interface was awkward. To get all the RRsets of a single domain, use
+	the new findAll method (the same applies to python version, the method
+	is named find_all).
+	(Trac #1483,#1484, git 0020456f8d118c9f3fd6fc585757c822b79a96f6)
+
+349.	[bug]		dvv
+	resolver: If an upstream server responds with FORMERR to an EDNS
+	query, try querying it without EDNS.
+	(Trac #1386, git 99ad0292af284a246fff20b3702fbd7902c45418)
+
+348.	[bug]		stephen
+	By default the logging output stream is now flushed after each write.
+	This fixes a problem seen on some systems where the log output from
+	different processes was jumbled up.  Flushing can be disabled by
+	setting the appropriate option in the logging configuration.
+	(Trac #1405, git 2f0aa20b44604b671e6bde78815db39381e563bf)
+
+347.	[bug]		jelte
+	Fixed a bug where adding Zonemgr/secondary_zones without explicitely
+	setting the class value of the added zone resulted in a cryptic
+	error in bindctl ("Error: class"). It will now correctly default to
+	IN if not set. This also adds better checks on the name and class
+	values, and better errors if they are bad.
+	(Trac #1414, git 7b122af8489acf0f28f935a19eca2c5509a3677f)
+
 346.	[build]*		jreed
 	Renamed libdhcp to libdhcp++.
 	(Trac #1446, git d394e64f4c44f16027b1e62b4ac34e054b49221d)
diff --git a/configure.ac b/configure.ac
index 671a9b6..81ead0c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -120,7 +120,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
 AC_SUBST(SET_ENV_LIBRARY_PATH)
 AC_SUBST(ENV_LIBRARY_PATH)
 
-m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1])
+m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1 python3.2])
 AC_ARG_WITH([pythonpath],
 AC_HELP_STRING([--with-pythonpath=PATH],
   [specify an absolute path to python executable when automatic version check (incorrectly) fails]),
@@ -369,6 +369,36 @@ AC_HEADER_STDBOOL
 AC_TYPE_SIZE_T
 
 
+# Detect OS type (it may be used to do OS-specific things, e.g.
+# interface detection in DHCP)
+AC_MSG_CHECKING(OS family)
+system=`uname -s`
+case $system in
+    Linux)
+      OS_TYPE="Linux"
+      CPPFLAGS="$CPPFLAGS -DOS_LINUX"
+      ;;
+    Darwin | FreeBSD | NetBSD | OpenBSD)
+      OS_TYPE="BSD"
+      CPPFLAGS="$CPPFLAGS -DOS_BSD"
+      ;;
+    Solaris)
+      OS_TYPE="Solaris"
+      CPPFLAGS="$CPPFLAGS -DOS_SOLARIS"
+      ;;
+    *)
+      OS_TYPE="Unknown"
+      AC_MSG_WARN("Unsupported OS: uname returned $system")
+      ;;
+esac
+AC_MSG_RESULT($OS_TYPE)
+
+AM_CONDITIONAL(OS_LINUX, test $OS_TYPE = Linux)
+AM_COND_IF([OS_LINUX], [AC_DEFINE([OS_LINUX], [1], [Running on Linux?])])
+AM_CONDITIONAL(OS_BSD, test $OS_TYPE = BSD)
+AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
+AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
+AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
 
 AC_MSG_CHECKING(for sa_len in struct sockaddr)
 AC_TRY_COMPILE([
@@ -443,7 +473,7 @@ AC_SUBST(USE_LCOV)
 botan_path="yes"
 AC_ARG_WITH([botan],
   AC_HELP_STRING([--with-botan=PATH],
-    [specify exact directory of Botan library]),
+    [specify the path to botan-config (PATH/bin/botan-config will be used)]),
     [botan_path="$withval"])
 if test "${botan_path}" = "no" ; then
     AC_MSG_ERROR([Need botan for libcryptolink])
@@ -831,6 +861,20 @@ EV_SET(NULL, 0, 0, 0, 0, 0, udata);],
 	])
 fi
 
+# perfdhcp: If the clock_gettime() function does not exist on the system,
+# use an alternative supplied in the code based on gettimeofday().
+CLOCK_GETTIME_LDFLAGS=
+AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_GETTIME_LDFLAGS=-lrt], [])
+AC_SUBST([CLOCK_GETTIME_LDFLAGS])
+
+# perfdhcp: if getifaddrs() does not exist, have the code output a message
+# that it can't be run on this version of the operating system.  For the
+# systems on which BIND 10 is built, this means Solaris 10. (Replacements
+# for this function are long and involved, and the function is reported present
+# on Solaris 11 and later, either in the libsocket or libnsl libraries.)
+AC_SEARCH_LIBS([getifaddrs], [socket nsl],
+               [AC_DEFINE([HAVE_GETIFADDRS], [1], [getifaddrs() present])])
+
 # /dev/poll issue: ASIO uses /dev/poll by default if it's available (generally
 # the case with Solaris).  Unfortunately its /dev/poll specific code would
 # trigger the gcc's "missing-field-initializers" warning, which would
@@ -890,6 +934,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/auth/Makefile
                  src/bin/auth/tests/Makefile
                  src/bin/auth/benchmarks/Makefile
+                 src/bin/ddns/Makefile
+                 src/bin/ddns/tests/Makefile
                  src/bin/dhcp6/Makefile
                  src/bin/dhcp6/tests/Makefile
 		 src/bin/dhcp4/Makefile
@@ -925,6 +971,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/acl/tests/Makefile
                  src/lib/python/isc/util/Makefile
                  src/lib/python/isc/util/tests/Makefile
+                 src/lib/python/isc/util/io/Makefile
+                 src/lib/python/isc/util/io/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
                  src/lib/python/isc/datasrc/tests/Makefile
                  src/lib/python/isc/dns/Makefile
@@ -993,6 +1041,7 @@ AC_CONFIG_FILES([Makefile
                  tests/tools/Makefile
                  tests/tools/badpacket/Makefile
                  tests/tools/badpacket/tests/Makefile
+                 tests/tools/perfdhcp/Makefile
                ])
 AC_OUTPUT([doc/version.ent
            compatcheck/sqlite3-difftbl-check.py
@@ -1002,6 +1051,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test
            src/bin/cmdctl/cmdctl.spec.pre
+           src/bin/ddns/ddns.py
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
            src/bin/xfrin/run_b10-xfrin.sh
@@ -1135,6 +1185,7 @@ Flags:
   CXXFLAGS:      $CXXFLAGS
   LDFLAGS:       $LDFLAGS
   B10_CXXFLAGS:  $B10_CXXFLAGS
+  OS Family:     $OS_TYPE
 dnl includes too
   Python:        ${PYTHON_INCLUDES}
                  ${PYTHON_CXXFLAGS}
diff --git a/doc/Doxyfile b/doc/Doxyfile
index ee5aaf8..c9c5c5a 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -573,7 +573,7 @@ INPUT                  = ../src/lib/exceptions ../src/lib/cc \
     ../src/bin/auth ../src/bin/resolver ../src/lib/bench ../src/lib/log \
     ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
-    ../src/bin/sockcreator/ ../src/lib/util/ \
+    ../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
     ../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp
 
 # This tag can be used to specify the character encoding of the source files
diff --git a/doc/guide/Makefile.am b/doc/guide/Makefile.am
index 239f235..ffe89c9 100644
--- a/doc/guide/Makefile.am
+++ b/doc/guide/Makefile.am
@@ -11,6 +11,8 @@ bind10-guide.html: bind10-guide.xml
 	xsltproc --novalid --xinclude --nonet \
 		--path $(top_builddir)/doc \
 		-o $@ \
+		--stringparam section.autolabel 1 \
+		--stringparam section.label.includes.component.label 1 \
 		--stringparam html.stylesheet $(srcdir)/bind10-guide.css \
 		http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl \
 		$(srcdir)/bind10-guide.xml
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index 2972cdf..f6206a5 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,26 +1,28 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20111021. The most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168229451102"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p c
 lass="releaseinfo">This is the reference guide for BIND 10 version
-        20111021.</p></div><div><p class="copyright">Copyright © 2010-2011 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
-	Internet Systems Consortium (ISC). It includes DNS libraries
-	and modular components for controlling authoritative and
-	recursive DNS servers.
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers managed by Internet Systems Consortium (ISC). It includes DNS libraries, modular components for controlling authoritative and recursive DNS servers, and experimental DHCPv4 and DHCPv6 servers. This is the reference guide for BIND 10 version 20111129. The most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id2479
 03"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the reference guide for BIND 10 version
+        20111129.</p></div><div><p class="copyright">Copyright © 2010-2011 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a framework that features Domain Name System
+      (DNS) suite and Dynamic Host Configuration Protocol (DHCP)
+      servers managed by Internet Systems Consortium (ISC). It
+      includes DNS libraries, modular components for controlling
+      authoritative and recursive DNS servers, and experimental DHCPv4
+      and DHCPv6 servers.
       </p><p>
-        This is the reference guide for BIND 10 version 20111021.
-	The most up-to-date version of this document (in PDF, HTML,
-	and plain text formats), along with other documents for
-	BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
-	</p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229451238">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168229451265">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436567">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436859">Download Tar File</a></span></dt><dt><span c
 lass="section"><a href="#id1168229436878">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436939">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229437037">Build</a></span></dt><dt><span class="section"><a href="#id1168229437052">Install</a></span></dt><dt><span class="section"><a href="#id1168229437076">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">Configuration of started processes</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a><
 /span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438007">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438072">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438171">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438302">Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438340">Enabling IXFR</a></span></dt><dt><span class="section"><a href="#id1168229438382">Trigger an Incoming Zone Transfer Ma
 nually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438673">Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438891">Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">14. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439042">Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439052">Loggers</a></span></dt><dt><span class="section"><a href="#id1168229439294">Output Options</a></span></dt><dt><span class="section"><a href="#id1168229439468">Example session</a></span></dt></dl></dd><dt><s
 pan class="section"><a href="#id1168229440023">Logging Message Format</a></span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#id1168229437338"></a></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229451238">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168229451265">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+        This is the reference guide for BIND 10 version 20111129.
+        The most up-to-date version of this document (in PDF, HTML,
+        and plain text formats), along with other documents for
+        BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
+        </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id457902">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#id457914">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id458364">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id458582">2.3.1. Download Tar File</a></
 span></dt><dt><span class="section"><a href="#id458605">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id458679">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id458789">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id458806">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id458832">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configuration of started processes</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl
 ">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id459869">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id459942">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id459978">8.3. Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dd><dl><dt><span class="section"><a href="#id460133">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id460177">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Man
 ager</a></span></dt><dt><span class="section"><a href="#id460320">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#resolverserver">11. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id460633">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id460772">11.2. Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp4">12. DHCPv4 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"
 ><a href="#dhcp6">13. DHCPv6 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#libdhcp">14. libdhcp++ library</a></span></dt><dd><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#packet-handling">14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">15. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">16. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#id461660">16.1. Logging configuration</a></span></d
 t><dd><dl><dt><span class="section"><a href="#id461675">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id461978">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id462172">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id462428">16.2. Logging Message Format</a></span></dt></dl></dd><dt><span class="chapter"><a href="#id462551">17. Acknowledgements</a></span></dt></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#id459146"></a></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id457902">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#id457914">1.2. Required Software</a></span></dt><dt><span class="section"><a 
 href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></div><p>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
       and provides a modular environment for serving and maintaining DNS.
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         This guide covers the experimental prototype of
-        BIND 10 version 20111021.
+        BIND 10 version 20111129.
       </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         BIND 10 provides a EDNS0- and DNSSEC-capable
         authoritative DNS server and a caching recursive name server
         which also provides forwarding.
-      </p></div><div class="section" title="Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229451238"></a>Supported Platforms</h2></div></div></div><p>
+      </p></div><div class="section" title="1.1. Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id457902"></a>1.1. Supported Platforms</h2></div></div></div><p>
   BIND 10 builds have been tested on Debian GNU/Linux 5,
   Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, and CentOS
   Linux 5.3.
@@ -30,39 +32,39 @@
 
         It is planned for BIND 10 to build, install and run on
         Windows and standard Unix-type platforms.
-      </p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229451265"></a>Required Software</h2></div></div></div><p>
+      </p></div><div class="section" title="1.2. Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id457914"></a>1.2. Required Software</h2></div></div></div><p>
         BIND 10 requires Python 3.1.  Later versions may work, but Python
         3.1 is the minimum version which will work.
       </p><p>
-	BIND 10 uses the Botan crypto library for C++. It requires
-	at least Botan version 1.8.
+        BIND 10 uses the Botan crypto library for C++. It requires
+        at least Botan version 1.8.
       </p><p>
-	BIND 10 uses the log4cplus C++ logging library. It requires
-	at least log4cplus version 1.0.3.
+        BIND 10 uses the log4cplus C++ logging library. It requires
+        at least log4cplus version 1.0.3.
       </p><p>
-	The authoritative server requires SQLite 3.3.9 or newer.
-	The <span class="command"><strong>b10-xfrin</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>,
-	and <span class="command"><strong>b10-zonemgr</strong></span> modules require the
-	libpython3 library and the Python _sqlite3.so module.
+        The authoritative server requires SQLite 3.3.9 or newer.
+        The <span class="command"><strong>b10-xfrin</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>,
+        and <span class="command"><strong>b10-zonemgr</strong></span> modules require the
+        libpython3 library and the Python _sqlite3.so module.
       </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
           Some operating systems do not provide these dependencies
           in their default installation nor standard packages
           collections.
           You may need to install them separately.
-        </p></div></div><div class="section" title="Starting and Stopping the Server"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="starting_stopping"></a>Starting and Stopping the Server</h2></div></div></div><p>
+        </p></div></div><div class="section" title="1.3. Starting and Stopping the Server"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="starting_stopping"></a>1.3. Starting and Stopping the Server</h2></div></div></div><p>
         BIND 10 is modular.  Part of this modularity is
         accomplished using multiple cooperating processes which, together,
-	provide the server functionality.  This is a change from
-	the previous generation of BIND software, which used a
-	single process.
+        provide the server functionality.  This is a change from
+        the previous generation of BIND software, which used a
+        single process.
       </p><p>
-	At first, running many different processes may seem confusing.
-	However, these processes are started, stopped, and maintained
-	by a single command, <span class="command"><strong>bind10</strong></span>.
-	This command starts a master process which will start other
-	processes as needed.
-	The processes started by the <span class="command"><strong>bind10</strong></span>
-	command have names starting with "b10-", including:
+        At first, running many different processes may seem confusing.
+        However, these processes are started, stopped, and maintained
+        by a single command, <span class="command"><strong>bind10</strong></span>.
+        This command starts a master process which will start other
+        processes as needed.
+        The processes started by the <span class="command"><strong>bind10</strong></span>
+        command have names starting with "b10-", including:
       </p><p>
 
         </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
@@ -99,21 +101,21 @@
             </li><li class="listitem">
               <span class="command"><strong>b10-xfrout</strong></span> —
               Outgoing zone transfer service.
-	      This process is used to handle transfer requests to
-	      send a local zone to a remote secondary server,
-	      when acting as a master server.
+              This process is used to handle transfer requests to
+              send a local zone to a remote secondary server,
+              when acting as a master server.
             </li><li class="listitem">
               <span class="command"><strong>b10-zonemgr</strong></span> —
               Secondary manager.
-	      This process keeps track of timers and other
+              This process keeps track of timers and other
               necessary information for BIND 10 to act as a slave server.
             </li></ul></div><p>
       </p><p>
-	These are ran automatically by <span class="command"><strong>bind10</strong></span>
-	and do not need to be run manually.
-      </p></div><div class="section" title="Managing BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="managing_once_running"></a>Managing BIND 10</h2></div></div></div><p>
-	Once BIND 10 is running, a few commands are used to interact
-	directly with the system:
+        These are ran automatically by <span class="command"><strong>bind10</strong></span>
+        and do not need to be run manually.
+      </p></div><div class="section" title="1.4. Managing BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="managing_once_running"></a>1.4. Managing BIND 10</h2></div></div></div><p>
+        Once BIND 10 is running, a few commands are used to interact
+        directly with the system:
         </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
               <span class="command"><strong>bindctl</strong></span> —
               interactive administration interface.
@@ -140,7 +142,7 @@
       and, of course, DNS. These include detailed developer
       documentation and code examples.
 
-    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229436567">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436859">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168229436878">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436939">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229437037">Build</a></span></dt><dt><span class="section"><a href="#id1168229437052">Install</a></span></dt><dt><span class="section"><a href="#id1168229437076">Install Hierarchy<
 /a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229436567"></a>Building Requirements</h2></div></div></div><p>
+    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id458364">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id458582">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id458605">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id458679">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id458789">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id458806">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id458832">2.3.6. Install Hi
 erarchy</a></span></dt></dl></dd></dl></div><div class="section" title="2.1. Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id458364"></a>2.1. Building Requirements</h2></div></div></div><p>
           In addition to the run-time requirements, building BIND 10
           from source code requires various development include headers.
         </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -154,12 +156,12 @@
   
   
         </p><p>
-	  To build BIND 10, also install the Botan (at least version
-	  1.8) and the log4cplus (at least version 1.0.3)
+          To build BIND 10, also install the Botan (at least version
+          1.8) and the log4cplus (at least version 1.0.3)
           development include headers.
         </p><p>
 
-	  The Python Library and Python _sqlite3 module are required to
+          The Python Library and Python _sqlite3 module are required to
           enable the Xfrout and Xfrin support.
         </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
           The Python related libraries and modules need to be built
@@ -169,7 +171,7 @@
           standard development headers, make, and pkg-config.
           BIND 10 builds have been tested with GCC g++ 3.4.3, 4.1.2,
           4.1.3, 4.2.1, 4.3.2, and 4.4.1; Clang++ 2.8; and Sun C++ 5.10.
-        </p></div><div class="section" title="Quick start"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart"></a>Quick start</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        </p></div><div class="section" title="2.2. Quick start"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart"></a>2.2. Quick start</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
           This quickly covers the standard steps for installing
           and deploying BIND 10 as an authoritative name server using
           its defaults. For troubleshooting, full customizations and further
@@ -198,20 +200,20 @@
             </p><pre class="screen">$ <strong class="userinput"><code>b10-loadzone <em class="replaceable"><code>your.zone.example.org</code></em></code></strong></pre><p>
           </p></li><li class="listitem">
             Test the new zone.
-          </li></ol></div></div><div class="section" title="Installation from source"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="install"></a>Installation from source</h2></div></div></div><p>
+          </li></ol></div></div><div class="section" title="2.3. Installation from source"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="install"></a>2.3. Installation from source</h2></div></div></div><p>
         BIND 10 is open source software written in C++ and Python.
         It is freely available in source code form from ISC via
         the Git code revision control system or as a downloadable
         tar file. It may also be available in pre-compiled ready-to-use
         packages from operating system vendors.
-      </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436859"></a>Download Tar File</h3></div></div></div><p>
+      </p><div class="section" title="2.3.1. Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id458582"></a>2.3.1. Download Tar File</h3></div></div></div><p>
           Downloading a release tar file is the recommended method to
           obtain the source code.
         </p><p>
           The BIND 10 releases are available as tar file downloads from
           <a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
           Periodic development snapshots may also be available.
-        </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436878"></a>Retrieve from Git</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.2. Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id458605"></a>2.3.2. Retrieve from Git</h3></div></div></div><p>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
           environment is not recommended.
@@ -245,7 +247,7 @@
           <span class="command"><strong>autoheader</strong></span>,
           <span class="command"><strong>automake</strong></span>,
           and related commands.
-        </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436939"></a>Configure before the build</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.3. Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id458679"></a>2.3.3. Configure before the build</h3></div></div></div><p>
           BIND 10 uses the GNU Build System to discover build environment
           details.
           To generate the makefiles using the defaults, simply run:
@@ -276,16 +278,16 @@
         </p><p>
           If the configure fails, it may be due to missing or old
           dependencies.
-        </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437037"></a>Build</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.4. Build"><div class="titlepage"><div><div><h3 class="title"><a name="id458789"></a>2.3.4. Build</h3></div></div></div><p>
     After the configure step is complete, to build the executables
     from the C++ code and prepare the Python scripts, run:
 
           </p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
-        </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437052"></a>Install</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.5. Install"><div class="titlepage"><div><div><h3 class="title"><a name="id458806"></a>2.3.5. Install</h3></div></div></div><p>
           To install the BIND 10 executables, support files,
           and documentation, run:
           </p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
-        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437076"></a>Install Hierarchy</h3></div></div></div><p>
+        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="2.3.6. Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id458832"></a>2.3.6. Install Hierarchy</h3></div></div></div><p>
           The following is the layout of the complete BIND 10 installation:
           </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
                 <code class="filename">bin/</code> —
@@ -315,7 +317,7 @@
                 <code class="filename">var/bind10-devel/</code> —
                 data source and configuration databases.
               </li></ul></div><p>
-        </p></div></div></div><div class="chapter" title="Chapter 3. Starting BIND10 with bind10"><div class="titlepage"><div><div><h2 class="title"><a name="bind10"></a>Chapter 3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">Configuration of started processes</a></span></dt></dl></div><p>
+        </p></div></div></div><div class="chapter" title="Chapter 3. Starting BIND10 with bind10"><div class="titlepage"><div><div><h2 class="title"><a name="bind10"></a>Chapter 3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configuration of started processes</a></span></dt></dl></div><p>
       BIND 10 provides the <span class="command"><strong>bind10</strong></span> command which
       starts up the required processes.
       <span class="command"><strong>bind10</strong></span>
@@ -345,7 +347,7 @@
       <span class="command"><strong>b10-xfrin</strong></span> for inbound DNS zone transfers,
       <span class="command"><strong>b10-xfrout</strong></span> for outbound DNS zone transfers,
       and <span class="command"><strong>b10-zonemgr</strong></span> for secondary service.
-    </p><div class="section" title="Starting BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="start"></a>Starting BIND 10</h2></div></div></div><p>
+    </p><div class="section" title="3.1. Starting BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="start"></a>3.1. Starting BIND 10</h2></div></div></div><p>
         To start the BIND 10 service, simply run <span class="command"><strong>bind10</strong></span>.
         Run it with the <code class="option">--verbose</code> switch to
         get additional debugging or diagnostic output.
@@ -354,7 +356,7 @@
           the process names for the Python-based daemons will be renamed
           to better identify them instead of just <span class="quote">“<span class="quote">python</span>”</span>.
           This is not needed on some operating systems.
-        </p></div></div><div class="section" title="Configuration of started processes"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="bind10.config"></a>Configuration of started processes</h2></div></div></div><p>
+        </p></div></div><div class="section" title="3.2. Configuration of started processes"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="bind10.config"></a>3.2. Configuration of started processes</h2></div></div></div><p>
         The processes to be started can be configured, with the exception
         of the <span class="command"><strong>b10-sockcreator</strong></span>, <span class="command"><strong>b10-msgq</strong></span>
         and <span class="command"><strong>b10-cfgmgr</strong></span>.
@@ -385,35 +387,35 @@
         during startup or shutdown. Unless specified, the component is started
         in usual way. This is the list of components that need to be started
         in a special way, with the value of special used for them:
-        </p><div class="table"><a name="id1168229437338"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr><tr><td align="left">setuid</td><td align="left">setuid</td><td align="left">Virtual component, see below</td></tr></tbody></table></div></div><p><br class="table-break">
+        </p><div class="table"><a name="id459146"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr><tr><td align="left">setuid</td><td align="left">setuid</td><td align="left">Virtual component, see below</td></tr></tbody></table></div></div><p><br class="table-break">
       </p><p>
-	The kind specifies how a failure of the component should
-	be handled.  If it is set to <span class="quote">“<span class="quote">dispensable</span>”</span>
-	(the default unless you set something else), it will get
-	started again if it fails. If it is set to <span class="quote">“<span class="quote">needed</span>”</span>
-	and it fails at startup, the whole <span class="command"><strong>bind10</strong></span>
-	shuts down and exits with error exit code. But if it fails
-	some time later, it is just started again. If you set it
-	to <span class="quote">“<span class="quote">core</span>”</span>, you indicate that the system is
-	not usable without the component and if such component
-	fails, the system shuts down no matter when the failure
-	happened.  This is the behaviour of the core components
-	(the ones you can't turn off), but you can declare any
-	other components as core as well if you wish (but you can
-	turn these off, they just can't fail).
+        The kind specifies how a failure of the component should
+        be handled.  If it is set to <span class="quote">“<span class="quote">dispensable</span>”</span>
+        (the default unless you set something else), it will get
+        started again if it fails. If it is set to <span class="quote">“<span class="quote">needed</span>”</span>
+        and it fails at startup, the whole <span class="command"><strong>bind10</strong></span>
+        shuts down and exits with error exit code. But if it fails
+        some time later, it is just started again. If you set it
+        to <span class="quote">“<span class="quote">core</span>”</span>, you indicate that the system is
+        not usable without the component and if such component
+        fails, the system shuts down no matter when the failure
+        happened.  This is the behaviour of the core components
+        (the ones you can't turn off), but you can declare any
+        other components as core as well if you wish (but you can
+        turn these off, they just can't fail).
       </p><p>
         The priority defines order in which the components should start.
         The ones with higher number are started sooner than the ones with
         lower ones. If you don't set it, 0 (zero) is used as the priority.
       </p><p>
         There are other parameters we didn't use in our example.
-	One of them is <span class="quote">“<span class="quote">address</span>”</span>. It is the address
-	used by the component on the <span class="command"><strong>b10-msgq</strong></span>
-	message bus. The special components already know their
-	address, but the usual ones don't. The address is by
-	convention the thing after <span class="emphasis"><em>b10-</em></span>, with
-	the first letter capital (eg. <span class="command"><strong>b10-stats</strong></span>
-	would have <span class="quote">“<span class="quote">Stats</span>”</span> as its address).
+        One of them is <span class="quote">“<span class="quote">address</span>”</span>. It is the address
+        used by the component on the <span class="command"><strong>b10-msgq</strong></span>
+        message bus. The special components already know their
+        address, but the usual ones don't. The address is by
+        convention the thing after <span class="emphasis"><em>b10-</em></span>, with
+        the first letter capital (eg. <span class="command"><strong>b10-stats</strong></span>
+        would have <span class="quote">“<span class="quote">Stats</span>”</span> as its address).
 
       </p><p>
         The last one is process. It is the name of the process to be started.
@@ -426,34 +428,34 @@
           such situation, so it would probably not do what you want. Such
           support is yet to be implemented.
         </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-	  The configuration is quite powerful, but that includes
-	  a lot of space for mistakes. You could turn off the
-	  <span class="command"><strong>b10-cmdctl</strong></span>, but then you couldn't
-	  change it back the usual way, as it would require it to
-	  be running (you would have to find and edit the configuration
-	  directly).  Also, some modules might have dependencies
-	  -- <span class="command"><strong>b10-stats-httpd</strong></span> need
-	  <span class="command"><strong>b10-stats</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>
-	  needs the <span class="command"><strong>b10-auth</strong></span> to be running, etc.
+          The configuration is quite powerful, but that includes
+          a lot of space for mistakes. You could turn off the
+          <span class="command"><strong>b10-cmdctl</strong></span>, but then you couldn't
+          change it back the usual way, as it would require it to
+          be running (you would have to find and edit the configuration
+          directly).  Also, some modules might have dependencies
+          -- <span class="command"><strong>b10-stats-httpd</strong></span> need
+          <span class="command"><strong>b10-stats</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>
+          needs the <span class="command"><strong>b10-auth</strong></span> to be running, etc.
 
 
 
         </p><p>
           In short, you should think twice before disabling something here.
         </p></div><p>
-	Now, to the mysterious setuid virtual component. If you
-	use the <span class="command"><strong>-u</strong></span> option to start the
-	<span class="command"><strong>bind10</strong></span> as root, but change the user
-	later, we need to start the <span class="command"><strong>b10-auth</strong></span> or
-	<span class="command"><strong>b10-resolver</strong></span> as root (until the socket
-	creator is finished). So we need to specify
-	the time when the switch from root do the given user happens
-	and that's what the setuid component is for. The switch is
-	done at the time the setuid component would be started, if
-	it was a process. The default configuration contains the
-	setuid component with priority 5, <span class="command"><strong>b10-auth</strong></span>
-	has 10 to be started before the switch and everything else
-	is without priority, so it is started after the switch.
+        Now, to the mysterious setuid virtual component. If you
+        use the <span class="command"><strong>-u</strong></span> option to start the
+        <span class="command"><strong>bind10</strong></span> as root, but change the user
+        later, we need to start the <span class="command"><strong>b10-auth</strong></span> or
+        <span class="command"><strong>b10-resolver</strong></span> as root (until the socket
+        creator is finished). So we need to specify
+        the time when the switch from root do the given user happens
+        and that's what the setuid component is for. The switch is
+        done at the time the setuid component would be started, if
+        it was a process. The default configuration contains the
+        setuid component with priority 5, <span class="command"><strong>b10-auth</strong></span>
+        has 10 to be started before the switch and everything else
+        is without priority, so it is started after the switch.
       </p></div></div><div class="chapter" title="Chapter 4. Command channel"><div class="titlepage"><div><div><h2 class="title"><a name="msgq"></a>Chapter 4. Command channel</h2></div></div></div><p>
         The BIND 10 components use the <span class="command"><strong>b10-msgq</strong></span>
         message routing daemon to communicate with other BIND 10 components.
@@ -514,7 +516,7 @@
       Normally it is not started manually, but is automatically
       started using the <span class="command"><strong>bind10</strong></span> master process
       (as covered in <a class="xref" href="#bind10" title="Chapter 3. Starting BIND10 with bind10">Chapter 3, <i>Starting BIND10 with <span class="command"><strong>bind10</strong></span></i></a>).
-    </p></div><div class="chapter" title="Chapter 6. Remote control daemon"><div class="titlepage"><div><div><h2 class="title"><a name="cmdctl"></a>Chapter 6. Remote control daemon</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 6. Remote control daemon"><div class="titlepage"><div><div><h2 class="title"><a name="cmdctl"></a>Chapter 6. Remote control daemon</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></div><p>
       <span class="command"><strong>b10-cmdctl</strong></span> is the gateway between
       administrators and the BIND 10 system.
       It is a HTTPS server that uses standard HTTP Digest
@@ -576,7 +578,7 @@
       Each HTTPS connection is stateless and timesout in 1200 seconds
       by default.  This can be
       redefined by using the <code class="option">--idle-timeout</code> command line argument.
-    </p><div class="section" title="Configuration specification for b10-cmdctl"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmdctl.spec"></a>Configuration specification for b10-cmdctl</h2></div></div></div><p>
+    </p><div class="section" title="6.1. Configuration specification for b10-cmdctl"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmdctl.spec"></a>6.1. Configuration specification for b10-cmdctl</h2></div></div></div><p>
         The configuration items for <span class="command"><strong>b10-cmdctl</strong></span> are:
 key_file
 cert_file
@@ -610,12 +612,12 @@ shutdown
       the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
       channel) the configuration on to the specified module.
     </p><p>
-    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438007">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438072">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438171">Loading Master Zones Files</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id459869">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id459942">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id459978">8.3. Loading Master Zones Files</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
       It supports EDNS0 and DNSSEC. It supports IPv6.
       Normally it is started by the <span class="command"><strong>bind10</strong></span> master
       process.
-    </p><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438007"></a>Server Configurations</h2></div></div></div><p>
+    </p><div class="section" title="8.1. Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id459869"></a>8.1. Server Configurations</h2></div></div></div><p>
         <span class="command"><strong>b10-auth</strong></span> is configured via the
         <span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
         The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -635,7 +637,7 @@ This may be a temporary setting until then.
         </p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
               </dd></dl></div><p>
 
-      </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438072"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      </p></div><div class="section" title="8.2. Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id459942"></a>8.2. Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
         supports a SQLite3 data source backend and in-memory data source
         backend.
@@ -649,7 +651,7 @@ This may be a temporary setting until then.
         The default is <code class="filename">/usr/local/var/</code>.)
   This data file location may be changed by defining the
   <span class="quote">“<span class="quote">database_file</span>”</span> configuration.
-      </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438171"></a>Loading Master Zones Files</h2></div></div></div><p>
+      </p></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id459978"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
         RFC 1035 style DNS master zone files may imported
         into a BIND 10 data source by using the
         <span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -678,7 +680,7 @@ This may be a temporary setting until then.
         If you reload a zone already existing in the database,
         all records from that prior zone disappear and a whole new set
         appears.
-      </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438302">Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438340">Enabling IXFR</a></span></dt><dt><span class="section"><a href="#id1168229438382">Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
+      </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id460133">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id460177">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#id460320">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
       Incoming zones are transferred using the <span class="command"><strong>b10-xfrin</strong></span>
       process which is started by <span class="command"><strong>bind10</strong></span>.
       When received, the zone is stored in the corresponding BIND 10
@@ -696,15 +698,15 @@ This may be a temporary setting until then.
      In the current development release of BIND 10, incoming zone
      transfers are only available for SQLite3-based data sources,
      that is, they don't work for an in-memory data source.
-    </p></div><div class="section" title="Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438302"></a>Configuration for Incoming Zone Transfers</h2></div></div></div><p>
-	In practice, you need to specify a list of secondary zones to
-	enable incoming zone transfers for these zones (you can still
-	trigger a zone transfer manually, without a prior configuration
-	(see below)).
+    </p></div><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id460133"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
+        In practice, you need to specify a list of secondary zones to
+        enable incoming zone transfers for these zones (you can still
+        trigger a zone transfer manually, without a prior configuration
+        (see below)).
       </p><p>
-	For example, to enable zone transfers for a zone named "example.com"
-	(whose master address is assumed to be 2001:db8::53 here),
-	run the following at the <span class="command"><strong>bindctl</strong></span> prompt:
+        For example, to enable zone transfers for a zone named "example.com"
+        (whose master address is assumed to be 2001:db8::53 here),
+        run the following at the <span class="command"><strong>bindctl</strong></span> prompt:
 
       </p><pre class="screen">> <strong class="userinput"><code>config add Xfrin/zones</code></strong>
 > <strong class="userinput"><code>config set Xfrin/zones[0]/name "<code class="option">example.com</code>"</code></strong>
@@ -712,7 +714,7 @@ This may be a temporary setting until then.
 > <strong class="userinput"><code>config commit</code></strong></pre><p>
 
       (We assume there has been no zone configuration before).
-      </p></div><div class="section" title="Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438340"></a>Enabling IXFR</h2></div></div></div><p>
+      </p></div><div class="section" title="9.2. Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id460177"></a>9.2. Enabling IXFR</h2></div></div></div><p>
         As noted above, <span class="command"><strong>b10-xfrin</strong></span> uses AXFR for
         zone transfers by default.  To enable IXFR for zone transfers
         for a particular zone, set the <strong class="userinput"><code>use_ixfr</code></strong>
@@ -734,12 +736,42 @@ This may be a temporary setting until then.
       make this selection automatically.
       These features will be implemented in a near future
       version, at which point we will enable IXFR by default.
-      </p></div></div><div class="section" title="Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438382"></a>Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
-	To manually trigger a zone transfer to retrieve a remote zone,
-	you may use the <span class="command"><strong>bindctl</strong></span> utility.
-	For example, at the <span class="command"><strong>bindctl</strong></span> prompt run:
+      </p></div></div><div class="section" title="9.3. Secondary Manager"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="zonemgr"></a>9.3. Secondary Manager</h2></div></div></div><p>
+        The <span class="command"><strong>b10-zonemgr</strong></span> process is started by
+        <span class="command"><strong>bind10</strong></span>.
+        It keeps track of SOA refresh, retry, and expire timers
+        and other details for BIND 10 to perform as a slave.
+        When the <span class="command"><strong>b10-auth</strong></span> authoritative DNS server
+        receives a NOTIFY message, <span class="command"><strong>b10-zonemgr</strong></span>
+        may tell <span class="command"><strong>b10-xfrin</strong></span> to do a refresh
+        to start an inbound zone transfer.
+        The secondary manager resets its counters when a new zone is
+        transferred in.
+      </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        Access control (such as allowing notifies) is not yet provided.
+        The primary/secondary service is not yet complete.
+      </p></div><p>
+        The following example shows using <span class="command"><strong>bindctl</strong></span>
+        to configure the server to be a secondary for the example zone:
+
+      </p><pre class="screen">> <strong class="userinput"><code>config add Zonemgr/secondary_zones</code></strong>
+> <strong class="userinput"><code>config set Zonemgr/secondary_zones[0]/name "<code class="option">example.com</code>"</code></strong>
+> <strong class="userinput"><code>config set Zonemgr/secondary_zones[0]/class "<code class="option">IN</code>"</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
 
-	</p><pre class="screen">> <strong class="userinput"><code>Xfrin retransfer zone_name="<code class="option">foo.example.org</code>" master=<code class="option">192.0.2.99</code></code></strong></pre><p>
+
+
+      </p><p>
+        If the zone does not exist in the data source already
+        (i.e. no SOA record for it), <span class="command"><strong>b10-zonemgr</strong></span>
+        will automatically tell <span class="command"><strong>b10-xfrin</strong></span>
+        to transfer the zone in.
+      </p></div><div class="section" title="9.4. Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id460320"></a>9.4. Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
+        To manually trigger a zone transfer to retrieve a remote zone,
+        you may use the <span class="command"><strong>bindctl</strong></span> utility.
+        For example, at the <span class="command"><strong>bindctl</strong></span> prompt run:
+
+        </p><pre class="screen">> <strong class="userinput"><code>Xfrin retransfer zone_name="<code class="option">foo.example.org</code>" master=<code class="option">192.0.2.99</code></code></strong></pre><p>
       </p></div></div><div class="chapter" title="Chapter 10. Outbound Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrout"></a>Chapter 10. Outbound Zone Transfers</h2></div></div></div><p>
       The <span class="command"><strong>b10-xfrout</strong></span> process is started by
       <span class="command"><strong>bind10</strong></span>.
@@ -768,9 +800,9 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 > <strong class="userinput"><code>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1"},</code></strong>
 <strong class="userinput"><code>                                                 {"action": "ACCEPT", "from": "2001:db8::1"}]</code></strong>
 > <strong class="userinput"><code>config commit</code></strong></pre><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-	In the above example the lines
-	for <code class="option">transfer_acl</code> were divided for
-	readability.  In the actual input it must be in a single line.
+        In the above example the lines
+        for <code class="option">transfer_acl</code> were divided for
+        readability.  In the actual input it must be in a single line.
     </p></div><p>
       If you want to require TSIG in access control, a separate TSIG
       "key ring" must be configured specifically
@@ -787,25 +819,11 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
       This is necessary because the <span class="command"><strong>b10-auth</strong></span> server
       also checks TSIGs and it uses the system wide configuration.
     </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-	In a future version, <span class="command"><strong>b10-xfrout</strong></span> will also
-	use the system wide TSIG configuration.
-	The way to specify zone specific configuration (ACLs, etc) is
-	likely to be changed, too.
-    </p></div></div><div class="chapter" title="Chapter 11. Secondary Manager"><div class="titlepage"><div><div><h2 class="title"><a name="zonemgr"></a>Chapter 11. Secondary Manager</h2></div></div></div><p>
-      The <span class="command"><strong>b10-zonemgr</strong></span> process is started by
-      <span class="command"><strong>bind10</strong></span>.
-      It keeps track of SOA refresh, retry, and expire timers
-      and other details for BIND 10 to perform as a slave.
-      When the <span class="command"><strong>b10-auth</strong></span> authoritative DNS server
-      receives a NOTIFY message, <span class="command"><strong>b10-zonemgr</strong></span>
-      may tell <span class="command"><strong>b10-xfrin</strong></span> to do a refresh
-      to start an inbound zone transfer.
-      The secondary manager resets its counters when a new zone is
-      transferred in.
-    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-     Access control (such as allowing notifies) is not yet provided.
-     The primary/secondary service is not yet complete.
-    </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438673">Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438891">Forwarding</a></span></dt></dl></div><p>
+        In a future version, <span class="command"><strong>b10-xfrout</strong></span> will also
+        use the system wide TSIG configuration.
+        The way to specify zone specific configuration (ACLs, etc) is
+        likely to be changed, too.
+    </p></div></div><div class="chapter" title="Chapter 11. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 11. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id460633">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id460772">11.2. Forwarding</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-resolver</strong></span> process is started by
       <span class="command"><strong>bind10</strong></span>.
 
@@ -844,31 +862,31 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 </pre><p>
     </p><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
        as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
-       Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438673"></a>Access Control</h2></div></div></div><p>
+       Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="11.1. Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id460633"></a>11.1. Access Control</h2></div></div></div><p>
         By default, the <span class="command"><strong>b10-resolver</strong></span> daemon only accepts
         DNS queries from the localhost (127.0.0.1 and ::1).
         The <code class="option">Resolver/query_acl</code> configuration may
-	be used to reject, drop, or allow specific IPs or networks.
+        be used to reject, drop, or allow specific IPs or networks.
         This configuration list is first match.
       </p><p>
-	The configuration's <code class="option">action</code> item may be
-	set to <span class="quote">“<span class="quote">ACCEPT</span>”</span> to allow the incoming query,
-	<span class="quote">“<span class="quote">REJECT</span>”</span> to respond with a DNS REFUSED return
-	code, or <span class="quote">“<span class="quote">DROP</span>”</span> to ignore the query without
-	any response (such as a blackhole).  For more information,
-	see the respective debugging messages:  <a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_ACCEPTED" target="_top">RESOLVER_QUERY_ACCEPTED</a>,
-	<a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_REJECTED" target="_top">RESOLVER_QUERY_REJECTED</a>,
-	and <a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_DROPPED" target="_top">RESOLVER_QUERY_DROPPED</a>.
+        The configuration's <code class="option">action</code> item may be
+        set to <span class="quote">“<span class="quote">ACCEPT</span>”</span> to allow the incoming query,
+        <span class="quote">“<span class="quote">REJECT</span>”</span> to respond with a DNS REFUSED return
+        code, or <span class="quote">“<span class="quote">DROP</span>”</span> to ignore the query without
+        any response (such as a blackhole).  For more information,
+        see the respective debugging messages:  <a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_ACCEPTED" target="_top">RESOLVER_QUERY_ACCEPTED</a>,
+        <a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_REJECTED" target="_top">RESOLVER_QUERY_REJECTED</a>,
+        and <a class="ulink" href="bind10-messages.html#RESOLVER_QUERY_DROPPED" target="_top">RESOLVER_QUERY_DROPPED</a>.
       </p><p>
-	The required configuration's <code class="option">from</code> item is set
+        The required configuration's <code class="option">from</code> item is set
         to an IPv4 or IPv6 address, addresses with an network mask, or to
-	the special lowercase keywords <span class="quote">“<span class="quote">any6</span>”</span> (for
-	any IPv6 address) or <span class="quote">“<span class="quote">any4</span>”</span> (for any IPv4
-	address).
+        the special lowercase keywords <span class="quote">“<span class="quote">any6</span>”</span> (for
+        any IPv6 address) or <span class="quote">“<span class="quote">any4</span>”</span> (for any IPv4
+        address).
       </p><p>
-	For example to allow the <em class="replaceable"><code>192.168.1.0/24</code></em>
-	network to use your recursive name server, at the
-	<span class="command"><strong>bindctl</strong></span> prompt run:
+        For example to allow the <em class="replaceable"><code>192.168.1.0/24</code></em>
+        network to use your recursive name server, at the
+        <span class="command"><strong>bindctl</strong></span> prompt run:
       </p><pre class="screen">
 > <strong class="userinput"><code>config add Resolver/query_acl</code></strong>
 > <strong class="userinput"><code>config set Resolver/query_acl[<em class="replaceable"><code>2</code></em>]/action "ACCEPT"</code></strong>
@@ -877,7 +895,7 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 </pre><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
        as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
        Resolver/query_acl</code></strong></span>”</span> if needed.)</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>This prototype access control configuration
-      syntax may be changed.</p></div></div><div class="section" title="Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438891"></a>Forwarding</h2></div></div></div><p>
+      syntax may be changed.</p></div></div><div class="section" title="11.2. Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id460772"></a>11.2. Forwarding</h2></div></div></div><p>
 
         To enable forwarding, the upstream address and port must be
         configured to forward queries to, such as:
@@ -897,7 +915,260 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 > <strong class="userinput"><code>config set Resolver/forward_addresses []</code></strong>
 > <strong class="userinput"><code>config commit</code></strong>
 </pre><p>
-      </p></div></div><div class="chapter" title="Chapter 13. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 13. Statistics</h2></div></div></div><p>
+      </p></div></div><div class="chapter" title="Chapter 12. DHCPv4 Server"><div class="titlepage"><div><div><h2 class="title"><a name="dhcp4"></a>Chapter 12. DHCPv4 Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></div><p>Dynamic Host Configuration Protocol for IPv4 (DHCP or
+    DHCPv4) and Dynamic Host Configuration Protocol for IPv6 (DHCPv6)
+    are protocols that allow one node (server) to provision
+    configuration parameters to many hosts and devices (clients). To
+    ease deployment in larger networks, additional nodes (relays) may
+    be deployed that facilitate communication between servers and
+    clients. Even though principles of both DHCPv4 and DHCPv6 are
+    somewhat similar, these are two radically different
+    protocols. BIND10 offers server implementations for both DHCPv4
+    and DHCPv6. This chapter is about DHCP for IPv4. For description of
+    DHCPv6 server, see <a class="xref" href="#dhcp6" title="Chapter 13. DHCPv6 Server">Chapter 13, <i>DHCPv6 Server</i></a>.</p><p>DHCPv6 server component is currently under intense
+    development. You may want to check out <a class="ulink" href="http://bind10.isc.org/wiki/Kea" target="_top">BIND10 DHCP (Kea) wiki</a>
+    and recent posts on <a class="ulink" href="https://lists.isc.org/mailman/listinfo/bind10-dev" target="_top">BIND10
+    developers mailing list</a>.</p><p>DHCPv4 and DHCPv6 components in BIND10 architecture are
+    internally code named <span class="quote">“<span class="quote">Kea</span>”</span>.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        As of December 2011, both DHCPv4 and DHCPv6 components are
+        skeleton servers. That means that while they are capable of
+        performing DHCP configuration, they are not fully functional
+        yet. In particular, both do not have functional lease
+        databases. This means that they will assign the same, fixed,
+        hardcoded addresses to any client that will ask. See <a class="xref" href="#dhcp4-limit" title="12.4. DHCPv4 Server Limitations">Section 12.4, “DHCPv4 Server Limitations”</a> and <a class="xref" href="#dhcp6-limit" title="13.4. DHCPv6 Server Limitations">Section 13.4, “DHCPv6 Server Limitations”</a> for
+        detailed description.
+      </p></div><div class="section" title="12.1. DHCPv4 Server Usage"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp4-usage"></a>12.1. DHCPv4 Server Usage</h2></div></div></div><p>BIND10 provides DHCPv4 server component since December
+      2011. It is a skeleton server and can be described as an early
+      prototype that is not fully functional yet. It is mature enough
+      to conduct first tests in lab environment, but it has
+      significant limitations. See <a class="xref" href="#dhcp4-limit" title="12.4. DHCPv4 Server Limitations">Section 12.4, “DHCPv4 Server Limitations”</a> for
+      details.
+      </p><p>
+        DHCPv4 server is implemented as <span class="command"><strong>b10-dhcp4</strong></span>
+        daemon. As it is not configurable yet, it is fully autonomous,
+        i.e. it does not interact with <span class="command"><strong>b10-cfgmgr</strong></span>.
+        To start DHCPv4 server, simply input:
+
+        </p><pre class="screen">
+#<strong class="userinput"><code>cd src/bin/dhcp4</code></strong>
+#<strong class="userinput"><code>./b10-dhcp4</code></strong>
+</pre><p>
+
+        Depending on your installation, <span class="command"><strong>b10-dhcp4</strong></span>
+        binary may reside in src/bin/dhcp4 in your source code
+        directory, in /usr/local/bin/b10-dhcp4 or other directory
+        you specified during compilation.
+
+        After start, server will detect available network interfaces
+        and will attempt to open UDP sockets on all interfaces that
+        are up, running, are not loopback and have IPv4 address
+        assigned.
+
+        Server will then listen to incoming traffic. Currently
+        supported client messages are DISCOVER and REQUEST. Server
+        will respond to them with OFFER and ACK, respectively.
+
+        As DHCPv4 server opens privileged ports, it requires root
+        access. Make sure you run this daemon as root.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+            Integration with <span class="command"><strong>bind10</strong></span> is
+            planned. Ultimately, <span class="command"><strong>b10-dhcp4</strong></span> will not
+            be started directly, but rather via
+            <span class="command"><strong>bind10</strong></span>. Please be aware of this planned
+            change.
+          </p></div></div><div class="section" title="12.2. DHCPv4 Server Configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp4-config"></a>12.2. DHCPv4 Server Configuration</h2></div></div></div><p>
+        DHCPv4 server does not have lease database implemented yet
+        or any support for configuration, so every time the same set
+        of configuration options (including the same fixed address)
+        will be assigned every time.
+      </p><p>
+        At this stage of development, the only way to alter server
+        configuration is to tweak its source code. To do so, please
+        edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
+        parameters and recompile:
+        </p><pre class="screen">
+const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
+const std::string HARDCODED_NETMASK = "255.255.255.0";
+const uint32_t    HARDCODED_LEASE_TIME = 60; // in seconds
+const std::string HARDCODED_GATEWAY = "192.0.2.1";
+const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
+const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
+const std::string HARDCODED_SERVER_ID = "192.0.2.1";</pre><p>
+
+        Lease database and configuration support is planned for 2012.
+      </p></div><div class="section" title="12.3. Supported standards"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp4-std"></a>12.3. Supported standards</h2></div></div></div><p>The following standards and draft standards are currently
+      supported:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">RFC2131: Supported messages are DISCOVER, OFFER,
+            REQUEST, and ACK.</li><li class="listitem">RFC2132: Supported options are: PAD (0),
+            END(255), Message Type(53), DHCP Server Identifier (54),
+            Domain Name (15), DNS Servers (6), IP Address Lease Time
+            (51), Subnet mask (1), and Routers (3).</li></ul></div></div><div class="section" title="12.4. DHCPv4 Server Limitations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp4-limit"></a>12.4. DHCPv4 Server Limitations</h2></div></div></div><p> These are the current limitations of DHCPv4 server
+      software. Most of them are reflections of the early stage of
+      development and should be treated as <span class="quote">“<span class="quote">not implemented
+      yet</span>”</span>, rather than actual limitations.</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">During initial IPv4 node configuration, server is
+            expected to send packets to a node that does not have IPv4
+            address assigned yet. Server requires certain tricks (or
+            hacks) to transmit such packets. This is not implemented
+            yet, therefore DHCPv4 server supports relayed traffic only
+            (that is normal point to point communication).</li><li class="listitem"><span class="command"><strong>b10-dhcp4</strong></span> provides a single,
+            fixed, hardcoded lease to any client that asks.  There is
+            no lease manager implemented. If two clients request
+            addresses, they will both get the same fixed
+            address.</li><li class="listitem"><span class="command"><strong>b10-dhcp4</strong></span> does not support any
+            configuration mechanisms yet. The whole configuration is
+            currently hardcoded. The only way to tweak configuration
+            is to directly modify source code. See see <a class="xref" href="#dhcp4-config" title="12.2. DHCPv4 Server Configuration">Section 12.2, “DHCPv4 Server Configuration”</a> for details.</li><li class="listitem">Upon start, server will open sockets on all
+            interfaces that are not loopback, are up and running and
+            have IPv4 address.  Support for multiple interfaces is not
+            coded in reception routines yet, so if you are running
+            this code on a machine that has many interfaces and
+            <span class="command"><strong>b10-dhcp4</strong></span> happens to listen on wrong
+            interface, the easiest way to work around this problem is
+            to turn down other interfaces. This limitation will be
+            fixed shortly.</li><li class="listitem">PRL (Parameter Request List, a list of options
+            requested by a client) is currently ignored and server
+            assigns DNS SERVER and DOMAIN NAME options.</li><li class="listitem"><span class="command"><strong>b10-dhcp4</strong></span> does not support
+            BOOTP. That is a design choice. This limitation is
+            permanent. If you have legacy nodes that can't use DHCP and
+            require BOOTP support, please use latest version of ISC DHCP
+            <a class="ulink" href="http://www.isc.org/software/dhcp" target="_top">http://www.isc.org/software/dhcp</a>.</li><li class="listitem">Interface detection is currently working on Linux
+            only. See <a class="xref" href="#iface-detect" title="14.1. Interface detection">Section 14.1, “Interface detection”</a> for details.</li><li class="listitem"><span class="command"><strong>b10-dhcp4</strong></span> does not verify that
+            assigned address is unused. According to RFC2131, the
+            allocating server should verify that address is no used by
+            sending ICMP echo request.</li><li class="listitem">Address renewal (RENEW), rebinding (REBIND),
+            confirmation (CONFIRM), duplication report (DECLINE) and
+            release (RELEASE) are not supported yet.</li><li class="listitem">DNS Update is not supported yet.</li><li class="listitem">-v (verbose) command line option is currently
+            permanently enabled.</li></ul></div></div></div><div class="chapter" title="Chapter 13. DHCPv6 Server"><div class="titlepage"><div><div><h2 class="title"><a name="dhcp6"></a>Chapter 13. DHCPv6 Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></div><p>Dynamic Host Configuration Protocol for IPv6 (DHCPv6) is
+    specified in RFC3315. BIND10 provides DHCPv6 server implementation
+    that is described in this chapter. For DHCPv4 server
+    implementation, see <a class="xref" href="#dhcp4" title="Chapter 12. DHCPv4 Server">Chapter 12, <i>DHCPv4 Server</i></a>.
+    </p><p>DHCPv6 server component is currently under intense
+    development. You may want to check out <a class="ulink" href="http://bind10.isc.org/wiki/Kea" target="_top">BIND10 DHCP (Kea) wiki</a>
+    and recent posts on <a class="ulink" href="https://lists.isc.org/mailman/listinfo/bind10-dev" target="_top">BIND10
+    developers mailing list</a>.</p><p>DHCPv4 and DHCPv6 components in BIND10 architecture are
+    internally code named <span class="quote">“<span class="quote">Kea</span>”</span>.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        As of December 2011, both DHCPv4 and DHCPv6 components are
+        skeleton servers. That means that while they are capable of
+        performing DHCP configuration, they are not fully functional
+        yet. In particular, both do not have functional lease
+        databases. This means that they will assign the same, fixed,
+        hardcoded addresses to any client that will ask. See <a class="xref" href="#dhcp4-limit" title="12.4. DHCPv4 Server Limitations">Section 12.4, “DHCPv4 Server Limitations”</a> and <a class="xref" href="#dhcp6-limit" title="13.4. DHCPv6 Server Limitations">Section 13.4, “DHCPv6 Server Limitations”</a> for
+        detailed description.
+      </p></div><div class="section" title="13.1. DHCPv6 Server Usage"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp6-usage"></a>13.1. DHCPv6 Server Usage</h2></div></div></div><p>
+        BIND10 provides DHCPv6 server component since September
+        2011. It is a skeleton server and can be described as an early
+        prototype that is not fully functional yet. It is mature
+        enough to conduct first tests in lab environment, but it has
+        significant limitations. See <a class="xref" href="#dhcp6-limit" title="13.4. DHCPv6 Server Limitations">Section 13.4, “DHCPv6 Server Limitations”</a> for
+        details.
+      </p><p>
+        DHCPv6 server is implemented as <span class="command"><strong>b10-dhcp6</strong></span>
+        daemon. As it is not configurable yet, it is fully autonomous,
+        i.e. it does not interact with <span class="command"><strong>b10-cfgmgr</strong></span>.
+        To start DHCPv6 server, simply input:
+
+        </p><pre class="screen">
+#<strong class="userinput"><code>cd src/bin/dhcp6</code></strong>
+#<strong class="userinput"><code>./b10-dhcp6</code></strong>
+</pre><p>
+
+        Depending on your installation, <span class="command"><strong>b10-dhcp6</strong></span>
+        binary may reside in src/bin/dhcp6 in your source code
+        directory, in /usr/local/bin/b10-dhcp6 or other directory
+        you specified during compilation.
+
+        After start, server will detect available network interfaces
+        and will attempt to open UDP sockets on all interfaces that
+        are up, running, are not loopback, are multicast-capable and
+        have IPv6 address assigned.
+
+        Server will then listen to incoming traffic. Currently
+        supported client messages are SOLICIT and REQUEST. Server
+        will respond to them with ADVERTISE and REPLY, respectively.
+
+        As DHCPv6 server opens privileged ports, it requires root
+        access. Make sure you run this daemon as root.
+      </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+            Integration with <span class="command"><strong>bind10</strong></span> is
+            planned. Ultimately, <span class="command"><strong>b10-dhcp6</strong></span> will not
+            be started directly, but rather via
+            <span class="command"><strong>bind10</strong></span>. Please be aware of this planned
+            change.
+          </p></div></div><div class="section" title="13.2. DHCPv6 Server Configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp6-config"></a>13.2. DHCPv6 Server Configuration</h2></div></div></div><p>
+        DHCPv4 server does not have lease database implemented yet
+        or any support for configuration, so every time the same set
+        of configuration options (including the same fixed address)
+        will be assigned every time.
+      </p><p>
+        At this stage of development, the only way to alter server
+        configuration is to tweak its source code. To do so, please
+        edit src/bin/dhcp6/dhcp6_srv.cc file and modify following
+        parameters and recompile:
+        </p><pre class="screen">
+const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
+const uint32_t HARDCODED_T1 = 1500; // in seconds
+const uint32_t HARDCODED_T2 = 2600; // in seconds
+const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
+const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
+const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";</pre><p>
+
+        Lease database and configuration support is planned for 2012.
+      </p></div><div class="section" title="13.3. Supported DHCPv6 Standards"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp6-std"></a>13.3. Supported DHCPv6 Standards</h2></div></div></div><p>The following standards and draft standards are currently
+      supported:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">RFC3315: Supported messages are SOLICIT,
+            ADVERTISE, REQUEST, and REPLY. Supported options are
+            SERVER_ID, CLIENT_ID, IA_NA, and IAADDRESS.</li><li class="listitem">RFC3646: Supported option is DNS_SERVERS.</li></ul></div></div><div class="section" title="13.4. DHCPv6 Server Limitations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dhcp6-limit"></a>13.4. DHCPv6 Server Limitations</h2></div></div></div><p> These are the current limitations of DHCPv6 server
+      software. Most of them are reflections of the early stage of
+      development and should be treated as <span class="quote">“<span class="quote">not implemented
+      yet</span>”</span>, rather than actual limitations.</p><p>
+      </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">Relayed traffic is not supported.</li><li class="listitem"><span class="command"><strong>b10-dhcp6</strong></span> provides a single,
+          fixed, hardcoded lease to any client that asks. There is no
+          lease manager implemented. If two clients request addresses,
+          they will both get the same fixed address.</li><li class="listitem"><span class="command"><strong>b10-dhcp6</strong></span> does not support any
+            configuration mechanisms yet. The whole configuration is
+            currently hardcoded. The only way to tweak configuration
+            is to directly modify source code. See see <a class="xref" href="#dhcp6-config" title="13.2. DHCPv6 Server Configuration">Section 13.2, “DHCPv6 Server Configuration”</a> for details.</li><li class="listitem">Upon start, server will open sockets on all
+          interfaces that are not loopback, are up, running and are
+          multicast capable and have IPv6 address.  Support for
+          multiple interfaces is not coded in reception routines yet,
+          so if you are running this code on a machine that has many
+          interfaces and <span class="command"><strong>b10-dhcp6</strong></span> happens to
+          listen on wrong interface, the easiest way to work around
+          this problem is to turn down other interfaces. This
+          limitation will be fixed shortly.</li><li class="listitem">ORO (Option Request Option, a list of options
+          requested by a client) is currently ignored and server
+          assigns DNS SERVER option.</li><li class="listitem">Temporary addresses are not supported yet.</li><li class="listitem">Prefix delegation is not supported yet.</li><li class="listitem">Address renewal (RENEW), rebinding (REBIND),
+          confirmation (CONFIRM), duplication report (DECLINE) and
+          release (RELEASE) are not supported yet.</li><li class="listitem">DNS Update is not supported yet.</li><li class="listitem">Interface detection is currently working on Linux
+          only. See <a class="xref" href="#iface-detect" title="14.1. Interface detection">Section 14.1, “Interface detection”</a> for details.</li><li class="listitem">-v (verbose) command line option is currently permanently
+          enabled.</li></ul></div><p>
+      </p></div></div><div class="chapter" title="Chapter 14. libdhcp++ library"><div class="titlepage"><div><div><h2 class="title"><a name="libdhcp"></a>Chapter 14. libdhcp++ library</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#packet-handling">14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></div><p>libdhcp++ is a common library written in C++ that handles
+    many DHCP-related tasks, like DHCPv4 and DHCPv6 packets parsing,
+    manipulation and assembly, option parsing, manipulation and
+    assembly, network interface detection and socket operations, like
+    socket creations, data transmission and reception and socket
+    closing.
+    </p><p>
+    While this library is currently used by
+    <span class="command"><strong>b10-dhcp4</strong></span> and <span class="command"><strong>b10-dhcp6</strong></span>
+    only, it is designed to be portable, universal library useful for
+    any kind of DHCP-related software.
+    </p><div class="section" title="14.1. Interface detection"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="iface-detect"></a>14.1. Interface detection</h2></div></div></div><p>Both DHCPv4 and DHCPv6 components share network
+      interface detection routines. Interface detection is
+      currently only supported on Linux systems.</p><p>For non-linux systems, there is currently stub
+      implementation provided. As DHCP servers need to know available
+      addresses, there is a simple mechanism implemented to provide
+      that information. User is expected to create interfaces.txt
+      file. Format of this file is simple. It contains list of
+      interfaces along with available address on each interface. This
+      mechanism is temporary and is going to be removed as soon as
+      interface detection becomes available on non-linux
+      systems. Example of interfaces.txt file looks as follows:
+      </p><pre class="screen">
+# For DHCPv6, please specify link-local address (starts with fe80::)
+# If in doubt, check output of 'ifconfig -a' command.
+eth0 fe80::21e:8cff:fe9b:7349
+
+# For DHCPv4, please use following format:
+#eth0 192.0.2.5</pre><p>
+      </p></div><div class="section" title="14.2. DHCPv4/DHCPv6 packet handling"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="packet-handling"></a>14.2. DHCPv4/DHCPv6 packet handling</h2></div></div></div><p>TODO: Describe packet handling here, with pointers to wiki</p></div></div><div class="chapter" title="Chapter 15. Statistics"><div class="titlepage"><div><div><h2 class="title"><a name="statistics"></a>Chapter 15. Statistics</h2></div></div></div><p>
       The <span class="command"><strong>b10-stats</strong></span> process is started by
       <span class="command"><strong>bind10</strong></span>.
       It periodically collects statistics data from various modules
@@ -931,52 +1202,52 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
     }
 }
        </pre><p>
-    </p></div><div class="chapter" title="Chapter 14. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 14. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229439042">Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439052">Loggers</a></span></dt><dt><span class="section"><a href="#id1168229439294">Output Options</a></span></dt><dt><span class="section"><a href="#id1168229439468">Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id1168229440023">Logging Message Format</a></span></dt></dl></div><div class="section" title="Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229439042"></a>Logging configuration</h2></div></div></div><p>
+    </p></div><div class="chapter" title="Chapter 16. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 16. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id461660">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id461675">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id461978">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id462172">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id462428">16.2. Logging Message Format</a></span></dt></dl></div><div class="section" title="16.1. Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id461660"></a>16.1. Logging configuration</h2></div></div></div><p>
 
-	The logging system in BIND 10 is configured through the
-	Logging module. All BIND 10 modules will look at the
-	configuration in Logging to see what should be logged and
-	to where.
+        The logging system in BIND 10 is configured through the
+        Logging module. All BIND 10 modules will look at the
+        configuration in Logging to see what should be logged and
+        to where.
 
 
 
-      </p><div class="section" title="Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229439052"></a>Loggers</h3></div></div></div><p>
+      </p><div class="section" title="16.1.1. Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="id461675"></a>16.1.1. Loggers</h3></div></div></div><p>
 
-	  Within BIND 10, a message is logged through a component
-	  called a "logger". Different parts of BIND 10 log messages
-	  through different loggers, and each logger can be configured
-	  independently of one another.
+          Within BIND 10, a message is logged through a component
+          called a "logger". Different parts of BIND 10 log messages
+          through different loggers, and each logger can be configured
+          independently of one another.
 
         </p><p>
 
-	  In the Logging module, you can specify the configuration
-	  for zero or more loggers; any that are not specified will
-	  take appropriate default values..
+          In the Logging module, you can specify the configuration
+          for zero or more loggers; any that are not specified will
+          take appropriate default values..
 
         </p><p>
 
-	  The three most important elements of a logger configuration
-	  are the <code class="option">name</code> (the component that is
-	  generating the messages), the <code class="option">severity</code>
-	  (what to log), and the <code class="option">output_options</code>
-	  (where to log).
-
-        </p><div class="section" title="name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439077"></a>name (string)</h4></div></div></div><p>
-	  Each logger in the system has a name, the name being that
-	  of the component using it to log messages. For instance,
-	  if you want to configure logging for the resolver module,
-	  you add an entry for a logger named <span class="quote">“<span class="quote">Resolver</span>”</span>. This
-	  configuration will then be used by the loggers in the
-	  Resolver module, and all the libraries used by it.
+          The three most important elements of a logger configuration
+          are the <code class="option">name</code> (the component that is
+          generating the messages), the <code class="option">severity</code>
+          (what to log), and the <code class="option">output_options</code>
+          (where to log).
+
+        </p><div class="section" title="16.1.1.1. name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id461706"></a>16.1.1.1. name (string)</h4></div></div></div><p>
+          Each logger in the system has a name, the name being that
+          of the component using it to log messages. For instance,
+          if you want to configure logging for the resolver module,
+          you add an entry for a logger named <span class="quote">“<span class="quote">Resolver</span>”</span>. This
+          configuration will then be used by the loggers in the
+          Resolver module, and all the libraries used by it.
               </p><p>
 
-	  If you want to specify logging for one specific library
-	  within the module, you set the name to
-	  <em class="replaceable"><code>module.library</code></em>.  For example, the
-	  logger used by the nameserver address store component
-	  has the full name of <span class="quote">“<span class="quote">Resolver.nsas</span>”</span>. If
-	  there is no entry in Logging for a particular library,
-	  it will use the configuration given for the module.
+          If you want to specify logging for one specific library
+          within the module, you set the name to
+          <em class="replaceable"><code>module.library</code></em>.  For example, the
+          logger used by the nameserver address store component
+          has the full name of <span class="quote">“<span class="quote">Resolver.nsas</span>”</span>. If
+          there is no entry in Logging for a particular library,
+          it will use the configuration given for the module.
 
 
 
@@ -984,163 +1255,163 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 
 
 
-	  To illustrate this, suppose you want the cache library
-	  to log messages of severity DEBUG, and the rest of the
-	  resolver code to log messages of severity INFO. To achieve
-	  this you specify two loggers, one with the name
-	  <span class="quote">“<span class="quote">Resolver</span>”</span> and severity INFO, and one with
-	  the name <span class="quote">“<span class="quote">Resolver.cache</span>”</span> with severity
-	  DEBUG. As there are no entries for other libraries (e.g.
-	  the nsas), they will use the configuration for the module
-	  (<span class="quote">“<span class="quote">Resolver</span>”</span>), so giving the desired behavior.
+          To illustrate this, suppose you want the cache library
+          to log messages of severity DEBUG, and the rest of the
+          resolver code to log messages of severity INFO. To achieve
+          this you specify two loggers, one with the name
+          <span class="quote">“<span class="quote">Resolver</span>”</span> and severity INFO, and one with
+          the name <span class="quote">“<span class="quote">Resolver.cache</span>”</span> with severity
+          DEBUG. As there are no entries for other libraries (e.g.
+          the nsas), they will use the configuration for the module
+          (<span class="quote">“<span class="quote">Resolver</span>”</span>), so giving the desired behavior.
 
         </p><p>
 
-	  One special case is that of a module name of <span class="quote">“<span class="quote">*</span>”</span>
-	  (asterisks), which is interpreted as <span class="emphasis"><em>any</em></span>
-	  module. You can set global logging options by using this,
-	  including setting the logging configuration for a library
-	  that is used by multiple modules (e.g. <span class="quote">“<span class="quote">*.config</span>”</span>
-	  specifies the configuration library code in whatever
-	  module is using it).
+          One special case is that of a module name of <span class="quote">“<span class="quote">*</span>”</span>
+          (asterisks), which is interpreted as <span class="emphasis"><em>any</em></span>
+          module. You can set global logging options by using this,
+          including setting the logging configuration for a library
+          that is used by multiple modules (e.g. <span class="quote">“<span class="quote">*.config</span>”</span>
+          specifies the configuration library code in whatever
+          module is using it).
 
         </p><p>
 
-	  If there are multiple logger specifications in the
-	  configuration that might match a particular logger, the
-	  specification with the more specific logger name takes
-	  precedence. For example, if there are entries for for
-	  both <span class="quote">“<span class="quote">*</span>”</span> and <span class="quote">“<span class="quote">Resolver</span>”</span>, the
-	  resolver module — and all libraries it uses —
-	  will log messages according to the configuration in the
-	  second entry (<span class="quote">“<span class="quote">Resolver</span>”</span>). All other modules
-	  will use the configuration of the first entry
-	  (<span class="quote">“<span class="quote">*</span>”</span>). If there was also a configuration
-	  entry for <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, the cache library
-	  within the resolver would use that in preference to the
-	  entry for <span class="quote">“<span class="quote">Resolver</span>”</span>.
+          If there are multiple logger specifications in the
+          configuration that might match a particular logger, the
+          specification with the more specific logger name takes
+          precedence. For example, if there are entries for for
+          both <span class="quote">“<span class="quote">*</span>”</span> and <span class="quote">“<span class="quote">Resolver</span>”</span>, the
+          resolver module — and all libraries it uses —
+          will log messages according to the configuration in the
+          second entry (<span class="quote">“<span class="quote">Resolver</span>”</span>). All other modules
+          will use the configuration of the first entry
+          (<span class="quote">“<span class="quote">*</span>”</span>). If there was also a configuration
+          entry for <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, the cache library
+          within the resolver would use that in preference to the
+          entry for <span class="quote">“<span class="quote">Resolver</span>”</span>.
 
         </p><p>
 
-	  One final note about the naming. When specifying the
-	  module name within a logger, use the name of the module
-	  as specified in <span class="command"><strong>bindctl</strong></span>, e.g.
-	  <span class="quote">“<span class="quote">Resolver</span>”</span> for the resolver module,
-	  <span class="quote">“<span class="quote">Xfrout</span>”</span> for the xfrout module, etc. When
-	  the message is logged, the message will include the name
-	  of the logger generating the message, but with the module
-	  name replaced by the name of the process implementing
-	  the module (so for example, a message generated by the
-	  <span class="quote">“<span class="quote">Auth.cache</span>”</span> logger will appear in the output
-	  with a logger name of <span class="quote">“<span class="quote">b10-auth.cache</span>”</span>).
+          One final note about the naming. When specifying the
+          module name within a logger, use the name of the module
+          as specified in <span class="command"><strong>bindctl</strong></span>, e.g.
+          <span class="quote">“<span class="quote">Resolver</span>”</span> for the resolver module,
+          <span class="quote">“<span class="quote">Xfrout</span>”</span> for the xfrout module, etc. When
+          the message is logged, the message will include the name
+          of the logger generating the message, but with the module
+          name replaced by the name of the process implementing
+          the module (so for example, a message generated by the
+          <span class="quote">“<span class="quote">Auth.cache</span>”</span> logger will appear in the output
+          with a logger name of <span class="quote">“<span class="quote">b10-auth.cache</span>”</span>).
 
-        </p></div><div class="section" title="severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439176"></a>severity (string)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.2. severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id461840"></a>16.1.1.2. severity (string)</h4></div></div></div><p>
 
           This specifies the category of messages logged.
-	  Each message is logged with an associated severity which
-	  may be one of the following (in descending order of
-	  severity):
+          Each message is logged with an associated severity which
+          may be one of the following (in descending order of
+          severity):
         </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> FATAL </li><li class="listitem"> ERROR </li><li class="listitem"> WARN </li><li class="listitem"> INFO </li><li class="listitem"> DEBUG </li></ul></div><p>
 
-	  When the severity of a logger is set to one of these
-	  values, it will only log messages of that severity, and
-	  the severities above it. The severity may also be set to
-	  NONE, in which case all messages from that logger are
-	  inhibited.
+          When the severity of a logger is set to one of these
+          values, it will only log messages of that severity, and
+          the severities above it. The severity may also be set to
+          NONE, in which case all messages from that logger are
+          inhibited.
 
 
 
-        </p></div><div class="section" title="output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439227"></a>output_options (list)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.3. output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="id461898"></a>16.1.1.3. output_options (list)</h4></div></div></div><p>
 
-	  Each logger can have zero or more
-	  <code class="option">output_options</code>. These specify where log
-	  messages are sent to. These are explained in detail below.
+          Each logger can have zero or more
+          <code class="option">output_options</code>. These specify where log
+          messages are sent to. These are explained in detail below.
 
         </p><p>
 
           The other options for a logger are:
 
-        </p></div><div class="section" title="debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439243"></a>debuglevel (integer)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.4. debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="id461917"></a>16.1.1.4. debuglevel (integer)</h4></div></div></div><p>
 
-	  When a logger's severity is set to DEBUG, this value
-	  specifies what debug messages should be printed. It ranges
-	  from 0 (least verbose) to 99 (most verbose).
+          When a logger's severity is set to DEBUG, this value
+          specifies what debug messages should be printed. It ranges
+          from 0 (least verbose) to 99 (most verbose).
         </p><p>
 
           If severity for the logger is not DEBUG, this value is ignored.
 
-        </p></div><div class="section" title="additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439258"></a>additive (true or false)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.5. additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="id461937"></a>16.1.1.5. additive (true or false)</h4></div></div></div><p>
 
-	  If this is true, the <code class="option">output_options</code> from
-	  the parent will be used. For example, if there are two
-	  loggers configured; <span class="quote">“<span class="quote">Resolver</span>”</span> and
-	  <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, and <code class="option">additive</code>
-	  is true in the second, it will write the log messages
-	  not only to the destinations specified for
-	  <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, but also to the destinations
-	  as specified in the <code class="option">output_options</code> in
-	  the logger named <span class="quote">“<span class="quote">Resolver</span>”</span>.
+          If this is true, the <code class="option">output_options</code> from
+          the parent will be used. For example, if there are two
+          loggers configured; <span class="quote">“<span class="quote">Resolver</span>”</span> and
+          <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, and <code class="option">additive</code>
+          is true in the second, it will write the log messages
+          not only to the destinations specified for
+          <span class="quote">“<span class="quote">Resolver.cache</span>”</span>, but also to the destinations
+          as specified in the <code class="option">output_options</code> in
+          the logger named <span class="quote">“<span class="quote">Resolver</span>”</span>.
 
 
 
-      </p></div></div><div class="section" title="Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229439294"></a>Output Options</h3></div></div></div><p>
+      </p></div></div><div class="section" title="16.1.2. Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="id461978"></a>16.1.2. Output Options</h3></div></div></div><p>
 
-	  The main settings for an output option are the
-	  <code class="option">destination</code> and a value called
-	  <code class="option">output</code>, the meaning of which depends on
-	  the destination that is set.
+          The main settings for an output option are the
+          <code class="option">destination</code> and a value called
+          <code class="option">output</code>, the meaning of which depends on
+          the destination that is set.
 
-        </p><div class="section" title="destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439309"></a>destination (string)</h4></div></div></div><p>
+        </p><div class="section" title="16.1.2.1. destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id461995"></a>16.1.2.1. destination (string)</h4></div></div></div><p>
 
             The destination is the type of output. It can be one of:
 
-          </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229439341"></a>output (string)</h4></div></div></div><p>
+          </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="16.1.2.2. output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id462029"></a>16.1.2.2. output (string)</h4></div></div></div><p>
 
-	  Depending on what is set as the output destination, this
-	  value is interpreted as follows:
+          Depending on what is set as the output destination, this
+          value is interpreted as follows:
 
         </p><div class="variablelist"><dl><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">console</span>”</span></span></dt><dd>
-		 The value of output must be one of <span class="quote">“<span class="quote">stdout</span>”</span>
-		 (messages printed to standard output) or
-		 <span class="quote">“<span class="quote">stderr</span>”</span> (messages printed to standard
-		 error).
+                 The value of output must be one of <span class="quote">“<span class="quote">stdout</span>”</span>
+                 (messages printed to standard output) or
+                 <span class="quote">“<span class="quote">stderr</span>”</span> (messages printed to standard
+                 error).
               </dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span></span></dt><dd>
-		The value of output is interpreted as a file name;
-		log messages will be appended to this file.
+                The value of output is interpreted as a file name;
+                log messages will be appended to this file.
               </dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">syslog</span>”</span></span></dt><dd>
-		The value of output is interpreted as the
-		<span class="command"><strong>syslog</strong></span> facility (e.g.
-		<span class="emphasis"><em>local0</em></span>) that should be used
-		for log messages.
+                The value of output is interpreted as the
+                <span class="command"><strong>syslog</strong></span> facility (e.g.
+                <span class="emphasis"><em>local0</em></span>) that should be used
+                for log messages.
               </dd></dl></div><p>
 
           The other options for <code class="option">output_options</code> are:
 
-        </p><div class="section" title="flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229439427"></a>flush (true of false)</h5></div></div></div><p>
-	    Flush buffers after each log message. Doing this will
-	    reduce performance but will ensure that if the program
-	    terminates abnormally, all messages up to the point of
-	    termination are output.
-          </p></div><div class="section" title="maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229439436"></a>maxsize (integer)</h5></div></div></div><p>
-	    Only relevant when destination is file, this is maximum
-	    file size of output files in bytes. When the maximum
-	    size is reached, the file is renamed and a new file opened.
-	    (For example, a ".1" is appended to the name —
-	    if a ".1" file exists, it is renamed ".2",
+        </p><div class="section" title="16.1.2.2.1. flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="id462122"></a>16.1.2.2.1. flush (true of false)</h5></div></div></div><p>
+            Flush buffers after each log message. Doing this will
+            reduce performance but will ensure that if the program
+            terminates abnormally, all messages up to the point of
+            termination are output.
+          </p></div><div class="section" title="16.1.2.2.2. maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id462134"></a>16.1.2.2.2. maxsize (integer)</h5></div></div></div><p>
+            Only relevant when destination is file, this is maximum
+            file size of output files in bytes. When the maximum
+            size is reached, the file is renamed and a new file opened.
+            (For example, a ".1" is appended to the name —
+            if a ".1" file exists, it is renamed ".2",
             etc.)
           </p><p>
             If this is 0, no maximum file size is used.
-          </p></div><div class="section" title="maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229439449"></a>maxver (integer)</h5></div></div></div><p>
-	    Maximum number of old log files to keep around when
-	    rolling the output file. Only relevant when
-	    <code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span>.
-          </p></div></div></div><div class="section" title="Example session"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229439468"></a>Example session</h3></div></div></div><p>
-
-	  In this example we want to set the global logging to
-	  write to the file <code class="filename">/var/log/my_bind10.log</code>,
-	  at severity WARN. We want the authoritative server to
-	  log at DEBUG with debuglevel 40, to a different file
-	  (<code class="filename">/tmp/debug_messages</code>).
+          </p></div><div class="section" title="16.1.2.2.3. maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id462151"></a>16.1.2.2.3. maxver (integer)</h5></div></div></div><p>
+            Maximum number of old log files to keep around when
+            rolling the output file. Only relevant when
+            <code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span>.
+          </p></div></div></div><div class="section" title="16.1.3. Example session"><div class="titlepage"><div><div><h3 class="title"><a name="id462172"></a>16.1.3. Example session</h3></div></div></div><p>
+
+          In this example we want to set the global logging to
+          write to the file <code class="filename">/var/log/my_bind10.log</code>,
+          at severity WARN. We want the authoritative server to
+          log at DEBUG with debuglevel 40, to a different file
+          (<code class="filename">/tmp/debug_messages</code>).
 
         </p><p>
 
@@ -1155,9 +1426,9 @@ Logging/loggers	[]	list
 
         </p><p>
 
-	  By default, no specific loggers are configured, in which
-	  case the severity defaults to INFO and the output is
-	  written to stderr.
+          By default, no specific loggers are configured, in which
+          case the severity defaults to INFO and the output is
+          written to stderr.
 
         </p><p>
 
@@ -1172,8 +1443,8 @@ Logging/loggers/	list	(modified)
 
         </p><p>
 
-	  The loggers value line changed to indicate that it is no
-	  longer an empty list:
+          The loggers value line changed to indicate that it is no
+          longer an empty list:
 
         </p><p>
 
@@ -1187,9 +1458,9 @@ Logging/loggers[0]/output_options	[]	list	(default)
 
         </p><p>
 
-	  The name is mandatory, so we must set it. We will also
-	  change the severity as well. Let's start with the global
-	  logger.
+          The name is mandatory, so we must set it. We will also
+          change the severity as well. Let's start with the global
+          logger.
 
         </p><p>
 
@@ -1205,8 +1476,8 @@ Logging/loggers[0]/output_options	[]	list	(default)
 
         </p><p>
 
-	  Of course, we need to specify where we want the log
-	  messages to go, so we add an entry for an output option.
+          Of course, we need to specify where we want the log
+          messages to go, so we add an entry for an output option.
 
         </p><p>
 
@@ -1234,8 +1505,8 @@ Logging/loggers[0]/output_options[0]/maxver	0	integer	(default)
 
         </p><p>
 
-	  Which would make the entire configuration for this logger
-	  look like:
+          Which would make the entire configuration for this logger
+          look like:
 
         </p><p>
 
@@ -1253,8 +1524,8 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         </p><p>
 
-	  That looks OK, so let's commit it before we add the
-	  configuration for the authoritative server's logger.
+          That looks OK, so let's commit it before we add the
+          configuration for the authoritative server's logger.
 
         </p><p>
 
@@ -1262,8 +1533,8 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         </p><p>
 
-	  Now that we have set it, and checked each value along
-	  the way, adding a second entry is quite similar.
+          Now that we have set it, and checked each value along
+          the way, adding a second entry is quite similar.
 
         </p><p>
 
@@ -1279,10 +1550,10 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         </p><p>
 
-	  And that's it. Once we have found whatever it was we
-	  needed the debug messages for, we can simply remove the
-	  second logger to let the authoritative server use the
-	  same settings as the rest.
+          And that's it. Once we have found whatever it was we
+          needed the debug messages for, we can simply remove the
+          second logger to let the authoritative server use the
+          same settings as the rest.
 
         </p><p>
 
@@ -1292,15 +1563,15 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         </p><p>
 
-	  And every module will now be using the values from the
-	  logger named <span class="quote">“<span class="quote">*</span>”</span>.
+          And every module will now be using the values from the
+          logger named <span class="quote">“<span class="quote">*</span>”</span>.
 
-        </p></div></div><div class="section" title="Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229440023"></a>Logging Message Format</h2></div></div></div><p>
-	  Each message written by BIND 10 to the configured logging
-	  destinations comprises a number of components that identify
-	  the origin of the message and, if the message indicates
-	  a problem, information about the problem that may be
-	  useful in fixing it.
+        </p></div></div><div class="section" title="16.2. Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id462428"></a>16.2. Logging Message Format</h2></div></div></div><p>
+          Each message written by BIND 10 to the configured logging
+          destinations comprises a number of components that identify
+          the origin of the message and, if the message indicates
+          a problem, information about the problem that may be
+          useful in fixing it.
       </p><p>
           Consider the message below logged to a file:
           </p><pre class="screen">2011-06-15 13:48:22.034 ERROR [b10-resolver.asiolink]
@@ -1325,20 +1596,21 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
             (which in the example is the asynchronous I/O link
             module, asiolink).
           </p></dd><dt><span class="term">ASIODNS_OPENSOCK</span></dt><dd><p>
-	    The message identification.  Every message in BIND 10
-	    has a unique identification, which can be used as an
-	    index into the <a class="ulink" href="bind10-messages.html" target="_top"><em class="citetitle">BIND 10 Messages
-	    Manual</em></a> (<a class="ulink" href="http://bind10.isc.org/docs/bind10-messages.html" target="_top">http://bind10.isc.org/docs/bind10-messages.html</a>) from which more information can be obtained.
+            The message identification.  Every message in BIND 10
+            has a unique identification, which can be used as an
+            index into the <a class="ulink" href="bind10-messages.html" target="_top"><em class="citetitle">BIND 10 Messages
+            Manual</em></a> (<a class="ulink" href="http://bind10.isc.org/docs/bind10-messages.html" target="_top">http://bind10.isc.org/docs/bind10-messages.html</a>) from which more information can be obtained.
           </p></dd><dt><span class="term">error 111 opening TCP socket to 127.0.0.1(53)</span></dt><dd><p>
-	      A brief description of the cause of the problem.
-	      Within this text, information relating to the condition
-	      that caused the message to be logged will be included.
-	      In this example, error number 111 (an operating
-	      system-specific error number) was encountered when
-	      trying to open a TCP connection to port 53 on the
-	      local system (address 127.0.0.1).  The next step
-	      would be to find out the reason for the failure by
-	      consulting your system's documentation to identify
-	      what error number 111 means.
+              A brief description of the cause of the problem.
+              Within this text, information relating to the condition
+              that caused the message to be logged will be included.
+              In this example, error number 111 (an operating
+              system-specific error number) was encountered when
+              trying to open a TCP connection to port 53 on the
+              local system (address 127.0.0.1).  The next step
+              would be to find out the reason for the failure by
+              consulting your system's documentation to identify
+              what error number 111 means.
           </p></dd></dl></div><p>
-      </p></div></div></div></body></html>
+      </p></div></div><div class="chapter" title="Chapter 17. Acknowledgements"><div class="titlepage"><div><div><h2 class="title"><a name="id462551"></a>Chapter 17. Acknowledgements</h2></div></div></div><p>ISC would like to acknowledge generous support for
+    development of DHCPv4 and DHCPv6 components provided by <a class="ulink" href="http://www.comcast.com" target="_top">Comcast</a>.</p></div></div></body></html>
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index b186111..c58a97f 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -34,17 +34,19 @@
     </copyright>
 
     <abstract>
-      <para>BIND 10 is a Domain Name System (DNS) suite managed by
-	Internet Systems Consortium (ISC). It includes DNS libraries
-	and modular components for controlling authoritative and
-	recursive DNS servers.
+      <para>BIND 10 is a framework that features Domain Name System
+      (DNS) suite and Dynamic Host Configuration Protocol (DHCP)
+      servers managed by Internet Systems Consortium (ISC). It
+      includes DNS libraries, modular components for controlling
+      authoritative and recursive DNS servers, and experimental DHCPv4
+      and DHCPv6 servers.
       </para>
       <para>
         This is the reference guide for BIND 10 version &__VERSION__;.
-	The most up-to-date version of this document (in PDF, HTML,
-	and plain text formats), along with other documents for
-	BIND 10, can be found at <ulink url="http://bind10.isc.org/docs"/>.
-	</para> </abstract>
+        The most up-to-date version of this document (in PDF, HTML,
+        and plain text formats), along with other documents for
+        BIND 10, can be found at <ulink url="http://bind10.isc.org/docs"/>.
+        </para> </abstract>
 
       <releaseinfo>This is the reference guide for BIND 10 version
         &__VERSION__;.</releaseinfo>
@@ -98,20 +100,20 @@
       </para>
 
       <para>
-	BIND 10 uses the Botan crypto library for C++. It requires
-	at least Botan version 1.8.
+        BIND 10 uses the Botan crypto library for C++. It requires
+        at least Botan version 1.8.
       </para>
 
       <para>
-	BIND 10 uses the log4cplus C++ logging library. It requires
-	at least log4cplus version 1.0.3.
+        BIND 10 uses the log4cplus C++ logging library. It requires
+        at least log4cplus version 1.0.3.
       </para>
 
       <para>
-	The authoritative server requires SQLite 3.3.9 or newer.
-	The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
-	and <command>b10-zonemgr</command> modules require the
-	libpython3 library and the Python _sqlite3.so module.
+        The authoritative server requires SQLite 3.3.9 or newer.
+        The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
+        and <command>b10-zonemgr</command> modules require the
+        libpython3 library and the Python _sqlite3.so module.
       </para>
 <!-- TODO: this will change ... -->
 
@@ -133,19 +135,19 @@
       <para>
         BIND 10 is modular.  Part of this modularity is
         accomplished using multiple cooperating processes which, together,
-	provide the server functionality.  This is a change from
-	the previous generation of BIND software, which used a
-	single process.
+        provide the server functionality.  This is a change from
+        the previous generation of BIND software, which used a
+        single process.
       </para>
 
       <para>
-	At first, running many different processes may seem confusing.
-	However, these processes are started, stopped, and maintained
-	by a single command, <command>bind10</command>.
-	This command starts a master process which will start other
-	processes as needed.
-	The processes started by the <command>bind10</command>
-	command have names starting with "b10-", including:
+        At first, running many different processes may seem confusing.
+        However, these processes are started, stopped, and maintained
+        by a single command, <command>bind10</command>.
+        This command starts a master process which will start other
+        processes as needed.
+        The processes started by the <command>bind10</command>
+        command have names starting with "b10-", including:
       </para>
 
       <para>
@@ -215,9 +217,9 @@
             <simpara>
               <command>b10-xfrout</command> —
               Outgoing zone transfer service.
-	      This process is used to handle transfer requests to
-	      send a local zone to a remote secondary server,
-	      when acting as a master server.
+              This process is used to handle transfer requests to
+              send a local zone to a remote secondary server,
+              when acting as a master server.
             </simpara>
           </listitem>
 
@@ -225,7 +227,7 @@
             <simpara>
               <command>b10-zonemgr</command> —
               Secondary manager.
-	      This process keeps track of timers and other
+              This process keeps track of timers and other
               necessary information for BIND 10 to act as a slave server.
             </simpara>
           </listitem>
@@ -234,8 +236,8 @@
       </para>
 
       <para>
-	These are ran automatically by <command>bind10</command>
-	and do not need to be run manually.
+        These are ran automatically by <command>bind10</command>
+        and do not need to be run manually.
       </para>
 
     </section>
@@ -244,8 +246,8 @@
       <title>Managing BIND 10</title>
 
       <para>
-	Once BIND 10 is running, a few commands are used to interact
-	directly with the system:
+        Once BIND 10 is running, a few commands are used to interact
+        directly with the system:
         <itemizedlist>
           <listitem>
             <simpara>
@@ -342,8 +344,8 @@ var/
         </para>
 
         <para>
-	  To build BIND 10, also install the Botan (at least version
-	  1.8) and the log4cplus (at least version 1.0.3)
+          To build BIND 10, also install the Botan (at least version
+          1.8) and the log4cplus (at least version 1.0.3)
           development include headers.
         </para>
 
@@ -355,7 +357,7 @@ Debian and Ubuntu:
 
         <para>
 <!-- TODO: is this needed at build time? test time? -->
-	  The Python Library and Python _sqlite3 module are required to
+          The Python Library and Python _sqlite3 module are required to
           enable the Xfrout and Xfrin support.
         </para>
 
@@ -818,20 +820,20 @@ Debian and Ubuntu:
       </para>
 
       <para>
-	The kind specifies how a failure of the component should
-	be handled.  If it is set to <quote>dispensable</quote>
-	(the default unless you set something else), it will get
-	started again if it fails. If it is set to <quote>needed</quote>
-	and it fails at startup, the whole <command>bind10</command>
-	shuts down and exits with error exit code. But if it fails
-	some time later, it is just started again. If you set it
-	to <quote>core</quote>, you indicate that the system is
-	not usable without the component and if such component
-	fails, the system shuts down no matter when the failure
-	happened.  This is the behaviour of the core components
-	(the ones you can't turn off), but you can declare any
-	other components as core as well if you wish (but you can
-	turn these off, they just can't fail).
+        The kind specifies how a failure of the component should
+        be handled.  If it is set to <quote>dispensable</quote>
+        (the default unless you set something else), it will get
+        started again if it fails. If it is set to <quote>needed</quote>
+        and it fails at startup, the whole <command>bind10</command>
+        shuts down and exits with error exit code. But if it fails
+        some time later, it is just started again. If you set it
+        to <quote>core</quote>, you indicate that the system is
+        not usable without the component and if such component
+        fails, the system shuts down no matter when the failure
+        happened.  This is the behaviour of the core components
+        (the ones you can't turn off), but you can declare any
+        other components as core as well if you wish (but you can
+        turn these off, they just can't fail).
       </para>
 
       <para>
@@ -842,13 +844,13 @@ Debian and Ubuntu:
 
       <para>
         There are other parameters we didn't use in our example.
-	One of them is <quote>address</quote>. It is the address
-	used by the component on the <command>b10-msgq</command>
-	message bus. The special components already know their
-	address, but the usual ones don't. The address is by
-	convention the thing after <emphasis>b10-</emphasis>, with
-	the first letter capital (eg. <command>b10-stats</command>
-	would have <quote>Stats</quote> as its address).
+        One of them is <quote>address</quote>. It is the address
+        used by the component on the <command>b10-msgq</command>
+        message bus. The special components already know their
+        address, but the usual ones don't. The address is by
+        convention the thing after <emphasis>b10-</emphasis>, with
+        the first letter capital (eg. <command>b10-stats</command>
+        would have <quote>Stats</quote> as its address).
 <!-- TODO: this should be simplified so we don't even have to document it -->
       </para>
 
@@ -877,15 +879,15 @@ address, but the usual ones don't." mean? -->
 
       <note>
         <para>
-	  The configuration is quite powerful, but that includes
-	  a lot of space for mistakes. You could turn off the
-	  <command>b10-cmdctl</command>, but then you couldn't
-	  change it back the usual way, as it would require it to
-	  be running (you would have to find and edit the configuration
-	  directly).  Also, some modules might have dependencies
-	  -- <command>b10-stats-httpd</command> need
-	  <command>b10-stats</command>, <command>b10-xfrout</command>
-	  needs the <command>b10-auth</command> to be running, etc.
+          The configuration is quite powerful, but that includes
+          a lot of space for mistakes. You could turn off the
+          <command>b10-cmdctl</command>, but then you couldn't
+          change it back the usual way, as it would require it to
+          be running (you would have to find and edit the configuration
+          directly).  Also, some modules might have dependencies
+          -- <command>b10-stats-httpd</command> need
+          <command>b10-stats</command>, <command>b10-xfrout</command>
+          needs the <command>b10-auth</command> to be running, etc.
 
 <!-- TODO: should we define dependencies? -->
 
@@ -896,19 +898,19 @@ address, but the usual ones don't." mean? -->
       </note>
 
       <para>
-	Now, to the mysterious setuid virtual component. If you
-	use the <command>-u</command> option to start the
-	<command>bind10</command> as root, but change the user
-	later, we need to start the <command>b10-auth</command> or
-	<command>b10-resolver</command> as root (until the socket
-	creator is finished).<!-- TODO --> So we need to specify
-	the time when the switch from root do the given user happens
-	and that's what the setuid component is for. The switch is
-	done at the time the setuid component would be started, if
-	it was a process. The default configuration contains the
-	setuid component with priority 5, <command>b10-auth</command>
-	has 10 to be started before the switch and everything else
-	is without priority, so it is started after the switch.
+        Now, to the mysterious setuid virtual component. If you
+        use the <command>-u</command> option to start the
+        <command>bind10</command> as root, but change the user
+        later, we need to start the <command>b10-auth</command> or
+        <command>b10-resolver</command> as root (until the socket
+        creator is finished).<!-- TODO --> So we need to specify
+        the time when the switch from root do the given user happens
+        and that's what the setuid component is for. The switch is
+        done at the time the setuid component would be started, if
+        it was a process. The default configuration contains the
+        setuid component with priority 5, <command>b10-auth</command>
+        has 10 to be started before the switch and everything else
+        is without priority, so it is started after the switch.
       </para>
 
     </section>
@@ -1441,16 +1443,16 @@ TODO
     <section>
       <title>Configuration for Incoming Zone Transfers</title>
       <para>
-	In practice, you need to specify a list of secondary zones to
-	enable incoming zone transfers for these zones (you can still
-	trigger a zone transfer manually, without a prior configuration
-	(see below)).
+        In practice, you need to specify a list of secondary zones to
+        enable incoming zone transfers for these zones (you can still
+        trigger a zone transfer manually, without a prior configuration
+        (see below)).
       </para>
 
       <para>
-	For example, to enable zone transfers for a zone named "example.com"
-	(whose master address is assumed to be 2001:db8::53 here),
-	run the following at the <command>bindctl</command> prompt:
+        For example, to enable zone transfers for a zone named "example.com"
+        (whose master address is assumed to be 2001:db8::53 here),
+        run the following at the <command>bindctl</command> prompt:
 
       <screen>> <userinput>config add Xfrin/zones</userinput>
 > <userinput>config set Xfrin/zones[0]/name "<option>example.com</option>"</userinput>
@@ -1549,11 +1551,11 @@ what if a NOTIFY is sent?
       <title>Trigger an Incoming Zone Transfer Manually</title>
 
       <para>
-	To manually trigger a zone transfer to retrieve a remote zone,
-	you may use the <command>bindctl</command> utility.
-	For example, at the <command>bindctl</command> prompt run:
+        To manually trigger a zone transfer to retrieve a remote zone,
+        you may use the <command>bindctl</command> utility.
+        For example, at the <command>bindctl</command> prompt run:
 
-	<screen>> <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
+        <screen>> <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
       </para>
     </section>
 
@@ -1603,9 +1605,9 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
 > <userinput>config commit</userinput></screen>
 
     <note><simpara>
-	In the above example the lines
-	for <option>transfer_acl</option> were divided for
-	readability.  In the actual input it must be in a single line.
+        In the above example the lines
+        for <option>transfer_acl</option> were divided for
+        readability.  In the actual input it must be in a single line.
     </simpara></note>
 
     <para>
@@ -1630,10 +1632,10 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
     </para>
 
     <note><simpara>
-	In a future version, <command>b10-xfrout</command> will also
-	use the system wide TSIG configuration.
-	The way to specify zone specific configuration (ACLs, etc) is
-	likely to be changed, too.
+        In a future version, <command>b10-xfrout</command> will also
+        use the system wide TSIG configuration.
+        The way to specify zone specific configuration (ACLs, etc) is
+        likely to be changed, too.
     </simpara></note>
 
 <!--
@@ -1711,30 +1713,30 @@ what is XfroutClient xfr_client??
         By default, the <command>b10-resolver</command> daemon only accepts
         DNS queries from the localhost (127.0.0.1 and ::1).
         The <option>Resolver/query_acl</option> configuration may
-	be used to reject, drop, or allow specific IPs or networks.
+        be used to reject, drop, or allow specific IPs or networks.
         This configuration list is first match.
       </para>
 
       <para>
-	The configuration's <option>action</option> item may be
-	set to <quote>ACCEPT</quote> to allow the incoming query,
-	<quote>REJECT</quote> to respond with a DNS REFUSED return
-	code, or <quote>DROP</quote> to ignore the query without
-	any response (such as a blackhole).  For more information,
-	see the respective debugging messages:  <ulink
-	url="bind10-messages.html#RESOLVER_QUERY_ACCEPTED">RESOLVER_QUERY_ACCEPTED</ulink>,
-	<ulink
-	url="bind10-messages.html#RESOLVER_QUERY_REJECTED">RESOLVER_QUERY_REJECTED</ulink>,
-	and <ulink
+        The configuration's <option>action</option> item may be
+        set to <quote>ACCEPT</quote> to allow the incoming query,
+        <quote>REJECT</quote> to respond with a DNS REFUSED return
+        code, or <quote>DROP</quote> to ignore the query without
+        any response (such as a blackhole).  For more information,
+        see the respective debugging messages:  <ulink
+        url="bind10-messages.html#RESOLVER_QUERY_ACCEPTED">RESOLVER_QUERY_ACCEPTED</ulink>,
+        <ulink
+        url="bind10-messages.html#RESOLVER_QUERY_REJECTED">RESOLVER_QUERY_REJECTED</ulink>,
+        and <ulink
 url="bind10-messages.html#RESOLVER_QUERY_DROPPED">RESOLVER_QUERY_DROPPED</ulink>.
       </para>
 
       <para>
-	The required configuration's <option>from</option> item is set
+        The required configuration's <option>from</option> item is set
         to an IPv4 or IPv6 address, addresses with an network mask, or to
-	the special lowercase keywords <quote>any6</quote> (for
-	any IPv6 address) or <quote>any4</quote> (for any IPv4
-	address).
+        the special lowercase keywords <quote>any6</quote> (for
+        any IPv6 address) or <quote>any4</quote> (for any IPv4
+        address).
       </para>
 
 <!-- TODO:
@@ -1745,9 +1747,9 @@ TODO: tsig
 -->
 
       <para>
-	For example to allow the <replaceable>192.168.1.0/24</replaceable>
-	network to use your recursive name server, at the
-	<command>bindctl</command> prompt run:
+        For example to allow the <replaceable>192.168.1.0/24</replaceable>
+        network to use your recursive name server, at the
+        <command>bindctl</command> prompt run:
       </para>
 
       <screen>
@@ -1806,6 +1808,459 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
 
   </chapter>
 
+  <chapter id="dhcp4">
+    <title>DHCPv4 Server</title>
+    <para>Dynamic Host Configuration Protocol for IPv4 (DHCP or
+    DHCPv4) and Dynamic Host Configuration Protocol for IPv6 (DHCPv6)
+    are protocols that allow one node (server) to provision
+    configuration parameters to many hosts and devices (clients). To
+    ease deployment in larger networks, additional nodes (relays) may
+    be deployed that facilitate communication between servers and
+    clients. Even though principles of both DHCPv4 and DHCPv6 are
+    somewhat similar, these are two radically different
+    protocols. BIND10 offers server implementations for both DHCPv4
+    and DHCPv6. This chapter is about DHCP for IPv4. For a description
+    of the DHCPv6 server, see <xref linkend="dhcp6"/>.</para>
+
+    <para>The DHCPv4 server component is currently under intense
+    development. You may want to check out <ulink
+    url="http://bind10.isc.org/wiki/Kea">BIND10 DHCP (Kea) wiki</ulink>
+    and recent posts on <ulink
+    url="https://lists.isc.org/mailman/listinfo/bind10-dev">BIND10
+    developers mailing list</ulink>.</para>
+
+    <para>The DHCPv4 and DHCPv6 components in BIND10 architecture are
+    internally code named <quote>Kea</quote>.</para>
+
+    <note>
+      <para>
+        As of December 2011, both DHCPv4 and DHCPv6 components are
+        skeleton servers. That means that while they are capable of
+        performing DHCP configuration, they are not fully functional
+        yet. In particular, neither has functional lease
+        databases. This means that they will assign the same, fixed,
+        hardcoded addresses to any client that will ask. See <xref
+        linkend="dhcp4-limit"/> and <xref linkend="dhcp6-limit"/> for
+        detailed description.
+      </para>
+    </note>
+
+    <section id="dhcp4-usage">
+      <title>DHCPv4 Server Usage</title>
+      <para>BIND10 provides the DHCPv4 server component since December
+      2011. It is a skeleton server and can be described as an early
+      prototype that is not fully functional yet. It is mature enough
+      to conduct first tests in lab environment, but it has
+      significant limitations. See <xref linkend="dhcp4-limit"/> for
+      details.
+      </para>
+
+      <para>
+        The DHCPv4 server is implemented as <command>b10-dhcp4</command>
+        daemon. As it is not configurable yet, it is fully autonomous,
+        that is it does not interact with <command>b10-cfgmgr</command>.
+        To start DHCPv4 server, simply input:
+
+        <screen>
+#<userinput>cd src/bin/dhcp4</userinput>
+#<userinput>./b10-dhcp4</userinput>
+</screen>
+
+        Depending on your installation, <command>b10-dhcp4</command>
+        binary may reside in src/bin/dhcp4 in your source code
+        directory, in /usr/local/bin/b10-dhcp4 or other directory
+        you specified during compilation.
+
+        At start, the server will detect available network interfaces
+        and will attempt to open UDP sockets on all interfaces that
+        are up, running, are not loopback, and have IPv4 address
+        assigned.
+
+        The server will then listen to incoming traffic. Currently
+        supported client messages are DISCOVER and REQUEST. The server
+        will respond to them with OFFER and ACK, respectively.
+
+        Since the DHCPv4 server opens privileged ports, it requires root
+        access. Make sure you run this daemon as root.</para>
+
+        <note>
+          <para>
+            Integration with <command>bind10</command> is
+            planned. Ultimately, <command>b10-dhcp4</command> will not
+            be started directly, but rather via
+            <command>bind10</command>. Please be aware of this planned
+            change.
+          </para>
+        </note>
+
+    </section>
+
+    <section id="dhcp4-config">
+      <title>DHCPv4 Server Configuration</title>
+      <para>
+        The DHCPv4 server does not have a lease database implemented yet
+        nor any support for configuration, so every time the same set
+        of configuration options (including the same fixed address)
+        will be assigned every time.
+      </para>
+      <para>
+        At this stage of development, the only way to alter the server
+        configuration is to tweak its source code. To do so, please
+        edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
+        parameters and recompile:
+        <screen>
+const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
+const std::string HARDCODED_NETMASK = "255.255.255.0";
+const uint32_t    HARDCODED_LEASE_TIME = 60; // in seconds
+const std::string HARDCODED_GATEWAY = "192.0.2.1";
+const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
+const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
+const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
+
+        Lease database and configuration support is planned for 2012.
+      </para>
+    </section>
+
+    <section id="dhcp4-std">
+      <title>Supported standards</title>
+      <para>The following standards and draft standards are currently
+      supported:</para>
+      <itemizedlist>
+          <listitem>
+            <simpara>RFC2131: Supported messages are DISCOVER, OFFER,
+            REQUEST, and ACK.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>RFC2132: Supported options are: PAD (0),
+            END(255), Message Type(53), DHCP Server Identifier (54),
+            Domain Name (15), DNS Servers (6), IP Address Lease Time
+            (51), Subnet mask (1), and Routers (3).</simpara>
+          </listitem>
+      </itemizedlist>
+    </section>
+
+    <section id="dhcp4-limit">
+      <title>DHCPv4 Server Limitations</title> 
+      <para>These are the current limitations of the DHCPv4 server
+      software. Most of them are reflections of the early stage of
+      development and should be treated as <quote>not implemented
+      yet</quote>, rather than actual limitations.</para>
+      <itemizedlist>
+          <listitem>
+            <simpara>During initial IPv4 node configuration, the
+            server is expected to send packets to a node that does not
+            have IPv4 address assigned yet. The server requires
+            certain tricks (or hacks) to transmit such packets. This
+            is not implemented yet, therefore DHCPv4 server supports
+            relayed traffic only (that is, normal point to point
+            communication).</simpara>
+          </listitem>
+          <listitem>
+            <simpara><command>b10-dhcp4</command> provides a single,
+            fixed, hardcoded lease to any client that asks.  There is
+            no lease manager implemented. If two clients request
+            addresses, they will both get the same fixed
+            address.</simpara>
+          </listitem>
+          <listitem>
+            <simpara><command>b10-dhcp4</command> does not support any
+            configuration mechanisms yet. The whole configuration is
+            currently hardcoded. The only way to tweak configuration
+            is to directly modify source code. See see <xref
+            linkend="dhcp4-config"/> for details.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>Upon start, the server will open sockets on all
+            interfaces that are not loopback, are up and running and
+            have IPv4 address.  Support for multiple interfaces is not
+            coded in reception routines yet, so if you are running
+            this code on a machine that has many interfaces and
+            <command>b10-dhcp4</command> happens to listen on wrong
+            interface, the easiest way to work around this problem is
+            to turn down other interfaces. This limitation will be
+            fixed shortly.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>PRL (Parameter Request List, a list of options
+            requested by a client) is currently ignored and server
+            assigns DNS SERVER and DOMAIN NAME options.</simpara>
+          </listitem>
+          <listitem>
+            <simpara><command>b10-dhcp4</command> does not support
+            BOOTP. That is a design choice. This limitation is
+            permanent. If you have legacy nodes that can't use DHCP and
+            require BOOTP support, please use latest version of ISC DHCP
+            <ulink url="http://www.isc.org/software/dhcp"/>.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>Interface detection is currently working on Linux
+            only. See <xref linkend="iface-detect"/> for details.</simpara>
+          </listitem>
+          <listitem>
+            <simpara><command>b10-dhcp4</command> does not verify that
+            assigned address is unused. According to RFC2131, the
+            allocating server should verify that address is no used by
+            sending ICMP echo request.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>Address renewal (RENEW), rebinding (REBIND),
+            confirmation (CONFIRM), duplication report (DECLINE) and
+            release (RELEASE) are not supported yet.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>DNS Update is not supported yet.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>-v (verbose) command line option is currently
+            the default, and cannot be disabled.</simpara>
+          </listitem>
+      </itemizedlist>
+    </section>
+
+  </chapter>
+
+  <chapter id="dhcp6">
+    <title>DHCPv6 Server</title>
+    <para>Dynamic Host Configuration Protocol for IPv6 (DHCPv6) is
+    specified in RFC3315. BIND10 provides DHCPv6 server implementation
+    that is described in this chapter. For a description of the DHCPv4
+    server implementation, see <xref linkend="dhcp4"/>.
+    </para>
+
+    <para>The DHCPv6 server component is currently under intense
+    development. You may want to check out <ulink
+    url="http://bind10.isc.org/wiki/Kea">BIND10 DHCP (Kea) wiki</ulink>
+    and recent posts on <ulink
+    url="https://lists.isc.org/mailman/listinfo/bind10-dev">BIND10
+    developers mailing list</ulink>.</para>
+
+    <para>The DHCPv4 and DHCPv6 components in BIND10 architecture are
+    internally code named <quote>Kea</quote>.</para>
+
+    <note>
+      <para>
+        As of December 2011, both DHCPv4 and DHCPv6 components are
+        skeleton servers. That means that while they are capable of
+        performing DHCP configuration, they are not fully functional
+        yet. In particular, neither has functional lease
+        databases. This means that they will assign the same, fixed,
+        hardcoded addresses to any client that will ask. See <xref
+        linkend="dhcp4-limit"/> and <xref linkend="dhcp6-limit"/> for
+        detailed description.
+      </para>
+    </note>
+
+    <section id="dhcp6-usage">
+      <title>DHCPv6 Server Usage</title>
+      <para>
+        BIND10 provides the DHCPv6 server component since September
+        2011. It is a skeleton server and can be described as an early
+        prototype that is not fully functional yet. It is mature
+        enough to conduct first tests in lab environment, but it has
+        significant limitations. See <xref linkend="dhcp6-limit"/> for
+        details.
+      </para>
+
+      <para>
+        The DHCPv6 server is implemented as <command>b10-dhcp6</command>
+        daemon. As it is not configurable yet, it is fully autonomous,
+        that is it does not interact with <command>b10-cfgmgr</command>.
+        To start DHCPv6 server, simply input:
+
+        <screen>
+#<userinput>cd src/bin/dhcp6</userinput>
+#<userinput>./b10-dhcp6</userinput>
+</screen>
+
+        Depending on your installation, <command>b10-dhcp6</command>
+        binary may reside in src/bin/dhcp6 in your source code
+        directory, in /usr/local/bin/b10-dhcp6 or other directory
+        you specified during compilation.
+
+        At start, server will detect available network interfaces
+        and will attempt to open UDP sockets on all interfaces that
+        are up, running, are not loopback, are multicast-capable, and
+        have IPv6 address assigned.
+
+        The server will then listen to incoming traffic. Currently
+        supported client messages are SOLICIT and REQUEST. The server
+        will respond to them with ADVERTISE and REPLY, respectively.
+
+        Since the DHCPv6 server opens privileged ports, it requires root
+        access. Make sure you run this daemon as root.
+      </para>
+
+        <note>
+          <para>
+            Integration with <command>bind10</command> is
+            planned. Ultimately, <command>b10-dhcp6</command> will not
+            be started directly, but rather via
+            <command>bind10</command>. Please be aware of this planned
+            change.
+          </para>
+        </note>
+
+    </section>
+
+    <section id="dhcp6-config">
+      <title>DHCPv6 Server Configuration</title>
+      <para>
+        The DHCPv6 server does not have lease database implemented yet
+        or any support for configuration, so every time the same set
+        of configuration options (including the same fixed address)
+        will be assigned every time.
+      </para>
+      <para>
+        At this stage of development, the only way to alter server
+        configuration is to tweak its source code. To do so, please
+        edit src/bin/dhcp6/dhcp6_srv.cc file and modify following
+        parameters and recompile:
+        <screen>
+const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
+const uint32_t HARDCODED_T1 = 1500; // in seconds
+const uint32_t HARDCODED_T2 = 2600; // in seconds
+const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
+const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
+const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";</screen>
+
+        Lease database and configuration support is planned for 2012.
+      </para>
+
+    </section>
+
+    <section id="dhcp6-std">
+      <title>Supported DHCPv6 Standards</title>
+      <para>The following standards and draft standards are currently
+      supported:</para>
+      <itemizedlist>
+          <listitem>
+            <simpara>RFC3315: Supported messages are SOLICIT,
+            ADVERTISE, REQUEST, and REPLY. Supported options are
+            SERVER_ID, CLIENT_ID, IA_NA, and IAADDRESS.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>RFC3646: Supported option is DNS_SERVERS.</simpara>
+          </listitem>
+      </itemizedlist>
+    </section>
+
+    <section id="dhcp6-limit">
+      <title>DHCPv6 Server Limitations</title>
+      <para> These are the current limitations of the DHCPv6 server
+      software. Most of them are reflections of the early stage of
+      development and should be treated as <quote>not implemented
+      yet</quote>, rather than actual limitations.</para>
+      <para>
+      <itemizedlist>
+        <listitem>
+          <simpara>Relayed traffic is not supported.</simpara>
+        </listitem>
+        <listitem>
+          <simpara><command>b10-dhcp6</command> provides a single,
+          fixed, hardcoded lease to any client that asks. There is no
+          lease manager implemented. If two clients request addresses,
+          they will both get the same fixed address.</simpara>
+        </listitem>
+        <listitem>
+          <simpara><command>b10-dhcp6</command> does not support any
+            configuration mechanisms yet. The whole configuration is
+            currently hardcoded. The only way to tweak configuration
+            is to directly modify source code. See see <xref
+            linkend="dhcp6-config"/> for details.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>Upon start, the server will open sockets on all
+          interfaces that are not loopback, are up, running and are
+          multicast capable and have IPv6 address.  Support for
+          multiple interfaces is not coded in reception routines yet,
+          so if you are running this code on a machine that has many
+          interfaces and <command>b10-dhcp6</command> happens to
+          listen on wrong interface, the easiest way to work around
+          this problem is to turn down other interfaces. This
+          limitation will be fixed shortly.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>ORO (Option Request Option, a list of options
+          requested by a client) is currently ignored and server
+          assigns DNS SERVER option.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>Temporary addresses are not supported yet.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>Prefix delegation is not supported yet.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>Address renewal (RENEW), rebinding (REBIND),
+          confirmation (CONFIRM), duplication report (DECLINE) and
+          release (RELEASE) are not supported yet.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>DNS Update is not supported yet.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>Interface detection is currently working on Linux
+          only. See <xref linkend="iface-detect"/> for details.</simpara>
+        </listitem>
+        <listitem>
+          <simpara>-v (verbose) command line option is currently the
+          default, and cannot be disabled.</simpara>
+        </listitem>
+      </itemizedlist>
+      </para>
+    </section>
+
+  </chapter>
+
+  <chapter id="libdhcp">
+    <title>libdhcp++ library</title>
+    <para>libdhcp++ is a common library written in C++ that handles
+    many DHCP-related tasks, like DHCPv4 and DHCPv6 packets parsing,
+    manipulation and assembly, option parsing, manipulation and
+    assembly, network interface detection and socket operations, like
+    socket creations, data transmission and reception and socket
+    closing.
+    </para>
+
+    <para>
+    While this library is currently used by
+    <command>b10-dhcp4</command> and <command>b10-dhcp6</command>
+    only, it is designed to be portable, universal library useful for
+    any kind of DHCP-related software.
+    </para>
+
+    <section id="iface-detect">
+      <title>Interface detection</title>
+      <para>Both DHCPv4 and DHCPv6 components share network
+      interface detection routines. Interface detection is
+      currently only supported on Linux systems.</para>
+
+      <para>For non-Linux systems, there is currently stub
+      implementation provided. As DHCP servers need to know available
+      addresses, there is a simple mechanism implemented to provide
+      that information. User is expected to create interfaces.txt
+      file. Format of this file is simple. It contains list of
+      interfaces along with available address on each interface. This
+      mechanism is temporary and is going to be removed as soon as
+      interface detection becomes available on non-Linux
+      systems. Here is an example of the interfaces.txt file:
+      <screen>
+# For DHCPv6, please specify link-local address (starts with fe80::)
+# If in doubt, check output of 'ifconfig -a' command.
+eth0 fe80::21e:8cff:fe9b:7349
+
+# For DHCPv4, please use following format:
+#eth0 192.0.2.5</screen>
+      </para>
+    </section>
+
+    <section id="packet-handling">
+      <title>DHCPv4/DHCPv6 packet handling</title>
+      <para>TODO: Describe packet handling here, with pointers to wiki</para>
+    </section>
+
+  </chapter>
+
   <chapter id="statistics">
     <title>Statistics</title>
 
@@ -1857,10 +2312,10 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
 
       <para>
 
-	The logging system in BIND 10 is configured through the
-	Logging module. All BIND 10 modules will look at the
-	configuration in Logging to see what should be logged and
-	to where.
+        The logging system in BIND 10 is configured through the
+        Logging module. All BIND 10 modules will look at the
+        configuration in Logging to see what should be logged and
+        to where.
 
 <!-- TODO: what is context of Logging module for readers of this guide? -->
 
@@ -1871,28 +2326,28 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
 
         <para>
 
-	  Within BIND 10, a message is logged through a component
-	  called a "logger". Different parts of BIND 10 log messages
-	  through different loggers, and each logger can be configured
-	  independently of one another.
+          Within BIND 10, a message is logged through a component
+          called a "logger". Different parts of BIND 10 log messages
+          through different loggers, and each logger can be configured
+          independently of one another.
 
         </para>
 
         <para>
 
-	  In the Logging module, you can specify the configuration
-	  for zero or more loggers; any that are not specified will
-	  take appropriate default values..
+          In the Logging module, you can specify the configuration
+          for zero or more loggers; any that are not specified will
+          take appropriate default values..
 
         </para>
 
         <para>
 
-	  The three most important elements of a logger configuration
-	  are the <option>name</option> (the component that is
-	  generating the messages), the <option>severity</option>
-	  (what to log), and the <option>output_options</option>
-	  (where to log).
+          The three most important elements of a logger configuration
+          are the <option>name</option> (the component that is
+          generating the messages), the <option>severity</option>
+          (what to log), and the <option>output_options</option>
+          (where to log).
 
         </para>
 
@@ -1900,12 +2355,12 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
           <title>name (string)</title>
 
           <para>
-	  Each logger in the system has a name, the name being that
-	  of the component using it to log messages. For instance,
-	  if you want to configure logging for the resolver module,
-	  you add an entry for a logger named <quote>Resolver</quote>. This
-	  configuration will then be used by the loggers in the
-	  Resolver module, and all the libraries used by it.
+          Each logger in the system has a name, the name being that
+          of the component using it to log messages. For instance,
+          if you want to configure logging for the resolver module,
+          you add an entry for a logger named <quote>Resolver</quote>. This
+          configuration will then be used by the loggers in the
+          Resolver module, and all the libraries used by it.
               </para>
 
 <!-- TODO: later we will have a way to know names of all modules
@@ -1917,13 +2372,13 @@ Right now you can only see what their names are if they are running
 
         <para>
 
-	  If you want to specify logging for one specific library
-	  within the module, you set the name to
-	  <replaceable>module.library</replaceable>.  For example, the
-	  logger used by the nameserver address store component
-	  has the full name of <quote>Resolver.nsas</quote>. If
-	  there is no entry in Logging for a particular library,
-	  it will use the configuration given for the module.
+          If you want to specify logging for one specific library
+          within the module, you set the name to
+          <replaceable>module.library</replaceable>.  For example, the
+          logger used by the nameserver address store component
+          has the full name of <quote>Resolver.nsas</quote>. If
+          there is no entry in Logging for a particular library,
+          it will use the configuration given for the module.
 
 <!-- TODO: how to know these specific names?
 
@@ -1938,61 +2393,61 @@ specify module-wide logging and see what appears...
 
 <!-- TODO: severity has not been covered yet -->
 
-	  To illustrate this, suppose you want the cache library
-	  to log messages of severity DEBUG, and the rest of the
-	  resolver code to log messages of severity INFO. To achieve
-	  this you specify two loggers, one with the name
-	  <quote>Resolver</quote> and severity INFO, and one with
-	  the name <quote>Resolver.cache</quote> with severity
-	  DEBUG. As there are no entries for other libraries (e.g.
-	  the nsas), they will use the configuration for the module
-	  (<quote>Resolver</quote>), so giving the desired behavior.
+          To illustrate this, suppose you want the cache library
+          to log messages of severity DEBUG, and the rest of the
+          resolver code to log messages of severity INFO. To achieve
+          this you specify two loggers, one with the name
+          <quote>Resolver</quote> and severity INFO, and one with
+          the name <quote>Resolver.cache</quote> with severity
+          DEBUG. As there are no entries for other libraries (e.g.
+          the nsas), they will use the configuration for the module
+          (<quote>Resolver</quote>), so giving the desired behavior.
 
         </para>
 
         <para>
 
-	  One special case is that of a module name of <quote>*</quote>
-	  (asterisks), which is interpreted as <emphasis>any</emphasis>
-	  module. You can set global logging options by using this,
-	  including setting the logging configuration for a library
-	  that is used by multiple modules (e.g. <quote>*.config</quote>
-	  specifies the configuration library code in whatever
-	  module is using it).
+          One special case is that of a module name of <quote>*</quote>
+          (asterisks), which is interpreted as <emphasis>any</emphasis>
+          module. You can set global logging options by using this,
+          including setting the logging configuration for a library
+          that is used by multiple modules (e.g. <quote>*.config</quote>
+          specifies the configuration library code in whatever
+          module is using it).
 
         </para>
 
         <para>
 
-	  If there are multiple logger specifications in the
-	  configuration that might match a particular logger, the
-	  specification with the more specific logger name takes
-	  precedence. For example, if there are entries for for
-	  both <quote>*</quote> and <quote>Resolver</quote>, the
-	  resolver module — and all libraries it uses —
-	  will log messages according to the configuration in the
-	  second entry (<quote>Resolver</quote>). All other modules
-	  will use the configuration of the first entry
-	  (<quote>*</quote>). If there was also a configuration
-	  entry for <quote>Resolver.cache</quote>, the cache library
-	  within the resolver would use that in preference to the
-	  entry for <quote>Resolver</quote>.
+          If there are multiple logger specifications in the
+          configuration that might match a particular logger, the
+          specification with the more specific logger name takes
+          precedence. For example, if there are entries for for
+          both <quote>*</quote> and <quote>Resolver</quote>, the
+          resolver module — and all libraries it uses —
+          will log messages according to the configuration in the
+          second entry (<quote>Resolver</quote>). All other modules
+          will use the configuration of the first entry
+          (<quote>*</quote>). If there was also a configuration
+          entry for <quote>Resolver.cache</quote>, the cache library
+          within the resolver would use that in preference to the
+          entry for <quote>Resolver</quote>.
 
         </para>
 
         <para>
 
-	  One final note about the naming. When specifying the
-	  module name within a logger, use the name of the module
-	  as specified in <command>bindctl</command>, e.g.
-	  <quote>Resolver</quote> for the resolver module,
-	  <quote>Xfrout</quote> for the xfrout module, etc. When
-	  the message is logged, the message will include the name
-	  of the logger generating the message, but with the module
-	  name replaced by the name of the process implementing
-	  the module (so for example, a message generated by the
-	  <quote>Auth.cache</quote> logger will appear in the output
-	  with a logger name of <quote>b10-auth.cache</quote>).
+          One final note about the naming. When specifying the
+          module name within a logger, use the name of the module
+          as specified in <command>bindctl</command>, e.g.
+          <quote>Resolver</quote> for the resolver module,
+          <quote>Xfrout</quote> for the xfrout module, etc. When
+          the message is logged, the message will include the name
+          of the logger generating the message, but with the module
+          name replaced by the name of the process implementing
+          the module (so for example, a message generated by the
+          <quote>Auth.cache</quote> logger will appear in the output
+          with a logger name of <quote>b10-auth.cache</quote>).
 
         </para>
 
@@ -2004,9 +2459,9 @@ specify module-wide logging and see what appears...
         <para>
 
           This specifies the category of messages logged.
-	  Each message is logged with an associated severity which
-	  may be one of the following (in descending order of
-	  severity):
+          Each message is logged with an associated severity which
+          may be one of the following (in descending order of
+          severity):
         </para>
 
         <itemizedlist>
@@ -2033,11 +2488,11 @@ specify module-wide logging and see what appears...
 
         <para>
 
-	  When the severity of a logger is set to one of these
-	  values, it will only log messages of that severity, and
-	  the severities above it. The severity may also be set to
-	  NONE, in which case all messages from that logger are
-	  inhibited.
+          When the severity of a logger is set to one of these
+          values, it will only log messages of that severity, and
+          the severities above it. The severity may also be set to
+          NONE, in which case all messages from that logger are
+          inhibited.
 
 <!-- TODO: worded wrong? If I set to INFO, why would it show DEBUG which is literally below in that list? -->
 
@@ -2050,9 +2505,9 @@ specify module-wide logging and see what appears...
 
         <para>
 
-	  Each logger can have zero or more
-	  <option>output_options</option>. These specify where log
-	  messages are sent to. These are explained in detail below.
+          Each logger can have zero or more
+          <option>output_options</option>. These specify where log
+          messages are sent to. These are explained in detail below.
 
         </para>
 
@@ -2069,15 +2524,15 @@ specify module-wide logging and see what appears...
 
         <para>
 
-	  When a logger's severity is set to DEBUG, this value
-	  specifies what debug messages should be printed. It ranges
-	  from 0 (least verbose) to 99 (most verbose).
+          When a logger's severity is set to DEBUG, this value
+          specifies what debug messages should be printed. It ranges
+          from 0 (least verbose) to 99 (most verbose).
         </para>
 
 
 <!-- TODO: complete this sentence:
 
-	  The general classification of debug message types is
+          The general classification of debug message types is
 
 TODO; there's a ticket to determine these levels, see #1074
 
@@ -2096,15 +2551,15 @@ TODO; there's a ticket to determine these levels, see #1074
 
         <para>
 
-	  If this is true, the <option>output_options</option> from
-	  the parent will be used. For example, if there are two
-	  loggers configured; <quote>Resolver</quote> and
-	  <quote>Resolver.cache</quote>, and <option>additive</option>
-	  is true in the second, it will write the log messages
-	  not only to the destinations specified for
-	  <quote>Resolver.cache</quote>, but also to the destinations
-	  as specified in the <option>output_options</option> in
-	  the logger named <quote>Resolver</quote>.
+          If this is true, the <option>output_options</option> from
+          the parent will be used. For example, if there are two
+          loggers configured; <quote>Resolver</quote> and
+          <quote>Resolver.cache</quote>, and <option>additive</option>
+          is true in the second, it will write the log messages
+          not only to the destinations specified for
+          <quote>Resolver.cache</quote>, but also to the destinations
+          as specified in the <option>output_options</option> in
+          the logger named <quote>Resolver</quote>.
 
 <!-- TODO: check this -->
 
@@ -2119,10 +2574,10 @@ TODO; there's a ticket to determine these levels, see #1074
 
         <para>
 
-	  The main settings for an output option are the
-	  <option>destination</option> and a value called
-	  <option>output</option>, the meaning of which depends on
-	  the destination that is set.
+          The main settings for an output option are the
+          <option>destination</option> and a value called
+          <option>output</option>, the meaning of which depends on
+          the destination that is set.
 
         </para>
 
@@ -2158,8 +2613,8 @@ TODO; there's a ticket to determine these levels, see #1074
 
         <para>
 
-	  Depending on what is set as the output destination, this
-	  value is interpreted as follows:
+          Depending on what is set as the output destination, this
+          value is interpreted as follows:
 
         </para>
 
@@ -2169,10 +2624,10 @@ TODO; there's a ticket to determine these levels, see #1074
             <term><option>destination</option> is <quote>console</quote></term>
             <listitem>
               <simpara>
-		 The value of output must be one of <quote>stdout</quote>
-		 (messages printed to standard output) or
-		 <quote>stderr</quote> (messages printed to standard
-		 error).
+                 The value of output must be one of <quote>stdout</quote>
+                 (messages printed to standard output) or
+                 <quote>stderr</quote> (messages printed to standard
+                 error).
               </simpara>
             </listitem>
           </varlistentry>
@@ -2181,8 +2636,8 @@ TODO; there's a ticket to determine these levels, see #1074
             <term><option>destination</option> is <quote>file</quote></term>
             <listitem>
               <simpara>
-		The value of output is interpreted as a file name;
-		log messages will be appended to this file.
+                The value of output is interpreted as a file name;
+                log messages will be appended to this file.
               </simpara>
             </listitem>
           </varlistentry>
@@ -2191,10 +2646,10 @@ TODO; there's a ticket to determine these levels, see #1074
             <term><option>destination</option> is <quote>syslog</quote></term>
             <listitem>
               <simpara>
-		The value of output is interpreted as the
-		<command>syslog</command> facility (e.g.
-		<emphasis>local0</emphasis>) that should be used
-		for log messages.
+                The value of output is interpreted as the
+                <command>syslog</command> facility (e.g.
+                <emphasis>local0</emphasis>) that should be used
+                for log messages.
               </simpara>
             </listitem>
           </varlistentry>
@@ -2211,10 +2666,10 @@ TODO; there's a ticket to determine these levels, see #1074
           <title>flush (true of false)</title>
 
           <para>
-	    Flush buffers after each log message. Doing this will
-	    reduce performance but will ensure that if the program
-	    terminates abnormally, all messages up to the point of
-	    termination are output.
+            Flush buffers after each log message. Doing this will
+            reduce performance but will ensure that if the program
+            terminates abnormally, all messages up to the point of
+            termination are output.
           </para>
 
         </section>
@@ -2223,11 +2678,11 @@ TODO; there's a ticket to determine these levels, see #1074
           <title>maxsize (integer)</title>
 
           <para>
-	    Only relevant when destination is file, this is maximum
-	    file size of output files in bytes. When the maximum
-	    size is reached, the file is renamed and a new file opened.
-	    (For example, a ".1" is appended to the name —
-	    if a ".1" file exists, it is renamed ".2",
+            Only relevant when destination is file, this is maximum
+            file size of output files in bytes. When the maximum
+            size is reached, the file is renamed and a new file opened.
+            (For example, a ".1" is appended to the name —
+            if a ".1" file exists, it is renamed ".2",
             etc.)
           </para>
 
@@ -2241,9 +2696,9 @@ TODO; there's a ticket to determine these levels, see #1074
           <title>maxver (integer)</title>
 
           <para>
-	    Maximum number of old log files to keep around when
-	    rolling the output file. Only relevant when
-	    <option>destination</option> is <quote>file</quote>.
+            Maximum number of old log files to keep around when
+            rolling the output file. Only relevant when
+            <option>destination</option> is <quote>file</quote>.
           </para>
 
         </section>
@@ -2257,11 +2712,11 @@ TODO; there's a ticket to determine these levels, see #1074
 
         <para>
 
-	  In this example we want to set the global logging to
-	  write to the file <filename>/var/log/my_bind10.log</filename>,
-	  at severity WARN. We want the authoritative server to
-	  log at DEBUG with debuglevel 40, to a different file
-	  (<filename>/tmp/debug_messages</filename>).
+          In this example we want to set the global logging to
+          write to the file <filename>/var/log/my_bind10.log</filename>,
+          at severity WARN. We want the authoritative server to
+          log at DEBUG with debuglevel 40, to a different file
+          (<filename>/tmp/debug_messages</filename>).
 
         </para>
 
@@ -2282,9 +2737,9 @@ Logging/loggers	[]	list
 
         <para>
 
-	  By default, no specific loggers are configured, in which
-	  case the severity defaults to INFO and the output is
-	  written to stderr.
+          By default, no specific loggers are configured, in which
+          case the severity defaults to INFO and the output is
+          written to stderr.
 
         </para>
 
@@ -2306,8 +2761,8 @@ Logging/loggers/	list	(modified)
 
         <para>
 
-	  The loggers value line changed to indicate that it is no
-	  longer an empty list:
+          The loggers value line changed to indicate that it is no
+          longer an empty list:
 
         </para>
 
@@ -2325,9 +2780,9 @@ Logging/loggers[0]/output_options	[]	list	(default)
 
         <para>
 
-	  The name is mandatory, so we must set it. We will also
-	  change the severity as well. Let's start with the global
-	  logger.
+          The name is mandatory, so we must set it. We will also
+          change the severity as well. Let's start with the global
+          logger.
 
         </para>
 
@@ -2347,8 +2802,8 @@ Logging/loggers[0]/output_options	[]	list	(default)
 
         <para>
 
-	  Of course, we need to specify where we want the log
-	  messages to go, so we add an entry for an output option.
+          Of course, we need to specify where we want the log
+          messages to go, so we add an entry for an output option.
 
         </para>
 
@@ -2384,8 +2839,8 @@ Logging/loggers[0]/output_options[0]/maxver	0	integer	(default)
 
         <para>
 
-	  Which would make the entire configuration for this logger
-	  look like:
+          Which would make the entire configuration for this logger
+          look like:
 
         </para>
 
@@ -2407,8 +2862,8 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         <para>
 
-	  That looks OK, so let's commit it before we add the
-	  configuration for the authoritative server's logger.
+          That looks OK, so let's commit it before we add the
+          configuration for the authoritative server's logger.
 
         </para>
 
@@ -2420,8 +2875,8 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         <para>
 
-	  Now that we have set it, and checked each value along
-	  the way, adding a second entry is quite similar.
+          Now that we have set it, and checked each value along
+          the way, adding a second entry is quite similar.
 
         </para>
 
@@ -2441,10 +2896,10 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         <para>
 
-	  And that's it. Once we have found whatever it was we
-	  needed the debug messages for, we can simply remove the
-	  second logger to let the authoritative server use the
-	  same settings as the rest.
+          And that's it. Once we have found whatever it was we
+          needed the debug messages for, we can simply remove the
+          second logger to let the authoritative server use the
+          same settings as the rest.
 
         </para>
 
@@ -2458,8 +2913,8 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
         <para>
 
-	  And every module will now be using the values from the
-	  logger named <quote>*</quote>.
+          And every module will now be using the values from the
+          logger named <quote>*</quote>.
 
         </para>
 
@@ -2471,11 +2926,11 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
       <title>Logging Message Format</title>
 
       <para>
-	  Each message written by BIND 10 to the configured logging
-	  destinations comprises a number of components that identify
-	  the origin of the message and, if the message indicates
-	  a problem, information about the problem that may be
-	  useful in fixing it.
+          Each message written by BIND 10 to the configured logging
+          destinations comprises a number of components that identify
+          the origin of the message and, if the message indicates
+          a problem, information about the problem that may be
+          useful in fixing it.
       </para>
 
       <para>
@@ -2525,29 +2980,29 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
           <varlistentry>
           <term>ASIODNS_OPENSOCK</term>
           <listitem><para>
-	    The message identification.  Every message in BIND 10
-	    has a unique identification, which can be used as an
-	    index into the <ulink
-	    url="bind10-messages.html"><citetitle>BIND 10 Messages
-	    Manual</citetitle></ulink> (<ulink
-	    url="http://bind10.isc.org/docs/bind10-messages.html"
-	    />) from which more information can be obtained.
+            The message identification.  Every message in BIND 10
+            has a unique identification, which can be used as an
+            index into the <ulink
+            url="bind10-messages.html"><citetitle>BIND 10 Messages
+            Manual</citetitle></ulink> (<ulink
+            url="http://bind10.isc.org/docs/bind10-messages.html"
+            />) from which more information can be obtained.
           </para></listitem>
           </varlistentry>
 
           <varlistentry>
           <term>error 111 opening TCP socket to 127.0.0.1(53)</term>
           <listitem><para>
-	      A brief description of the cause of the problem.
-	      Within this text, information relating to the condition
-	      that caused the message to be logged will be included.
-	      In this example, error number 111 (an operating
-	      system-specific error number) was encountered when
-	      trying to open a TCP connection to port 53 on the
-	      local system (address 127.0.0.1).  The next step
-	      would be to find out the reason for the failure by
-	      consulting your system's documentation to identify
-	      what error number 111 means.
+              A brief description of the cause of the problem.
+              Within this text, information relating to the condition
+              that caused the message to be logged will be included.
+              In this example, error number 111 (an operating
+              system-specific error number) was encountered when
+              trying to open a TCP connection to port 53 on the
+              local system (address 127.0.0.1).  The next step
+              would be to find out the reason for the failure by
+              consulting your system's documentation to identify
+              what error number 111 means.
           </para></listitem>
           </varlistentry>
           </variablelist>
@@ -2557,6 +3012,18 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 
   </chapter>
 
+<!-- TODO: acknolwedgements must be unnumbered -->
+
+  <chapter>
+    <title>Acknowledgements</title>
+    <para>ISC would like to acknowledge generous support for
+    development of DHCPv4 and DHCPv6 components provided by <ulink
+    url="http://www.comcast.com">Comcast</ulink>.</para>
+  </chapter>
+
+<!-- TODO: Add bibliography section (mostly RFCs, probably) -->
+
+
 <!-- TODO: how to help: run unit tests, join lists, review trac tickets -->
 
   <!-- <index>    <title>Index</title> </index> -->
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index ba7ae81..7c6cdb8 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = bind10 bindctl cfgmgr loadzone msgq host cmdctl auth xfrin xfrout \
-	usermgr zonemgr stats tests resolver sockcreator dhcp6 dhcp4
+SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
+	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6
 
 check-recursive: all-recursive
diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc
index d684c68..2ae520c 100644
--- a/src/bin/auth/auth_config.cc
+++ b/src/bin/auth/auth_config.cc
@@ -35,7 +35,6 @@
 #include <server_common/portconfig.h>
 
 using namespace std;
-using boost::shared_ptr;
 using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::datasrc;
@@ -56,7 +55,7 @@ public:
     virtual void commit();
 private:
     AuthSrv& server_;
-    vector<shared_ptr<AuthConfigParser> > datasources_;
+    vector<boost::shared_ptr<AuthConfigParser> > datasources_;
     set<string> configured_sources_;
 };
 
@@ -86,8 +85,8 @@ DatasourcesConfig::build(ConstElementPtr config_value) {
                       datasrc_type->stringValue() << "' already configured");
         }
         
-        shared_ptr<AuthConfigParser> datasrc_config =
-            shared_ptr<AuthConfigParser>(
+        boost::shared_ptr<AuthConfigParser> datasrc_config =
+            boost::shared_ptr<AuthConfigParser>(
                 createAuthConfigParser(server_, string("datasources/") +
                                        datasrc_type->stringValue(),
                                        true));
@@ -109,7 +108,8 @@ DatasourcesConfig::commit() {
     // Currently memory data source for class IN is the only possibility.
     server_.setInMemoryClient(RRClass::IN(), AuthSrv::InMemoryClientPtr());
 
-    BOOST_FOREACH(shared_ptr<AuthConfigParser> datasrc_config, datasources_) {
+    BOOST_FOREACH(boost::shared_ptr<AuthConfigParser> datasrc_config,
+                  datasources_) {
         datasrc_config->commit();
     }
 }
@@ -163,7 +163,7 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) {
             isc_throw(AuthConfigError, "Missing zone file for zone: "
                       << origin->str());
         }
-        shared_ptr<InMemoryZoneFinder> zone_finder(new
+        boost::shared_ptr<InMemoryZoneFinder> zone_finder(new
                                                    InMemoryZoneFinder(rrclass_,
             Name(origin->stringValue())));
         const result::Result result = memory_client_->addZone(zone_finder);
@@ -327,7 +327,7 @@ configureAuthServer(AuthSrv& server, ConstElementPtr config_set) {
                   "Null pointer is passed to configuration parser");
     }
 
-    typedef shared_ptr<AuthConfigParser> ParserPtr;
+    typedef boost::shared_ptr<AuthConfigParser> ParserPtr;
     vector<ParserPtr> parsers;
     typedef pair<string, ConstElementPtr> ConfigPair;
     try {
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index da05e48..9c5e594 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -76,7 +76,6 @@ using namespace isc::xfr;
 using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
-using boost::shared_ptr;
 
 class AuthSrvImpl {
 private:
@@ -124,7 +123,7 @@ public:
     AddressList listen_addresses_;
 
     /// The TSIG keyring
-    const shared_ptr<TSIGKeyRing>* keyring_;
+    const boost::shared_ptr<TSIGKeyRing>* keyring_;
 
     /// Bind the ModuleSpec object in config_session_ with
     /// isc:config::ModuleSpec::validateStatistics.
@@ -228,12 +227,13 @@ private:
     AuthSrv* server_;
 };
 
-AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client) :
-    impl_(new AuthSrvImpl(use_cache, xfrout_client)),
-    checkin_(new ConfigChecker(this)),
-    dns_lookup_(new MessageLookup(this)),
-    dns_answer_(new MessageAnswer(this))
-{}
+AuthSrv::AuthSrv(const bool use_cache, AbstractXfroutClient& xfrout_client)
+{
+    impl_ = new AuthSrvImpl(use_cache, xfrout_client);
+    checkin_ = new ConfigChecker(this);
+    dns_lookup_ = new MessageLookup(this);
+    dns_answer_ = new MessageAnswer(this);
+}
 
 void
 AuthSrv::stop() {
@@ -786,6 +786,6 @@ AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
 }
 
 void
-AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
+AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
     impl_->keyring_ = keyring;
 }
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index 940d57b..280558d 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -32,7 +32,6 @@
 #include <auth/command.h>
 
 using boost::scoped_ptr;
-using boost::shared_ptr;
 using namespace isc::auth;
 using namespace isc::config;
 using namespace isc::data;
@@ -136,7 +135,7 @@ public:
         // that doesn't block other server operations.
         // TODO: we may (should?) want to check the "last load time" and
         // the timestamp of the file and skip loading if the file isn't newer.
-        shared_ptr<InMemoryZoneFinder> zone_finder(
+        boost::shared_ptr<InMemoryZoneFinder> zone_finder(
             new InMemoryZoneFinder(old_zone_finder->getClass(),
                                    old_zone_finder->getOrigin()));
         zone_finder->load(old_zone_finder->getFileName());
@@ -147,7 +146,7 @@ public:
 
 private:
     // zone finder to be updated with the new file.
-    shared_ptr<InMemoryZoneFinder> old_zone_finder;
+    boost::shared_ptr<InMemoryZoneFinder> old_zone_finder;
 
     // A helper private method to parse and validate command parameters.
     // On success, it sets 'old_zone_finder' to the zone to be updated.
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index f159262..2bf8f5e 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -15,6 +15,8 @@
 #include <algorithm>            // for std::max
 #include <vector>
 #include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
 
 #include <dns/message.h>
 #include <dns/rcode.h>
@@ -67,7 +69,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 
     // Find A rrset
     if (qname_ != qname || qtype_ != RRType::A()) {
-        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL,
+        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
                                                     options | dnssec_opt_);
         if (a_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
@@ -78,7 +80,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
     // Find AAAA rrset
     if (qname_ != qname || qtype_ != RRType::AAAA()) {
         ZoneFinder::FindResult aaaa_result =
-            zone.find(qname, RRType::AAAA(), NULL, options | dnssec_opt_);
+            zone.find(qname, RRType::AAAA(), options | dnssec_opt_);
         if (aaaa_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(aaaa_result.rrset),
@@ -90,7 +92,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 void
 Query::addSOA(ZoneFinder& finder) {
     ZoneFinder::FindResult soa_result(finder.find(finder.getOrigin(),
-        RRType::SOA(), NULL, dnssec_opt_));
+        RRType::SOA(), dnssec_opt_));
     if (soa_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
@@ -146,7 +148,7 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // otherwise we shouldn't have got NXDOMAIN for the original query in
     // the first place).
     const ZoneFinder::FindResult fresult = finder.find(wildname,
-                                                       RRType::NSEC(), NULL,
+                                                       RRType::NSEC(),
                                                        dnssec_opt_);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -171,7 +173,7 @@ Query::addWildcardProof(ZoneFinder& finder) {
     // substitution.  Confirm that by specifying NO_WILDCARD.  It should result
     // in NXDOMAIN and an NSEC RR that proves it should be returned.
     const ZoneFinder::FindResult fresult =
-        finder.find(qname_, RRType::NSEC(), NULL,
+        finder.find(qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -194,7 +196,7 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
                       boost::const_pointer_cast<RRset>(nsec), dnssec_);
     
     const ZoneFinder::FindResult fresult =
-        finder.find(qname_, RRType::NSEC(), NULL,
+        finder.find(qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -213,8 +215,7 @@ void
 Query::addAuthAdditional(ZoneFinder& finder) {
     // Fill in authority and addtional sections.
     ZoneFinder::FindResult ns_result = finder.find(finder.getOrigin(),
-                                                   RRType::NS(), NULL,
-                                                   dnssec_opt_);
+                                                   RRType::NS(), dnssec_opt_);
     // zone origin name should have NS records
     if (ns_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
@@ -229,7 +230,6 @@ Query::addAuthAdditional(ZoneFinder& finder) {
 
 void
 Query::process() {
-    bool keep_doing = true;
     const bool qtype_is_any = (qtype_ == RRType::ANY());
 
     response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
@@ -251,147 +251,151 @@ Query::process() {
     // Found a zone which is the nearest ancestor to QNAME, set the AA bit
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     response_.setRcode(Rcode::NOERROR());
-    while (keep_doing) {
-        keep_doing = false;
-        std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
-        const ZoneFinder::FindResult db_result(
-            zfinder.find(qname_, qtype_, target.get(), dnssec_opt_));
-        switch (db_result.code) {
-            case ZoneFinder::DNAME: {
-                // First, put the dname into the answer
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<RRset>(db_result.rrset),
-                    dnssec_);
+    std::vector<ConstRRsetPtr> target;
+    boost::function0<ZoneFinder::FindResult> find;
+    if (qtype_is_any) {
+        find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
+                           boost::ref(target), dnssec_opt_);
+    } else {
+        find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
+                           dnssec_opt_);
+    }
+    ZoneFinder::FindResult db_result(find());
+    switch (db_result.code) {
+        case ZoneFinder::DNAME: {
+            // First, put the dname into the answer
+            response_.addRRset(Message::SECTION_ANSWER,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
+            /*
+             * Empty DNAME should never get in, as it is impossible to
+             * create one in master file.
+             *
+             * FIXME: Other way to prevent this should be done
+             */
+            assert(db_result.rrset->getRdataCount() > 0);
+            // Get the data of DNAME
+            const rdata::generic::DNAME& dname(
+                dynamic_cast<const rdata::generic::DNAME&>(
+                db_result.rrset->getRdataIterator()->getCurrent()));
+            // The yet unmatched prefix dname
+            const Name prefix(qname_.split(0, qname_.getLabelCount() -
+                db_result.rrset->getName().getLabelCount()));
+            // If we put it together, will it be too long?
+            // (The prefix contains trailing ., which will be removed
+            if (prefix.getLength() - Name::ROOT_NAME().getLength() +
+                dname.getDname().getLength() > Name::MAX_WIRE) {
                 /*
-                 * Empty DNAME should never get in, as it is impossible to
-                 * create one in master file.
-                 *
-                 * FIXME: Other way to prevent this should be done
+                 * In case the synthesized name is too long, section 4.1
+                 * of RFC 2672 mandates we return YXDOMAIN.
                  */
-                assert(db_result.rrset->getRdataCount() > 0);
-                // Get the data of DNAME
-                const rdata::generic::DNAME& dname(
-                    dynamic_cast<const rdata::generic::DNAME&>(
-                    db_result.rrset->getRdataIterator()->getCurrent()));
-                // The yet unmatched prefix dname
-                const Name prefix(qname_.split(0, qname_.getLabelCount() -
-                    db_result.rrset->getName().getLabelCount()));
-                // If we put it together, will it be too long?
-                // (The prefix contains trailing ., which will be removed
-                if (prefix.getLength() - Name::ROOT_NAME().getLength() +
-                    dname.getDname().getLength() > Name::MAX_WIRE) {
-                    /*
-                     * In case the synthesized name is too long, section 4.1
-                     * of RFC 2672 mandates we return YXDOMAIN.
-                     */
-                    response_.setRcode(Rcode::YXDOMAIN());
-                    return;
-                }
-                // The new CNAME we are creating (it will be unsigned even
-                // with DNSSEC, the DNAME is signed and it can be validated
-                // by that)
-                RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
-                    RRType::CNAME(), db_result.rrset->getTTL()));
-                // Construct the new target by replacing the end
-                cname->addRdata(rdata::generic::CNAME(qname_.split(0,
-                    qname_.getLabelCount() -
-                    db_result.rrset->getName().getLabelCount()).
-                    concatenate(dname.getDname())));
-                response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
-                break;
+                response_.setRcode(Rcode::YXDOMAIN());
+                return;
             }
-            case ZoneFinder::CNAME:
-            case ZoneFinder::WILDCARD_CNAME:
-                /*
-                 * We don't do chaining yet. Therefore handling a CNAME is
-                 * mostly the same as handling SUCCESS, but we didn't get
-                 * what we expected. It means no exceptions in ANY or NS
-                 * on the origin (though CNAME in origin is probably
-                 * forbidden anyway).
-                 *
-                 * So, just put it there.
-                 */
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<RRset>(db_result.rrset),
-                    dnssec_);
+            // The new CNAME we are creating (it will be unsigned even
+            // with DNSSEC, the DNAME is signed and it can be validated
+            // by that)
+            RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
+                RRType::CNAME(), db_result.rrset->getTTL()));
+            // Construct the new target by replacing the end
+            cname->addRdata(rdata::generic::CNAME(qname_.split(0,
+                qname_.getLabelCount() -
+                db_result.rrset->getName().getLabelCount()).
+                concatenate(dname.getDname())));
+            response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
+            break;
+        }
+        case ZoneFinder::CNAME:
+        case ZoneFinder::WILDCARD_CNAME:
+            /*
+             * We don't do chaining yet. Therefore handling a CNAME is
+             * mostly the same as handling SUCCESS, but we didn't get
+             * what we expected. It means no exceptions in ANY or NS
+             * on the origin (though CNAME in origin is probably
+             * forbidden anyway).
+             *
+             * So, just put it there.
+             */
+            response_.addRRset(Message::SECTION_ANSWER,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
 
-                // If the answer is a result of wildcard substitution,
-                // add a proof that there's no closer name.
-                if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
-                    addWildcardProof(*result.zone_finder);
-                }
-                break;
-            case ZoneFinder::SUCCESS:
-            case ZoneFinder::WILDCARD:
-                if (qtype_is_any) {
-                    // If quety type is ANY, insert all RRs under the domain
-                    // into answer section.
-                    BOOST_FOREACH(RRsetPtr rrset, *target) {
-                        response_.addRRset(Message::SECTION_ANSWER, rrset,
-                                           dnssec_);
-                        // Handle additional for answer section
-                        addAdditional(*result.zone_finder, *rrset.get());
-                    }
-                } else {
+            // If the answer is a result of wildcard substitution,
+            // add a proof that there's no closer name.
+            if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
+                addWildcardProof(*result.zone_finder);
+            }
+            break;
+        case ZoneFinder::SUCCESS:
+        case ZoneFinder::WILDCARD:
+            if (qtype_is_any) {
+                // If quety type is ANY, insert all RRs under the domain
+                // into answer section.
+                BOOST_FOREACH(ConstRRsetPtr rrset, target) {
                     response_.addRRset(Message::SECTION_ANSWER,
-                        boost::const_pointer_cast<RRset>(db_result.rrset),
-                        dnssec_);
+                        boost::const_pointer_cast<RRset>(rrset), dnssec_);
                     // Handle additional for answer section
-                    addAdditional(*result.zone_finder, *db_result.rrset);
+                    addAdditional(*result.zone_finder, *rrset.get());
                 }
-                // If apex NS records haven't been provided in the answer
-                // section, insert apex NS records into the authority section
-                // and AAAA/A RRS of each of the NS RDATA into the additional
-                // section.
-                if (qname_ != result.zone_finder->getOrigin() ||
-                    db_result.code != ZoneFinder::SUCCESS ||
-                    (qtype_ != RRType::NS() && !qtype_is_any))
-                {
-                    addAuthAdditional(*result.zone_finder);
-                }
-
-                // If the answer is a result of wildcard substitution,
-                // add a proof that there's no closer name.
-                if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
-                    addWildcardProof(*result.zone_finder);
-                }
-                break;
-            case ZoneFinder::DELEGATION:
-                response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-                response_.addRRset(Message::SECTION_AUTHORITY,
+            } else {
+                response_.addRRset(Message::SECTION_ANSWER,
                     boost::const_pointer_cast<RRset>(db_result.rrset),
                     dnssec_);
+                // Handle additional for answer section
                 addAdditional(*result.zone_finder, *db_result.rrset);
-                break;
-            case ZoneFinder::NXDOMAIN:
-                response_.setRcode(Rcode::NXDOMAIN());
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    addNXDOMAINProof(zfinder, db_result.rrset);
-                }
-                break;
-            case ZoneFinder::NXRRSET:
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    response_.addRRset(Message::SECTION_AUTHORITY,
-                                       boost::const_pointer_cast<RRset>(
-                                           db_result.rrset),
-                                       dnssec_);
-                }
-                break;
-            case ZoneFinder::WILDCARD_NXRRSET:
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    addWildcardNXRRSETProof(zfinder, db_result.rrset);
-                }
-                break;
-            default:
-                // This is basically a bug of the data source implementation,
-                // but could also happen in the middle of development where
-                // we try to add a new result code.
-                isc_throw(isc::NotImplemented, "Unknown result code");
-                break;
-        }
+            }
+            // If apex NS records haven't been provided in the answer
+            // section, insert apex NS records into the authority section
+            // and AAAA/A RRS of each of the NS RDATA into the additional
+            // section.
+            if (qname_ != result.zone_finder->getOrigin() ||
+                db_result.code != ZoneFinder::SUCCESS ||
+                (qtype_ != RRType::NS() && !qtype_is_any))
+            {
+                addAuthAdditional(*result.zone_finder);
+            }
+
+            // If the answer is a result of wildcard substitution,
+            // add a proof that there's no closer name.
+            if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
+                addWildcardProof(*result.zone_finder);
+            }
+            break;
+        case ZoneFinder::DELEGATION:
+            response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
+            response_.addRRset(Message::SECTION_AUTHORITY,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
+            addAdditional(*result.zone_finder, *db_result.rrset);
+            break;
+        case ZoneFinder::NXDOMAIN:
+            response_.setRcode(Rcode::NXDOMAIN());
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                addNXDOMAINProof(zfinder, db_result.rrset);
+            }
+            break;
+        case ZoneFinder::NXRRSET:
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                response_.addRRset(Message::SECTION_AUTHORITY,
+                                   boost::const_pointer_cast<RRset>(
+                                       db_result.rrset),
+                                   dnssec_);
+            }
+            break;
+        case ZoneFinder::WILDCARD_NXRRSET:
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                addWildcardNXRRSETProof(zfinder, db_result.rrset);
+            }
+            break;
+        default:
+            // This is basically a bug of the data source implementation,
+            // but could also happen in the middle of development where
+            // we try to add a new result code.
+            isc_throw(isc::NotImplemented, "Unknown result code");
+            break;
     }
 }
 
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index d90006a..329a2dc 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -54,7 +54,6 @@ using namespace isc::asiolink;
 using namespace isc::testutils;
 using namespace isc::server_common::portconfig;
 using isc::UnitTestUtil;
-using boost::shared_ptr;
 
 namespace {
 const char* const CONFIG_TESTDB =
@@ -251,7 +250,7 @@ TEST_F(AuthSrvTest, TSIGSigned) {
     createRequestPacket(request_message, IPPROTO_UDP, &context);
 
     // Run the message through the server
-    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(key);
     server.setTSIGKeyRing(&keyring);
     server.processMessage(*io_message, parse_message, response_obuffer,
@@ -285,7 +284,7 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
     createRequestPacket(request_message, IPPROTO_UDP, &context);
 
     // Process the message, but use a different key there
-    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     server.setTSIGKeyRing(&keyring);
     server.processMessage(*io_message, parse_message, response_obuffer,
                           &dnsserv);
@@ -317,7 +316,7 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
     createRequestPacket(request_message, IPPROTO_UDP, &context);
 
     // Process the message, but use a different key there
-    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
     server.setTSIGKeyRing(&keyring);
     server.processMessage(*io_message, parse_message, response_obuffer,
@@ -353,7 +352,7 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
     createRequestPacket(request_message, IPPROTO_UDP, &context);
 
     // Process the message, but use a different key there
-    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
+    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
     server.setTSIGKeyRing(&keyring);
     server.processMessage(*io_message, parse_message, response_obuffer,
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 14067ab..858759e 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -211,8 +211,10 @@ public:
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT);
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<ConstRRsetPtr>& target,
+                               const FindOptions options = FIND_DEFAULT);
 
     // If false is passed, it makes the zone broken as if it didn't have the
     // SOA.
@@ -305,8 +307,29 @@ substituteWild(const RRset& wild_rrset, const Name& real_name) {
 }
 
 ZoneFinder::FindResult
+MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
+                        const FindOptions options)
+{
+    ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
+    if (result.code == NXRRSET) {
+        const Domains::const_iterator found_domain = domains_.find(name);
+        if (!found_domain->second.empty()) {
+            for (RRsetStore::const_iterator found_rrset =
+                 found_domain->second.begin();
+                 found_rrset != found_domain->second.end(); ++found_rrset) {
+                // Insert RRs under the domain name into target
+                target.push_back(found_rrset->second);
+            }
+            return (FindResult(SUCCESS, RRsetPtr()));
+        }
+    }
+
+    return (result);
+}
+
+ZoneFinder::FindResult
 MockZoneFinder::find(const Name& name, const RRType& type,
-                     RRsetList* target, const FindOptions options)
+                     const FindOptions options)
 {
     // Emulating a broken zone: mandatory apex RRs are missing if specifically
     // configured so (which are rare cases).
@@ -358,17 +381,6 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             return (FindResult(SUCCESS, rrset));
         }
 
-        // If not found but we have a target, fill it with all RRsets here
-        if (!found_domain->second.empty() && target != NULL) {
-            for (found_rrset = found_domain->second.begin();
-                 found_rrset != found_domain->second.end(); ++found_rrset) {
-                // Insert RRs under the domain name into target
-                target->addRRset(
-                    boost::const_pointer_cast<RRset>(found_rrset->second));
-            }
-            return (FindResult(SUCCESS, found_domain->second.begin()->second));
-        }
-
         // Otherwise, if this domain name has CNAME, return it.
         found_rrset = found_domain->second.find(RRType::CNAME());
         if (found_rrset != found_domain->second.end()) {
diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in
index 9e4abc0..17d2c53 100755
--- a/src/bin/bind10/run_bind10.sh.in
+++ b/src/bin/bind10/run_bind10.sh.in
@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
+PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
diff --git a/src/bin/cfgmgr/plugins/logging.spec b/src/bin/cfgmgr/plugins/logging.spec
index e377b0e..da20ce5 100644
--- a/src/bin/cfgmgr/plugins/logging.spec
+++ b/src/bin/cfgmgr/plugins/logging.spec
@@ -57,7 +57,7 @@
                       { "item_name": "flush",
                         "item_type": "boolean",
                         "item_optional": false,
-                        "item_default": false
+                        "item_default": true
                       },
                       { "item_name": "maxsize",
                         "item_type": "integer",
diff --git a/src/bin/ddns/Makefile.am b/src/bin/ddns/Makefile.am
new file mode 100644
index 0000000..0b014cb
--- /dev/null
+++ b/src/bin/ddns/Makefile.am
@@ -0,0 +1,42 @@
+SUBDIRS = . tests
+
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+
+pkglibexec_SCRIPTS = b10-ddns
+
+b10_ddnsdir = $(pkgdatadir)
+b10_ddns_DATA = ddns.spec
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = b10-ddns ddns.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.pyc
+
+EXTRA_DIST =  ddns_messages.mes ddns.spec
+man_MANS = b10-ddns.8
+EXTRA_DIST += $(man_MANS) b10-ddns.xml
+
+if ENABLE_MAN
+
+b10-ddns.8: b10-ddns.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-ddns.xml
+
+endif
+
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py : ddns_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/ddns_messages.mes
+
+# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
+b10-ddns: ddns.py $(PYTHON_LOGMSGPKG_DIR)/work/ddns_messages.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
+	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" ddns.py >$@
+	chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/bin/ddns/b10-ddns.8 b/src/bin/ddns/b10-ddns.8
new file mode 100644
index 0000000..3e2e102
--- /dev/null
+++ b/src/bin/ddns/b10-ddns.8
@@ -0,0 +1,97 @@
+'\" t
+.\"     Title: b10-ddns
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: December 9, 2011
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-DDNS" "8" "December 9, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-ddns \- Dynamic DNS update service
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-ddns\fR\ 'u
+\fBb10\-ddns\fR [\fB\-v\fR | \fB\-\-verbose\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-ddns\fR
+daemon provides the BIND 10 Dynamic Update (DDNS) service, as specified in RFC 2136\&. Normally it is started by the
+\fBbind10\fR(8)
+boss process\&. When the
+\fBb10\-auth\fR
+DNS server receives a DDNS update,
+\fBb10\-ddns\fR
+updates the zone in the BIND 10 zone data store\&.
+.PP
+This daemon communicates with BIND 10 over a
+\fBb10-msgq\fR(8)
+C\-Channel connection\&. If this connection is not established,
+\fBb10\-ddns\fR
+will exit\&.
+.PP
+
+\fBb10\-ddns\fR
+receives its configurations from
+\fBb10-cfgmgr\fR(8)\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-v\fR, \fB\-\-verbose\fR
+.RS 4
+This value is ignored at this moment, but is provided for compatibility with the bind10 Boss process
+.RE
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable settings are:
+.PP
+
+\fIzones\fR
+The zones option is a named set of zones that can be updated with DDNS\&. Each entry has one element called update_acl, which is is a list of access control rules that define update permissions\&. By default this is empty; DDNS must be explicitely enabled per zone\&.
+.PP
+The module commands are:
+.PP
+
+\fBshutdown\fR
+Exits
+\fBb10\-ddns\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+.SH "SEE ALSO"
+.PP
+
+\fBb10-auth\fR(8),
+\fBb10-cfgmgr\fR(8),
+\fBb10-msgq\fR(8),
+\fBb10-xfrin\fR(8),
+\fBb10-xfrout\fR(8),
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-ddns\fR
+daemon was first implemented in December 2011 for the ISC BIND 10 project\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/ddns/b10-ddns.xml b/src/bin/ddns/b10-ddns.xml
new file mode 100644
index 0000000..e8b3f40
--- /dev/null
+++ b/src/bin/ddns/b10-ddns.xml
@@ -0,0 +1,161 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "—">]>
+<!--
+ - Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<refentry>
+
+  <refentryinfo>
+    <date>December 9, 2011</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-ddns</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-ddns</refname>
+    <refpurpose>Dynamic DNS update service</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2011</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-ddns</command>
+        <group choice="opt">
+          <arg choice="plain"><option>-v</option></arg>
+          <arg choice="plain"><option>--verbose</option></arg>
+        </group>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>The <command>b10-ddns</command> daemon provides the BIND 10
+      Dynamic Update (DDNS) service, as specified in RFC 2136.
+      Normally it is started by the
+      <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      boss process.
+      When the <command>b10-auth</command> DNS server receives
+      a DDNS update, <command>b10-ddns</command> updates the zone
+      in the BIND 10 zone data store.
+    </para>
+
+    <para>
+      This daemon communicates with BIND 10 over a
+      <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      C-Channel connection.  If this connection is not established,
+      <command>b10-ddns</command> will exit.
+    </para>
+
+    <para>
+     <command>b10-ddns</command> receives its configurations from
+<citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term>
+          <option>-v</option>,
+          <option>--verbose</option>
+        </term>
+        <listitem>
+          <para>
+            This value is ignored at this moment, but is provided for
+            compatibility with the bind10 Boss process
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+    <para>
+      The configurable settings are:
+    </para>
+    <para>
+      <varname>zones</varname>
+      The zones option is a named set of zones that can be updated with
+      DDNS. Each entry has one element called update_acl, which is
+      a list of access control rules that define update permissions.
+      By default this is empty; DDNS must be explicitely enabled per zone.
+    </para>
+
+    <para>
+      The module commands are:
+    </para>
+    <para>
+      <command>shutdown</command> Exits <command>b10-ddns</command>.
+      (Note that the BIND 10 boss process will restart this service.)
+    </para>
+
+  </refsect1>
+
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citetitle>BIND 10 Guide</citetitle>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>HISTORY</title>
+    <para>
+      The <command>b10-ddns</command> daemon was first implemented
+      in December 2011 for the ISC BIND 10 project.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
new file mode 100755
index 0000000..e5ce31d
--- /dev/null
+++ b/src/bin/ddns/ddns.py.in
@@ -0,0 +1,209 @@
+#!@PYTHON@
+
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import isc
+import bind10_config
+from isc.dns import *
+from isc.config.ccsession import *
+from isc.cc import SessionError, SessionTimeout
+import isc.util.process
+
+from isc.log_messages.ddns_messages import *
+
+from optparse import OptionParser, OptionValueError
+import os
+import signal
+
+isc.log.init("b10-ddns")
+logger = isc.log.Logger("ddns")
+
+DATA_PATH = bind10_config.DATA_PATH
+if "B10_FROM_SOURCE" in os.environ:
+    DATA_PATH = os.environ['B10_FROM_SOURCE'] + "/src/bin/ddns"
+SPECFILE_LOCATION = DATA_PATH + "/ddns.spec"
+
+
+isc.util.process.rename()
+
+class DDNSConfigError(Exception):
+    '''An exception indicating an error in updating ddns configuration.
+
+    This exception is raised when the ddns process encounters an error in
+    handling configuration updates.  Not all syntax error can be caught
+    at the module-CC layer, so ddns needs to (explicitly or implicitly)
+    validate the given configuration data itself.  When it finds an error
+    it raises this exception (either directly or by converting an exception
+    from other modules) as a unified error in configuration.
+    '''
+    pass
+
+class DDNSSessionError(Exception):
+    '''An exception raised for some unexpected events during a ddns session.
+    '''
+    pass
+
+class DDNSSession:
+    '''Class to handle one DDNS update'''
+
+    def __init__(self):
+        '''Initialize a DDNS Session'''
+        pass
+
+class DDNSServer:
+    def __init__(self, cc_session=None):
+        '''
+        Initialize the DDNS Server.
+        This sets up a ModuleCCSession for the BIND 10 system.
+        Parameters:
+        cc_session: If None (default), a new ModuleCCSession will be set up.
+                    If specified, the given session will be used. This is
+                    mainly used for testing.
+        '''
+        if cc_session is not None:
+            self._cc = cc_session
+        else:
+            self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
+                                                  self.config_handler,
+                                                  self.command_handler)
+
+        self._config_data = self._cc.get_full_config()
+        self._cc.start()
+        self._shutdown = False
+
+    def config_handler(self, new_config):
+        '''Update config data.'''
+        answer = create_answer(0)
+        return answer
+
+    def command_handler(self, cmd, args):
+        '''
+        Handle a CC session command, as sent from bindctl or other
+        BIND 10 modules.
+        '''
+        if cmd == "shutdown":
+            logger.info(DDNS_RECEIVED_SHUTDOWN_COMMAND)
+            self.trigger_shutdown()
+            answer = create_answer(0)
+        else:
+            answer = create_answer(1, "Unknown command: " + str(cmd))
+        return answer
+
+    def trigger_shutdown(self):
+        '''Initiate a shutdown sequence.
+
+        This method is expected to be called in various ways including
+        in the middle of a signal handler, and is designed to be as simple
+        as possible to minimize side effects.  Actual shutdown will take
+        place in a normal control flow.
+
+        '''
+        logger.info(DDNS_SHUTDOWN)
+        self._shutdown = True
+
+    def shutdown_cleanup(self):
+        '''
+        Perform any cleanup that is necessary when shutting down the server.
+        Do NOT call this to initialize shutdown, use trigger_shutdown().
+
+        Currently, it does nothing, but cleanup routines are expected.
+        '''
+        pass
+
+    def run(self):
+        '''
+        Get and process all commands sent from cfgmgr or other modules.
+        This loops waiting for events until self.shutdown() has been called.
+        '''
+        logger.info(DDNS_RUNNING)
+        while not self._shutdown:
+            # We do not catch any exceptions here right now, but this would
+            # be a good place to catch any exceptions that b10-ddns can
+            # recover from. We currently have no exception hierarchy to
+            # make such a distinction easily, but once we do, this would
+            # be the place to catch.
+            self._cc.check_command(False)
+        self.shutdown_cleanup()
+        logger.info(DDNS_STOPPED)
+
+def create_signal_handler(ddns_server):
+    '''
+    This creates a signal_handler for use in set_signal_handler, which
+    shuts down the given DDNSServer (or any object that has a shutdown()
+    method)
+    '''
+    def signal_handler(signal, frame):
+        '''
+        Handler for process signals. Since only signals to shut down are sent
+        here, the actual signal is not checked and the server is simply shut
+        down.
+        '''
+        ddns_server.trigger_shutdown()
+    return signal_handler
+
+def set_signal_handler(signal_handler):
+    '''
+    Sets the signal handler(s).
+    '''
+    signal.signal(signal.SIGTERM, signal_handler)
+    signal.signal(signal.SIGINT, signal_handler)
+
+def set_cmd_options(parser):
+    '''
+    Helper function to set command-line options
+    '''
+    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
+            help="display more about what is going on")
+
+def main(ddns_server=None):
+    '''
+    The main function.
+    Parameters:
+    ddns_server: If None (default), a DDNSServer object is initialized.
+                 If specified, the given DDNSServer will be used. This is
+                 mainly used for testing.
+    cc_session: If None (default), a new ModuleCCSession will be set up.
+                If specified, the given session will be used. This is
+                mainly used for testing.
+    '''
+    try:
+        parser = OptionParser()
+        set_cmd_options(parser)
+        (options, args) = parser.parse_args()
+        if options.verbose:
+            print("[b10-ddns] Warning: -v verbose option is ignored at this point.")
+
+        if ddns_server is None:
+            ddns_server = DDNSServer()
+        set_signal_handler(create_signal_handler(ddns_server))
+        ddns_server.run()
+    except KeyboardInterrupt:
+        logger.info(DDNS_STOPPED_BY_KEYBOARD)
+    except SessionError as e:
+        logger.error(DDNS_CC_SESSION_ERROR, str(e))
+    except ModuleCCSessionError as e:
+        logger.error(DDNS_MODULECC_SESSION_ERROR, str(e))
+    except DDNSConfigError as e:
+        logger.error(DDNS_CONFIG_ERROR, str(e))
+    except SessionTimeout as e:
+        logger.error(DDNS_CC_SESSION_TIMEOUT_ERROR)
+    except Exception as e:
+        logger.error(DDNS_UNCAUGHT_EXCEPTION, type(e).__name__, str(e))
+
+if '__main__' == __name__:
+    main()
diff --git a/src/bin/ddns/ddns.spec b/src/bin/ddns/ddns.spec
new file mode 100644
index 0000000..07cd2a9
--- /dev/null
+++ b/src/bin/ddns/ddns.spec
@@ -0,0 +1,42 @@
+{
+  "module_spec": {
+    "module_name": "DDNS",
+    "config_data": [
+      {
+        "item_name": "zones",
+        "item_type": "named_set",
+        "item_optional": false,
+        "item_default": {},
+        "named_set_item_spec": {
+          "item_name": "entry",
+          "item_type": "map",
+          "item_optional": true,
+          "item_default": {
+            "update_acl": [{"action": "ACCEPT", "from": "127.0.0.1"},
+                           {"action": "ACCEPT", "from": "::1"}]
+          },
+          "map_item_spec": [
+            {
+              "item_name": "update_acl",
+              "item_type": "list",
+              "item_optional": false,
+              "list_item_spec": {
+                "item_name": "acl_element",
+                "item_type": "any",
+                "item_optional": true
+              }
+            }
+          ]
+        }
+      }
+    ],
+    "commands": [
+      {
+        "command_name": "shutdown",
+        "command_description": "Shut down DDNS",
+        "command_args": []
+      }
+    ]
+  }
+}
+
diff --git a/src/bin/ddns/ddns_messages.mes b/src/bin/ddns/ddns_messages.mes
new file mode 100644
index 0000000..36c6ed1
--- /dev/null
+++ b/src/bin/ddns/ddns_messages.mes
@@ -0,0 +1,66 @@
+# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# No namespace declaration - these constants go in the global namespace
+# of the ddns messages python module.
+
+# When you add a message to this file, it is a good idea to run
+# <topsrcdir>/tools/reorder_message_file.py to make sure the
+# messages are in the correct order.
+
+% DDNS_CC_SESSION_ERROR error reading from cc channel: %1
+There was a problem reading from the command and control channel. The
+most likely cause is that the msgq process is not running.
+
+% DDNS_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
+There was a problem reading a response from another module over the
+command and control channel. The most likely cause is that the
+configuration manager b10-cfgmgr is not running.
+
+% DDNS_CONFIG_ERROR error found in configuration data: %1
+The ddns process encountered an error when installing the configuration at
+startup time.  Details of the error are included in the log message.
+
+% DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
+There was a problem in the lower level module handling configuration and
+control commands.  This could happen for various reasons, but the most likely
+cause is that the configuration database contains a syntax error and ddns
+failed to start at initialization.  A detailed error message from the module
+will also be displayed.
+
+% DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received
+The ddns process received a shutdown command from the command channel
+and will now shut down.
+
+% DDNS_RUNNING ddns server is running and listening for updates
+The ddns process has successfully started and is now ready to receive commands
+and updates.
+
+% DDNS_SHUTDOWN ddns server shutting down
+The ddns process is shutting down. It will no longer listen for new commands
+or updates. Any command or update that is being addressed at this moment will
+be completed, after which the process will exit.
+
+% DDNS_STOPPED ddns server has stopped
+The ddns process has successfully stopped and is no longer listening for or
+handling commands or updates, and will now exit.
+
+% DDNS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
+There was a keyboard interrupt signal to stop the ddns process. The
+process will now shut down.
+
+% DDNS_UNCAUGHT_EXCEPTION uncaught exception of type %1: %2
+The b10-ddns process encountered an uncaught exception and will now shut
+down. This is indicative of a programming error and should not happen under
+normal circumstances. The exception type and message are printed.
diff --git a/src/bin/ddns/tests/Makefile.am b/src/bin/ddns/tests/Makefile.am
new file mode 100644
index 0000000..d8b1c79
--- /dev/null
+++ b/src/bin/ddns/tests/Makefile.am
@@ -0,0 +1,28 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
+PYTESTS = ddns_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+# We set B10_FROM_BUILD below, so that the test can refer to the in-source
+# spec file.
+check-local:
+if ENABLE_PYTHON_COVERAGE
+	touch $(abs_top_srcdir)/.coverage
+	rm -f .coverage
+	${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	B10_FROM_SOURCE=$(abs_top_srcdir) \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/ddns:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
+	TESTDATASRCDIR=$(abs_srcdir)/testdata/ \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py
new file mode 100755
index 0000000..601c281
--- /dev/null
+++ b/src/bin/ddns/tests/ddns_test.py
@@ -0,0 +1,142 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+'''Tests for the DDNS module'''
+
+import unittest
+import isc
+import ddns
+import isc.config
+
+class MyCCSession(isc.config.ConfigData):
+    '''Fake session with minimal interface compliance'''
+    def __init__(self):
+        module_spec = isc.config.module_spec_from_file(
+            ddns.SPECFILE_LOCATION)
+        isc.config.ConfigData.__init__(self, module_spec)
+        self._started = False
+
+    def start(self):
+        '''Called by DDNSServer initialization, but not used in tests'''
+        self._started = True
+
+class MyDDNSServer():
+    '''Fake DDNS server used to test the main() function'''
+    def __init__(self):
+        self.reset()
+
+    def run(self):
+        '''
+        Fake the run() method of the DDNS server. This will set
+        self._run_called to True.
+        If self._exception is not None, this is raised as an exception
+        '''
+        self.run_called = True
+        if self._exception is not None:
+            self.exception_raised = True
+            raise self._exception
+
+    def set_exception(self, exception):
+        '''Set an exception to be raised when run() is called'''
+        self._exception = exception
+
+    def reset(self):
+        '''(Re)set to initial values'''
+        self.run_called = False
+        self.exception_raised = False
+        self._exception = None
+
+class TestDDNSServer(unittest.TestCase):
+    def setUp(self):
+        cc_session = MyCCSession()
+        self.assertFalse(cc_session._started)
+        self.ddns_server = ddns.DDNSServer(cc_session)
+        self.assertTrue(cc_session._started)
+
+    def test_config_handler(self):
+        # Config handler does not do anything yet, but should at least
+        # return 'ok' for now.
+        new_config = {}
+        answer = self.ddns_server.config_handler(new_config)
+        self.assertEqual((0, None), isc.config.parse_answer(answer))
+
+    def test_shutdown_command(self):
+        '''Test whether the shutdown command works'''
+        self.assertFalse(self.ddns_server._shutdown)
+        answer = self.ddns_server.command_handler('shutdown', None)
+        self.assertEqual((0, None), isc.config.parse_answer(answer))
+        self.assertTrue(self.ddns_server._shutdown)
+
+    def test_command_handler(self):
+        '''Test some commands.'''
+        # this command should not exist
+        answer = self.ddns_server.command_handler('bad_command', None)
+        self.assertEqual((1, 'Unknown command: bad_command'),
+                         isc.config.parse_answer(answer))
+
+    def test_signal_handler(self):
+        '''Test whether signal_handler calls shutdown()'''
+        signal_handler = ddns.create_signal_handler(self.ddns_server)
+        self.assertFalse(self.ddns_server._shutdown)
+        signal_handler(None, None)
+        self.assertTrue(self.ddns_server._shutdown)
+
+class TestMain(unittest.TestCase):
+    def setUp(self):
+        self._server = MyDDNSServer()
+
+    def test_main(self):
+        self.assertFalse(self._server.run_called)
+        ddns.main(self._server)
+        self.assertTrue(self._server.run_called)
+
+    def check_exception(self, ex):
+        '''Common test sequence to see if the given exception is caused.
+        '''
+        # Should technically not be necessary, but reset server to be sure
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(ex)
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+    def test_exceptions(self):
+        '''
+        Test whether exceptions are caught in main()
+        These exceptions should not bubble up.
+        '''
+        self._server.set_exception(KeyboardInterrupt())
+        self.assertFalse(self._server.exception_raised)
+        ddns.main(self._server)
+        self.assertTrue(self._server.exception_raised)
+
+        self.check_exception(isc.cc.SessionError("error"))
+        self.check_exception(isc.config.ModuleCCSessionError("error"))
+        self.check_exception(ddns.DDNSConfigError("error"))
+        self.check_exception(isc.cc.SessionTimeout("error"))
+        self.check_exception(Exception("error"))
+
+        # Add one that is not a subclass of Exception, and hence not
+        # caught. Misuse BaseException for that.
+        self._server.reset()
+        self.assertFalse(self._server.exception_raised)
+        self._server.set_exception(BaseException("error"))
+        self.assertRaises(BaseException, ddns.main, self._server)
+        self.assertTrue(self._server.exception_raised)
+        
+
+if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 9686a35..89b48c6 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -17,12 +17,23 @@
 #include <dhcp/iface_mgr.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <asiolink/io_address.h>
+#include <dhcp/option4_addrlst.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
+// These are hardcoded parameters. Currently this is a skeleton server that only
+// grants those options and a single, fixed, hardcoded lease.
+const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
+const std::string HARDCODED_NETMASK = "255.255.255.0";
+const uint32_t    HARDCODED_LEASE_TIME = 60; // in seconds
+const std::string HARDCODED_GATEWAY = "192.0.2.1";
+const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
+const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
+const std::string HARDCODED_SERVER_ID = "192.0.2.1";
+
 Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
     cout << "Initialization: opening sockets on port " << port << endl;
 
@@ -31,6 +42,9 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
     IfaceMgr::instance();
 
     /// @todo: instantiate LeaseMgr here once it is imlpemented.
+    IfaceMgr::instance().printIfaces();
+
+    IfaceMgr::instance().openSockets4(port);
 
     setServerID();
 
@@ -39,6 +53,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
 
 Dhcpv4Srv::~Dhcpv4Srv() {
     cout << "DHCPv4 server shutdown." << endl;
+    IfaceMgr::instance().closeSockets();
 }
 
 bool
@@ -47,16 +62,17 @@ Dhcpv4Srv::run() {
         boost::shared_ptr<Pkt4> query; // client's message
         boost::shared_ptr<Pkt4> rsp;   // server's response
 
-#if 0
-        // uncomment this once ticket 1239 is merged.
         query = IfaceMgr::instance().receive4();
-#endif
 
         if (query) {
-            if (!query->unpack()) {
-                cout << "Failed to parse incoming packet" << endl;
+            try {
+                query->unpack();
+            } catch (const std::exception& e) {
+                /// TODO: Printout reasons of failed parsing
+                cout << "Failed to parse incoming packet " << endl;
                 continue;
             }
+
             switch (query->getType()) {
             case DHCPDISCOVER:
                 rsp = processDiscover(query);
@@ -78,31 +94,35 @@ Dhcpv4Srv::run() {
                      << query->getType() << endl;
             }
 
-            cout << "Received " << query->len() << " bytes packet type="
-                 << query->getType() << endl;
+            cout << "Received message type " << int(query->getType()) << endl;
 
             // TODO: print out received packets only if verbose (or debug)
             // mode is enabled
             cout << query->toText();
 
             if (rsp) {
-                rsp->setRemoteAddr(query->getRemoteAddr());
+                if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
+                    rsp->setRemoteAddr(query->getRemoteAddr());
+                }
+                if (!rsp->getHops()) {
+                    rsp->setRemotePort(DHCP4_CLIENT_PORT);
+                } else {
+                    rsp->setRemotePort(DHCP4_SERVER_PORT);
+                }
+
                 rsp->setLocalAddr(query->getLocalAddr());
-                rsp->setRemotePort(DHCP4_CLIENT_PORT);
                 rsp->setLocalPort(DHCP4_SERVER_PORT);
                 rsp->setIface(query->getIface());
                 rsp->setIndex(query->getIndex());
 
-                cout << "Replying with:" << rsp->getType() << endl;
+                cout << "Replying with message type "
+                     << static_cast<int>(rsp->getType()) << ":" << endl;
                 cout << rsp->toText();
                 cout << "----" << endl;
                 if (rsp->pack()) {
                     cout << "Packet assembled correctly." << endl;
                 }
-#if 0
-                // uncomment this once ticket 1240 is merged.
-                IfaceMgr::instance().send4(rsp);
-#endif
+                IfaceMgr::instance().send(rsp);
             }
         }
 
@@ -126,16 +146,116 @@ Dhcpv4Srv::setServerID() {
 #endif
 }
 
+
+void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
+                                  boost::shared_ptr<Pkt4>& answer) {
+    answer->setIface(question->getIface());
+    answer->setIndex(question->getIndex());
+    answer->setCiaddr(question->getCiaddr());
+
+    answer->setSiaddr(IOAddress("0.0.0.0")); // explictly set this to 0
+    answer->setHops(question->getHops());
+
+    // copy MAC address
+    vector<uint8_t> mac(question->getChaddr(),
+                        question->getChaddr() + Pkt4::MAX_CHADDR_LEN);
+    answer->setHWAddr(question->getHtype(), question->getHlen(), mac);
+
+    // relay address
+    answer->setGiaddr(question->getGiaddr());
+
+    if (question->getGiaddr().toText() != "0.0.0.0") {
+        // relayed traffic
+        answer->setRemoteAddr(question->getGiaddr());
+    } else {
+        // direct traffic
+        answer->setRemoteAddr(question->getRemoteAddr());
+    }
+
+}
+
+void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type) {
+    boost::shared_ptr<Option> opt;
+
+    // add Message Type Option (type 53)
+    std::vector<uint8_t> tmp;
+    tmp.push_back(static_cast<uint8_t>(msg_type));
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
+    msg->addOption(opt);
+
+    // DHCP Server Identifier (type 54)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
+    msg->addOption(opt);
+
+    // more options will be added here later
+}
+
+
+void Dhcpv4Srv::appendRequestedOptions(boost::shared_ptr<Pkt4>& msg) {
+    boost::shared_ptr<Option> opt;
+
+    // Domain name (type 15)
+    vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
+    msg->addOption(opt);
+    // TODO: Add Option_String class
+
+    // DNS servers (type 6)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
+    msg->addOption(opt);
+}
+
+void Dhcpv4Srv::tryAssignLease(boost::shared_ptr<Pkt4>& msg) {
+    boost::shared_ptr<Option> opt;
+
+    // TODO: Implement actual lease assignment here
+    msg->setYiaddr(IOAddress(HARDCODED_LEASE));
+
+    // IP Address Lease time (type 51)
+    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
+    opt->setUint32(HARDCODED_LEASE_TIME);
+    msg->addOption(opt);
+    // TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
+
+    // Subnet mask (type 1)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
+    msg->addOption(opt);
+
+    // Router (type 3)
+    opt = boost::shared_ptr<Option>
+        (new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
+    msg->addOption(opt);
+}
+
 boost::shared_ptr<Pkt4>
 Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
-    /// TODO: Currently implemented echo mode. Implement this for real
-    return (discover);
+    boost::shared_ptr<Pkt4> offer = boost::shared_ptr<Pkt4>
+        (new Pkt4(DHCPOFFER, discover->getTransid()));
+
+    copyDefaultFields(discover, offer);
+    appendDefaultOptions(offer, DHCPOFFER);
+    appendRequestedOptions(offer);
+
+    tryAssignLease(offer);
+
+    return (offer);
 }
 
 boost::shared_ptr<Pkt4>
 Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
-    /// TODO: Currently implemented echo mode. Implement this for real
-    return (request);
+    boost::shared_ptr<Pkt4> ack = boost::shared_ptr<Pkt4>
+        (new Pkt4(DHCPACK, request->getTransid()));
+
+    copyDefaultFields(request, ack);
+    appendDefaultOptions(ack, DHCPACK);
+    appendRequestedOptions(ack);
+
+    tryAssignLease(ack);
+
+    return (ack);
 }
 
 void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 033ac5a..cd46977 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -106,6 +106,45 @@ protected:
     /// @param infRequest message received from client
     boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
 
+    /// @brief Copies default parameters from client's to server's message
+    ///
+    /// Some fields are copied from client's message into server's response,
+    /// e.g. client HW address, number of hops, transaction-id etc.
+    ///
+    /// @param question any message sent by client
+    /// @param answer any message server is going to send as response
+    void copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
+                           boost::shared_ptr<Pkt4>& answer);
+
+
+    /// @brief Appends options requested by client.
+    ///
+    /// This method assigns options that were requested by client
+    /// (sent in PRL) or are enforced by server.
+    ///
+    /// @param msg outgoing message (options will be added here)
+    void appendRequestedOptions(boost::shared_ptr<Pkt4>& msg);
+
+
+    /// @brief Assigns a lease and appends corresponding options
+    ///
+    /// This method chooses the most appropriate lease for reqesting
+    /// client and assigning it. Options corresponding to the lease
+    /// are added to specific message.
+    ///
+    /// Note: Lease manager is not implemented yet, so this method
+    /// used fixed, hardcoded lease.
+    ///
+    /// @param msg OFFER or ACK message (lease options will be added here)
+    void tryAssignLease(boost::shared_ptr<Pkt4>& msg);
+
+
+    /// @brief Appends default options to a message
+    ///
+    /// @param msg message object (options will be added to it)
+    /// @param msg_type specifies message type
+    void appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type);
+
     /// @brief Returns server-intentifier option
     ///
     /// @return server-id option
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index dcda356..b956aee 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -41,6 +41,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index c1976e1..cfc12fb 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -14,6 +14,7 @@
 
 #include <config.h>
 #include <iostream>
+#include <fstream>
 #include <sstream>
 
 #include <arpa/inet.h>
@@ -22,17 +23,20 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp/option.h>
+#include <asiolink/io_address.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::asiolink;
 
 namespace {
+const char* const INTERFACE_FILE = "interfaces.txt";
 
 class NakedDhcpv4Srv: public Dhcpv4Srv {
     // "naked" DHCPv4 server, exposes internal fields
 public:
-    NakedDhcpv4Srv() { }
+    NakedDhcpv4Srv():Dhcpv4Srv(DHCP4_SERVER_PORT + 10000) { }
 
     boost::shared_ptr<Pkt4> processDiscover(boost::shared_ptr<Pkt4>& discover) {
         return Dhcpv4Srv::processDiscover(discover);
@@ -54,9 +58,42 @@ public:
 class Dhcpv4SrvTest : public ::testing::Test {
 public:
     Dhcpv4SrvTest() {
+        unlink(INTERFACE_FILE);
+        fstream fakeifaces(INTERFACE_FILE, ios::out | ios::trunc);
+        if (if_nametoindex("lo") > 0) {
+            fakeifaces << "lo 127.0.0.1";
+        } else if (if_nametoindex("lo0") > 0) {
+            fakeifaces << "lo0 127.0.0.1";
+        }
+        fakeifaces.close();
+    }
+
+    void MessageCheck(const boost::shared_ptr<Pkt4>& q,
+                      const boost::shared_ptr<Pkt4>& a) {
+        ASSERT_TRUE(q);
+        ASSERT_TRUE(a);
+
+        EXPECT_EQ(q->getHops(),   a->getHops());
+        EXPECT_EQ(q->getIface(),  a->getIface());
+        EXPECT_EQ(q->getIndex(),  a->getIndex());
+        EXPECT_EQ(q->getGiaddr(), a->getGiaddr());
+
+        // check that bare minimum of required options are there
+        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
+        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
+        EXPECT_TRUE(a->getOption(DHO_DHCP_SERVER_IDENTIFIER));
+        EXPECT_TRUE(a->getOption(DHO_DHCP_LEASE_TIME));
+        EXPECT_TRUE(a->getOption(DHO_SUBNET_MASK));
+        EXPECT_TRUE(a->getOption(DHO_ROUTERS));
+        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME));
+        EXPECT_TRUE(a->getOption(DHO_DOMAIN_NAME_SERVERS));
+
+        // check that something is offered
+        EXPECT_TRUE(a->getYiaddr().toText() != "0.0.0.0");
     }
 
     ~Dhcpv4SrvTest() {
+        unlink(INTERFACE_FILE);
     };
 };
 
@@ -66,7 +103,7 @@ TEST_F(Dhcpv4SrvTest, basic) {
 
     Dhcpv4Srv* srv = NULL;
     ASSERT_NO_THROW({
-        srv = new Dhcpv4Srv();
+        srv = new Dhcpv4Srv(DHCP4_SERVER_PORT + 10000);
     });
 
     delete srv;
@@ -74,37 +111,113 @@ TEST_F(Dhcpv4SrvTest, basic) {
 
 TEST_F(Dhcpv4SrvTest, processDiscover) {
     NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+    vector<uint8_t> mac(6);
+    for (int i = 0; i < 6; i++) {
+        mac[i] = 255 - i;
+    }
 
     boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 1234));
+    boost::shared_ptr<Pkt4> offer;
+
+    pkt->setIface("eth0");
+    pkt->setIndex(17);
+    pkt->setHWAddr(1, 6, mac);
+    pkt->setRemoteAddr(IOAddress("192.0.2.56"));
+    pkt->setGiaddr(IOAddress("192.0.2.67"));
+
+    // let's make it a relayed message
+    pkt->setHops(3);
+    pkt->setRemotePort(DHCP4_SERVER_PORT);
 
     // should not throw
     EXPECT_NO_THROW(
-        srv->processDiscover(pkt);
+        offer = srv->processDiscover(pkt);
     );
 
     // should return something
-    EXPECT_TRUE(srv->processDiscover(pkt));
+    ASSERT_TRUE(offer);
+
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    // this is relayed message. It should be sent back to relay address.
+    EXPECT_EQ(pkt->getGiaddr(), offer->getRemoteAddr());
+
+    MessageCheck(pkt, offer);
+
+    // now repeat the test for directly sent message
+    pkt->setHops(0);
+    pkt->setGiaddr(IOAddress("0.0.0.0"));
+    pkt->setRemotePort(DHCP4_CLIENT_PORT);
+
+    EXPECT_NO_THROW(
+        offer = srv->processDiscover(pkt);
+    );
+
+    // should return something
+    ASSERT_TRUE(offer);
+
+    EXPECT_EQ(DHCPOFFER, offer->getType());
+
+    // this is direct message. It should be sent back to origin, not
+    // to relay.
+    EXPECT_EQ(pkt->getRemoteAddr(), offer->getRemoteAddr());
+
+    MessageCheck(pkt, offer);
 
-    // TODO: Implement more reasonable tests before starting
-    // work on processSomething() method.
     delete srv;
 }
 
 TEST_F(Dhcpv4SrvTest, processRequest) {
     NakedDhcpv4Srv* srv = new NakedDhcpv4Srv();
+    vector<uint8_t> mac(6);
+    for (int i = 0; i < 6; i++) {
+        mac[i] = i*10;
+    }
 
-    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPREQUEST, 1234));
+    boost::shared_ptr<Pkt4> req(new Pkt4(DHCPREQUEST, 1234));
+    boost::shared_ptr<Pkt4> ack;
+
+    req->setIface("eth0");
+    req->setIndex(17);
+    req->setHWAddr(1, 6, mac);
+    req->setRemoteAddr(IOAddress("192.0.2.56"));
+    req->setGiaddr(IOAddress("192.0.2.67"));
 
     // should not throw
+    ASSERT_NO_THROW(
+        ack = srv->processRequest(req);
+    );
+
+    // should return something
+    ASSERT_TRUE(ack);
+
+    EXPECT_EQ(DHCPACK, ack->getType());
+
+    // this is relayed message. It should be sent back to relay address.
+    EXPECT_EQ(req->getGiaddr(), ack->getRemoteAddr());
+
+    MessageCheck(req, ack);
+
+    // now repeat the test for directly sent message
+    req->setHops(0);
+    req->setGiaddr(IOAddress("0.0.0.0"));
+    req->setRemotePort(DHCP4_CLIENT_PORT);
+
     EXPECT_NO_THROW(
-        srv->processRequest(pkt);
+        ack = srv->processDiscover(req);
     );
 
     // should return something
-    EXPECT_TRUE(srv->processRequest(pkt));
+    ASSERT_TRUE(ack);
+
+    EXPECT_EQ(DHCPOFFER, ack->getType());
+
+    // this is direct message. It should be sent back to origin, not
+    // to relay.
+    EXPECT_EQ(ack->getRemoteAddr(), req->getRemoteAddr());
+
+    MessageCheck(req, ack);
 
-    // TODO: Implement more reasonable tests before starting
-    // work on processSomething() method.
     delete srv;
 }
 
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 0e93924..b1b0798 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -34,10 +34,10 @@ pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
 
-b10_dhcp6_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
 # and can't use @datadir@ because doesn't expand default ${prefix}
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 6bc7194..4e69f05 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -18,6 +18,7 @@
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_addrlst.h>
 #include <asiolink/io_address.h>
 #include <exceptions/exceptions.h>
 
@@ -26,17 +27,32 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
 
-Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
+const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
+const uint32_t HARDCODED_T1 = 1500; // in seconds
+const uint32_t HARDCODED_T2 = 2600; // in seconds
+const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
+const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
+const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
 
-//void Dhcpv6Srv::Dhcpv6Srv_impl(uint16_t port) {
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
     cout << "Initialization" << endl;
 
-    // First call to instance() will create IfaceMgr (it's a singleton).
-    // It may throw something if things go wrong.
-    IfaceMgr::instance();
+    // first call to instance() will create IfaceMgr (it's a singleton)
+    // it may throw something if things go wrong
+    try {
+        IfaceMgr::instance();
+    } catch (const std::exception &e) {
+        cout << "Failed to instantiate InterfaceManager:" << e.what() << ". Aborting." << endl;
+        shutdown = true;
+    }
+
+    if (IfaceMgr::instance().countIfaces() == 0) {
+        cout << "Failed to detect any network interfaces. Aborting." << endl;
+        shutdown = true;
+    }
 
     // Now try to open IPv6 sockets on detected interfaces.
-    IfaceMgr::instance().openSockets(port);
+    IfaceMgr::instance().openSockets6(port);
 
     /// @todo: instantiate LeaseMgr here once it is imlpemented.
 
@@ -107,8 +123,9 @@ Dhcpv6Srv::run() {
                 cout << "Replying with:" << rsp->getType() << endl;
                 cout << rsp->toText();
                 cout << "----" << endl;
-                if (rsp->pack()) {
-                    cout << "#### pack successful." << endl;
+                if (!rsp->pack()) {
+                    cout << "Failed to assemble response packet." << endl;
+                    continue;
                 }
                 IfaceMgr::instance().send(rsp);
             }
@@ -141,18 +158,45 @@ Dhcpv6Srv::setServerID() {
                                                      0, 14));
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processSolicit(boost::shared_ptr<Pkt6> solicit) {
+void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                                   boost::shared_ptr<Pkt6>& answer) {
+    // add client-id
+    boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
+    if (clientid) {
+        answer->addOption(clientid);
+    }
 
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_ADVERTISE,
-                                           solicit->getTransid(),
-                                           Pkt6::UDP));
+    // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
+}
+
+void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+                                   boost::shared_ptr<Pkt6>& answer) {
+    // TODO: question is currently unused, but we need it at least to know
+    // message type we are answering
 
+    // add server-id
+    answer->addOption(getServerID());
+}
+
+
+void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question*/,
+                                       boost::shared_ptr<Pkt6>& answer) {
+    // TODO: question is currently unused, but we need to extract ORO from it
+    // and act on its content. Now we just send DNS-SERVERS option.
+
+    // add dns-servers option
+    boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
+                                         IOAddress(HARDCODED_DNS_SERVER)));
+    answer->addOption(dnsservers);
+}
+
+void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
+                             boost::shared_ptr<Pkt6>& answer) {
     /// TODO Rewrite this once LeaseManager is implemented.
 
     // answer client's IA (this is mostly a dummy,
     // so let's answer only first IA and hope there is only one)
-    boost::shared_ptr<Option> ia_opt = solicit->getOption(D6O_IA_NA);
+    boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
     if (ia_opt) {
         // found IA
         Option* tmp = ia_opt.get();
@@ -160,38 +204,51 @@ Dhcpv6Srv::processSolicit(boost::shared_ptr<Pkt6> solicit) {
         if (ia_req) {
             boost::shared_ptr<Option6IA>
                 ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
-            ia_rsp->setT1(1500);
-            ia_rsp->setT2(2600);
+            ia_rsp->setT1(HARDCODED_T1);
+            ia_rsp->setT2(HARDCODED_T2);
             boost::shared_ptr<Option6IAAddr>
                 addr(new Option6IAAddr(D6O_IAADDR,
-                                       IOAddress("2001:db8:1234:5678::abcd"),
-                                       5000, 7000));
+                                       IOAddress(HARDCODED_LEASE),
+                                       HARDCODED_PREFERRED_LIFETIME,
+                                       HARDCODED_VALID_LIFETIME));
             ia_rsp->addOption(addr);
-            reply->addOption(ia_rsp);
+            answer->addOption(ia_rsp);
         }
     }
+}
 
-    // add client-id
-    boost::shared_ptr<Option> clientid = solicit->getOption(D6O_CLIENTID);
-    if (clientid) {
-        reply->addOption(clientid);
-    }
+boost::shared_ptr<Pkt6>
+Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
 
-    // add server-id
-    reply->addOption(getServerID());
-    return reply;
+    boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE,
+                                               solicit->getTransid()));
+
+    copyDefaultOptions(solicit, advertise);
+    appendDefaultOptions(solicit, advertise);
+    appendRequestedOptions(solicit, advertise);
+
+    assignLeases(solicit, advertise);
+
+    return (advertise);
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRequest(boost::shared_ptr<Pkt6> request) {
-    /// TODO: Implement processRequest() for real
-    boost::shared_ptr<Pkt6> reply = processSolicit(request);
-    reply->setType(DHCPV6_REPLY);
-    return reply;
+Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
+
+    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
+                                           request->getTransid()));
+
+    copyDefaultOptions(request, reply);
+    appendDefaultOptions(request, reply);
+    appendRequestedOptions(request, reply);
+
+    assignLeases(request, reply);
+
+    return (reply);
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRenew(boost::shared_ptr<Pkt6> renew) {
+Dhcpv6Srv::processRenew(const boost::shared_ptr<Pkt6>& renew) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            renew->getTransid(),
                                            Pkt6::UDP));
@@ -199,7 +256,7 @@ Dhcpv6Srv::processRenew(boost::shared_ptr<Pkt6> renew) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRebind(boost::shared_ptr<Pkt6> rebind) {
+Dhcpv6Srv::processRebind(const boost::shared_ptr<Pkt6>& rebind) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            rebind->getTransid(),
                                            Pkt6::UDP));
@@ -207,7 +264,7 @@ Dhcpv6Srv::processRebind(boost::shared_ptr<Pkt6> rebind) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processConfirm(boost::shared_ptr<Pkt6> confirm) {
+Dhcpv6Srv::processConfirm(const boost::shared_ptr<Pkt6>& confirm) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            confirm->getTransid(),
                                            Pkt6::UDP));
@@ -215,7 +272,7 @@ Dhcpv6Srv::processConfirm(boost::shared_ptr<Pkt6> confirm) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRelease(boost::shared_ptr<Pkt6> release) {
+Dhcpv6Srv::processRelease(const boost::shared_ptr<Pkt6>& release) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            release->getTransid(),
                                            Pkt6::UDP));
@@ -223,7 +280,7 @@ Dhcpv6Srv::processRelease(boost::shared_ptr<Pkt6> release) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processDecline(boost::shared_ptr<Pkt6> decline) {
+Dhcpv6Srv::processDecline(const boost::shared_ptr<Pkt6>& decline) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            decline->getTransid(),
                                            Pkt6::UDP));
@@ -231,7 +288,7 @@ Dhcpv6Srv::processDecline(boost::shared_ptr<Pkt6> decline) {
 }
 
 boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processInfRequest(boost::shared_ptr<Pkt6> infRequest) {
+Dhcpv6Srv::processInfRequest(const boost::shared_ptr<Pkt6>& infRequest) {
     boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
                                            infRequest->getTransid(),
                                            Pkt6::UDP));
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index bcc7818..44a9f8a 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -81,7 +81,7 @@ protected:
     ///
     /// @return ADVERTISE, REPLY message or NULL
     boost::shared_ptr<Pkt6>
-    processSolicit(boost::shared_ptr<Pkt6> solicit);
+    processSolicit(const boost::shared_ptr<Pkt6>& solicit);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
     ///
@@ -95,43 +95,87 @@ protected:
     ///
     /// @return REPLY message or NULL
     boost::shared_ptr<Pkt6>
-    processRequest(boost::shared_ptr<Pkt6> request);
+    processRequest(const boost::shared_ptr<Pkt6>& request);
 
     /// @brief Stub function that will handle incoming RENEW messages.
     ///
     /// @param renew message received from client
     boost::shared_ptr<Pkt6>
-    processRenew(boost::shared_ptr<Pkt6> renew);
+    processRenew(const boost::shared_ptr<Pkt6>& renew);
 
     /// @brief Stub function that will handle incoming REBIND messages.
     ///
     /// @param rebind message received from client
     boost::shared_ptr<Pkt6>
-    processRebind(boost::shared_ptr<Pkt6> rebind);
+    processRebind(const boost::shared_ptr<Pkt6>& rebind);
 
     /// @brief Stub function that will handle incoming CONFIRM messages.
     ///
     /// @param confirm message received from client
     boost::shared_ptr<Pkt6>
-    processConfirm(boost::shared_ptr<Pkt6> confirm);
+    processConfirm(const boost::shared_ptr<Pkt6>& confirm);
 
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
     /// @param release message received from client
     boost::shared_ptr<Pkt6>
-    processRelease(boost::shared_ptr<Pkt6> release);
+    processRelease(const boost::shared_ptr<Pkt6>& release);
 
     /// @brief Stub function that will handle incoming DECLINE messages.
     ///
     /// @param decline message received from client
     boost::shared_ptr<Pkt6>
-    processDecline(boost::shared_ptr<Pkt6> decline);
+    processDecline(const boost::shared_ptr<Pkt6>& decline);
 
     /// @brief Stub function that will handle incoming INF-REQUEST messages.
     ///
     /// @param infRequest message received from client
     boost::shared_ptr<Pkt6>
-    processInfRequest(boost::shared_ptr<Pkt6> infRequest);
+    processInfRequest(const boost::shared_ptr<Pkt6>& infRequest);
+
+    /// @brief Copies required options from client message to server answer
+    ///
+    /// Copies options that must appear in any server response (ADVERTISE, REPLY)
+    /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
+    /// One notable example is client-id. Other options may be copied as required.
+    ///
+    /// @param question client's message (options will be copied from here)
+    /// @param answer server's message (options will be copied here)
+    void copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                            boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Appends default options to server's answer.
+    ///
+    /// Adds required options to server's answer. In particular, server-id
+    /// is added. Possibly other mandatory options will be added, depending
+    /// on type (or content) of client message.
+    ///
+    /// @param question client's message
+    /// @param answer server's message (options will be added here)
+    void appendDefaultOptions(const boost::shared_ptr<Pkt6>& question,
+                              boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Appends requested options to server's answer.
+    ///
+    /// Appends options requested by client to the server's answer.
+    /// TODO: This method is currently a stub. It just appends DNS-SERVERS
+    /// option.
+    ///
+    /// @param question client's message
+    /// @param answer server's message (options will be added here)
+    void appendRequestedOptions(const boost::shared_ptr<Pkt6>& question,
+                                boost::shared_ptr<Pkt6>& answer);
+
+    /// @brief Assigns leases.
+    ///
+    /// TODO: This method is currently a stub. It just appends one
+    /// hardcoded lease. It supports addresses (IA_NA) only. It does NOT
+    /// support temporary addresses (IA_TA) nor prefixes (IA_PD).
+    ///
+    /// @param question client's message (with requested IA_NA)
+    /// @param answer server's message (IA_NA options will be added here)
+    void assignLeases(const boost::shared_ptr<Pkt6>& question,
+                      boost::shared_ptr<Pkt6>& answer);
 
     /// @brief Sets server-identifier.
     ///
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 092dd2e..09d1cec 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -14,6 +14,7 @@
 
 #include <config.h>
 #include <iostream>
+#include <fstream>
 #include <sstream>
 
 #include <arpa/inet.h>
@@ -29,7 +30,8 @@ using namespace isc::dhcp;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // Maybe it should be isc::test?
-namespace test {
+namespace {
+const char* const INTERFACE_FILE = "interfaces.txt";
 
 class NakedDhcpv6Srv: public Dhcpv6Srv {
     // "naked" Interface Manager, exposes internal fields
@@ -49,7 +51,18 @@ public:
 class Dhcpv6SrvTest : public ::testing::Test {
 public:
     Dhcpv6SrvTest() {
+        unlink(INTERFACE_FILE);
+        fstream fakeifaces(INTERFACE_FILE, ios::out | ios::trunc);
+        if (if_nametoindex("lo") > 0) {
+            fakeifaces << "lo ::1";
+        } else if (if_nametoindex("lo0") > 0) {
+            fakeifaces << "lo0 ::1";
+        }
+        fakeifaces.close();
     }
+    ~Dhcpv6SrvTest() {
+        unlink(INTERFACE_FILE);
+    };
 };
 
 TEST_F(Dhcpv6SrvTest, basic) {
@@ -116,7 +129,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     boost::shared_ptr<Option> tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE( tmp );
 
-    Option6IA* reply_ia = dynamic_cast<Option6IA*> ( tmp.get() );
+    Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
     EXPECT_EQ( 234, reply_ia->getIAID() );
 
     // check that there's an address included
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index 79146da..090f211 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -220,7 +220,10 @@ main(int argc, char* argv[]) {
         // Install all initial configurations.  If loading configuration
         // fails, it will be logged, but we start the server anyway, giving
         // the user a second chance to correct the configuration.
-        resolver->updateConfig(config_session->getFullConfig());
+        // By setting the 'startup' parameter to true, we ensure most of
+        // the default configuration will be installed even if listen_on
+        // fails.
+        resolver->updateConfig(config_session->getFullConfig(), true);
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIG_LOADED);
 
         LOG_INFO(resolver_logger, RESOLVER_STARTED);
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index bb1eb3b..367c16c 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -57,8 +57,6 @@
 #include "resolver_log.h"
 
 using namespace std;
-using boost::shared_ptr;
-
 using namespace isc;
 using namespace isc::util;
 using namespace isc::acl;
@@ -167,7 +165,7 @@ public:
         return (*query_acl_);
     }
 
-    void setQueryACL(shared_ptr<const RequestACL> new_acl) {
+    void setQueryACL(boost::shared_ptr<const RequestACL> new_acl) {
         query_acl_ = new_acl;
     }
 
@@ -195,7 +193,7 @@ public:
 
 private:
     /// ACL on incoming queries
-    shared_ptr<const RequestACL> query_acl_;
+    boost::shared_ptr<const RequestACL> query_acl_;
 
     /// Object to handle upstream queries
     RecursiveQuery* rec_query_;
@@ -354,13 +352,18 @@ private:
 Resolver::Resolver() :
     impl_(new ResolverImpl()),
     dnss_(NULL),
-    checkin_(new ConfigCheck(this)),
-    dns_lookup_(new MessageLookup(this)),
+    checkin_(NULL),
+    dns_lookup_(NULL),
     dns_answer_(new MessageAnswer),
     nsas_(NULL),
-    cache_(NULL),
-    configured_(false)
-{}
+    cache_(NULL)
+{
+    // Operations referring to "this" must be done in the constructor body
+    // (some compilers will issue warnings if "this" is referred to in the
+    // initialization list).
+    checkin_ = new ConfigCheck(this);
+    dns_lookup_ = new MessageLookup(this);
+}
 
 Resolver::~Resolver() {
     delete impl_;
@@ -581,7 +584,7 @@ ResolverImpl::processNormalQuery(const IOMessage& io_message,
 }
 
 ConstElementPtr
-Resolver::updateConfig(ConstElementPtr config) {
+Resolver::updateConfig(ConstElementPtr config, bool startup) {
     LOG_DEBUG(resolver_logger, RESOLVER_DBG_CONFIG, RESOLVER_CONFIG_UPDATED)
               .arg(*config);
 
@@ -597,9 +600,9 @@ Resolver::updateConfig(ConstElementPtr config) {
         AddressList listenAddresses(parseAddresses(listenAddressesE,
                                                       "listen_on"));
         const ConstElementPtr query_acl_cfg(config->get("query_acl"));
-        const shared_ptr<const RequestACL> query_acl =
+        const boost::shared_ptr<const RequestACL> query_acl =
             query_acl_cfg ? acl::dns::getRequestLoader().load(query_acl_cfg) :
-            shared_ptr<RequestACL>();
+            boost::shared_ptr<RequestACL>();
         bool set_timeouts(false);
         int qtimeout = impl_->query_timeout_;
         int ctimeout = impl_->client_timeout_;
@@ -654,7 +657,7 @@ Resolver::updateConfig(ConstElementPtr config) {
         // listenAddresses can fail to bind, so try them first
         bool need_query_restart = false;
         
-        if (listenAddressesE) {
+        if (!startup && listenAddressesE) {
             setListenAddresses(listenAddresses);
             need_query_restart = true;
         }
@@ -673,12 +676,15 @@ Resolver::updateConfig(ConstElementPtr config) {
         if (query_acl) {
             setQueryACL(query_acl);
         }
+        if (startup && listenAddressesE) {
+            setListenAddresses(listenAddresses);
+            need_query_restart = true;
+        }
 
         if (need_query_restart) {
             impl_->queryShutdown();
             impl_->querySetup(*dnss_, *nsas_, *cache_);
         }
-        setConfigured();
         return (isc::config::createAnswer());
 
     } catch (const isc::Exception& error) {
@@ -765,7 +771,7 @@ Resolver::getQueryACL() const {
 }
 
 void
-Resolver::setQueryACL(shared_ptr<const RequestACL> new_acl) {
+Resolver::setQueryACL(boost::shared_ptr<const RequestACL> new_acl) {
     if (!new_acl) {
         isc_throw(InvalidParameter, "NULL pointer is passed to setQueryACL");
     }
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index 4b9c773..096f522 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -95,8 +95,13 @@ public:
     isc::config::ModuleCCSession* getConfigSession() const;
     void setConfigSession(isc::config::ModuleCCSession* config_session);
 
-    /// \brief Handle commands from the config session
-    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config);
+    /// \brief Handle commands from the config session.
+    ///
+    /// By default, or if \c startup is set to false explicitly, this method
+    /// will ignore any other configuration parameters if listen_on fails;
+    /// if \c startup is true, it will install as many parameters as possible.
+    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr config,
+                                            bool startup = false);
 
     /// \brief Assign an ASIO IO Service queue to this Resolver object
     void setDNSService(isc::asiodns::DNSService& dnss);
@@ -130,13 +135,6 @@ public:
     isc::asiolink::SimpleCallback* getCheckinProvider() { return (checkin_); }
 
     /**
-     * \brief Tell the Resolver that is has already been configured
-     *        so that it will only set some defaults the first time
-     *        (used by updateConfig() and tests)
-     */
-    void setConfigured() { configured_ = true; };
-
-    /**
      * \brief Specify the list of upstream servers.
      *
      * Specify the list off addresses of upstream servers to forward queries
@@ -266,10 +264,6 @@ private:
     isc::asiodns::DNSAnswer* dns_answer_;
     isc::nsas::NameserverAddressStore* nsas_;
     isc::cache::ResolverCache* cache_;
-    // This value is initally false, and will be set to true
-    // when the initial configuration is done (updateConfig
-    // should act a tiny bit different on the very first call)
-    bool configured_;
 };
 
 #endif // __RESOLVER_H
diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc
index c089041..63d75c2 100644
--- a/src/bin/resolver/tests/resolver_config_unittest.cc
+++ b/src/bin/resolver/tests/resolver_config_unittest.cc
@@ -14,12 +14,22 @@
 
 #include <config.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <cerrno>
+#include <cstring>
 #include <string>
 
 #include <boost/scoped_ptr.hpp>
+#include <boost/noncopyable.hpp>
 
 #include <gtest/gtest.h>
 
+#include <exceptions/exceptions.h>
+
 #include <cc/data.h>
 
 #include <config/ccsession.h>
@@ -52,6 +62,16 @@ using namespace isc::server_common;
 using isc::UnitTestUtil;
 
 namespace {
+const char* const TEST_ADDRESS = "127.0.0.1";
+const char* const TEST_PORT = "53530";
+
+// An internal exception class
+class TestConfigError : public isc::Exception {
+public:
+    TestConfigError(const char *file, size_t line, const char *what):
+        isc::Exception(file, line, what) {}
+};
+
 class ResolverConfig : public ::testing::Test {
 protected:
     IOService ios;
@@ -63,7 +83,6 @@ protected:
     scoped_ptr<const RequestContext> request;
     ResolverConfig() : dnss(ios, NULL, NULL, NULL) {
         server.setDNSService(dnss);
-        server.setConfigured();
     }
     const RequestContext& createRequest(const string& source_addr) {
         endpoint.reset(IOEndpoint::create(IPPROTO_UDP, IOAddress(source_addr),
@@ -155,6 +174,109 @@ TEST_F(ResolverConfig, rootAddressConfig) {
     EXPECT_EQ(0, server.getRootAddresses().size());
 }
 
+// The following two are helper classes to manage some temporary system
+// resources in an RAII manner.
+class ScopedAddrInfo : public boost::noncopyable {
+public:
+    ScopedAddrInfo(struct addrinfo* ai) : ai_(ai) {}
+    ~ScopedAddrInfo() { freeaddrinfo(ai_);}
+private:
+    struct addrinfo* ai_;
+};
+
+struct ScopedSocket : public boost::noncopyable {
+public:
+    ScopedSocket(int fd) : fd_(fd) {}
+    ~ScopedSocket() { close(fd_); }
+private:
+    const int fd_;
+};
+
+int
+createSocket(const char* address, const char* port) {
+    struct addrinfo hints, *res;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+    const int error = getaddrinfo(address, port, &hints, &res);
+    if (error != 0) {
+        isc_throw(TestConfigError, "getaddrinfo failed: " <<
+                  gai_strerror(error));
+    }
+    ScopedAddrInfo scoped_res(res);
+    const int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+    if (s == -1) {
+        isc_throw(TestConfigError, "socket system call failed: " <<
+                  strerror(errno));
+    }
+    if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
+        close(s);
+        isc_throw(TestConfigError, "bind system call failed: " <<
+                  strerror(errno));
+    }
+    return (s);
+}
+
+void
+configAnswerCheck(ConstElementPtr config_answer, bool expect_success) {
+    EXPECT_EQ(Element::map, config_answer->getType());
+    EXPECT_TRUE(config_answer->contains("result"));
+
+    ConstElementPtr result = config_answer->get("result");
+    EXPECT_EQ(Element::list, result->getType());
+    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
+}
+
+TEST_F(ResolverConfig, listenOnConfig) {
+    ConstElementPtr config(Element::fromJSON("{"
+                                             "\"listen_on\": ["
+                                             " {"
+                                             "    \"address\": \"" +
+                                             string(TEST_ADDRESS) + "\","
+                                             "    \"port\": " +
+                                             string(TEST_PORT) + "}]}"));
+    configAnswerCheck(server.updateConfig(config), true);
+}
+
+TEST_F(ResolverConfig, listenOnConfigFail) {
+    // Create and bind a socket that would make the subsequent listen_on fail
+    ScopedSocket sock(createSocket(TEST_ADDRESS, TEST_PORT));
+
+    ConstElementPtr config(Element::fromJSON("{"
+                                             "\"listen_on\": ["
+                                             " {"
+                                             "    \"address\": \"" +
+                                             string(TEST_ADDRESS) + "\","
+                                             "    \"port\": " +
+                                             string(TEST_PORT) + "}]}"));
+    configAnswerCheck(server.updateConfig(config), false);
+}
+
+TEST_F(ResolverConfig, listenOnAndOtherConfig) {
+    // Create and bind a socket that would make the subsequent listen_on fail
+    ScopedSocket sock(createSocket(TEST_ADDRESS, TEST_PORT));
+
+    // We are going to install a pair of "root_addresses" and "listen_on"
+    // in a single update.
+    const string config_str("{\"root_addresses\": ["
+                            " {\"address\": \"192.0.2.1\","
+                            "   \"port\": 53}], "
+                            "\"listen_on\": ["
+                            " {\"address\": \"" + string(TEST_ADDRESS) + "\","
+                            "  \"port\": " + string(TEST_PORT) + "}]}");
+    // Normally, if listen_on fails the rest of the config parameters will
+    // be ignored.
+    ConstElementPtr config(Element::fromJSON(config_str));
+    configAnswerCheck(server.updateConfig(config), false);
+    EXPECT_EQ(0, server.getRootAddresses().size());
+
+    // On startup the other parameters will be installed anyway.
+    configAnswerCheck(server.updateConfig(config, true), false);
+    EXPECT_EQ(1, server.getRootAddresses().size());
+}
+
 void
 ResolverConfig::invalidTest(const string &JSON, const string& name) {
     isc::testutils::portconfig::configRejected(server, JSON, name);
diff --git a/src/bin/resolver/tests/resolver_unittest.cc b/src/bin/resolver/tests/resolver_unittest.cc
index 71474dd..6b5cf68 100644
--- a/src/bin/resolver/tests/resolver_unittest.cc
+++ b/src/bin/resolver/tests/resolver_unittest.cc
@@ -39,7 +39,6 @@ protected:
     ResolverTest() : server() {
         // By default queries from the "default remote address" will be
         // rejected, so we'll need to add an explicit ACL entry to allow that.
-        server.setConfigured();
         server.updateConfig(Element::fromJSON(
                                 "{ \"query_acl\": "
                                 "  [ {\"action\": \"ACCEPT\","
diff --git a/src/bin/resolver/tests/response_scrubber_unittest.cc b/src/bin/resolver/tests/response_scrubber_unittest.cc
index 1570def..37e2553 100644
--- a/src/bin/resolver/tests/response_scrubber_unittest.cc
+++ b/src/bin/resolver/tests/response_scrubber_unittest.cc
@@ -12,15 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-// $Id$
+#include <config.h>
 
 #include <string>
 #include <iostream>
 
 #include <gtest/gtest.h>
 
-#include <config.h>
-
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_address.h>
 #include <netinet/in.h>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index eb2c747..497ecd1 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -158,7 +158,7 @@ class MockDataSourceClient():
             return (DataSourceClient.PARTIALMATCH, self)
         raise ValueError('Unexpected input to mock client: bug in test case?')
 
-    def find(self, name, rrtype, target=None, options=ZoneFinder.FIND_DEFAULT):
+    def find(self, name, rrtype, options=ZoneFinder.FIND_DEFAULT):
         '''Mock ZoneFinder.find().
 
         It returns the predefined SOA RRset to queries for SOA of the common
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 1167bef..7b70b52 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -583,8 +583,7 @@ class XfrinConnection(asyncore.dispatcher):
             result, finder = self._datasrc_client.find_zone(self._zone_name)
         if result != DataSourceClient.SUCCESS:
             return None
-        result, soa_rrset = finder.find(self._zone_name, RRType.SOA(),
-                                        None, ZoneFinder.FIND_DEFAULT)
+        result, soa_rrset = finder.find(self._zone_name, RRType.SOA())
         if result != ZoneFinder.SUCCESS:
             logger.info(XFRIN_ZONE_NO_SOA, self.zone_str())
             return None
diff --git a/src/bin/xfrout/b10-xfrout.8 b/src/bin/xfrout/b10-xfrout.8
index c810c2f..c37198c 100644
--- a/src/bin/xfrout/b10-xfrout.8
+++ b/src/bin/xfrout/b10-xfrout.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-xfrout
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 1, 2010
+.\"      Date: December 15, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFROUT" "8" "December 1, 2010" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "December 15, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -27,7 +27,7 @@ b10-xfrout \- Outbound DNS zone transfer service
 .PP
 The
 \fBb10\-xfrout\fR
-daemon provides the BIND 10 outgoing DNS zone transfer service\&. It is also used to send outgoing NOTIFY messages\&. Normally it is started by the
+daemon provides the BIND 10 outgoing DNS zone transfer service using AXFR or IXFR\&. It is also used to send outgoing NOTIFY messages\&. Normally it is started by the
 \fBbind10\fR(8)
 boss process\&. When the
 \fBb10\-auth\fR
@@ -47,11 +47,10 @@ sends the zone as found in the BIND 10 zone data store\&.
 .ps -1
 .br
 .sp
-This development prototype release only supports AXFR\&. IXFR is not implemented\&.
+Currently IXFR only works if it gets the zone via \fBb10\-xfrin\fR and only on TCP\&.
 .sp .5v
 .RE
 .PP
-
 This daemon communicates with BIND 10 over a
 \fBb10-msgq\fR(8)
 C\-Channel connection\&. If this connection is not established,
@@ -72,16 +71,22 @@ defines the maximum number of outgoing zone transfers that can run concurrently\
 .PP
 
 \fItsig_key_ring\fR
-A list of TSIG keys (each of which is in the form of name:base64\-key[:algorithm]) used for access control on transfer requests\&. The default is an empty list\&.
+A list of TSIG keys (each of which is in the form of
+\fIname:base64\-key[:algorithm]\fR) used for access control on transfer requests\&. The default is an empty list\&.
 .PP
 
 \fItransfer_acl\fR
-A list of ACL elements that apply to all transfer requests by default (unless overridden in zone_config)\&. See the BIND 10 guide for configuration examples\&. The default is an element that allows any transfer requests\&.
+A list of ACL elements that apply to all transfer requests by default (unless overridden in
+\fIzone_config\fR)\&. See the
+BIND 10 Guide
+for configuration examples\&. The default is an element that allows any transfer requests\&.
 .PP
 
 \fIzone_config\fR
 A list of JSON objects (i\&.e\&. maps) that define per zone configuration concerning
-\fBb10\-xfrout\fR\&. The supported names of each object are "origin" (the origin name of the zone), "class" (the RR class of the zone, optional, default to "IN"), and "acl_element" (ACL only applicable to transfer requests for that zone)\&. See the BIND 10 guide for configuration examples\&. The default is an empty list, that is, no zone specific configuration\&.
+\fBb10\-xfrout\fR\&. The supported names of each object are "origin" (the origin name of the zone), "class" (the RR class of the zone, optional, default to "IN"), and "transfer_acl" (ACL only applicable to transfer requests for that zone)\&. See the
+BIND 10 Guide
+for configuration examples\&. The default is an empty list, that is, no zone specific configuration\&.
 .PP
 
 \fIlog_name\fR
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 4f6a7fa..87d0267 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 1, 2010</date>
+    <date>December 15, 2011</date>
   </refentryinfo>
 
   <refmeta>
@@ -52,7 +52,7 @@
   <refsect1>
     <title>DESCRIPTION</title>
     <para>The <command>b10-xfrout</command> daemon provides the BIND 10
-      outgoing DNS zone transfer service.
+      outgoing DNS zone transfer service using AXFR or IXFR.
       It is also used to send outgoing NOTIFY messages.
       Normally it is started by the
       <citerefentry><refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum></citerefentry>
@@ -67,13 +67,13 @@
  process?, and then the socket and xfr request is sent to xfrout.
 -->
 
+<!-- TODO: IXFR from differences, DDNS, UDP socket passing -->
     <note><simpara>
-      This development prototype release only supports AXFR.
-      IXFR is not implemented.
+      Currently IXFR only works if it gets the zone via
+      <command>b10-xfrin</command> and only on TCP.
     </simpara></note>
 
     <para>
-<!-- TODO: does it really use msgq? what for? -->
       This daemon communicates with BIND 10 over a
       <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
       C-Channel connection.  If this connection is not established,
@@ -100,15 +100,15 @@
     <para>
       <varname>tsig_key_ring</varname>
       A list of TSIG keys (each of which is in the form of
-      name:base64-key[:algorithm]) used for access control on transfer
-      requests.
+      <replaceable>name:base64-key[:algorithm]</replaceable>)
+      used for access control on transfer requests.
       The default is an empty list.
     </para>
     <para>
       <varname>transfer_acl</varname>
       A list of ACL elements that apply to all transfer requests by
-      default (unless overridden in zone_config).  See the BIND 10
-      guide for configuration examples.
+      default (unless overridden in <varname>zone_config</varname>).
+      See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an element that allows any transfer requests.
     </para>
     <para>
@@ -117,9 +117,9 @@
       configuration concerning <command>b10-xfrout</command>.
       The supported names of each object are "origin" (the origin
       name of the zone), "class" (the RR class of the zone, optional,
-      default to "IN"), and "acl_element" (ACL only applicable to
+      default to "IN"), and "transfer_acl" (ACL only applicable to
       transfer requests for that zone).
-      See the BIND 10 guide for configuration examples.
+      See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an empty list, that is, no zone specific configuration.
     </para>
     <para>
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 209bf22..67c6008 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -95,7 +95,7 @@ class MockDataSrcClient:
             return (isc.datasrc.DataSourceClient.NOTFOUND, None)
         return (isc.datasrc.DataSourceClient.SUCCESS, self)
 
-    def find(self, name, rrtype, target=None, options=ZoneFinder.FIND_DEFAULT):
+    def find(self, name, rrtype, options=ZoneFinder.FIND_DEFAULT):
         '''Mock ZoneFinder.find().
 
         (At the moment) this method only handles query for type SOA.
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index b4e8eb2..c6b3275 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -335,8 +335,7 @@ class XfroutSession():
         result, finder = self._datasrc_client.find_zone(zone_name)
         if result != DataSourceClient.SUCCESS:
             return (Rcode.NOTAUTH(), None)
-        result, soa_rrset = finder.find(zone_name, RRType.SOA(), None,
-                                        ZoneFinder.FIND_DEFAULT)
+        result, soa_rrset = finder.find(zone_name, RRType.SOA())
         if result != ZoneFinder.SUCCESS:
             return (Rcode.SERVFAIL(), None)
         # Especially for database-based zones, a working zone may be in
@@ -713,7 +712,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
             # This may happen when one xfrout process try to connect to
             # xfrout unix socket server, to check whether there is another
             # xfrout running.
-            if sock_fd == FD_COMM_ERROR:
+            if sock_fd == FD_SYSTEM_ERROR:
                 logger.error(XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR)
             return
 
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index bfc0a7b..1d24bbf 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-zonemgr
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: May 19, 2011
+.\"      Date: December 8, 2011
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-ZONEMGR" "8" "May 19, 2011" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "December 8, 2011" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -55,10 +55,11 @@ defines the minimum SOA RETRY time in seconds\&. The default is 5\&.
 .PP
 
 \fIrefresh_jitter\fR
-This value is a real number\&. The maximum amount is 0\&.5\&. The default is 0\&.25\&.
+is used to provide a time range for randomizing the refresh and retry timers to help avoid many zones needing to do a refresh or retry at the same time\&. This value is a real number\&. The maximum amount is 0\&.5 (the new timer will be within half the original time)\&. The default is 0\&.25 (up to a quarter sooner)\&. Set to 0 to disable this jitter\&.
 .PP
 
 \fIreload_jitter\fR
+
 This value is a real number\&. The default is 0\&.75\&.
 .PP
 
@@ -118,7 +119,6 @@ This is an internal command and not exposed to the administrator\&.
 \fBb10-cfgmgr\fR(8),
 \fBb10-msgq\fR(8),
 \fBb10-xfrin\fR(8),
-\fBb10-xfrout\fR(8),
 \fBbind10\fR(8),
 BIND 10 Guide\&.
 .SH "HISTORY"
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 769d332..8c6b904 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -20,6 +20,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
+	B10_FROM_BUILD=$(abs_top_builddir) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 80e41b3..600453d 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -48,28 +48,16 @@ class MySession():
     def group_recvmsg(self, nonblock, seq):
         return None, None
 
-class FakeConfig:
+class FakeCCSession(isc.config.ConfigData):
     def __init__(self):
-        self.zone_list = []
-        self.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN,
-                                              ZONE_NAME_CLASS2_CH])
-    def set_zone_list_from_name_classes(self, zones):
-        self.zone_list = map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
-    def get(self, name):
-        if name == 'lowerbound_refresh':
-            return LOWERBOUND_REFRESH
-        elif name == 'lowerbound_retry':
-            return LOWERBOUND_RETRY
-        elif name == 'max_transfer_timeout':
-            return MAX_TRANSFER_TIMEOUT
-        elif name == 'refresh_jitter':
-            return REFRESH_JITTER
-        elif name == 'reload_jitter':
-            return RELOAD_JITTER
-        elif name == 'secondary_zones':
-            return self.zone_list
+        module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
+        ConfigData.__init__(self, module_spec)
+
+    def get_remote_config_value(self, module_name, identifier):
+        if module_name == "Auth" and identifier == "database_file":
+            return "initdb.file", False
         else:
-            raise ValueError('Uknown config option')
+            return "unknown", False
 
 class MyZonemgrRefresh(ZonemgrRefresh):
     def __init__(self):
@@ -92,7 +80,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
         sqlite3_ds.get_zone_soa = get_zone_soa
 
         ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
-            self._slave_socket, FakeConfig())
+                                self._slave_socket, FakeCCSession())
         current_time = time.time()
         self._zonemgr_refresh_info = {
          ('example.net.', 'IN'): {
@@ -112,6 +100,7 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.stderr_backup = sys.stderr
         sys.stderr = open(os.devnull, 'w')
         self.zone_refresh = MyZonemgrRefresh()
+        self.cc_session = FakeCCSession()
 
     def test_random_jitter(self):
         max = 100025.120
@@ -458,7 +447,23 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "secondary_zones": [ { "name": "example.net.",
                                            "class": "IN" } ]
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
+        self.assertTrue(("example.net.", "IN") in
+                        self.zone_refresh._zonemgr_refresh_info)
+
+        # make sure it does fail if we don't provide a name
+        config_data = {
+                    "secondary_zones": [ { "class": "IN" } ]
+                }
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config_data, self.cc_session)
+
+        # But not if we don't provide a class
+        config_data = {
+                    "secondary_zones": [ { "name": "example.net." } ]
+                }
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
 
@@ -471,7 +476,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "reload_jitter" : 0.75,
                     "secondary_zones": []
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -482,7 +487,7 @@ class TestZonemgrRefresh(unittest.TestCase):
         config_data = {
                     "reload_jitter" : 0.35,
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -500,7 +505,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "secondary_zones": [ { "name": "doesnotexist",
                                            "class": "IN" } ]
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         name_class = ("doesnotexist.", "IN")
         self.assertTrue(self.zone_refresh._zonemgr_refresh_info[name_class]["zone_soa_rdata"]
                         is None)
@@ -520,7 +525,7 @@ class TestZonemgrRefresh(unittest.TestCase):
                     "reload_jitter" : 0.75,
                     "secondary_zones": []
                 }
-        self.zone_refresh.update_config_data(config_data)
+        self.zone_refresh.update_config_data(config_data, self.cc_session)
         self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
         self.assertEqual(30, self.zone_refresh._lowerbound_retry)
         self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
@@ -536,45 +541,67 @@ class TestZonemgrRefresh(unittest.TestCase):
         self.assertFalse(listener.is_alive())
 
     def test_secondary_zones(self):
+        def zone_list_from_name_classes(zones):
+            return map(lambda nc: {"name": nc[0], "class": nc[1]}, zones)
+
         """Test that we can modify the list of secondary zones"""
-        config = FakeConfig()
-        config.zone_list = []
+        config = self.cc_session.get_full_config()
+        config['secondary_zones'] = []
         # First, remove everything
-        self.zone_refresh.update_config_data(config)
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertEqual(self.zone_refresh._zonemgr_refresh_info, {})
         # Put something in
-        config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
-        self.zone_refresh.update_config_data(config)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([ZONE_NAME_CLASS1_IN])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
-        # This one does not exist
-        config.set_zone_list_from_name_classes(["example.net", "CH"])
-        self.zone_refresh.update_config_data(config)
-        self.assertFalse(("example.net.", "CH") in
-                        self.zone_refresh._zonemgr_refresh_info)
-        # Simply skip loading soa for the zone, the other configs should be updated successful
+        # Reset the data, set to use a different class, and make sure
+        # it does not get set to IN
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([ZONE_NAME_CLASS1_CH])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertFalse(("example.net.", "IN") in
-                        self.zone_refresh._zonemgr_refresh_info)
+                         self.zone_refresh._zonemgr_refresh_info)
         # Make sure it works even when we "accidentally" forget the final dot
-        config.set_zone_list_from_name_classes([("example.net", "IN")])
-        self.zone_refresh.update_config_data(config)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "IN")])
+        self.zone_refresh.update_config_data(config, self.cc_session)
         self.assertTrue(("example.net.", "IN") in
                         self.zone_refresh._zonemgr_refresh_info)
 
-    def tearDown(self):
-        sys.stderr= self.stderr_backup
-
+        # and with case-insensitive checking
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("Example.NeT.", "in")])
+        self.zone_refresh.update_config_data(config, self.cc_session)
+        self.assertTrue(("example.net.", "IN") in
+                        self.zone_refresh._zonemgr_refresh_info)
 
-class MyCCSession():
-    def __init__(self):
-        pass
-
-    def get_remote_config_value(self, module_name, identifier):
-        if module_name == "Auth" and identifier == "database_file":
-            return "initdb.file", False
-        else:
-            return "unknown", False
+        # Try some bad names
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example..net", "IN")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("", "IN")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        # Try a bad class
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "BADCLASS")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
+        config['secondary_zones'] = \
+            zone_list_from_name_classes([("example.net", "")])
+        self.assertRaises(ZonemgrException,
+                          self.zone_refresh.update_config_data,
+                          config, self.cc_session)
 
+    def tearDown(self):
+        sys.stderr= self.stderr_backup
 
 class MyZonemgr(Zonemgr):
 
@@ -583,7 +610,7 @@ class MyZonemgr(Zonemgr):
         self._zone_refresh = None
         self._shutdown_event = threading.Event()
         self._cc = MySession()
-        self._module_cc = MyCCSession()
+        self._module_cc = FakeCCSession()
         self._config_data = {
                     "lowerbound_refresh" : 10,
                     "lowerbound_retry" : 5,
@@ -622,7 +649,7 @@ class TestZonemgr(unittest.TestCase):
         self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
         # The zone doesn't exist in database, simply skip loading soa for it and log an warning
         self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
-                                                    config_data1)
+                                                    FakeCCSession())
         config_data1["secondary_zones"] = [{"name": "nonexistent.example",
                                             "class": "IN"}]
         self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -660,4 +687,5 @@ class TestZonemgr(unittest.TestCase):
         pass
 
 if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 5bdb765..4060bb5 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -28,6 +28,7 @@ import os
 import time
 import signal
 import isc
+import isc.dns
 import random
 import threading
 import select
@@ -98,7 +99,7 @@ class ZonemgrRefresh:
     can be stopped by calling shutdown() in another thread.
     """
 
-    def __init__(self, cc, db_file, slave_socket, config_data):
+    def __init__(self, cc, db_file, slave_socket, module_cc_session):
         self._cc = cc
         self._check_sock = slave_socket
         self._db_file = db_file
@@ -108,7 +109,8 @@ class ZonemgrRefresh:
         self._max_transfer_timeout = None
         self._refresh_jitter = None
         self._reload_jitter = None
-        self.update_config_data(config_data)
+        self.update_config_data(module_cc_session.get_full_config(),
+                                module_cc_session)
         self._running = False
 
     def _random_jitter(self, max, jitter):
@@ -424,7 +426,7 @@ class ZonemgrRefresh:
         self._read_sock = None
         self._write_sock = None
 
-    def update_config_data(self, new_config):
+    def update_config_data(self, new_config, module_cc_session):
         """ update ZonemgrRefresh config """
         # Get a new value, but only if it is defined (commonly used below)
         # We don't use "value or default", because if value would be
@@ -456,11 +458,42 @@ class ZonemgrRefresh:
             if secondary_zones is not None:
                 # Add new zones
                 for secondary_zone in new_config.get('secondary_zones'):
+                    if 'name' not in secondary_zone:
+                        raise ZonemgrException("Secondary zone specified "
+                                               "without a name")
                     name = secondary_zone['name']
-                    # Be tolerant to sclerotic users who forget the final dot
-                    if name[-1] != '.':
-                        name = name + '.'
-                    name_class = (name, secondary_zone['class'])
+
+                    # Convert to Name and back (both to check and to normalize)
+                    try:
+                        name = isc.dns.Name(name, True).to_text()
+                    # Name() can raise a number of different exceptions, just
+                    # catch 'em all.
+                    except Exception as isce:
+                        raise ZonemgrException("Bad zone name '" + name +
+                                               "': " + str(isce))
+
+                    # Currently we use an explicit get_default_value call
+                    # in case the class hasn't been set. Alternatively, we
+                    # could use
+                    # module_cc_session.get_value('secondary_zones[INDEX]/class')
+                    # To get either the value that was set, or the default if
+                    # it wasn't set.
+                    # But the real solution would be to make new_config a type
+                    # that contains default values itself
+                    # (then this entire method can be simplified a lot, and we
+                    # wouldn't need direct access to the ccsession object)
+                    if 'class' in secondary_zone:
+                        rr_class = secondary_zone['class']
+                    else:
+                        rr_class = module_cc_session.get_default_value(
+                                        'secondary_zones/class')
+                    # Convert rr_class to and from RRClass to check its value
+                    try:
+                        name_class = (name, isc.dns.RRClass(rr_class).to_text())
+                    except isc.dns.InvalidRRClass:
+                        raise ZonemgrException("Bad RR class '" +
+                                               rr_class +
+                                               "' for zone " + name)
                     required[name_class] = True
                     # Add it only if it isn't there already
                     if not name_class in self._zonemgr_refresh_info:
@@ -485,7 +518,7 @@ class Zonemgr:
         self._db_file = self.get_db_file()
         # Create socket pair for communicating between main thread and zonemgr timer thread
         self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data)
+        self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._module_cc)
         self._zone_refresh.run_timer()
 
         self._lock = threading.Lock()
@@ -540,7 +573,7 @@ class Zonemgr:
         self._config_data_check(complete)
         if self._zone_refresh is not None:
             try:
-                self._zone_refresh.update_config_data(complete)
+                self._zone_refresh.update_config_data(complete, self._module_cc)
             except Exception as e:
                 answer = create_answer(1, str(e))
                 ok = False
diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc
index b9cf91f..d16ec65 100644
--- a/src/lib/acl/dns.cc
+++ b/src/lib/acl/dns.cc
@@ -32,7 +32,6 @@
 #include <acl/logic_check.h>
 
 using namespace std;
-using boost::shared_ptr;
 using namespace isc::dns;
 using namespace isc::data;
 
@@ -78,7 +77,7 @@ internal::RequestCheckCreator::names() const {
     return (supported_names);
 }
 
-shared_ptr<RequestCheck>
+boost::shared_ptr<RequestCheck>
 internal::RequestCheckCreator::create(const string& name,
                                       ConstElementPtr definition,
                                       // unused:
@@ -90,10 +89,10 @@ internal::RequestCheckCreator::create(const string& name,
     }
 
     if (name == "from") {
-        return (shared_ptr<internal::RequestIPCheck>(
+        return (boost::shared_ptr<internal::RequestIPCheck>(
                     new internal::RequestIPCheck(definition->stringValue())));
     } else if (name == "key") {
-        return (shared_ptr<internal::RequestKeyCheck>(
+        return (boost::shared_ptr<internal::RequestKeyCheck>(
                     new internal::RequestKeyCheck(
                         Name(definition->stringValue()))));
     } else {
@@ -116,16 +115,17 @@ getRequestLoader() {
             auto_ptr<RequestLoader>(new RequestLoader(REJECT));
 
         // Register default check creator(s)
-        loader_ptr->registerCreator(shared_ptr<internal::RequestCheckCreator>(
-                                        new internal::RequestCheckCreator()));
         loader_ptr->registerCreator(
-            shared_ptr<NotCreator<RequestContext> >(
+            boost::shared_ptr<internal::RequestCheckCreator>(
+                new internal::RequestCheckCreator()));
+        loader_ptr->registerCreator(
+            boost::shared_ptr<NotCreator<RequestContext> >(
                 new NotCreator<RequestContext>("NOT")));
         loader_ptr->registerCreator(
-            shared_ptr<LogicCreator<AnyOfSpec, RequestContext> >(
+            boost::shared_ptr<LogicCreator<AnyOfSpec, RequestContext> >(
                 new LogicCreator<AnyOfSpec, RequestContext>("ANY")));
         loader_ptr->registerCreator(
-            shared_ptr<LogicCreator<AllOfSpec, RequestContext> >(
+            boost::shared_ptr<LogicCreator<AllOfSpec, RequestContext> >(
                 new LogicCreator<AllOfSpec, RequestContext>("ALL")));
 
         // From this point there shouldn't be any exception thrown
diff --git a/src/lib/acl/tests/acl_test.cc b/src/lib/acl/tests/acl_test.cc
index 15ffef6..485a5a3 100644
--- a/src/lib/acl/tests/acl_test.cc
+++ b/src/lib/acl/tests/acl_test.cc
@@ -18,7 +18,6 @@
 
 using namespace isc::acl;
 using namespace isc::acl::tests;
-using boost::shared_ptr;
 
 namespace {
 
@@ -46,8 +45,8 @@ public:
     Log log_;
     size_t next_check_;
     boost::shared_ptr<Check<Log> > getCheck(bool accepts) {
-        return (shared_ptr<Check<Log> >(new ConstCheck(accepts,
-                                                       next_check_++)));
+        return (boost::shared_ptr<Check<Log> >(new ConstCheck(accepts,
+                                                              next_check_++)));
     }
 };
 
diff --git a/src/lib/acl/tests/ip_check_unittest.cc b/src/lib/acl/tests/ip_check_unittest.cc
index 8b8c498..5f40baa 100644
--- a/src/lib/acl/tests/ip_check_unittest.cc
+++ b/src/lib/acl/tests/ip_check_unittest.cc
@@ -162,7 +162,7 @@ TEST(IPFunctionCheck, SplitIPAddress) {
 
 TEST(IPAddress, constructIPv4) {
     IPAddress ipaddr(tests::getSockAddr("192.0.2.1"));
-    const char expected_data[4] = { 192, 0, 2, 1 };
+    const uint8_t expected_data[4] = { 192, 0, 2, 1 };
     EXPECT_EQ(AF_INET, ipaddr.getFamily());
     EXPECT_EQ(4, ipaddr.getLength());
     EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 4));
@@ -170,9 +170,9 @@ TEST(IPAddress, constructIPv4) {
 
 TEST(IPAddress, constructIPv6) {
     IPAddress ipaddr(tests::getSockAddr("2001:db8:1234:abcd::53"));
-    const char expected_data[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0xab,
-                                     0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                                     0x00, 0x53 };
+    const uint8_t expected_data[16] = { 0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0xab,
+                                        0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x53 };
     EXPECT_EQ(AF_INET6, ipaddr.getFamily());
     EXPECT_EQ(16, ipaddr.getLength());
     EXPECT_EQ(0, memcmp(expected_data, ipaddr.getData(), 16));
diff --git a/src/lib/acl/tests/loader_test.cc b/src/lib/acl/tests/loader_test.cc
index 1705c0a..451f195 100644
--- a/src/lib/acl/tests/loader_test.cc
+++ b/src/lib/acl/tests/loader_test.cc
@@ -70,31 +70,30 @@ public:
     // Some convenience functions to set up
 
     // Create a NamedCreator, convert to shared pointer
-    shared_ptr<NamedCreator> namedCreator(const string& name,
+    boost::shared_ptr<NamedCreator> namedCreator(const string& name,
                                           bool abbreviatedList = true)
     {
-        return (shared_ptr<NamedCreator>(new NamedCreator(name,
-                                                          abbreviatedList)));
+        return (boost::shared_ptr<NamedCreator>(new NamedCreator(name,
+                                                    abbreviatedList)));
     }
     // Create and add a NamedCreator
     void addNamed(const string& name, bool abbreviatedList = true) {
         EXPECT_NO_THROW(loader_.registerCreator(
             namedCreator(name, abbreviatedList)));
     }
-    template<class Result> shared_ptr<Result> loadCheckAny(const string&
-                                                               definition)
+    template<class Result> boost::shared_ptr<Result> loadCheckAny(
+        const string& definition)
     {
         SCOPED_TRACE("Loading check " + definition);
-        shared_ptr<Check<Log> > loaded;
+        boost::shared_ptr<Check<Log> > loaded;
         EXPECT_NO_THROW(loaded = loader_.loadCheck(
                             Element::fromJSON(definition)));
-        shared_ptr<Result> result(dynamic_pointer_cast<Result>(
-            loaded));
+        boost::shared_ptr<Result> result(dynamic_pointer_cast<Result>(loaded));
         EXPECT_TRUE(result);
         return (result);
     }
     // Load a check and convert it to named check to examine it
-    shared_ptr<NamedCheck> loadCheck(const string& definition) {
+    boost::shared_ptr<NamedCheck> loadCheck(const string& definition) {
         return (loadCheckAny<NamedCheck>(definition));
     }
     // The loadCheck throws an exception
@@ -114,11 +113,12 @@ public:
     // Insert the throw, throwcheck and logcheck checks into the loader
     void aclSetup() {
         try {
-            loader_.registerCreator(shared_ptr<ThrowCreator>(new
-                                                             ThrowCreator()));
-            loader_.registerCreator(shared_ptr<ThrowCheckCreator>(
+            loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
+                new ThrowCreator()));
+            loader_.registerCreator(boost::shared_ptr<ThrowCheckCreator>(
                 new ThrowCheckCreator()));
-            loader_.registerCreator(shared_ptr<LogCreator>(new LogCreator()));
+            loader_.registerCreator(boost::shared_ptr<LogCreator>(
+                new LogCreator()));
         }
         // We ignore this exception here, because it happens when we try to
         // insert the creators multiple times. This is harmless.
@@ -133,7 +133,7 @@ public:
     {
         SCOPED_TRACE("Running ACL for " + JSON);
         aclSetup();
-        shared_ptr<ACL<Log> > acl;
+        boost::shared_ptr<ACL<Log> > acl;
         EXPECT_NO_THROW(acl = loader_.load(Element::fromJSON(JSON)));
         EXPECT_EQ(expectedResult, acl->execute(log_));
         log_.checkFirst(logged);
@@ -174,7 +174,7 @@ TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
     names.push_back("name1");
     names.push_back("name3");
     EXPECT_THROW(loader_.registerCreator(
-        shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
+        boost::shared_ptr<NamedCreator>(new NamedCreator(names))), LoaderError);
     // It should now reject both name2 and name3 as not known
     checkException("{\"name2\": null}");
     checkException("{\"name3\": null}");
@@ -183,7 +183,7 @@ TEST_F(LoaderTest, CreatorDuplicateUnchanged) {
 // Test that we can register a creator and load a check with the name
 TEST_F(LoaderTest, SimpleCheckLoad) {
     addNamed("name");
-    shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name\": 42}"));
     EXPECT_EQ("name", check->name_);
     EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
 }
@@ -192,7 +192,7 @@ TEST_F(LoaderTest, SimpleCheckLoad) {
 TEST_F(LoaderTest, MultiCreatorCheckLoad) {
     addNamed("name1");
     addNamed("name2");
-    shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name2\": 42}"));
     EXPECT_EQ("name2", check->name_);
     EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
 }
@@ -203,9 +203,9 @@ TEST_F(LoaderTest, MultiNameCheckLoad) {
     vector<string> names;
     names.push_back("name2");
     names.push_back("name3");
-    EXPECT_NO_THROW(loader_.registerCreator(shared_ptr<NamedCreator>(
+    EXPECT_NO_THROW(loader_.registerCreator(boost::shared_ptr<NamedCreator>(
         new NamedCreator(names))));
-    shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name3\": 42}"));
     EXPECT_EQ("name3", check->name_);
     EXPECT_TRUE(check->data_->equals(*Element::fromJSON("42")));
 }
@@ -230,7 +230,8 @@ TEST_F(LoaderTest, UnkownName) {
 
 // Exception from the creator is propagated
 TEST_F(LoaderTest, CheckPropagate) {
-    loader_.registerCreator(shared_ptr<ThrowCreator>(new ThrowCreator()));
+    loader_.registerCreator(boost::shared_ptr<ThrowCreator>(
+                                new ThrowCreator()));
     EXPECT_THROW(loader_.loadCheck(Element::fromJSON("{\"throw\": null}")),
                  TestCreatorError);
 }
@@ -239,7 +240,7 @@ TEST_F(LoaderTest, CheckPropagate) {
 TEST_F(LoaderTest, AndAbbrev) {
     addNamed("name1");
     addNamed("name2");
-    shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+    boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
         loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": 2}"));
     // If we don't have anything loaded, the rest would crash. It is already
     // reported from within loadCheckAny if it isn't loaded.
@@ -258,7 +259,7 @@ TEST_F(LoaderTest, AndAbbrev) {
 // The abbreviated form of parameters
 TEST_F(LoaderTest, OrAbbrev) {
     addNamed("name1");
-    shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
+    boost::shared_ptr<LogicOperator<AnyOfSpec, Log> > oper(
         loadCheckAny<LogicOperator<AnyOfSpec, Log> >("{\"name1\": [1, 2]}"));
     // If we don't have anything loaded, the rest would crash. It is already
     // reported from within loadCheckAny if it isn't loaded.
@@ -276,7 +277,7 @@ TEST_F(LoaderTest, OrAbbrev) {
 TEST_F(LoaderTest, BothAbbrev) {
     addNamed("name1");
     addNamed("name2");
-    shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
+    boost::shared_ptr<LogicOperator<AllOfSpec, Log> > oper(
         loadCheckAny<LogicOperator<AllOfSpec, Log> >("{\"name1\": 1, \"name2\": [3, 4]}"));
     // If we don't have anything loaded, the rest would crash. It is already
     // reported from within loadCheckAny if it isn't loaded.
@@ -302,7 +303,7 @@ TEST_F(LoaderTest, BothAbbrev) {
 // creator
 TEST_F(LoaderTest, ListCheck) {
     addNamed("name1", false);
-    shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": [1, 2]}"));
     EXPECT_EQ("name1", check->name_);
     EXPECT_TRUE(check->data_->equals(*Element::fromJSON("[1, 2]")));
 }
@@ -310,7 +311,7 @@ TEST_F(LoaderTest, ListCheck) {
 // Check the action key is ignored as it should be
 TEST_F(LoaderTest, CheckNoAction) {
     addNamed("name1");
-    shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
+    boost::shared_ptr<NamedCheck> check(loadCheck("{\"name1\": 1, \"action\": 2}"));
     EXPECT_EQ("name1", check->name_);
     EXPECT_TRUE(check->data_->equals(*Element::fromJSON("1")));
 }
diff --git a/src/lib/acl/tests/logic_check_test.cc b/src/lib/acl/tests/logic_check_test.cc
index 1c80277..0ecd2cc 100644
--- a/src/lib/acl/tests/logic_check_test.cc
+++ b/src/lib/acl/tests/logic_check_test.cc
@@ -52,7 +52,7 @@ testCheck(bool emptyResult) {
     EXPECT_EQ(emptyResult, oper.matches(log));
     log.checkFirst(0);
     // Fill it with some subexpressions
-    typedef shared_ptr<ConstCheck> CheckPtr;
+    typedef boost::shared_ptr<ConstCheck> CheckPtr;
     oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 0)));
     oper.addSubexpression(CheckPtr(new ConstCheck(emptyResult, 1)));
     // Check what happens when only the default-valued are there
@@ -80,7 +80,7 @@ TEST(LogicOperators, AnyOf) {
 // Fixture for the tests of the creators
 class LogicCreatorTest : public ::testing::Test {
 private:
-    typedef shared_ptr<Loader<Log>::CheckCreator> CreatorPtr;
+    typedef boost::shared_ptr<Loader<Log>::CheckCreator> CreatorPtr;
 public:
     // Register some creators, both tested ones and some auxiliary ones for
     // help
@@ -102,12 +102,12 @@ public:
     // Some convenience shortcut names
     typedef LogicOperator<AnyOfSpec, Log> AnyOf;
     typedef LogicOperator<AllOfSpec, Log> AllOf;
-    typedef shared_ptr<AnyOf> AnyOfPtr;
-    typedef shared_ptr<AllOf> AllOfPtr;
+    typedef boost::shared_ptr<AnyOf> AnyOfPtr;
+    typedef boost::shared_ptr<AllOf> AllOfPtr;
     // Loads the JSON as a check and tries to convert it to the given check
     // subclass
-    template<typename Result> shared_ptr<Result> load(const string& JSON) {
-        shared_ptr<Check<Log> > result;
+    template<typename Result> boost::shared_ptr<Result> load(const string& JSON) {
+        boost::shared_ptr<Check<Log> > result;
         EXPECT_NO_THROW(result = loader_.loadCheck(Element::fromJSON(JSON)));
         /*
          * Optimally, we would use a dynamic_pointer_cast here to both
@@ -122,9 +122,9 @@ public:
          * multiple inheritance.
          */
         EXPECT_STREQ(typeid(Result).name(), typeid(*result.get()).name());
-        shared_ptr<Result>
+        boost::shared_ptr<Result>
             resultConverted(static_pointer_cast<Result>(result));
-        EXPECT_NE(shared_ptr<Result>(), resultConverted);
+        EXPECT_NE(boost::shared_ptr<Result>(), resultConverted);
         return (resultConverted);
     }
 };
@@ -244,7 +244,8 @@ TEST_F(LogicCreatorTest, nested) {
 }
 
 void notTest(bool value) {
-    NotOperator<Log> notOp(shared_ptr<Check<Log> >(new ConstCheck(value, 0)));
+    NotOperator<Log> notOp(boost::shared_ptr<Check<Log> >(
+                                new ConstCheck(value, 0)));
     Log log;
     // It returns negated value
     EXPECT_EQ(!value, notOp.matches(log));
@@ -281,7 +282,7 @@ TEST_F(LogicCreatorTest, notInvalid) {
 }
 
 TEST_F(LogicCreatorTest, notValid) {
-    shared_ptr<NotOperator<Log> > notOp(load<NotOperator<Log> >("{\"NOT\":"
+    boost::shared_ptr<NotOperator<Log> > notOp(load<NotOperator<Log> >("{\"NOT\":"
                                                                 "  {\"logcheck\":"
                                                                 "     [0, true]}}"));
     EXPECT_FALSE(notOp->matches(log_));
diff --git a/src/lib/asiodns/dns_lookup.h b/src/lib/asiodns/dns_lookup.h
index 40290e4..5dc84ac 100644
--- a/src/lib/asiodns/dns_lookup.h
+++ b/src/lib/asiodns/dns_lookup.h
@@ -51,7 +51,9 @@ protected:
     ///
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
-    DNSLookup() : self_(this) {}
+    DNSLookup() {
+        self_ = this;
+    }
 public:
     /// \brief The destructor
     virtual ~DNSLookup() {}
diff --git a/src/lib/asiodns/dns_server.h b/src/lib/asiodns/dns_server.h
index f235860..d3a8528 100644
--- a/src/lib/asiodns/dns_server.h
+++ b/src/lib/asiodns/dns_server.h
@@ -53,7 +53,9 @@ protected:
     /// This is intentionally defined as \c protected, as this base class
     /// should never be instantiated except as part of a derived class.
     //@{
-    DNSServer() : self_(this) {}
+    DNSServer() {
+        self_ = this;
+    }
 public:
     /// \brief The destructor
     virtual ~DNSServer() {}
diff --git a/src/lib/asiodns/dns_service.cc b/src/lib/asiodns/dns_service.cc
index 94510fe..ed7d717 100644
--- a/src/lib/asiodns/dns_service.cc
+++ b/src/lib/asiodns/dns_service.cc
@@ -12,14 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
+
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
 
 #include <boost/lexical_cast.hpp>
 
-#include <config.h>
-
 #include <log/dummylog.h>
 
 #include <asio.hpp>
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index 8a91982..25ec955 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -175,12 +175,12 @@ struct IOFetchData {
 /// IOFetch Constructor - just initialize the private data
 
 IOFetch::IOFetch(Protocol protocol, IOService& service,
-    const isc::dns::Question& question, const IOAddress& address, uint16_t port,
-    OutputBufferPtr& buff, Callback* cb, int wait)
+    const isc::dns::Question& question, const IOAddress& address,
+    uint16_t port, OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
 {
     MessagePtr query_msg(new Message(Message::RENDER));
     initIOFetch(query_msg, protocol, service, question, address, port, buff,
-                cb, wait);
+                cb, wait, edns);
 }
 
 IOFetch::IOFetch(Protocol protocol, IOService& service,
@@ -214,7 +214,7 @@ void
 IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service,
                      const isc::dns::Question& question,
                      const IOAddress& address, uint16_t port,
-                     OutputBufferPtr& buff, Callback* cb, int wait)
+                     OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
 {
     data_ = boost::shared_ptr<IOFetchData>(new IOFetchData(
         protocol, service, address, port, buff, cb, wait));
@@ -224,9 +224,13 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
     query_msg->setRcode(Rcode::NOERROR());
     query_msg->setHeaderFlag(Message::HEADERFLAG_RD);
     query_msg->addQuestion(question);
-    EDNSPtr edns_query(new EDNS());
-    edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
-    query_msg->setEDNS(edns_query);
+
+    if (edns) {
+        EDNSPtr edns_query(new EDNS());
+        edns_query->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
+        query_msg->setEDNS(edns_query);
+    }
+
     MessageRenderer renderer(*data_->msgbuf);
     query_msg->toWire(renderer);
 }
diff --git a/src/lib/asiodns/io_fetch.h b/src/lib/asiodns/io_fetch.h
index 9626ffe..78c2da9 100644
--- a/src/lib/asiodns/io_fetch.h
+++ b/src/lib/asiodns/io_fetch.h
@@ -35,7 +35,7 @@ namespace isc {
 namespace asiodns {
 
 // Forward declarations
-class IOFetchData;
+struct IOFetchData;
 
 /// \brief Upstream Fetch Processing
 ///
@@ -131,11 +131,14 @@ public:
     ///        and deleting it if necessary.
     /// \param wait Timeout for the fetch (in ms).  The default value of
     ///        -1 indicates no timeout.
+    /// \param edns true if the request should be EDNS. The default value is
+    ///        true.
     IOFetch(Protocol protocol, isc::asiolink::IOService& service,
         const isc::dns::Question& question,
         const isc::asiolink::IOAddress& address,
         uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb,
-        int wait = -1);
+        int wait = -1,
+        bool edns = true);
 
     /// \brief Constructor
     ///  This constructor has one parameter "query_message", which
@@ -206,7 +209,8 @@ private:
     void initIOFetch(isc::dns::MessagePtr& query_message, Protocol protocol,
             isc::asiolink::IOService& service, const isc::dns::Question& question,
             const isc::asiolink::IOAddress& address, uint16_t port,
-            isc::util::OutputBufferPtr& buff, Callback* cb, int wait);
+            isc::util::OutputBufferPtr& buff, Callback* cb, int wait,
+            bool edns = true);
 
     /// \brief Log I/O Failure
     ///
diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc
index f91eb32..2606a21 100644
--- a/src/lib/asiodns/tcp_server.cc
+++ b/src/lib/asiodns/tcp_server.cc
@@ -70,7 +70,7 @@ TCPServer::TCPServer(io_service& io_service,
 }
 
 void
-TCPServer::operator()(error_code ec, size_t length) {
+TCPServer::operator()(asio::error_code ec, size_t length) {
     /// Because the coroutine reentry block is implemented as
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
index 1ef3192..c79ee7f 100644
--- a/src/lib/asiodns/tests/dns_server_unittest.cc
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -430,7 +430,7 @@ TEST_F(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
 }
 
 static void stopServerManyTimes(DNSServer *server, unsigned int times) {
-    for (int i = 0; i < times; ++i) {
+    for (unsigned int i = 0; i < times; ++i) {
         server->stop();
     }
 }
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index 52a51a1..936c6c7 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -169,8 +169,10 @@ public:
     ///        sent with the correct QID.
     /// \param length Amount of data received.
     void udpReceiveHandler(udp::endpoint* remote, udp::socket* socket,
-                    error_code ec = error_code(), size_t length = 0,
-                    bool bad_qid = false, bool second_send = false) {
+                           asio::error_code ec = asio::error_code(),
+                           size_t length = 0, bool bad_qid = false,
+                           bool second_send = false)
+    {
         if (debug_) {
             cout << "udpReceiveHandler(): error = " << ec.value() <<
                     ", length = " << length << endl;
@@ -218,7 +220,8 @@ public:
     ///
     /// \param socket Socket on which data will be received
     /// \param ec Boost error code, value should be zero.
-    void tcpAcceptHandler(tcp::socket* socket, error_code ec = error_code())
+    void tcpAcceptHandler(tcp::socket* socket,
+                          asio::error_code ec = asio::error_code())
     {
         if (debug_) {
             cout << "tcpAcceptHandler(): error = " << ec.value() << endl;
@@ -257,7 +260,8 @@ public:
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     /// \param length Amount of data received.
-    void tcpReceiveHandler(tcp::socket* socket, error_code ec = error_code(),
+    void tcpReceiveHandler(tcp::socket* socket,
+                           asio::error_code ec = asio::error_code(),
                            size_t length = 0)
     {
         if (debug_) {
@@ -386,7 +390,8 @@ public:
     /// \param ec Boost error code, value should be zero.
     /// \param length Number of bytes sent.
     void tcpSendHandler(size_t expected, tcp::socket* socket,
-                        error_code ec = error_code(), size_t length = 0)
+                        asio::error_code ec = asio::error_code(),
+                        size_t length = 0)
     {
         if (debug_) {
             cout << "tcpSendHandler(): error = " << ec.value() <<
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index f103e5a..72db2bf 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -170,7 +170,7 @@ UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
 /// The function operator is implemented with the "stackless coroutine"
 /// pattern; see internal/coroutine.h for details.
 void
-UDPServer::operator()(error_code ec, size_t length) {
+UDPServer::operator()(asio::error_code ec, size_t length) {
     /// Because the coroutine reentry block is implemented as
     /// a switch statement, inline variable declarations are not
     /// permitted.  Certain variables used below can be declared here.
diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h
index 4c19544..90fcc6f 100644
--- a/src/lib/asiodns/udp_server.h
+++ b/src/lib/asiodns/udp_server.h
@@ -99,7 +99,7 @@ private:
      * This way the overhead of copying is lower, we copy only one shared
      * pointer instead of about 10 of them.
      */
-    class Data;
+    struct Data;
     boost::shared_ptr<Data> data_;
 };
 
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 0fe1db4..370e8f5 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -38,7 +38,7 @@ namespace asiolink {
 // XXX: we cannot simply construct the address in the initialization list,
 // because we'd like to throw our own exception on failure.
 IOAddress::IOAddress(const string& address_str) {
-    error_code err;
+    asio::error_code err;
     asio_address_ = ip::address::from_string(address_str, err);
     if (err) {
         isc_throw(IOError, "Failed to convert string to address '"
diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc
index 70cc18b..76d5ee1 100644
--- a/src/lib/asiolink/io_service.cc
+++ b/src/lib/asiolink/io_service.cc
@@ -12,12 +12,12 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
+
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
 
-#include <config.h>
-
 #include <asio.hpp>
 #include <asiolink/io_service.h>
 
diff --git a/src/lib/asiolink/simple_callback.h b/src/lib/asiolink/simple_callback.h
index 92093ec..a297a1d 100644
--- a/src/lib/asiolink/simple_callback.h
+++ b/src/lib/asiolink/simple_callback.h
@@ -49,7 +49,9 @@ protected:
     ///
     /// This is intentionally defined as \c protected as this base class
     /// should never be instantiated (except as part of a derived class).
-    SimpleCallback() : self_(this) {}
+    SimpleCallback() {
+        self_ = this;
+    }
 public:
     /// \brief The destructor
     virtual ~SimpleCallback() {}
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
index c8876c8..2505d7b 100644
--- a/src/lib/asiolink/tcp_socket.h
+++ b/src/lib/asiolink/tcp_socket.h
@@ -276,7 +276,7 @@ TCPSocket<C>::asyncSend(const void* data, size_t length,
             // ... and send it
             socket_.async_send(asio::buffer(send_buffer_->getData(),
                                send_buffer_->getLength()), callback);
-        } catch (boost::numeric::bad_numeric_cast& e) {
+        } catch (boost::numeric::bad_numeric_cast&) {
             isc_throw(BufferTooLarge,
                       "attempt to send buffer larger than 64kB");
         }
diff --git a/src/lib/asiolink/tests/io_endpoint_unittest.cc b/src/lib/asiolink/tests/io_endpoint_unittest.cc
index c7283ec..948e708 100644
--- a/src/lib/asiolink/tests/io_endpoint_unittest.cc
+++ b/src/lib/asiolink/tests/io_endpoint_unittest.cc
@@ -25,11 +25,10 @@
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_error.h>
 
-using boost::shared_ptr;
 using namespace isc::asiolink;
 
 namespace {
-typedef shared_ptr<const IOEndpoint> ConstIOEndpointPtr;
+typedef boost::shared_ptr<const IOEndpoint> ConstIOEndpointPtr;
 
 TEST(IOEndpointTest, createUDPv4) {
     ConstIOEndpointPtr ep(IOEndpoint::create(IPPROTO_UDP,
diff --git a/src/lib/bench/benchmark.h b/src/lib/bench/benchmark.h
index 5eab1b7..7f77aa1 100644
--- a/src/lib/bench/benchmark.h
+++ b/src/lib/bench/benchmark.h
@@ -261,7 +261,7 @@ public:
 
         struct timeval beg, end;
         gettimeofday(&beg, NULL);
-        for (int i = 0; i < iterations_; ++i) {
+        for (unsigned int i = 0; i < iterations_; ++i) {
             sub_iterations_ += target_.run();
         }
         gettimeofday(&end, NULL);
diff --git a/src/lib/bench/benchmark_util.cc b/src/lib/bench/benchmark_util.cc
index c67a851..9cf3b26 100644
--- a/src/lib/bench/benchmark_util.cc
+++ b/src/lib/bench/benchmark_util.cc
@@ -103,7 +103,7 @@ loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
                 static_cast<const unsigned char*>(buffer.getData()) +
                 buffer.getLength());
             queries.push_back(query_data);
-        } catch (const Exception& error) {
+        } catch (const Exception&) {
             if (strict) {
                 isc_throw(BenchMarkError,
                           "failed to parse/create query around line " <<
diff --git a/src/lib/cache/resolver_cache.cc b/src/lib/cache/resolver_cache.cc
index 57935c0..20f470e 100644
--- a/src/lib/cache/resolver_cache.cc
+++ b/src/lib/cache/resolver_cache.cc
@@ -164,14 +164,16 @@ ResolverCache::ResolverCache()
 
 ResolverCache::ResolverCache(std::vector<CacheSizeInfo> caches_info)
 {
-    for (int i = 0; i < caches_info.size(); ++i) {
+    for (std::vector<CacheSizeInfo>::size_type i = 0;
+         i < caches_info.size(); ++i) {
         class_caches_.push_back(new ResolverClassCache(caches_info[i]));
     }
 }
 
 ResolverCache::~ResolverCache()
 {
-    for (int i = 0; i < class_caches_.size(); ++i) {
+    for (std::vector<ResolverClassCache*>::size_type i = 0;
+         i < class_caches_.size(); ++i) {
         delete class_caches_[i];
     }
 }
@@ -261,7 +263,8 @@ ResolverCache::update(const isc::dns::ConstRRsetPtr& rrset_ptr) {
 
 ResolverClassCache*
 ResolverCache::getClassCache(const isc::dns::RRClass& cache_class) const {
-    for (int i = 0; i < class_caches_.size(); ++i) {
+    for (std::vector<ResolverClassCache*>::size_type i = 0;
+         i < class_caches_.size(); ++i) {
         if (class_caches_[i]->getClass() == cache_class) {
             return (class_caches_[i]);
         }
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index ffa5346..77f948a 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -742,7 +742,7 @@ MapElement::find(const std::string& id, ConstElementPtr t) const {
             t = p;
             return (true);
         }
-    } catch (const TypeError& e) {
+    } catch (const TypeError&) {
         // ignore
     }
     return (false);
@@ -780,11 +780,11 @@ StringElement::equals(const Element& other) const {
 bool
 ListElement::equals(const Element& other) const {
     if (other.getType() == Element::list) {
-        const int s = size();
+        const size_t s = size();
         if (s != other.size()) {
             return (false);
         }
-        for (int i = 0; i < s; ++i) {
+        for (size_t i = 0; i < s; ++i) {
             if (!get(i)->equals(*other.get(i))) {
                 return (false);
             }
@@ -843,10 +843,14 @@ removeIdentical(ElementPtr a, ConstElementPtr b) {
         isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
     }
 
-    const std::map<std::string, ConstElementPtr>& m = a->mapValue();
+    // As maps do not allow entries with multiple keys, we can either iterate
+    // over a checking for identical entries in b or vice-versa.  As elements
+    // are removed from a if a match is found, we choose to iterate over b to
+    // avoid problems with element removal affecting the iterator.
+    const std::map<std::string, ConstElementPtr>& m = b->mapValue();
     for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
-        if (b->contains((*it).first)) {
+        if (a->contains((*it).first)) {
             if (a->get((*it).first)->equals(*b->get((*it).first))) {
                 a->remove((*it).first);
             }
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index 0052aca..1b21d21 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -367,7 +367,7 @@ Session::recvmsg(ConstElementPtr& env, ConstElementPtr& msg,
     size_t length = impl_->readDataLength();
     if (hasQueuedMsgs()) {
         ConstElementPtr q_el;
-        for (int i = 0; i < impl_->queue_->size(); i++) {
+        for (size_t i = 0; i < impl_->queue_->size(); i++) {
             q_el = impl_->queue_->get(i);
             if (( seq == -1 &&
                   !q_el->get(0)->contains("reply")
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index 53d5ab8..d8624cb 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -523,6 +523,12 @@ TEST(Element, removeIdentical) {
     removeIdentical(a, b);
     EXPECT_EQ(*a, *c);
 
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"c\": 3, \"b\": 2 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    removeIdentical(a, b);
+    EXPECT_EQ(*a, *c);
+
     EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)), TypeError);
 }
 
@@ -567,6 +573,11 @@ TEST(Element, constRemoveIdentical) {
     c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
     EXPECT_EQ(*removeIdentical(a, b), *c);
 
+    a = Element::fromJSON("{ \"a\": 1, \"b\": 2, \"c\": 3 }");
+    b = Element::fromJSON("{ \"c\": 3, \"b\": 2 }");
+    c = Element::fromJSON("{ \"a\": 1 }");
+    EXPECT_EQ(*removeIdentical(a, b), *c);
+
     EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)),
                  TypeError);
 }
diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc
index 4abeb87..df94c12 100644
--- a/src/lib/cryptolink/tests/crypto_unittests.cc
+++ b/src/lib/cryptolink/tests/crypto_unittests.cc
@@ -392,7 +392,8 @@ doRFC4231Tests(HashAlgorithm hash_algorithm,
     ASSERT_EQ(secret_list.size(), data_list.size());
     ASSERT_EQ(secret_list.size(), hmac_list.size());
 
-    for (int i = 0; i < data_list.size(); ++i) {
+    for (std::vector<std::string>::size_type i = 0;
+         i < data_list.size(); ++i) {
         SCOPED_TRACE("RFC4231 HMAC test for algorithm ID: " +
                      lexical_cast<std::string>(hash_algorithm) +
                      ", data ID: " + lexical_cast<std::string>(i));
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index bf7f6ae..2b324fc 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -35,7 +35,6 @@
 
 using namespace isc::dns;
 using namespace std;
-using boost::shared_ptr;
 using namespace isc::dns::rdata;
 
 namespace isc {
@@ -177,7 +176,8 @@ private:
 
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
-                                  bool check_ns, const string* construct_name)
+                                  bool check_ns, const string* construct_name,
+                                  bool any)
 {
     RRsigStore sig_store;
     bool records_found = false;
@@ -222,7 +222,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
                      columns[DatabaseAccessor::RDATA_COLUMN]));
             }
 
-            if (types.find(cur_type) != types.end()) {
+            if (types.find(cur_type) != types.end() || any) {
                 // This type is requested, so put it into result
                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
                 // Ths sigtype column was an optimization for finding the
@@ -287,6 +287,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         sig_store.appendSignatures(i->second);
     }
 
+    if (records_found && any) {
+        result[RRType::ANY()] = RRsetPtr();
+        // These will be sitting on the other RRsets.
+        result.erase(RRType::RRSIG());
+    }
+
     return (FoundRRsets(records_found, result));
 }
 
@@ -390,6 +396,25 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
     return (ConstRRsetPtr());
 }
 
+ZoneFinder::FindResult
+DatabaseClient::Finder::findAll(const isc::dns::Name& name,
+                                std::vector<isc::dns::ConstRRsetPtr>& target,
+                                const FindOptions options)
+{
+    return (findInternal(name, RRType::ANY(), &target, options));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::find(const isc::dns::Name& name,
+                             const isc::dns::RRType& type,
+                             const FindOptions options)
+{
+    if (type == RRType::ANY()) {
+        isc_throw(isc::Unexpected, "Use findAll to answer ANY");
+    }
+    return (findInternal(name, type, NULL, options));
+}
+
 DatabaseClient::Finder::DelegationSearchResult
 DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
                                             const FindOptions options)
@@ -551,7 +576,8 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
 ZoneFinder::FindResult
 DatabaseClient::Finder::findWildcardMatch(
     const isc::dns::Name& name, const isc::dns::RRType& type,
-    const FindOptions options, const DelegationSearchResult& dresult)
+    const FindOptions options, const DelegationSearchResult& dresult,
+    std::vector<isc::dns::ConstRRsetPtr>* target)
 {
     // Note that during the search we are going to search not only for the
     // requested type, but also for types that indicate a delegation -
@@ -559,7 +585,8 @@ DatabaseClient::Finder::findWildcardMatch(
     WantedTypes final_types(FINAL_TYPES());
     final_types.insert(type);
 
-    for (size_t i = 1; i <= (name.getLabelCount() - dresult.last_known); ++i) {
+    const size_t remove_labels = name.getLabelCount() - dresult.last_known;
+    for (size_t i = 1; i <= remove_labels; ++i) {
 
         // Strip off the left-more label(s) in the name and replace with a "*".
         const Name superdomain(name.split(i));
@@ -570,7 +597,7 @@ DatabaseClient::Finder::findWildcardMatch(
         // RFC 4592 section 4.4).
         // Search for a match.  The types are the same as with original query.
         FoundRRsets found = getRRsets(wildcard, final_types, true,
-                                      &construct_name);
+                                      &construct_name, type == RRType::ANY());
         if (found.first) {
             // Found something - but what?
 
@@ -595,7 +622,7 @@ DatabaseClient::Finder::findWildcardMatch(
                 // The wildcard match is the best one, find the final result
                 // at it.  Note that wildcard should never be the zone origin.
                 return (findOnNameResult(name, type, options, false,
-                                         found, &wildcard));
+                                         found, &wildcard, target));
             } else {
 
                 // more specified match found, cancel wildcard match
@@ -661,7 +688,9 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          const FindOptions options,
                                          const bool is_origin,
                                          const FoundRRsets& found,
-                                         const string* wildname)
+                                         const string* wildname,
+                                         std::vector<isc::dns::ConstRRsetPtr>*
+                                         target)
 {
     const bool wild = (wildname != NULL);
 
@@ -699,14 +728,29 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                    DATASRC_DATABASE_FOUND_CNAME));
 
     } else if (wti != found.second.end()) {
+        bool any(type == RRType::ANY());
+        isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
+                                DATASRC_DATABASE_FOUND_RRSET);
+        if (any) {
+            // An ANY query, copy everything to the target instead of returning
+            // directly.
+            for (FoundIterator it(found.second.begin());
+                 it != found.second.end(); ++it) {
+                if (it->second) {
+                    // Skip over the empty ANY
+                    target->push_back(it->second);
+                }
+            }
+            lid = wild ? DATASRC_DATABASE_WILDCARD_ANY :
+                DATASRC_DATABASE_FOUND_ANY;
+        }
         // Found an RR matching the query, so return it.  (Note that this
         // includes the case where we were explicitly querying for a CNAME and
         // found it.  It also includes the case where we were querying for an
         // NS RRset and found it at the apex of the zone.)
         return (logAndCreateResult(name, wildname, type,
                                    wild ? WILDCARD : SUCCESS, wti->second,
-                                   wild ? DATASRC_DATABASE_WILDCARD_MATCH :
-                                   DATASRC_DATABASE_FOUND_RRSET));
+                                   lid));
     }
 
     // If we get here, we have found something at the requested name but not
@@ -748,7 +792,9 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
 ZoneFinder::FindResult
 DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
                                          FindOptions options,
-                                         const DelegationSearchResult& dresult)
+                                         const DelegationSearchResult& dresult,
+                                         std::vector<isc::dns::ConstRRsetPtr>*
+                                         target)
 {
     const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
 
@@ -772,7 +818,7 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
         // (i.e. all results except NXDOMAIN) return it; otherwise fall
         // through to the NXDOMAIN case below.
         const ZoneFinder::FindResult wresult =
-            findWildcardMatch(name, type, options, dresult);
+            findWildcardMatch(name, type, options, dresult, target);
         if (wresult.code != NXDOMAIN) {
             return (FindResult(wresult.code, wresult.rrset));
         }
@@ -787,14 +833,24 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
 }
 
 ZoneFinder::FindResult
-DatabaseClient::Finder::find(const isc::dns::Name& name,
+DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
-                             isc::dns::RRsetList*,
+                             std::vector<isc::dns::ConstRRsetPtr>* target,
                              const FindOptions options)
 {
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
               .arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
 
+    // find() variants generally expect 'name' to be included in the zone.
+    // Otherwise the search algorithm below won't work correctly, so we
+    // reject the unexpected case first.
+    const NameComparisonResult::NameRelation reln =
+        name.compare(getOrigin()).getRelation();
+    if (reln != NameComparisonResult::SUBDOMAIN &&
+        reln != NameComparisonResult::EQUAL) {
+        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+    }
+
     // First, go through all superdomains from the origin down, searching for
     // nodes that indicate a delegation (i.e. NS or DNAME, ignoring NS records
     // at the apex).  If one is found, the search stops there.
@@ -821,16 +877,18 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     WantedTypes final_types(FINAL_TYPES());
     final_types.insert(type);
     const FoundRRsets found = getRRsets(name.toText(), final_types,
-                                        !is_origin);
+                                        !is_origin, NULL,
+                                        type == RRType::ANY());
 
     if (found.first) {
         // Something found at the domain name.  Look into it further to get
         // the final result.
-        return (findOnNameResult(name, type, options, is_origin, found, NULL));
+        return (findOnNameResult(name, type, options, is_origin, found, NULL,
+                                 target));
     } else {
         // Did not find anything at all at the domain name, so check for
         // subdomains or wildcards.
-        return (findNoNameResult(name, type, options, dresult));
+        return (findNoNameResult(name, type, options, dresult, target));
     }
 }
 
@@ -874,7 +932,7 @@ namespace {
 /// for next time.
 class DatabaseIterator : public ZoneIterator {
 public:
-    DatabaseIterator(shared_ptr<DatabaseAccessor> accessor,
+    DatabaseIterator(boost::shared_ptr<DatabaseAccessor> accessor,
                      const Name& zone_name,
                      const RRClass& rrclass,
                      bool separate_rrs) :
@@ -898,7 +956,7 @@ public:
         // Find the SOA of the zone (may or may not succeed).  Note that
         // this must be done before starting the iteration context.
         soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
-            find(zone_name, RRType::SOA(), NULL).rrset;
+            find(zone_name, RRType::SOA()).rrset;
 
         // Request the context
         context_ = accessor_->getAllRecords(zone.second);
@@ -970,7 +1028,7 @@ private:
     }
 
     // The dedicated accessor
-    shared_ptr<DatabaseAccessor> accessor_;
+    boost::shared_ptr<DatabaseAccessor> accessor_;
     // The context
     DatabaseAccessor::IteratorContextPtr context_;
     // Class of the zone
@@ -1006,7 +1064,7 @@ DatabaseClient::getIterator(const isc::dns::Name& name,
 //
 class DatabaseUpdater : public ZoneUpdater {
 public:
-    DatabaseUpdater(shared_ptr<DatabaseAccessor> accessor, int zone_id,
+    DatabaseUpdater(boost::shared_ptr<DatabaseAccessor> accessor, int zone_id,
             const Name& zone_name, const RRClass& zone_class,
             bool journaling) :
         committed_(false), accessor_(accessor), zone_id_(zone_id),
@@ -1052,7 +1110,7 @@ private:
     typedef DatabaseAccessor Accessor;
 
     bool committed_;
-    shared_ptr<DatabaseAccessor> accessor_;
+    boost::shared_ptr<DatabaseAccessor> accessor_;
     const int zone_id_;
     const string db_name_;
     const string zone_name_;
@@ -1229,7 +1287,7 @@ DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace,
         isc_throw(isc::BadValue, "Can't store journal and replace the whole "
                   "zone at the same time");
     }
-    shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
+    boost::shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
     const std::pair<bool, int> zone(update_accessor->startUpdateZone(
                                         name.toText(), replace));
     if (!zone.first) {
@@ -1249,7 +1307,7 @@ private:
     // A shortcut typedef to keep the code concise.
     typedef DatabaseAccessor Accessor;
 public:
-    DatabaseJournalReader(shared_ptr<Accessor> accessor, const Name& zone,
+    DatabaseJournalReader(boost::shared_ptr<Accessor> accessor, const Name& zone,
                           int zone_id, const RRClass& rrclass, uint32_t begin,
                           uint32_t end) :
         accessor_(accessor), zone_(zone), rrclass_(rrclass),
@@ -1297,7 +1355,7 @@ public:
     }
 
 private:
-    shared_ptr<Accessor> accessor_;
+    boost::shared_ptr<Accessor> accessor_;
     const Name zone_;
     const RRClass rrclass_;
     Accessor::IteratorContextPtr context_;
@@ -1312,7 +1370,7 @@ DatabaseClient::getJournalReader(const isc::dns::Name& zone,
                                  uint32_t begin_serial,
                                  uint32_t end_serial) const
 {
-    shared_ptr<DatabaseAccessor> jnl_accessor(accessor_->clone());
+    boost::shared_ptr<DatabaseAccessor> jnl_accessor(accessor_->clone());
     const pair<bool, int> zoneinfo(jnl_accessor->getZone(zone.toText()));
     if (!zoneinfo.first) {
         return (pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>(
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index c1b71cd..fdbaa0a 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -714,11 +714,6 @@ public:
         /// (this implementation is not complete, and currently only
         /// does full matches, CNAMES, and the signatures for matches and
         /// CNAMEs)
-        /// \note target was used in the original design to handle ANY
-        ///       queries. This is not implemented yet, and may use
-        ///       target again for that, but it might also use something
-        ///       different. It is left in for compatibility at the moment.
-        /// \note options are ignored at this moment
         ///
         /// \note Maybe counter intuitively, this method is not a const member
         /// function.  This is intentional; some of the underlying
@@ -741,13 +736,19 @@ public:
         ///
         /// \param name The name to find
         /// \param type The RRType to find
-        /// \param target Unused at this moment
         /// \param options Options about how to search.
         ///     See ZoneFinder::FindOptions.
         virtual FindResult find(const isc::dns::Name& name,
                                 const isc::dns::RRType& type,
-                                isc::dns::RRsetList* target = NULL,
                                 const FindOptions options = FIND_DEFAULT);
+        /// \brief Implementation of the ZoneFinder::findAll method.
+        ///
+        /// In short, it is mostly the same thing as find, but it returns all
+        /// RRsets in the named node through the target parameter in successful
+        /// case. It acts the same in the unsuccessful one.
+        virtual FindResult findAll(const isc::dns::Name& name,
+                                   std::vector<isc::dns::ConstRRsetPtr>& target,
+                                   const FindOptions options = FIND_DEFAULT);
 
         /// \brief Implementation of ZoneFinder::findPreviousName method.
         virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -773,12 +774,57 @@ public:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
         const isc::dns::Name origin_;
-        //
         /// \brief Shortcut name for the result of getRRsets
         typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
             FoundRRsets;
         /// \brief Just shortcut for set of types
         typedef std::set<dns::RRType> WantedTypes;
+        /// \brief Internal logit of find and findAll methods.
+        ///
+        /// Most of their handling is in the "error" cases and delegations
+        /// and so on. So they share the logic here and find and findAll provide
+        /// just an interface for it.
+        ///
+        /// Parameters and behaviour is like of those combined together.
+        /// Unexpected parameters, like type != ANY and having the target, are
+        /// just that - unexpected and not checked.
+        FindResult findInternal(const isc::dns::Name& name,
+                                const isc::dns::RRType& type,
+                                std::vector<isc::dns::ConstRRsetPtr>* target,
+                                const FindOptions options = FIND_DEFAULT);
+        /// \brief Searches database for RRsets of one domain.
+        ///
+        /// This method scans RRs of single domain specified by name and
+        /// extracts any RRsets found and requested by parameters.
+        ///
+        /// It is used internally by find(), because it is called multiple
+        /// times (usually with different domains).
+        ///
+        /// \param name Which domain name should be scanned.
+        /// \param types List of types the caller is interested in.
+        /// \param check_ns If this is set to true, it checks nothing lives
+        ///     together with NS record (with few little exceptions, like RRSIG
+        ///     or NSEC). This check is meant for non-apex NS records.
+        /// \param construct_name If this is NULL, the resulting RRsets have
+        ///     their name set to name. If it is not NULL, it overrides the name
+        ///     and uses this one (this can be used for wildcard synthesized
+        ///     records).
+        /// \param any If this is true, it records all the types, not only the
+        ///     ones requested by types. It also puts a NULL pointer under the
+        ///     ANY type into the result, if it finds any RRs at all, to easy the
+        ///     identification of success.
+        /// \return A pair, where the first element indicates if the domain
+        ///     contains any RRs at all (not only the requested, it may happen
+        ///     this is set to true, but the second part is empty). The second
+        ///     part is map from RRtypes to RRsets of the corresponding types.
+        ///     If the RRset is not present in DB, the RRtype is not there at
+        ///     all (so you'll not find NULL pointer in the result).
+        /// \throw DataSourceError If there's a low-level error with the
+        ///     database or the database contains bad data.
+        FoundRRsets getRRsets(const std::string& name,
+                              const WantedTypes& types, bool check_ns,
+                              const std::string* construct_name = NULL,
+                              bool any = false);
 
         /// \brief Search result of \c findDelegationPoint().
         ///
@@ -813,35 +859,6 @@ public:
             const size_t last_known; ///< No. labels in last non-empty domain
         };
 
-        /// \brief Searches database for RRsets of one domain.
-        ///
-        /// This method scans RRs of single domain specified by name and
-        /// extracts any RRsets found and requested by parameters.
-        ///
-        /// It is used internally by find(), because it is called multiple
-        /// times (usually with different domains).
-        ///
-        /// \param name Which domain name should be scanned.
-        /// \param types List of types the caller is interested in.
-        /// \param check_ns If this is set to true, it checks nothing lives
-        ///     together with NS record (with few little exceptions, like RRSIG
-        ///     or NSEC). This check is meant for non-apex NS records.
-        /// \param construct_name If this is NULL, the resulting RRsets have
-        ///     their name set to name. If it is not NULL, it overrides the name
-        ///     and uses this one (this can be used for wildcard synthesized
-        ///     records).
-        /// \return A pair, where the first element indicates if the domain
-        ///     contains any RRs at all (not only the requested, it may happen
-        ///     this is set to true, but the second part is empty). The second
-        ///     part is map from RRtypes to RRsets of the corresponding types.
-        ///     If the RRset is not present in DB, the RRtype is not there at
-        ///     all (so you'll not find NULL pointer in the result).
-        /// \throw DataSourceError If there's a low-level error with the
-        ///     database or the database contains bad data.
-        FoundRRsets getRRsets(const std::string& name,
-                              const WantedTypes& types, bool check_ns,
-                              const std::string* construct_name = NULL);
-
         /// \brief Find delegation point
         ///
         /// Given a name, searches through the superdomains from the origin
@@ -908,6 +925,9 @@ public:
         ///        for ZoneFinder::FindOptions.
         /// \param dresult Result of the search through the zone for a
         ///        delegation.
+        /// \param target If the type happens to be ANY, it will insert all
+        ///        the RRsets of the found name (if any is found) here instead
+        ///        of being returned by the result.
         ///
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
@@ -919,49 +939,54 @@ public:
         FindResult findWildcardMatch(
             const isc::dns::Name& name,
             const isc::dns::RRType& type, const FindOptions options,
-            const DelegationSearchResult& dresult);
+            const DelegationSearchResult& dresult,
+            std::vector<isc::dns::ConstRRsetPtr>* target);
 
-	/// \brief Handle matching results for name
-	///
-	/// This is called when something is found in the underlying database
-	/// whose domain name is an exact match of the name to be searched for.
-	/// It explores four possible cases to decide the final lookup result:
-	/// - The name is a zone cut due to an NS RR.
-	/// - CNAME is found (while the requested RR type is not CNAME).
-	///   In this case multiple CNAMEs are checked and rejected with
-	///   a \c DataSourceError exception.
-	/// - Requested type is not found at that name.
-	/// - A record of the requested type is found.
-	/// and returns a corresponding find result.
-	///
-	/// This method is commonly used for normal (non wildcard) and wildcard
-	/// matches.
-	///
+        /// \brief Handle matching results for name
+        ///
+        /// This is called when something is found in the underlying database
+        /// whose domain name is an exact match of the name to be searched for.
+        /// It explores four possible cases to decide the final lookup result:
+        /// - The name is a zone cut due to an NS RR.
+        /// - CNAME is found (while the requested RR type is not CNAME).
+        ///   In this case multiple CNAMEs are checked and rejected with
+        ///   a \c DataSourceError exception.
+        /// - Requested type is not found at that name.
+        /// - A record of the requested type is found, or the query is ANY and
+        ///   some records were found.
+        /// and returns a corresponding find result.
+        ///
+        /// This method is commonly used for normal (non wildcard) and wildcard
+        /// matches.
+        ///
         /// \param name The name to find
         /// \param type The RRType to find
         /// \param options Options about how to search. See the documentation
         ///        for ZoneFinder::FindOptions.
-	/// \param is_origin If name is the zone's origin name.
-	/// \param found A set of found RRsets in the search for the name
-	///        and type.  It could contain one or more of the requested
-	///        type, CNAME, NS, and NSEC RRsets of the name.
-	/// \param wildname If non NULL, the method is called on a wildcard
-	///                 match, and points to a string object representing
-	///                 a textual form of the matched wildcard name;
-	///                 it's NULL in the case of non wildcard match.
-	///
+        /// \param is_origin If name is the zone's origin name.
+        /// \param found A set of found RRsets in the search for the name
+        ///        and type.  It could contain one or more of the requested
+        ///        type, CNAME, NS, and NSEC RRsets of the name.
+        /// \param wildname If non NULL, the method is called on a wildcard
+        ///                 match, and points to a string object representing
+        ///                 a textual form of the matched wildcard name;
+        ///                 it's NULL in the case of non wildcard match.
+        /// \param target When the query is any, this must be set to a vector
+        ///    where the result will be stored.
+        ///
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
-	///         indicating the match type (corresponding to the each of
-	///         the above 4 cases).  The return value is intended to be
-	///         usable as a return value of the caller of this helper
-	///         method.
+        ///         indicating the match type (corresponding to the each of
+        ///         the above 4 cases).  The return value is intended to be
+        ///         usable as a return value of the caller of this helper
+        ///         method.
         FindResult findOnNameResult(const isc::dns::Name& name,
 				    const isc::dns::RRType& type,
 				    const FindOptions options,
 				    const bool is_origin,
 				    const FoundRRsets& found,
-				    const std::string* wildname);
+				    const std::string* wildname,
+                    std::vector<isc::dns::ConstRRsetPtr>* target);
 
         /// \brief Handle no match for name
         ///
@@ -983,6 +1008,9 @@ public:
         ///        for ZoneFinder::FindOptions.
         /// \param dresult Result of the search through the zone for a
         ///        delegation.
+        /// \param target If the query is for type ANY, the successfull result,
+        ///        if there happens to be one, will be returned through the
+        ///        parameter, as it doesn't fit into the result.
         ///
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
@@ -992,17 +1020,19 @@ public:
         FindResult findNoNameResult(const isc::dns::Name& name,
                                     const isc::dns::RRType& type,
                                     FindOptions options,
-                                    const DelegationSearchResult& dresult);
+                                    const DelegationSearchResult& dresult,
+                                    std::vector<isc::dns::ConstRRsetPtr>*
+                                    target);
 
         /// Logs condition and creates result
         ///
         /// A convenience function used by findOnNameResult(), it both creates
-	/// the FindResult object that find() will return to its caller as well
+        /// the FindResult object that find() will return to its caller as well
         /// as logging a debug message for the information being returned.
         ///
         /// \param name Domain name of the RR that was being sought.
         /// \param wildname Domain name string of a matched wildcard name or
-	/// NULL for non wildcard match.
+        /// NULL for non wildcard match.
         /// \param type Type of RR being sought.
         /// \param code Result of the find operation
         /// \param rrset RRset found as a result of the find (which may be
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index 01fb082..fd46896 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -78,6 +78,10 @@ different TTL values. This isn't allowed on the wire and is considered
 an error, so we set it to the lowest value we found (but we don't modify the
 database). The data in database should be checked and fixed.
 
+% DATASRC_DATABASE_FOUND_ANY search in datasource %1 resulted in returning all records of %2
+The data returned by the database backend contained data for the given domain
+name, so all the RRsets of the domain are returned.
+
 % DATASRC_DATABASE_FOUND_CNAME search in datasource %1 for %2/%3/%4 found CNAME, resulting in %5
 When searching the domain for a name a CNAME was found at that name.
 Even though it was not the RR type being sought, it is returned.  (The
@@ -178,6 +182,11 @@ whether the data is still valid.  The zone name, its class, and the
 underlying database name as well as the error message thrown from the
 database module are shown in the log message.
 
+% DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2
+The database doesn't contain directly matching name.  When searching
+for a wildcard match, a wildcard record matching the name of the query
+containing some RRsets was found. All the RRsets of the node are returned.
+
 % DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %3 because %2 contains NS (data source %1)
 The database was queried to provide glue data and it didn't find direct match.
 It could create it from given wildcard, but matching wildcards is forbidden
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index a79ee5b..d09eb6d 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -422,7 +422,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
 
     // Implementation of InMemoryZoneFinder::find
     FindResult find(const Name& name, RRType type,
-                    RRsetList* target, const FindOptions options) const
+                    std::vector<ConstRRsetPtr> *target,
+                    const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
             arg(type);
@@ -572,9 +573,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             for (found = node->getData()->begin();
                  found != node->getData()->end(); ++found)
             {
-                target->addRRset(
-                    boost::const_pointer_cast<RRset>(prepareRRset(name,
-                    found->second, rename)));
+                target->push_back(prepareRRset(name, found->second, rename));
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
@@ -629,9 +628,17 @@ InMemoryZoneFinder::getClass() const {
 
 ZoneFinder::FindResult
 InMemoryZoneFinder::find(const Name& name, const RRType& type,
-                 RRsetList* target, const FindOptions options)
+                 const FindOptions options)
+{
+    return (impl_->find(name, type, NULL, options));
+}
+
+ZoneFinder::FindResult
+InMemoryZoneFinder::findAll(const Name& name,
+                            std::vector<ConstRRsetPtr>& target,
+                            const FindOptions options)
 {
-    return (impl_->find(name, type, target, options));
+    return (impl_->find(name, RRType::ANY(), &target, options));
 }
 
 result::Result
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index b852eb3..32cf518 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -69,14 +69,20 @@ public:
     ///
     /// See documentation in \c Zone.
     ///
-    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET,
-    /// and also SUCCESS if target is not NULL(TYPE_ANY query).
-    /// (the base class documentation does not seem to require that).
+    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT);
 
+    /// \brief Version of find that returns all types at once
+    ///
+    /// It acts the same as find, just that when the correct node is found,
+    /// all the RRsets are filled into the target parameter instead of being
+    /// returned by the result.
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<isc::dns::ConstRRsetPtr>& target,
+                               const FindOptions options = FIND_DEFAULT);
+
     /// \brief Imelementation of the ZoneFinder::findPreviousName method
     ///
     /// This one throws NotImplemented exception, as InMemory doesn't
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index b6c098a..4757a45 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -295,15 +295,20 @@ private:
 // This is only to support NULL nodes.
 template <typename T>
 RBNode<T>::RBNode() :
-    parent_(this),
-    left_(this),
-    right_(this),
+    parent_(NULL),
+    left_(NULL),
+    right_(NULL),
     color_(BLACK),
     // dummy name, the value doesn't matter:
     name_(isc::dns::Name::ROOT_NAME()),
-    down_(this),
+    down_(NULL),
     flags_(0)
 {
+    // Some compilers object to use of "this" in initializer lists.
+    parent_ = this;
+    left_ = this;
+    right_ = this;
+    down_ = this;
 }
 
 template <typename T>
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 6dd6b0a..113d03c 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -17,6 +17,7 @@ endif
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
+noinst_PROGRAMS =
 if HAVE_GTEST
 TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
 
@@ -84,25 +85,7 @@ run_unittests_memory_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 run_unittests_memory_LDADD = $(common_ldadd)
 
-endif
-
-noinst_PROGRAMS = $(TESTS)
-
-EXTRA_DIST =  testdata/brokendb.sqlite3
-EXTRA_DIST += testdata/example.com.signed
-EXTRA_DIST += testdata/example.org
-EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
-EXTRA_DIST += testdata/mkbrokendb.c
-EXTRA_DIST += testdata/root.zone
-EXTRA_DIST += testdata/sql1.example.com.signed
-EXTRA_DIST += testdata/sql2.example.com.signed
-EXTRA_DIST += testdata/test-root.sqlite3
-EXTRA_DIST += testdata/test.sqlite3
-EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3
+noinst_PROGRAMS+= $(TESTS)
 
 # For the factory unit tests, we need to specify that we want
 # the loadable backend libraries from the build tree, and not from 
@@ -121,3 +104,21 @@ run_unittests_factory_LDADD = $(common_ldadd)
 check-local:
 	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests_factory
 endif
+
+endif
+
+EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/example.com.signed
+EXTRA_DIST += testdata/example.org
+EXTRA_DIST += testdata/example.org.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
+EXTRA_DIST += testdata/mkbrokendb.c
+EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/sql1.example.com.signed
+EXTRA_DIST += testdata/sql2.example.com.signed
+EXTRA_DIST += testdata/test-root.sqlite3
+EXTRA_DIST += testdata/test.sqlite3
+EXTRA_DIST += testdata/test.sqlite3.nodiffs
+EXTRA_DIST += testdata/rwtest.sqlite3
+EXTRA_DIST += testdata/diffs.sqlite3
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 920c9a2..c56ba40 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -41,7 +41,6 @@ using namespace isc::datasrc;
 using namespace std;
 // don't import the entire boost namespace.  It will unexpectedly hide uint32_t
 // for some systems.
-using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 using boost::lexical_cast;
 using namespace isc::dns;
@@ -229,9 +228,9 @@ public:
         }
     }
 
-    virtual shared_ptr<DatabaseAccessor> clone() {
+    virtual boost::shared_ptr<DatabaseAccessor> clone() {
         // This accessor is stateless, so we can simply return a new instance.
-        return (shared_ptr<DatabaseAccessor>(new NopAccessor));
+        return (boost::shared_ptr<DatabaseAccessor>(new NopAccessor));
     }
 
     virtual std::pair<bool, int> startUpdateZone(const std::string&, bool) {
@@ -311,7 +310,7 @@ struct JournalEntry {
     DatabaseAccessor::DiffOperation operation_;
     std::string data_[DatabaseAccessor::DIFF_PARAM_COUNT];
     bool operator==(const JournalEntry& other) const {
-        for (size_t i(0); i < DatabaseAccessor::DIFF_PARAM_COUNT; ++ i) {
+        for (size_t i = 0; i < DatabaseAccessor::DIFF_PARAM_COUNT; ++ i) {
             if (data_[i] != other.data_[i]) {
                 return false;
             }
@@ -350,8 +349,8 @@ public:
         fillData();
     }
 
-    virtual shared_ptr<DatabaseAccessor> clone() {
-        shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
+    virtual boost::shared_ptr<DatabaseAccessor> clone() {
+        boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
         cloned_accessor->readonly_records_ = &readonly_records_master_;
         cloned_accessor->update_records_ = &update_records_master_;
         cloned_accessor->empty_records_ = &empty_records_master_;
@@ -388,11 +387,11 @@ private:
             // 'hardcoded' names to trigger exceptions
             // On these names some exceptions are thrown, to test the robustness
             // of the find() method.
-            if (searched_name_ == "dsexception.in.search.") {
+            if (searched_name_ == "dsexception.example.org.") {
                 isc_throw(DataSourceError, "datasource exception on search");
-            } else if (searched_name_ == "iscexception.in.search.") {
+            } else if (searched_name_ == "iscexception.example.org.") {
                 isc_throw(isc::Exception, "isc exception on search");
-            } else if (searched_name_ == "basicexception.in.search.") {
+            } else if (searched_name_ == "basicexception.example.org.") {
                 throw std::exception();
             }
 
@@ -406,7 +405,7 @@ private:
                 cur_name.clear();
                 // Just walk everything and check if it is a subdomain.
                 // If it is, just copy all data from there.
-                for (Domains::const_iterator i(cur_records.begin());
+                for (Domains::const_iterator i = cur_records.begin();
                      i != cur_records.end(); ++i) {
                     const Name local(i->first);
                     if (local.compare(Name(name)).getRelation() ==
@@ -421,11 +420,12 @@ private:
         }
 
         virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
-            if (searched_name_ == "dsexception.in.getnext.") {
+            if (searched_name_ == "dsexception.getnext.example.org.") {
                 isc_throw(DataSourceError, "datasource exception on getnextrecord");
-            } else if (searched_name_ == "iscexception.in.getnext.") {
+            } else if (searched_name_ == "iscexception.getnext.example.org.") {
                 isc_throw(isc::Exception, "isc exception on getnextrecord");
-            } else if (searched_name_ == "basicexception.in.getnext.") {
+            } else if (searched_name_ ==
+                       "basicexception.getnext.example.org.") {
                 throw std::exception();
             }
 
@@ -442,7 +442,7 @@ private:
 
     private:
         const std::string searched_name_;
-        int cur_record_;
+        size_t cur_record_;
         std::vector< std::vector<std::string> > cur_name;
     };
 
@@ -706,7 +706,7 @@ public:
     }
 
     // This allows the test code to get the accessor used in an update context
-    shared_ptr<const MockAccessor> getLatestClone() const {
+    boost::shared_ptr<const MockAccessor> getLatestClone() const {
         return (latest_clone_);
     }
 
@@ -807,7 +807,7 @@ public:
         // Clean the journal, but keep local copy to check
         journal.swap(*journal_entries_);
         ASSERT_EQ(expected.size(), journal.size());
-        for (size_t i(0); i < expected.size(); ++ i) {
+        for (size_t i = 0; i < expected.size(); ++ i) {
             EXPECT_TRUE(expected[i] == journal[i]);
         }
     }
@@ -883,7 +883,7 @@ private:
         ASSERT_EQ(0, readonly_records_->count(name));
         // Append the name to all of them
         for (std::vector<std::vector<std::string> >::iterator
-             i(cur_name_.begin()); i != cur_name_.end(); ++ i) {
+             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
             i->push_back(name);
         }
         (*readonly_records_)[name] = cur_name_;
@@ -992,7 +992,7 @@ public:
         current_accessor_ = new ACCESSOR_TYPE();
         is_mock_ = (dynamic_cast<MockAccessor*>(current_accessor_) != NULL);
         client_.reset(new DatabaseClient(qclass_,
-                                         shared_ptr<ACCESSOR_TYPE>(
+                                         boost::shared_ptr<ACCESSOR_TYPE>(
                                              current_accessor_)));
     }
 
@@ -1002,9 +1002,9 @@ public:
      */
     void checkZoneFinder(const DataSourceClient::FindResult& zone) {
         ASSERT_NE(ZoneFinderPtr(), zone.zone_finder) << "No zone finder";
-        shared_ptr<DatabaseClient::Finder> finder(
+        boost::shared_ptr<DatabaseClient::Finder> finder(
             dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
-        ASSERT_NE(shared_ptr<DatabaseClient::Finder>(), finder) <<
+        ASSERT_NE(boost::shared_ptr<DatabaseClient::Finder>(), finder) <<
             "Wrong type of finder";
         if (is_mock_) {
             EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
@@ -1012,10 +1012,10 @@ public:
         EXPECT_EQ(current_accessor_, &finder->getAccessor());
     }
 
-    shared_ptr<DatabaseClient::Finder> getFinder() {
+    boost::shared_ptr<DatabaseClient::Finder> getFinder() {
         DataSourceClient::FindResult zone(client_->findZone(zname_));
         EXPECT_EQ(result::SUCCESS, zone.code);
-        shared_ptr<DatabaseClient::Finder> finder(
+        boost::shared_ptr<DatabaseClient::Finder> finder(
             dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
         if (is_mock_) {
             EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
@@ -1102,11 +1102,11 @@ public:
 
     // Will be deleted by client_, just keep the current value for comparison.
     ACCESSOR_TYPE* current_accessor_;
-    shared_ptr<DatabaseClient> client_;
+    boost::shared_ptr<DatabaseClient> client_;
     const std::string database_name_;
 
     // The zone finder of the test zone commonly used in various tests.
-    shared_ptr<DatabaseClient::Finder> finder_;
+    boost::shared_ptr<DatabaseClient::Finder> finder_;
 
     // Some shortcut variables for commonly used test parameters
     const Name zname_; // the zone name stored in the test data source
@@ -1120,7 +1120,7 @@ public:
 
     // update related objects to be tested
     ZoneUpdaterPtr updater_;
-    shared_ptr<const DatabaseAccessor> update_accessor_;
+    boost::shared_ptr<const DatabaseAccessor> update_accessor_;
 
     // placeholders
     const std::vector<std::string> empty_rdatas_; // for NXRRSET/NXDOMAIN
@@ -1194,7 +1194,7 @@ TEST(GenericDatabaseClientTest, noAccessorException) {
     // We need a dummy variable here; some compiler would regard it a mere
     // declaration instead of an instantiation and make the test fail.
     EXPECT_THROW(DatabaseClient dummy(RRClass::IN(),
-                                      shared_ptr<DatabaseAccessor>()),
+                                      boost::shared_ptr<DatabaseAccessor>()),
                  isc::InvalidParameter);
 }
 
@@ -1215,8 +1215,9 @@ TEST(GenericDatabaseClientTest, noZoneNotImplementedIterator) {
 }
 
 TEST(GenericDatabaseClientTest, notImplementedIterator) {
-    EXPECT_THROW(DatabaseClient(RRClass::IN(), shared_ptr<DatabaseAccessor>(
-        new NopAccessor())).getIterator(Name("example.org")),
+    EXPECT_THROW(DatabaseClient(RRClass::IN(),
+                    boost::shared_ptr<DatabaseAccessor>(
+                        new NopAccessor())).getIterator(Name("example.org")),
                  isc::NotImplemented);
 }
 
@@ -1419,8 +1420,7 @@ doFindTest(ZoneFinder& finder,
            const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
 {
     SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
-    const ZoneFinder::FindResult result = finder.find(name, type, NULL,
-                                                      options);
+    const ZoneFinder::FindResult result = finder.find(name, type, options);
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
     if (!expected_rdatas.empty() && result.rrset) {
         checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
@@ -1444,6 +1444,39 @@ doFindTest(ZoneFinder& finder,
     }
 }
 
+void
+doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
+                    ZoneFinder::Result expected_result,
+                    const isc::dns::RRType expected_type,
+                    std::vector<std::string> expected_rdata,
+                    const isc::dns::Name& expected_name =
+                    isc::dns::Name::ROOT_NAME(),
+                    const ZoneFinder::FindOptions options =
+                    ZoneFinder::FIND_DEFAULT)
+{
+    SCOPED_TRACE("All test for " + name.toText());
+    std::vector<ConstRRsetPtr> target;
+    ZoneFinder::FindResult result(finder.findAll(name, target, options));
+    EXPECT_TRUE(target.empty());
+    EXPECT_EQ(expected_result, result.code);
+    EXPECT_EQ(expected_type, result.rrset->getType());
+    RdataIteratorPtr it(result.rrset->getRdataIterator());
+    std::vector<std::string> rdata;
+    while (!it->isLast()) {
+        rdata.push_back(it->getCurrent().toText());
+        it->next();
+    }
+    std::sort(rdata.begin(), rdata.end());
+    std::sort(expected_rdata.begin(), expected_rdata.end());
+    ASSERT_EQ(expected_rdata.size(), rdata.size());
+    for (size_t i(0); i < expected_rdata.size(); ++ i) {
+        EXPECT_EQ(expected_rdata[i], rdata[i]);
+    }
+    EXPECT_TRUE(expected_rdata == rdata);
+    EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
+              expected_name, result.rrset->getName());
+}
+
 // When asking for an RRset where RRs somehow have different TTLs, it should 
 // convert to the lowest one.
 TEST_F(MockDatabaseClientTest, ttldiff) {
@@ -1499,7 +1532,7 @@ TEST_F(MockDatabaseClientTest, ttldiff_separate_rrs) {
 }
 
 TYPED_TEST(DatabaseClientTest, find) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.clear();
     this->expected_sig_rdatas_.clear();
@@ -1670,58 +1703,58 @@ TYPED_TEST(DatabaseClientTest, find) {
 
     EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 
     // Trigger the hardcoded exceptions and see if find() has cleaned up
     if (this->is_mock_) {
-        EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."),
+        EXPECT_THROW(finder->find(Name("dsexception.example.org."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      DataSourceError);
-        EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."),
+        EXPECT_THROW(finder->find(Name("iscexception.example.org."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      isc::Exception);
-        EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."),
+        EXPECT_THROW(finder->find(Name("basicexception.example.org."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      std::exception);
-        EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."),
+        EXPECT_THROW(finder->find(Name("dsexception.getnext.example.org"),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      DataSourceError);
-        EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."),
+        EXPECT_THROW(finder->find(Name("iscexception.getnext.example.org."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      isc::Exception);
-        EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."),
+        EXPECT_THROW(finder->find(Name("basicexception.getnext.example.org."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      std::exception);
     }
 
@@ -1737,8 +1770,43 @@ TYPED_TEST(DatabaseClientTest, find) {
                this->expected_rdatas_, this->expected_sig_rdatas_);
 }
 
+TYPED_TEST(DatabaseClientTest, findOutOfZone) {
+    // If the query name is out-of-zone it should result in NXDOMAIN
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    vector<ConstRRsetPtr> target;
+
+    // Superdomain
+    doFindTest(*finder, Name("org"), this->qtype_, this->qtype_,
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->empty_rdatas_, this->empty_rdatas_);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target).code);
+    // sharing a common ancestor
+    doFindTest(*finder, Name("noexample.org"), this->qtype_, this->qtype_,
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->empty_rdatas_, this->empty_rdatas_);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("noexample.org"),
+                                                    target).code);
+    // totally unrelated domain, smaller number of labels
+    doFindTest(*finder, Name("com"), this->qtype_, this->qtype_,
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->empty_rdatas_, this->empty_rdatas_);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target).code);
+    // totally unrelated domain, same number of labels
+    doFindTest(*finder, Name("example.com"), this->qtype_, this->qtype_,
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->empty_rdatas_, this->empty_rdatas_);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("example.com"),
+                                                    target).code);
+    // totally unrelated domain, larger number of labels
+    doFindTest(*finder, Name("more.example.com"), this->qtype_, this->qtype_,
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->empty_rdatas_, this->empty_rdatas_);
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("more.example.com"),
+                                                    target).code);
+}
+
 TYPED_TEST(DatabaseClientTest, findDelegation) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     // The apex should not be considered delegation point and we can access
     // data
@@ -1840,23 +1908,23 @@ TYPED_TEST(DatabaseClientTest, findDelegation) {
 
     // This is broken dname, it contains two targets
     EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 
     // Broken NS - it lives together with something else
     EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 }
 
 TYPED_TEST(DatabaseClientTest, emptyDomain) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     // This domain doesn't exist, but a subdomain of it does.
     // Therefore we should pretend the domain is there, but contains no RRsets
@@ -1868,7 +1936,7 @@ TYPED_TEST(DatabaseClientTest, emptyDomain) {
 // Glue-OK mode. Just go through NS delegations down (but not through
 // DNAME) and pretend it is not there.
 TYPED_TEST(DatabaseClientTest, glueOK) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.clear();
     this->expected_sig_rdatas_.clear();
@@ -1925,7 +1993,7 @@ TYPED_TEST(DatabaseClientTest, glueOK) {
 }
 
 TYPED_TEST(DatabaseClientTest, wildcard) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     // First, simple wildcard match
     // Check also that the RRSIG is added from the wildcard (not modified)
@@ -2018,7 +2086,7 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
         "wild.*.foo.*.bar.example.org.",
         NULL
     };
-    for (const char** name(positive_names); *name != NULL; ++ name) {
+    for (const char** name = positive_names; *name != NULL; ++ name) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
                    this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
                    this->expected_rdatas_,
@@ -2043,7 +2111,7 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     };
     // Unless FIND_DNSSEC is specified, this is no different from other
     // NXRRSET case.
-    for (const char** name(negative_names); *name != NULL; ++ name) {
+    for (const char** name = negative_names; *name != NULL; ++ name) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
                    this->qtype_, this->rrttl_, ZoneFinder::NXRRSET,
                    this->expected_rdatas_, this->expected_sig_rdatas_);
@@ -2059,7 +2127,7 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
     this->expected_rdatas_.clear();
     this->expected_rdatas_.push_back("wild.*.foo.*.bar.example.org. NSEC");
     this->expected_sig_rdatas_.clear();
-    for (const char** name(negative_dnssec_names); *name != NULL; ++ name) {
+    for (const char** name = negative_dnssec_names; *name != NULL; ++ name) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
                    RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
                    this->expected_rdatas_, this->expected_sig_rdatas_,
@@ -2098,7 +2166,7 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
 TYPED_TEST(DatabaseClientTest, noWildcard) {
     // Tests with the NO_WILDCARD flag.
 
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     // This would match *.wild.example.org, but with NO_WILDCARD should
     // result in NXDOMAIN.
@@ -2159,7 +2227,7 @@ TYPED_TEST(DatabaseClientTest, noWildcard) {
 TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
     // The domain exists, but doesn't have this RRType
     // So we should get its NSEC
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
     this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
@@ -2178,7 +2246,7 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
     //
     // The user will have to query us again to get the correct
     // answer (eg. prove there's not an exact match)
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.push_back("cancel.here.wild.example.org. A NSEC "
                                      "RRSIG");
@@ -2195,7 +2263,7 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
 
 TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
     // The domain doesn't exist, so we must get the right NSEC
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
     this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
@@ -2232,7 +2300,7 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
 
 TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
     // Same as NXDOMAIN_NSEC, but with empty non-terminal
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC");
     doFindTest(*finder, isc::dns::Name("nonterminal.example.org."),
@@ -2255,11 +2323,105 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
                                Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
+TYPED_TEST(DatabaseClientTest, anyFromFind) {
+    // Find will reject answering an ANY query
+    EXPECT_THROW(this->getFinder()->find(isc::dns::Name("www2.example.org."),
+                                         RRType::ANY()), isc::Unexpected);
+}
+
+// Test the findAll method.
+TYPED_TEST(DatabaseClientTest, getAll) {
+    // The domain doesn't exist, so we must get the right NSEC
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    // It should act the same on the "failures"
+    std::vector<ConstRRsetPtr> target;
+    EXPECT_EQ(ZoneFinder::NXDOMAIN,
+              finder->findAll(isc::dns::Name("nothere.example.org."),
+                              target).code);
+    EXPECT_TRUE(target.empty());
+    EXPECT_EQ(ZoneFinder::NXRRSET,
+              finder->findAll(isc::dns::Name("here.wild.example.org."),
+                              target).code);
+    this->expected_rdatas_.push_back("ns.delegation.example.org.");
+    this->expected_rdatas_.push_back("ns.example.com.");
+    doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
+                        ZoneFinder::DELEGATION, RRType::NS(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("delegation.example.org."));
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("www.example.org.");
+    doFindAllTestResult(*finder, isc::dns::Name("cname.example.org"),
+                        ZoneFinder::CNAME, RRType::CNAME(),
+                        this->expected_rdatas_);
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("dname.example.com.");
+    doFindAllTestResult(*finder, isc::dns::Name("a.dname.example.org"),
+                        ZoneFinder::DNAME, RRType::DNAME(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("dname.example.org."));
+    // It should get the data on success
+    EXPECT_EQ(ZoneFinder::SUCCESS,
+              finder->findAll(isc::dns::Name("www2.example.org."),
+                              target).code);
+    ASSERT_EQ(2, target.size());
+    size_t a_idx(target[1]->getType() == RRType::A());
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    std::string previous;
+    size_t count(0);
+    for (RdataIteratorPtr it(target[a_idx]->getRdataIterator());
+         !it->isLast(); it->next()) {
+        count ++;
+        EXPECT_NE(previous, it->getCurrent().toText());
+        EXPECT_TRUE(it->getCurrent().toText() == "192.0.2.1" ||
+                    it->getCurrent().toText() == "192.0.2.2");
+        previous = it->getCurrent().toText();
+    }
+    EXPECT_EQ(2, count);
+    EXPECT_EQ(RRType::AAAA(), target[1 - a_idx]->getType());
+    RdataIteratorPtr it(target[1 - a_idx]->getRdataIterator());
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("2001:db8::1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // And on wildcard. Check the signatures as well.
+    target.clear();
+    EXPECT_EQ(ZoneFinder::WILDCARD,
+              finder->findAll(isc::dns::Name("a.wild.example.org"),
+                              target, ZoneFinder::FIND_DNSSEC).code);
+    ASSERT_EQ(2, target.size());
+    a_idx = target[1]->getType() == RRType::A();
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    it = target[a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("192.0.2.5", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    ConstRRsetPtr sig(target[a_idx]->getRRsig());
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+    EXPECT_EQ(RRType::NSEC(), target[1 - a_idx]->getType());
+    it = target[1 - a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("cancel.here.wild.example.org. A RRSIG NSEC",
+              it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    sig = target[1 - a_idx]->getRRsig();
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+}
+
 TYPED_TEST(DatabaseClientTest, getOrigin) {
     DataSourceClient::FindResult
         zone(this->client_->findZone(Name("example.org")));
     ASSERT_EQ(result::SUCCESS, zone.code);
-    shared_ptr<DatabaseClient::Finder> finder(
+    boost::shared_ptr<DatabaseClient::Finder> finder(
         dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
     if (this->is_mock_) {
         EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
@@ -2301,7 +2463,7 @@ TYPED_TEST(DatabaseClientTest, updaterFinder) {
 
 TYPED_TEST(DatabaseClientTest, flushZone) {
     // A simple update case: flush the entire zone
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     // Before update, the name exists.
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
@@ -2420,7 +2582,7 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
 
 TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) {
     // Similar to the previous test, but not replacing the existing data.
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->updater_ = this->client_->getUpdater(this->zname_, false);
     this->updater_->addRRset(*this->rrset_);
@@ -2547,12 +2709,13 @@ TYPED_TEST(DatabaseClientTest, addDeviantRR) {
     this->expected_rdatas_.clear();
     this->expected_rdatas_.push_back("192.0.2.100");
     {
-        // Note: with the find() implementation being more strict about
-        // zone cuts, this test may fail.  Then the test should be updated.
+        // Note: find() rejects out-of-zone query name with NXDOMAIN
+        // regardless of whether adding the RR succeeded, so this check
+        // actually doesn't confirm it.
         SCOPED_TRACE("add out-of-zone RR");
         doFindTest(this->updater_->getFinder(), Name("example.com"),
                    this->qtype_, this->qtype_, this->rrttl_,
-                   ZoneFinder::SUCCESS, this->expected_rdatas_,
+                   ZoneFinder::NXDOMAIN, this->empty_rdatas_,
                    this->empty_rdatas_);
     }
 }
@@ -2577,7 +2740,7 @@ TYPED_TEST(DatabaseClientTest, addRRsetWithRRSIG) {
 }
 
 TYPED_TEST(DatabaseClientTest, deleteRRset) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     this->rrset_.reset(new RRset(this->qname_, this->qclass_, this->qtype_,
                                  this->rrttl_));
@@ -2849,7 +3012,7 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
 
     // Commit the changes, confirm the entire changes applied.
     this->updater_->commit();
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
     this->expected_rdatas_.clear();
     this->expected_rdatas_.push_back("192.0.2.2");
     this->expected_rdatas_.push_back("192.0.2.1");
@@ -2865,7 +3028,7 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
 }
 
 TYPED_TEST(DatabaseClientTest, previous) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     EXPECT_EQ(Name("www.example.org."),
               finder->findPreviousName(Name("www2.example.org.")));
@@ -2892,7 +3055,7 @@ TYPED_TEST(DatabaseClientTest, previous) {
 }
 
 TYPED_TEST(DatabaseClientTest, invalidRdata) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
                  DataSourceError);
@@ -2901,7 +3064,7 @@ TYPED_TEST(DatabaseClientTest, invalidRdata) {
 }
 
 TEST_F(MockDatabaseClientTest, missingNSEC) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     /*
      * FIXME: For now, we can't really distinguish this bogus input
@@ -2919,7 +3082,7 @@ TEST_F(MockDatabaseClientTest, missingNSEC) {
 }
 
 TEST_F(MockDatabaseClientTest, badName) {
-    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
 
     EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")),
                  DataSourceError);
@@ -2973,7 +3136,7 @@ TYPED_TEST(DatabaseClientTest, journalMultiple) {
     this->updater_ = this->client_->getUpdater(this->zname_, false, true);
     std::string soa_rdata = "ns1.example.org. admin.example.org. "
         "1234 3600 1800 2419200 7200";
-    for (size_t i(1); i < 100; ++ i) {
+    for (size_t i = 1; i < 100; ++ i) {
         // Remove the old SOA
         this->updater_->deleteRRset(*this->soa_);
         expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234 + i - 1,
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a1bd94e..8a5a8db 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include <boost/bind.hpp>
+#include <boost/foreach.hpp>
 
 #include <exceptions/exceptions.h>
 
@@ -389,7 +390,6 @@ public:
                   ZoneFinder::Result result,
                   bool check_answer = true,
                   const ConstRRsetPtr& answer = ConstRRsetPtr(),
-                  RRsetList* target = NULL,
                   InMemoryZoneFinder* zone_finder = NULL,
                   ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
                   bool check_wild_answer = false)
@@ -402,7 +402,7 @@ public:
         EXPECT_NO_THROW({
                 ZoneFinder::FindResult find_result(zone_finder->find(
                                                        name, rrtype,
-                                                       target, options));
+                                                       options));
                 // Check it returns correct answers
                 EXPECT_EQ(result, find_result.code);
                 if (check_answer) {
@@ -438,6 +438,32 @@ public:
                 }
             });
     }
+    /**
+     * \brief Calls the findAll on the finder and checks the result.
+     */
+    std::vector<ConstRRsetPtr> findAllTest(const Name& name,
+                                           ZoneFinder::Result result,
+                                           size_t expected_size,
+                                           InMemoryZoneFinder* finder = NULL,
+                                           const ConstRRsetPtr &rrset_result =
+                                           ConstRRsetPtr(),
+                                           ZoneFinder::FindOptions options =
+                                           ZoneFinder::FIND_DEFAULT)
+    {
+        if (finder == NULL) {
+            finder = &zone_finder_;
+        }
+        std::vector<ConstRRsetPtr> target;
+        ZoneFinder::FindResult findResult(finder->findAll(name, target,
+                                                          options));
+        EXPECT_EQ(result, findResult.code);
+        EXPECT_EQ(rrset_result, findResult.rrset);
+        BOOST_FOREACH(const ConstRRsetPtr& rrset, target) {
+            EXPECT_EQ(name, rrset->getName());
+        }
+        EXPECT_EQ(expected_size, target.size());
+        return (target);
+    }
     // Internal part of the cancelWildcard test that is multiple times
     void doCancelWildcardTest();
 };
@@ -522,7 +548,7 @@ TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
                                            RRTTL(300)));
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_));
     findTest(Name("cname.child.example.org"), RRType::AAAA(),
-             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL, NULL,
+             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 }
 
@@ -598,7 +624,7 @@ TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
 
     findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
     findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
-             NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             NULL, ZoneFinder::FIND_GLUE_OK);
 }
 
 // Test adding child zones and zone cut handling
@@ -636,25 +662,19 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
 
     // origin
-    RRsetList origin_rrsets;
-    findTest(origin_, RRType::ANY(), ZoneFinder::SUCCESS, true,
-             ConstRRsetPtr(), &origin_rrsets);
-    EXPECT_EQ(2, origin_rrsets.size());
-    EXPECT_EQ(rr_a_, origin_rrsets.findRRset(RRType::A(), RRClass::IN()));
-    EXPECT_EQ(rr_ns_, origin_rrsets.findRRset(RRType::NS(), RRClass::IN()));
+    std::vector<ConstRRsetPtr> rrsets(findAllTest(origin_, ZoneFinder::SUCCESS,
+                                                  2));
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_a_));
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_ns_));
 
     // out zone name
-    RRsetList out_rrsets;
-    findTest(Name("example.com"), RRType::ANY(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), &out_rrsets);
-    EXPECT_EQ(0, out_rrsets.size());
-
-    RRsetList glue_child_rrsets;
-    findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::SUCCESS,
-             true, ConstRRsetPtr(), &glue_child_rrsets);
-    EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
-                                                     RRClass::IN()));
-    EXPECT_EQ(1, glue_child_rrsets.size());
+    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN, 0);
+
+    rrsets = findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, 1);
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_child_glue_));
 
     // TODO: test NXRRSET case after rbtree non-terminal logic has
     // been implemented
@@ -663,16 +683,12 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
 
     // zone cut
-    RRsetList child_rrsets;
-    findTest(rr_child_ns_->getName(), RRType::ANY(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_, &child_rrsets);
-    EXPECT_EQ(0, child_rrsets.size());
+    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION, 0, NULL,
+                rr_child_ns_);
 
     // glue for this zone cut
-    RRsetList new_glue_child_rrsets;
-    findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_, &new_glue_child_rrsets);
-    EXPECT_EQ(0, new_glue_child_rrsets.size());
+    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION, 0, NULL,
+                rr_child_ns_);
 }
 
 TEST_F(InMemoryZoneFinderTest, glue) {
@@ -693,26 +709,26 @@ TEST_F(InMemoryZoneFinderTest, glue) {
 
     // If we do it in the "glue OK" mode, we should find the exact match.
     findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
-             rr_child_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             rr_child_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXRRSET case
     findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
-             true, ConstRRsetPtr(), NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             true, ConstRRsetPtr(), NULL, ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXDOMAIN case
     findTest(Name("www.child.example.org"), RRType::A(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 
     // nested cut case.  The glue should be found.
     findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
              ZoneFinder::SUCCESS,
-             true, rr_grandchild_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             true, rr_grandchild_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
 
     // A non-existent name in nested cut.  This should result in delegation
     // at the highest zone cut.
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 }
 
@@ -801,14 +817,14 @@ TEST_F(InMemoryZoneFinderTest, load) {
 
     // Now see there are some rrsets (we don't look inside, though)
     findTest(Name("."), RRType::SOA(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
     findTest(Name("."), RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
     findTest(Name("a.root-servers.net."), RRType::A(), ZoneFinder::SUCCESS,
-             false, ConstRRsetPtr(), NULL, &rootzone);
+             false, ConstRRsetPtr(), &rootzone);
     // But this should no longer be here
     findTest(rr_ns_a_->getName(), RRType::AAAA(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
@@ -846,14 +862,14 @@ TEST_F(InMemoryZoneFinderTest, wildcard) {
     {
         SCOPED_TRACE("Search at created child");
         findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 false, rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true);
+                 false, rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
     }
 
     // Search another created name, this time little bit lower
     {
         SCOPED_TRACE("Search at created grand-child");
         findTest(Name("a.b.wild.example.org"), RRType::A(),
-                 ZoneFinder::SUCCESS, false, rr_wild_, NULL, NULL,
+                 ZoneFinder::SUCCESS, false, rr_wild_, NULL,
                  ZoneFinder::FIND_DEFAULT, true);
     }
 
@@ -885,7 +901,7 @@ TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
     {
         SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
         findTest(Name("a.child.example.org"), RRType::A(),
-                 ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+                 ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
                  ZoneFinder::FIND_GLUE_OK);
     }
 }
@@ -897,9 +913,9 @@ TEST_F(InMemoryZoneFinderTest, anyWildcard) {
     // First try directly the name (normal match)
     {
         SCOPED_TRACE("Asking direcly for *");
-        RRsetList target;
-        findTest(Name("*.wild.example.org"), RRType::ANY(),
-                 ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target);
+        const std::vector<ConstRRsetPtr>
+            target(findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
+                               1));
         ASSERT_EQ(1, target.size());
         EXPECT_EQ(RRType::A(), (*target.begin())->getType());
         EXPECT_EQ(Name("*.wild.example.org"), (*target.begin())->getName());
@@ -908,10 +924,9 @@ TEST_F(InMemoryZoneFinderTest, anyWildcard) {
     // Then a wildcard match
     {
         SCOPED_TRACE("Asking in the wild way");
-        RRsetList target;
-        findTest(Name("a.wild.example.org"), RRType::ANY(),
-                 ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target);
-        ASSERT_EQ(1, target.size());
+        const std::vector<ConstRRsetPtr>
+            target(findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
+                               1));
         EXPECT_EQ(RRType::A(), (*target.begin())->getType());
         EXPECT_EQ(Name("a.wild.example.org"), (*target.begin())->getName());
     }
@@ -943,15 +958,9 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
 
     {
         SCOPED_TRACE("Asking for ANY record");
-        RRsetList normalTarget;
-        findTest(Name("*.foo.example.org"), RRType::ANY(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), &normalTarget);
-        EXPECT_EQ(0, normalTarget.size());
-
-        RRsetList wildTarget;
-        findTest(Name("a.foo.example.org"), RRType::ANY(),
-                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(), &wildTarget);
-        EXPECT_EQ(0, wildTarget.size());
+        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET, 0);
+
+        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, 0);
     }
 
     {
@@ -981,7 +990,7 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
             NULL
         };
 
-        for (const char** name(names); *name != NULL; ++ name) {
+        for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
             findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
         }
@@ -999,7 +1008,7 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
     {
         SCOPED_TRACE("Asking directly for A on parent nodes");
 
-        for (const char** name(names); *name != NULL; ++ name) {
+        for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
             findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
         }
@@ -1008,13 +1017,10 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
     {
         SCOPED_TRACE("Asking for ANY on parent nodes");
 
-        for (const char** name(names); *name != NULL; ++ name) {
+        for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
 
-            RRsetList target;
-            findTest(Name(*name), RRType::ANY(), ZoneFinder::NXRRSET, true,
-                ConstRRsetPtr(), &target);
-            EXPECT_EQ(0, target.size());
+            findAllTest(Name(*name), ZoneFinder::NXRRSET, 0);
         }
     }
 }
@@ -1050,11 +1056,11 @@ InMemoryZoneFinderTest::doCancelWildcardTest() {
             NULL
         };
 
-        for (const char** name(names); *name != NULL; ++ name) {
+        for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
 
             findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
-                     rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true);
+                     rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
         }
     }
 
@@ -1125,13 +1131,13 @@ TEST_F(InMemoryZoneFinderTest, swap) {
     EXPECT_EQ(RRClass::IN(), finder2.getClass());
     // make sure the zone data is swapped, too
     findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), NULL, &finder1);
+             ConstRRsetPtr(), &finder1);
     findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &finder1);
+             ConstRRsetPtr(), &finder1);
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &finder2);
+             ConstRRsetPtr(), &finder2);
     findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), NULL, &finder2);
+             ConstRRsetPtr(), &finder2);
 }
 
 TEST_F(InMemoryZoneFinderTest, getFileName) {
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 61341f6..5122136 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -29,7 +29,6 @@
 
 using namespace std;
 using namespace isc::datasrc;
-using boost::shared_ptr;
 using boost::lexical_cast;
 using isc::data::ConstElementPtr;
 using isc::data::Element;
@@ -527,7 +526,7 @@ TEST_F(SQLite3Create, lockedtest) {
 }
 
 TEST_F(SQLite3AccessorTest, clone) {
-    shared_ptr<DatabaseAccessor> cloned = accessor->clone();
+    boost::shared_ptr<DatabaseAccessor> cloned = accessor->clone();
     EXPECT_EQ(accessor->getDBName(), cloned->getDBName());
 
     // The cloned accessor should have a separate connection and search
@@ -604,7 +603,7 @@ protected:
     vector<const char* const*> empty_stored; // indicate no corresponding data
 
     // Another accessor, emulating one running on a different process/thread
-    shared_ptr<SQLite3Accessor> another_accessor;
+    boost::shared_ptr<SQLite3Accessor> another_accessor;
     DatabaseAccessor::IteratorContextPtr iterator;
 };
 
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index e028bea..b922662 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -237,13 +237,6 @@ public:
     ///   a successful match, and the code of \c SUCCESS will be returned.
     /// - If the search name matches a delegation point of DNAME, it returns
     ///   the code of \c DNAME and that DNAME RR.
-    /// - If the target isn't NULL, all RRsets under the domain are inserted
-    ///   there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned
-    ///   instead of normall processing. This is intended to handle ANY query.
-    ///
-    /// \note This behavior is controversial as we discussed in
-    /// https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html
-    /// We should revisit the interface before we heavily rely on it.
     ///
     /// The \c options parameter specifies customized behavior of the search.
     /// Their semantics is as follows (they are or bit-field):
@@ -264,6 +257,16 @@ public:
     ///   proof of the non existence of any matching wildcard or non existence
     ///   of an exact match when a wildcard match is found.
     ///
+    /// In general, \c name is expected to be included in the zone, that is,
+    /// it should be equal to or a subdomain of the zone origin.  Otherwise
+    /// this method will return \c NXDOMAIN with an empty RRset.  But such a
+    /// case should rather be considered a caller's bug.
+    ///
+    /// \note For this reason it's probably better to throw an exception
+    /// than returning \c NXDOMAIN.  This point should be revisited in a near
+    /// future version.  In any case applications shouldn't call this method
+    /// for an out-of-zone name.
+    ///
     /// \exception std::bad_alloc Memory allocation such as for constructing
     ///  the resulting RRset fails
     /// \exception DataSourceError Derived class specific exception, e.g.
@@ -276,16 +279,33 @@ public:
     ///
     /// \param name The domain name to be searched for.
     /// \param type The RR type to be searched for.
-    /// \param target If target is not NULL, insert all RRs under the domain
-    /// into it.
     /// \param options The search options.
     /// \return A \c FindResult object enclosing the search result (see above).
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options
                             = FIND_DEFAULT) = 0;
 
+    ///
+    /// \brief Finds all RRsets in the given name.
+    ///
+    /// This function works almost exactly in the same way as the find one. The
+    /// only difference is, when the lookup is successful (eg. the code is
+    /// SUCCESS or WILDCARD), all the RRsets residing in the named node are
+    /// copied into the \c target parameter and the rrset member of the result
+    /// is NULL. All the other (unsuccessful) cases are handled the same,
+    /// including returning delegations, NSEC/NSEC3 proofs, etc. The options
+    /// parameter works the same way and it should conform to the same exception
+    /// restrictions.
+    ///
+    /// \param name \see find, parameter name
+    /// \param target the successfull result is returned through this
+    /// \param options \see find, parameter options
+    /// \return \see find and it's result
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<isc::dns::ConstRRsetPtr> &target,
+                               const FindOptions options = FIND_DEFAULT) = 0;
+
     /// \brief Get previous name in the zone
     ///
     /// Gets the previous name in the DNSSEC order. This can be used
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 99ee112..5eada15 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -11,6 +11,8 @@ lib_LTLIBRARIES = libdhcp++.la
 libdhcp___la_SOURCES  =
 libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
 libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
+libdhcp___la_SOURCES += iface_mgr_linux.cc
+libdhcp___la_SOURCES += iface_mgr_bsd.cc
 libdhcp___la_SOURCES += option.cc option.h
 libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
 libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index 98381ac..e058960 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -157,7 +157,7 @@ static const uint16_t DHCP4_SERVER_PORT = 67;
 
 /// Magic cookie validating dhcp options field (and bootp vendor
 /// extensions field).
-///static const char* DHCP_OPTIONS_COOKIE = "\143\202\123\143";
+static const uint32_t DHCP_OPTIONS_COOKIE = 0x63825363;
 
 // TODO: Following are leftovers from dhcp.h import from ISC DHCP
 // They will be converted to C++-style defines once they will start
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 25999a0..b704370 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -12,12 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
 #include <sstream>
 #include <fstream>
 #include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <exceptions/exceptions.h>
@@ -52,8 +54,10 @@ IfaceMgr::instance() {
 }
 
 IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
-    :name_(name), ifindex_(ifindex), mac_len_(0) {
-
+    :name_(name), ifindex_(ifindex), mac_len_(0), flag_loopback_(false),
+     flag_up_(false), flag_running_(false), flag_multicast_(false),
+     flag_broadcast_(false), flags_(0), hardware_type_(0)
+{
     memset(mac_, 0, sizeof(mac_));
 }
 
@@ -71,7 +75,7 @@ IfaceMgr::Iface::getPlainMac() const {
     tmp << hex;
     for (int i = 0; i < mac_len_; i++) {
         tmp.width(2);
-        tmp << mac_[i];
+        tmp <<  static_cast<int>(mac_[i]);
         if (i < mac_len_-1) {
             tmp << ":";
         }
@@ -80,14 +84,14 @@ IfaceMgr::Iface::getPlainMac() const {
 }
 
 bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
-
-    // Let's delete all addresses that match. It really shouldn't matter
-    // if we delete first or all, as the OS should allow to add a single
-    // address to an interface only once. If OS allows multiple instances
-    // of the same address added, we are in deep problems anyway.
-    size_t size = addrs_.size();
-    addrs_.erase(remove(addrs_.begin(), addrs_.end(), addr), addrs_.end());
-    return (addrs_.size() < size);
+    for (AddressCollection::iterator a = addrs_.begin();
+         a!=addrs_.end(); ++a) {
+        if (*a==addr) {
+            addrs_.erase(a);
+            return (true);
+        }
+    }
+    return (false);
 }
 
 bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
@@ -126,7 +130,7 @@ IfaceMgr::IfaceMgr()
         // interface detection is implemented. Otherwise
         // it is not possible to run tests in a portable
         // way (see detectIfaces() method).
-        // throw ex;
+        throw ex;
     }
 }
 
@@ -145,14 +149,14 @@ void IfaceMgr::closeSockets() {
 }
 
 IfaceMgr::~IfaceMgr() {
-    closeSockets();
-
     // control_buf_ is deleted automatically (scoped_ptr)
     control_buf_len_ = 0;
+
+    closeSockets();
 }
 
 void
-IfaceMgr::detectIfaces() {
+IfaceMgr::stubDetectIfaces() {
     string ifaceName, linkLocal;
 
     // TODO do the actual detection. Currently interface detection is faked
@@ -166,8 +170,8 @@ IfaceMgr::detectIfaces() {
         ifstream interfaces("interfaces.txt");
 
         if (!interfaces.good()) {
-            cout << "Failed to read interfaces.txt file." << endl;
-            isc_throw(Unexpected, "Failed to read interfaces.txt");
+            cout << "interfaces.txt file is not available. Stub interface detection skipped." << endl;
+            return;
         }
         interfaces >> ifaceName;
         interfaces >> linkLocal;
@@ -191,68 +195,142 @@ IfaceMgr::detectIfaces() {
     }
 }
 
-void
-IfaceMgr::openSockets(uint16_t port) {
-    int sock1, sock2;
+#if !defined(OS_LINUX) && !defined(OS_BSD)
+void IfaceMgr::detectIfaces() {
+    stubDetectIfaces();
+}
+#endif
 
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
+bool IfaceMgr::openSockets4(uint16_t port) {
+    int sock;
+    int count = 0;
+
+    for (IfaceCollection::iterator iface=ifaces_.begin();
+         iface!=ifaces_.end();
+         ++iface) {
+
+        cout << "Trying interface " << iface->getFullName() << endl;
+
+        if (iface->flag_loopback_ ||
+            !iface->flag_up_ ||
+            !iface->flag_running_) {
+            continue;
+        }
 
         AddressCollection addrs = iface->getAddresses();
 
-        for (AddressCollection::iterator addr = addrs.begin();
+        for (AddressCollection::iterator addr= addrs.begin();
              addr != addrs.end();
              ++addr) {
 
-            sock1 = openSocket(iface->getName(), *addr, port);
-            if (sock1 < 0) {
-                isc_throw(Unexpected, "Failed to open unicast socket on "
-                          << " interface " << iface->getFullName());
+            // skip IPv6 addresses
+            if (addr->getFamily() != AF_INET) {
+                continue;
+            }
+
+            sock = openSocket(iface->getName(), *addr, port);
+            if (sock<0) {
+                cout << "Failed to open unicast socket." << endl;
+                return (false);
+            }
+
+            count++;
+        }
+    }
+    return (count > 0);
+
+}
+
+bool IfaceMgr::openSockets6(uint16_t port) {
+    int sock;
+    int count = 0;
+
+    for (IfaceCollection::iterator iface=ifaces_.begin();
+         iface!=ifaces_.end();
+         ++iface) {
+
+        if (iface->flag_loopback_ ||
+            !iface->flag_up_ ||
+            !iface->flag_running_) {
+            continue;
+        }
+
+        AddressCollection addrs = iface->getAddresses();
+
+        for (AddressCollection::iterator addr= addrs.begin();
+             addr != addrs.end();
+             ++addr) {
+
+            // skip IPv4 addresses
+            if (addr->getFamily() != AF_INET6) {
+                continue;
+            }
+
+            sock = openSocket(iface->getName(), *addr, port);
+            if (sock<0) {
+                cout << "Failed to open unicast socket." << endl;
+                return (false);
             }
 
-            if ( !joinMcast(sock1, iface->getName(),
-                             string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
-                close(sock1);
+            if ( !joinMulticast(sock, iface->getName(),
+                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+                close(sock);
                 isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
             }
 
+            count++;
+#if defined(OS_LINUX)
             // this doesn't work too well on NetBSD
-            sock2 = openSocket(iface->getName(),
-                               IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
-                               port);
-            if (sock2 < 0) {
+            int sock2 = openSocket(iface->getName(),
+                                   IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+                                   port);
+            if (sock2<0) {
                 isc_throw(Unexpected, "Failed to open multicast socket on "
                           << " interface " << iface->getFullName());
-                iface->delSocket(sock1); // delete previously opened socket
+                iface->delSocket(sock); // delete previously opened socket
             }
+#endif
         }
     }
+    return (count > 0);
 }
 
 void
 IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
-    for (IfaceCollection::const_iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        out << "Detected interface " << iface->getFullName() << endl;
-        out << "  " << iface->getAddresses().size() << " addr(s):" << endl;
-        const AddressCollection addrs = iface->getAddresses();
+    for (IfaceCollection::const_iterator iface=ifaces_.begin();
+         iface!=ifaces_.end();
+         ++iface) {
+
+        const AddressCollection& addrs = iface->getAddresses();
+
+        out << "Detected interface " << iface->getFullName()
+             << ", hwtype=" << iface->hardware_type_ << ", maclen=" << iface->mac_len_
+             << ", mac=" << iface->getPlainMac();
+        out << ", flags=" << hex << iface->flags_ << dec << "("
+            << (iface->flag_loopback_?"LOOPBACK ":"")
+            << (iface->flag_up_?"UP ":"")
+            << (iface->flag_running_?"RUNNING ":"")
+            << (iface->flag_multicast_?"MULTICAST ":"")
+            << (iface->flag_broadcast_?"BROADCAST ":"")
+            << ")" << endl;
+        out << "  " << addrs.size() << " addr(s):";
 
         for (AddressCollection::const_iterator addr = addrs.begin();
              addr != addrs.end(); ++addr) {
-            out << "  " << addr->toText() << endl;
+            out << "  " << addr->toText();
         }
-        out << "  mac: " << iface->getPlainMac() << endl;
+        out << endl;
     }
 }
 
 IfaceMgr::Iface*
 IfaceMgr::getIface(int ifindex) {
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        if (iface->getIndex() == ifindex) {
+    for (IfaceCollection::iterator iface=ifaces_.begin();
+         iface!=ifaces_.end();
+         ++iface) {
+        if (iface->getIndex() == ifindex)
             return (&(*iface));
-        }
     }
 
     return (NULL); // not found
@@ -260,18 +338,18 @@ IfaceMgr::getIface(int ifindex) {
 
 IfaceMgr::Iface*
 IfaceMgr::getIface(const std::string& ifname) {
-    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface != ifaces_.end(); ++iface) {
-        if (iface->getName() == ifname) {
+    for (IfaceCollection::iterator iface=ifaces_.begin();
+         iface!=ifaces_.end();
+         ++iface) {
+        if (iface->getName() == ifname)
             return (&(*iface));
-        }
     }
 
     return (NULL); // not found
 }
 
-int
-IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
+int IfaceMgr::openSocket(const std::string& ifname,
+                     const IOAddress& addr,
                      int port) {
     Iface* iface = getIface(ifname);
     if (!iface) {
@@ -288,8 +366,7 @@ IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
     }
 }
 
-int
-IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
 
     cout << "Creating UDP4 socket on " << iface.getFullName()
          << " " << addr.toText() << "/port=" << port << endl;
@@ -298,8 +375,10 @@ IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
     memset(&addr4, 0, sizeof(sockaddr));
     addr4.sin_family = AF_INET;
     addr4.sin_port = htons(port);
-    memcpy(&addr4.sin_addr, addr.getAddress().to_v4().to_bytes().data(),
-           sizeof(addr4.sin_addr));
+
+    addr4.sin_addr.s_addr = htonl(addr);
+    //addr4.sin_addr.s_addr = 0; // anyaddr: this will receive 0.0.0.0 => 255.255.255.255 traffic
+    // addr4.sin_addr.s_addr = 0xffffffffu; // broadcast address. This will receive 0.0.0.0 => 255.255.255.255 as well
 
     int sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock < 0) {
@@ -312,8 +391,8 @@ IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
                   << "/port=" << port);
     }
 
-    // If there is no support for IP_PKTINFO, we are really out of luck.
-    // It will be difficult to understand, where this packet came from.
+    // if there is no support for IP_PKTINFO, we are really out of luck
+    // it will be difficult to undersand, where this packet came from
 #if defined(IP_PKTINFO)
     int flag = 1;
     if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
@@ -325,13 +404,13 @@ IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
     cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
         addr.toText() << "/port=" << port << endl;
 
-    iface.addSocket(SocketInfo(sock, addr, port));
+    SocketInfo info(sock, addr, port);
+    iface.addSocket(info);
 
     return (sock);
 }
 
-int
-IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
 
     cout << "Creating UDP6 socket on " << iface.getFullName()
          << " " << addr.toText() << "/port=" << port << endl;
@@ -347,7 +426,7 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
            addr.getAddress().to_v6().to_bytes().data(),
            sizeof(addr6.sin6_addr));
 #ifdef HAVE_SA_LEN
-    addr6->sin6_len = sizeof(addr6);
+    addr6.sin6_len = sizeof(addr6);
 #endif
 
     // TODO: use sockcreator once it becomes available
@@ -358,8 +437,8 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
         isc_throw(Unexpected, "Failed to create UDP6 socket.");
     }
 
-    // Set the REUSEADDR option so that we don't fail to start if
-    // we're being restarted.
+    /* Set the REUSEADDR option so that we don't fail to start if
+       we're being restarted. */
     int flag = 1;
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                    (char *)&flag, sizeof(flag)) < 0) {
@@ -373,14 +452,14 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
                   << "/port=" << port);
     }
 #ifdef IPV6_RECVPKTINFO
-    // RFC3542 - a new way
+    /* RFC3542 - a new way */
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
         isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
     }
 #else
-    // RFC2292 - an old way
+    /* RFC2292 - an old way */
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
@@ -394,7 +473,7 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
         // are link and site-scoped, so there is no sense to join those groups
         // with global addresses.
 
-        if ( !joinMcast( sock, iface.getName(),
+        if ( !joinMulticast( sock, iface.getName(),
                          string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
             close(sock);
             isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
@@ -405,13 +484,14 @@ IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
     cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
         addr.toText() << "/port=" << port << endl;
 
-    iface.addSocket(SocketInfo(sock, addr, port));
+    SocketInfo info(sock, addr, port);
+    iface.addSocket(info);
 
     return (sock);
 }
 
 bool
-IfaceMgr::joinMcast(int sock, const std::string& ifname,
+IfaceMgr::joinMulticast(int sock, const std::string& ifname,
 const std::string & mcast) {
 
     struct ipv6_mreq mreq;
@@ -506,19 +586,231 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
 }
 
 bool
-IfaceMgr::send(boost::shared_ptr<Pkt4>& )
+IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
 {
-    /// TODO: Implement this (ticket #1240)
-    isc_throw(NotImplemented, "Pkt4 send not implemented yet.");
+    struct msghdr m;
+    struct iovec v;
+    int result;
+
+    Iface* iface = getIface(pkt->getIface());
+    if (!iface) {
+        isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
+                  << pkt->getIface() << ") specified.");
+    }
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+
+    // Set the target address we're sending to.
+    sockaddr_in to;
+    memset(&to, 0, sizeof(to));
+    to.sin_family = AF_INET;
+    to.sin_port = htons(pkt->getRemotePort());
+    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+
+    m.msg_name = &to;
+    m.msg_namelen = sizeof(to);
+
+    // Set the data buffer we're sending. (Using this wacky
+    // "scatter-gather" stuff... we only have a single chunk
+    // of data to send, so we declare a single vector entry.)
+    v.iov_base = (char *) pkt->getBuffer().getData();
+    v.iov_len = pkt->getBuffer().getLength();
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+// OS_LINUX defines are part of ticket #1237
+#if defined(OS_LINUX)
+    // Setting the interface is a bit more involved.
+    //
+    // We have to create a "control message", and set that to
+    // define the IPv4 packet information. We could set the
+    // source address if we wanted, but we can safely let the
+    // kernel decide what that should be.
+    struct in_pktinfo *pktinfo;
+    struct cmsghdr *cmsg;
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+    cmsg = CMSG_FIRSTHDR(&m);
+    cmsg->cmsg_level = IPPROTO_IP;
+    cmsg->cmsg_type = IP_PKTINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+    memset(pktinfo, 0, sizeof(*pktinfo));
+    pktinfo->ipi_ifindex = pkt->getIndex();
+    m.msg_controllen = cmsg->cmsg_len;
+#endif
+
+    cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
+         << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
+         << " over socket " << getSocket(*pkt) << " on interface "
+         << getIface(pkt->getIface())->getFullName() << endl;
+
+    result = sendmsg(getSocket(*pkt), &m, 0);
+    if (result < 0) {
+        isc_throw(Unexpected, "Pkt4 send failed.");
+    }
+
+    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
+         << " on " << iface->getFullName() << " interface: "
+         << " dst=" << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
+         << ", src=" << pkt->getLocalAddr().toText() << ":" << pkt->getLocalPort()
+         << endl;
+
+    return (result);
 }
 
 
 boost::shared_ptr<Pkt4>
 IfaceMgr::receive4() {
-    isc_throw(NotImplemented, "Pkt4 reception not implemented yet.");
 
-    // TODO: To be implemented (ticket #1239)
-    return (boost::shared_ptr<Pkt4>()); // NULL
+    const SocketInfo* candidate = 0;
+    IfaceCollection::const_iterator iface;
+    for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
+
+#if 0
+        // uncomment this once #1237 is merged
+        // Let's skip loopback and downed interfaces.
+        if (iface->flag_loopback_ ||
+            !iface->flag_up_ ||
+            !iface->flag_running_) {
+            continue;
+        }
+#endif
+
+        SocketCollection::const_iterator s = iface->sockets_.begin();
+        while (s != iface->sockets_.end()) {
+
+            // We don't want IPv6 addresses here.
+            if (s->addr_.getFamily() != AF_INET) {
+                ++s;
+                continue;
+            }
+
+            // This address looks good.
+            if (!candidate) {
+                candidate = &(*s);
+                break;
+            }
+
+            ++s;
+        }
+
+        if (candidate) {
+            break;
+        }
+    }
+
+    if (!candidate) {
+        isc_throw(Unexpected, "Failed to find any suitable sockets on all interfaces.");
+    }
+
+    cout << "Trying to receive over UDP4 socket " << candidate->sockfd_ << " bound to "
+         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+         << iface->getFullName() << endl;
+
+    // Now we have a socket, let's get some data from it!
+
+    struct msghdr m;
+    struct iovec v;
+    int result;
+    struct sockaddr_in from_addr;
+    struct in_addr to_addr;
+    boost::shared_ptr<Pkt4> pkt;
+    const uint32_t RCVBUFSIZE = 1500;
+    static uint8_t buf[RCVBUFSIZE];
+
+    memset(&control_buf_[0], 0, control_buf_len_);
+    memset(&from_addr, 0, sizeof(from_addr));
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
+
+    // Point so we can get the from address.
+    m.msg_name = &from_addr;
+    m.msg_namelen = sizeof(from_addr);
+
+    v.iov_base = (void*)buf;
+    v.iov_len = RCVBUFSIZE;
+    m.msg_iov = &v;
+    m.msg_iovlen = 1;
+
+    // Getting the interface is a bit more involved.
+    //
+    // We set up some space for a "control message". We have
+    // previously asked the kernel to give us packet
+    // information (when we initialized the interface), so we
+    // should get the destination address from that.
+    m.msg_control = &control_buf_[0];
+    m.msg_controllen = control_buf_len_;
+
+    result = recvmsg(candidate->sockfd_, &m, 0);
+
+    if (result < 0) {
+        cout << "Failed to receive UDP4 data." << endl;
+        return (boost::shared_ptr<Pkt4>()); // NULL
+    }
+
+    unsigned int ifindex = iface->getIndex();
+
+// OS_LINUX defines are part of ticket #1237
+#if defined(OS_LINUX)
+    struct cmsghdr* cmsg;
+    struct in_pktinfo* pktinfo;
+
+    int found_pktinfo = 0;
+    cmsg = CMSG_FIRSTHDR(&m);
+    while (cmsg != NULL) {
+        if ((cmsg->cmsg_level == IPPROTO_IP) &&
+            (cmsg->cmsg_type == IP_PKTINFO)) {
+            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+
+            ifindex = pktinfo->ipi_ifindex;
+            to_addr = pktinfo->ipi_addr;
+
+            // This field is useful, when we are bound to unicast
+            // address e.g. 192.0.2.1 and the packet was sent to
+            // broadcast. This will return broadcast address, not
+            // the address we are bound to.
+
+            // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
+            // cout << "The other addr is: " << tmp.toText() << endl;
+
+            // Perhaps we should uncomment this:
+            // to_addr = pktinfo->ipi_spec_dst;
+            found_pktinfo = 1;
+        }
+        cmsg = CMSG_NXTHDR(&m, cmsg);
+    }
+    if (!found_pktinfo) {
+        cout << "Unable to find pktinfo" << endl;
+        return (boost::shared_ptr<Pkt4>()); // NULL
+    }
+#endif
+
+    IOAddress to(htonl(to_addr.s_addr));
+    IOAddress from(htonl(from_addr.sin_addr.s_addr));
+    uint16_t from_port = htons(from_addr.sin_port);
+
+    cout << "Received " << result << " bytes from " << from.toText()
+         << "/port=" << from_port
+         << " sent to " << to.toText() << " over interface "
+         << iface->getFullName() << endl;
+
+    // we have all data let's create Pkt4 object
+    pkt = boost::shared_ptr<Pkt4>(new Pkt4(buf, result));
+
+    pkt->setIface(iface->getName());
+    pkt->setIndex(ifindex);
+    pkt->setLocalAddr(to);
+    pkt->setRemoteAddr(from);
+    pkt->setRemotePort(from_port);
+    pkt->setLocalPort(candidate->port_);
+
+    return (pkt);
 }
 
 boost::shared_ptr<Pkt6>
@@ -552,27 +844,35 @@ IfaceMgr::receive6() {
     memset(&from, 0, sizeof(from));
     memset(&to_addr, 0, sizeof(to_addr));
 
-    // Initialize our message header structure.
+    /*
+     * Initialize our message header structure.
+     */
     memset(&m, 0, sizeof(m));
 
-    // Point so we can get the from address.
+    /*
+     * Point so we can get the from address.
+     */
     m.msg_name = &from;
     m.msg_namelen = sizeof(from);
 
-    // Set the data buffer we're receiving. (Using this wacky
-    // "scatter-gather" stuff... but we that doesn't really make
-    // sense for us, so we use a single vector entry.)
+    /*
+     * Set the data buffer we're receiving. (Using this wacky
+     * "scatter-gather" stuff... but we that doesn't really make
+     * sense for us, so we use a single vector entry.)
+     */
     v.iov_base = (void*)&pkt->data_[0];
     v.iov_len = pkt->data_len_;
     m.msg_iov = &v;
     m.msg_iovlen = 1;
 
-    // Getting the interface is a bit more involved.
-    //
-    // We set up some space for a "control message". We have
-    // previously asked the kernel to give us packet
-    // information (when we initialized the interface), so we
-    // should get the destination address from that.
+    /*
+     * Getting the interface is a bit more involved.
+     *
+     * We set up some space for a "control message". We have
+     * previously asked the kernel to give us packet
+     * information (when we initialized the interface), so we
+     * should get the destination address from that.
+     */
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
@@ -580,38 +880,51 @@ IfaceMgr::receive6() {
     /// all available sockets. For now, we just take the
     /// first interface and use first socket from it.
     IfaceCollection::const_iterator iface = ifaces_.begin();
-    if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
-    }
-    SocketCollection::const_iterator s = iface->sockets_.begin();
     const SocketInfo* candidate = 0;
-    while (s != iface->sockets_.end()) {
-        if (s->addr_.getAddress().to_v6().is_multicast()) {
-            candidate = &(*s);
-            break;
+    while (iface != ifaces_.end()) {
+        SocketCollection::const_iterator s = iface->sockets_.begin();
+        while (s != iface->sockets_.end()) {
+            if (s->addr_.getFamily() != AF_INET6) {
+                ++s;
+                continue;
+            }
+            if (s->addr_.getAddress().to_v6().is_multicast()) {
+                candidate = &(*s);
+                break;
+            }
+            if (!candidate) {
+                candidate = &(*s); // it's not multicast, but it's better than nothing
+            }
+            ++s;
         }
-        if (!candidate) {
-            candidate = &(*s); // it's not multicast, but it's better than none
+        if (candidate) {
+            break;
         }
-        ++s;
+        ++iface;
     }
+    if (iface == ifaces_.end()) {
+        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+    }
+
     if (!candidate) {
         isc_throw(Unexpected, "Interface " << iface->getFullName()
                   << " does not have any sockets open.");
     }
 
-    cout << "Trying to receive over socket " << candidate->sockfd_ << " bound to "
+    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
          << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
          << iface->getFullName() << endl;
     result = recvmsg(candidate->sockfd_, &m, 0);
 
     if (result >= 0) {
-        // If we did read successfully, then we need to loop
-        // through the control messages we received and
-        // find the one with our destination address.
-        //
-        // We also keep a flag to see if we found it. If we
-        // didn't, then we consider this to be an error.
+        /*
+         * If we did read successfully, then we need to loop
+         * through the control messages we received and
+         * find the one with our destination address.
+         *
+         * We also keep a flag to see if we found it. If we
+         * didn't, then we consider this to be an error.
+         */
         int found_pktinfo = 0;
         cmsg = CMSG_FIRSTHDR(&m);
         while (cmsg != NULL) {
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 759d657..5eba76b 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -20,6 +20,8 @@
 #include <boost/scoped_array.hpp>
 #include <boost/noncopyable.hpp>
 #include <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/dhcp4.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 
@@ -85,6 +87,14 @@ public:
         /// @return MAC address as a plain text (string)
         std::string getPlainMac() const;
 
+        /// @brief Sets flag_*_ fields based on bitmask value returned by OS
+        ///
+        /// Note: Implementation of this method is OS-dependent as bits have
+        /// different meaning on each OS.
+        ///
+        /// @param flags bitmask value returned by OS in interface detection
+        void setFlags(uint32_t flags);
+
         /// @brief Returns interface index.
         ///
         /// @return interface index
@@ -157,11 +167,35 @@ public:
         /// list of assigned addresses
         AddressCollection addrs_;
 
+    public:
         /// link-layer address
         uint8_t mac_[MAX_MAC_LEN];
 
         /// length of link-layer address (usually 6)
         int mac_len_;
+
+        /// specifies if selected interface is loopback
+        bool flag_loopback_;
+
+        /// specifies if selected interface is up
+        bool flag_up_;
+
+        /// flag specifies if selected interface is running
+        /// (e.g. cable plugged in, wifi associated)
+        bool flag_running_;
+
+        /// flag specifies if selected interface is multicast capable
+        bool flag_multicast_;
+
+        /// flag specifies if selected interface is broadcast capable
+        bool flag_broadcast_;
+
+        /// interface flags (this value is as is returned by OS,
+        /// it may mean different things on different OSes)
+        uint32_t flags_;
+
+        /// hardware type
+        uint16_t hardware_type_;
     };
 
     // TODO performance improvement: we may change this into
@@ -298,13 +332,27 @@ public:
     /// Will throw exception if socket creation fails.
     ///
     /// @param port specifies port number (usually DHCP6_SERVER_PORT)
-    void openSockets(uint16_t port);
-
+    ///
+    /// @return true if any sockets were open
+    bool openSockets6(uint16_t port = DHCP6_SERVER_PORT);
 
     /// @brief Closes all open sockets.
     /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
     void closeSockets();
 
+    /// Opens IPv4 sockets on detected interfaces.
+    /// Will throw exception if socket creation fails.
+    ///
+    /// @param port specifies port number (usually DHCP4_SERVER_PORT)
+    ///
+    /// @return true if any sockets were open
+    bool openSockets4(uint16_t port = DHCP4_SERVER_PORT);
+
+    /// @brief returns number of detected interfaces
+    ///
+    /// @return number of detected interfaces
+    uint16_t countIfaces() { return ifaces_.size(); }
+
     // don't use private, we need derived classes in tests
 protected:
 
@@ -314,7 +362,7 @@ protected:
     /// anyone to create instances of IfaceMgr. Use instance() method instead.
     IfaceMgr();
 
-    ~IfaceMgr();
+    virtual ~IfaceMgr();
 
     /// @brief Opens IPv4 socket.
     ///
@@ -357,6 +405,15 @@ protected:
     void
     detectIfaces();
 
+    /// @brief Stub implementation of network interface detection.
+    ///
+    /// This implementations reads a single line from interfaces.txt file
+    /// and pretends to detect such interface. First interface name and
+    /// link-local IPv6 address or IPv4 address is read from the
+    /// intefaces.txt file.
+    void
+    stubDetectIfaces();
+
     // TODO: having 2 maps (ifindex->iface and ifname->iface would)
     //      probably be better for performance reasons
 
@@ -402,8 +459,8 @@ private:
     /// @return true if multicast join was successful
     ///
     bool
-    joinMcast(int sock, const std::string& ifname,
-              const std::string& mcast);
+    joinMulticast(int sock, const std::string& ifname,
+                  const std::string& mcast);
 
 };
 
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
new file mode 100644
index 0000000..7786b92
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#if defined(OS_BSD)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace isc {
+
+void
+IfaceMgr::detectIfaces() {
+    // TODO do the actual detection on BSDs. Currently just calling
+    // stub implementation.
+    stubDetectIfaces();
+}
+
+}
+
+#endif
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
new file mode 100644
index 0000000..657c943
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -0,0 +1,381 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#include <config.h>
+
+#if defined(OS_LINUX)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+#include <net/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+/// This is a structure that defines context for netlink connection.
+struct rtnl_handle
+{
+    int                fd;
+    struct sockaddr_nl local;
+    struct sockaddr_nl peer;
+    __u32              seq;
+    __u32              dump;
+};
+
+struct nlmsg_list
+{
+    struct nlmsg_list *next;
+    struct nlmsghdr h;
+};
+
+const int sndbuf = 32768;
+const int rcvbuf = 32768;
+
+namespace isc {
+
+
+/// @brief Opens netlink socket and initializes handle structure.
+///
+/// @exception Unexpected Thrown if socket configuration fails.
+///
+/// @param handle Context will be stored in this structure.
+void rtnl_open_socket(struct rtnl_handle& handle) {
+    // equivalent of rtnl_open
+    handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+    if (handle.fd < 0) {
+        isc_throw(Unexpected, "Failed to create NETLINK socket.");
+    }
+
+    if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
+        isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
+    }
+
+    if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
+        isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
+    }
+
+    memset(&handle.local, 0, sizeof(handle.local));
+    handle.local.nl_family = AF_NETLINK;
+    handle.local.nl_groups = 0;
+
+    if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
+        isc_throw(Unexpected, "Failed to bind netlink socket.");
+    }
+
+    socklen_t addr_len = sizeof(handle.local);
+    if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
+        isc_throw(Unexpected, "Getsockname for netlink socket failed.");
+    }
+
+    // just 2 sanity checks and we are done
+    if ( (addr_len != sizeof(handle.local)) ||
+         (handle.local.nl_family != AF_NETLINK) ) {
+        isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
+    }
+}
+
+/// @brief Sends request over NETLINK socket.
+///
+/// @param handle context structure
+/// @param family requested information family
+/// @param type request type (RTM_GETLINK or RTM_GETADDR)
+void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
+    struct {
+        struct nlmsghdr nlh;
+        struct rtgenmsg g;
+    } req;
+    struct sockaddr_nl nladdr;
+
+    memset(&nladdr, 0, sizeof(nladdr));
+    nladdr.nl_family = AF_NETLINK;
+
+    memset(&req, 0, sizeof(req));
+    req.nlh.nlmsg_len = sizeof(req);
+    req.nlh.nlmsg_type = type;
+    req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+    req.nlh.nlmsg_pid = 0;
+    req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
+    req.g.rtgen_family = family;
+
+    int status =  sendto(handle.fd, (void*)&req, sizeof(req), 0,
+                         (struct sockaddr*)&nladdr, sizeof(nladdr));
+
+    if (status<0) {
+        isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
+    }
+}
+
+/// @brief Appends nlmsg to a list
+///
+/// @param n a message to be added
+/// @param link_info a list
+void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
+{
+    struct nlmsg_list *h;
+    struct nlmsg_list **lp;
+
+    h = (nlmsg_list*)malloc(n->nlmsg_len+sizeof(void*));
+    if (h == NULL) {
+        isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
+                  << " bytes.");
+    }
+
+    memcpy(&h->h, n, n->nlmsg_len);
+    h->next = NULL;
+
+    for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
+    *lp = h;
+}
+
+void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+    memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+    while (RTA_OK(rta, len)) {
+        if (rta->rta_type <= max)
+            tb[rta->rta_type] = rta;
+        rta = RTA_NEXT(rta,len);
+    }
+    if (len) {
+        isc_throw(Unexpected, "Failed to parse RTATTR in netlink message.");
+    }
+}
+
+void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
+    uint8_t addr[16];
+    struct rtattr * rta_tb[IFA_MAX+1];
+
+    for ( ;addr_info ;  addr_info = addr_info->next) {
+        struct nlmsghdr *n = &addr_info->h;
+        struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
+
+        // these are not the addresses you are looking for
+        if ( ifa->ifa_index != iface.getIndex()) {
+            continue;
+        }
+
+        if ( ifa->ifa_family == AF_INET6 ) {
+            memset(rta_tb, 0, sizeof(rta_tb));
+            parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+            if (!rta_tb[IFA_LOCAL])
+                rta_tb[IFA_LOCAL]   = rta_tb[IFA_ADDRESS];
+            if (!rta_tb[IFA_ADDRESS])
+                rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+            memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
+            IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
+            iface.addAddress(a);
+
+            /// TODO: Read lifetimes of configured addresses
+        }
+
+        if ( ifa->ifa_family == AF_INET ) {
+            memset(rta_tb, 0, sizeof(rta_tb));
+            parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+            if (!rta_tb[IFA_LOCAL])
+                rta_tb[IFA_LOCAL]   = rta_tb[IFA_ADDRESS];
+            if (!rta_tb[IFA_ADDRESS])
+                rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+            memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
+            IOAddress a = IOAddress::from_bytes(AF_INET, addr);
+            iface.addAddress(a);
+        }
+    }
+}
+
+
+void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
+
+    struct sockaddr_nl nladdr;
+    struct iovec iov;
+    struct msghdr msg;
+    memset(&msg, 0, sizeof(struct msghdr));
+    msg.msg_name = &nladdr;
+    msg.msg_namelen = sizeof(nladdr);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    char buf[rcvbuf];
+
+    iov.iov_base = buf;
+    while (1) {
+        int status;
+        struct nlmsghdr *h;
+
+        iov.iov_len = sizeof(buf);
+        status = recvmsg(rth.fd, &msg, 0);
+
+        if (status < 0) {
+            if (errno == EINTR)
+                continue;
+            isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
+        }
+
+        if (status == 0) {
+            isc_throw(Unexpected, "EOF while reading netlink socket.");
+        }
+
+        h = (struct nlmsghdr*)buf;
+        while (NLMSG_OK(h, status)) {
+
+            // why we received this anyway?
+            if (nladdr.nl_pid != 0 ||
+                h->nlmsg_pid != rth.local.nl_pid ||
+                h->nlmsg_seq != rth.dump) {
+                h = NLMSG_NEXT(h, status);
+                continue;
+            }
+
+            if (h->nlmsg_type == NLMSG_DONE) {
+                // end of message
+                return;
+            }
+
+            if (h->nlmsg_type == NLMSG_ERROR) {
+                struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+                if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+                    // we are really out of luck here. We can't even say what is
+                    // wrong as error message is truncated. D'oh.
+                    isc_throw(Unexpected, "Netlink reply read failed.");
+                } else {
+                    isc_throw(Unexpected, "Netlink reply read error " << -err->error);
+                }
+                // never happens we throw before we reach here
+                return;
+            }
+
+            // store the data
+            rtnl_store_reply(h, &info);
+
+            h = NLMSG_NEXT(h, status);
+        }
+        if (msg.msg_flags & MSG_TRUNC) {
+            isc_throw(Unexpected, "Message received over netlink truncated.");
+        }
+        if (status) {
+            isc_throw(Unexpected, "Trailing garbage of " << status << " bytes received over netlink.");
+        }
+    }
+}
+
+/// @brief releases nlmsg list
+///
+/// @param head first element of the list to be released
+void release_list(struct nlmsg_list *head) {
+    struct nlmsg_list *tmp;
+    while (head) {
+        tmp = head->next;
+        free(head);
+        head = tmp;
+    }
+}
+
+void IfaceMgr::detectIfaces() {
+    cout << "Linux: detecting interfaces." << endl;
+
+    struct nlmsg_list* link_info = NULL; // link info list
+    struct nlmsg_list* addr_info = NULL; // address info list
+    struct nlmsg_list* l = NULL;
+    struct rtnl_handle rth;
+
+    // required to display information about interface
+    struct ifinfomsg* ifi = NULL;
+    struct rtattr* tb[IFLA_MAX+1];
+    int len = 0;
+    memset(tb, 0, sizeof(tb));
+    memset(&rth,0, sizeof(rth));
+
+    // open socket
+    rtnl_open_socket(rth);
+
+    // now we have open functional socket, let's use it!
+    // ask for list of interface...
+    rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
+
+    // Get reply and store it in link_info list.
+    rtnl_process_reply(rth, link_info);
+
+    // Now ask for list of addresses (AF_UNSPEC = of any family)
+    rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
+
+    // Get reply and store it in addr_info list.
+    rtnl_process_reply(rth, addr_info);
+
+    // Now build list with interface names
+    for (l=link_info; l; l = l->next) {
+        ifi = (ifinfomsg*)NLMSG_DATA(&l->h);
+        len = (&l->h)->nlmsg_len;
+        len -= NLMSG_LENGTH(sizeof(*ifi));
+        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+        Iface* iface = new Iface(string( (char*)RTA_DATA(tb[IFLA_IFNAME])), ifi->ifi_index);
+
+        iface->hardware_type_ = ifi->ifi_type;
+        iface->setFlags(ifi->ifi_flags);
+
+        iface->mac_len_ = 0;
+        memset(iface->mac_, 0, IfaceMgr::MAX_MAC_LEN);
+        // Does inetface has LL_ADDR?
+        if (tb[IFLA_ADDRESS]) {
+            iface->mac_len_ = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+            if (iface->mac_len_ > IfaceMgr::MAX_MAC_LEN) {
+                iface->mac_len_ = 0;
+                isc_throw(Unexpected, "Interface " << iface->getFullName()
+                          << " was detected to have link address of length " << RTA_PAYLOAD(tb[IFLA_ADDRESS])
+                          << ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
+            }
+            memcpy(iface->mac_, RTA_DATA(tb[IFLA_ADDRESS]), iface->mac_len_);
+        }
+        else {
+            // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
+            // dereference it in this manner
+            // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
+            iface->mac_len_ = 0;
+        }
+
+        ipaddrs_get(*iface, addr_info);
+        ifaces_.push_back(*iface);
+    }
+
+    release_list(link_info);
+    release_list(addr_info);
+
+    printIfaces();
+}
+
+/// @brief sets flag_*_ fields.
+///
+/// This implementation is OS-specific as bits have different meaning
+/// on different OSes.
+///
+/// @param flags flags bitfield read from OS
+void IfaceMgr::Iface::setFlags(uint32_t flags) {
+    flags_ = flags;
+
+    flag_loopback_ = flags & IFF_LOOPBACK;
+    flag_up_ = flags & IFF_UP;
+    flag_running_ = flags & IFF_RUNNING;
+    flag_multicast_ = flags & IFF_MULTICAST;
+    flag_broadcast_ = flags & IFF_BROADCAST;
+}
+
+}
+
+#endif // if defined(LINUX)
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 78e91c9..801c136 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -93,13 +93,13 @@ LibDHCP::unpackOptions4(const std::vector<uint8_t>& buf,
     // 2 - header of DHCPv4 option
     while (offset + 1 <= buf.size()) {
         uint8_t opt_type = buf[offset++];
-        if (offset + 1 == buf.size()) {
-            if (opt_type == DHO_END)
-                return; // just return. Don't need to add DHO_END option
-            else {
-                isc_throw(OutOfRange, "Attempt to parse truncated option "
-                          << opt_type);
-            }
+
+        if (opt_type == DHO_END)
+          return; // just return. Don't need to add DHO_END option
+
+        if (offset + 1 >= buf.size()) {
+          isc_throw(OutOfRange, "Attempt to parse truncated option "
+                    << opt_type);
         }
 
         uint8_t opt_len =  buf[offset++];
@@ -139,7 +139,7 @@ LibDHCP::packOptions6(boost::shared_array<uint8_t> data,
             offset = it->second->pack(data, data_len, offset);
         }
     }
-    catch (const Exception& e) {
+    catch (const Exception&) {
         cout << "Packet build failed (Option build failed)." << endl;
         throw;
     }
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 96077b7..23de315 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -301,6 +301,46 @@ Option::addOption(boost::shared_ptr<Option> opt) {
     options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
 }
 
+uint8_t Option::getUint8() {
+    if (data_.size() < sizeof(uint8_t) ) {
+        isc_throw(OutOfRange, "Attempt to read uint8 from option " << type_
+                  << " that has size " << data_.size());
+    }
+    return (data_[0]);
+}
+
+uint16_t Option::getUint16() {
+    if (data_.size() < sizeof(uint16_t) ) {
+        isc_throw(OutOfRange, "Attempt to read uint16 from option " << type_
+                  << " that has size " << data_.size());
+    }
+
+    return ( readUint16(&data_[0]) );
+}
+
+uint32_t Option::getUint32() {
+    if (data_.size() < sizeof(uint32_t) ) {
+        isc_throw(OutOfRange, "Attempt to read uint32 from option " << type_
+                  << " that has size " << data_.size());
+    }
+    return ( readUint32(&data_[0]) );
+}
+
+void Option::setUint8(uint8_t value) {
+  data_.resize(1);
+  data_[0] = value;
+}
+
+void Option::setUint16(uint16_t value) {
+  data_.resize(2);
+  writeUint16(value, &data_[0]);
+}
+
+void Option::setUint32(uint32_t value) {
+  data_.resize(4);
+  writeUint32(value, &data_[0]);
+}
+
 Option::~Option() {
 
 }
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index 088d094..d80fc05 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -236,9 +236,50 @@ public:
     bool
     delOption(unsigned short type);
 
+    /// @brief Returns content of first byte.
+    ///
+    /// @exception OutOfRange Thrown if the option has a length of 0.
+    ///
+    /// @return value of the first byte
+    uint8_t getUint8();
+
+    /// @brief Returns content of first word.
+    ///
+    /// @exception OutOfRange Thrown if the option has a length less than 2.
+    ///
+    /// @return uint16_t value stored on first two bytes
+    uint16_t getUint16();
+
+    /// @brief Returns content of first double word.
+    ///
+    /// @exception OutOfRange Thrown if the option has a length less than 4.
+    ///
+    /// @return uint32_t value stored on first four bytes
+    uint32_t getUint32();
+
+    /// @brief Sets content of this option to singe uint8 value.
+    ///
+    /// Option it resized appropriately (to length of 1 octet).
+    ///
+    /// @param value value to be set
+    void setUint8(uint8_t value);
+
+    /// @brief Sets content of this option to singe uint16 value.
+    ///
+    /// Option it resized appropriately (to length of 2 octets).
+    ///
+    /// @param value value to be set
+    void setUint16(uint16_t value);
+
+    /// @brief Sets content of this option to singe uint32 value.
+    ///
+    /// Option it resized appropriately (to length of 4 octets).
+    ///
+    /// @param value value to be set
+    void setUint32(uint32_t value);
+
     /// just to force that every option has virtual dtor
-    virtual
-    ~Option();
+    virtual ~Option();
 
 protected:
     /// Builds raw (over-wire) buffer of this option, including all
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 219af67..e456631 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -59,7 +59,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
      :local_addr_(DEFAULT_ADDRESS),
       remote_addr_(DEFAULT_ADDRESS),
       iface_(""),
-      ifindex_(-1),
+      ifindex_(0),
       local_port_(DHCP4_SERVER_PORT),
       remote_port_(DHCP4_CLIENT_PORT),
       op_(BOOTREQUEST),
@@ -75,8 +75,8 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
 {
     if (len < DHCPV4_PKT_HDR_LEN) {
         isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
-                  << " received, at least " << DHCPV4_PKT_HDR_LEN
-                  << "is expected");
+                  << ") received, at least " << DHCPV4_PKT_HDR_LEN
+                  << " is expected.");
     }
 
     data_.resize(len);
@@ -114,6 +114,9 @@ Pkt4::pack() {
     bufferOut_.writeData(sname_, MAX_SNAME_LEN);
     bufferOut_.writeData(file_, MAX_FILE_LEN);
 
+    // write DHCP magic cookie
+    bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE);
+
     LibDHCP::packOptions(bufferOut_, options_);
 
     // add END option that indicates end of options
@@ -122,13 +125,14 @@ Pkt4::pack() {
 
     return (true);
 }
-bool
+
+void
 Pkt4::unpack() {
 
     // input buffer (used during message reception)
     isc::util::InputBuffer bufferIn(&data_[0], data_.size());
 
-    if (bufferIn.getLength()<DHCPV4_PKT_HDR_LEN) {
+    if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) {
         isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
                   << bufferIn.getLength() << " received, at least "
                   << DHCPV4_PKT_HDR_LEN << "is expected");
@@ -149,24 +153,69 @@ Pkt4::unpack() {
     bufferIn.readData(sname_, MAX_SNAME_LEN);
     bufferIn.readData(file_, MAX_FILE_LEN);
 
+    if (bufferIn.getLength() == bufferIn.getPosition()) {
+        // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
+        // particular, it does not have magic cookie, a 4 byte sequence that
+        // differentiates between DHCP and BOOTP packets.
+        isc_throw(InvalidOperation, "Recevied BOOTP packet. BOOTP is not supported.");
+    }
+
+    if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
+      // there is not enough data to hold magic DHCP cookie
+      isc_throw(Unexpected, "Truncated or no DHCP packet.");
+    }
+
+    uint32_t magic = bufferIn.readUint32();
+    if (magic != DHCP_OPTIONS_COOKIE) {
+      isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
+    }
+
     size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
     vector<uint8_t> optsBuffer;
-    // fist use of readVector
+
+    // First use of readVector.
     bufferIn.readVector(optsBuffer, opts_len);
     LibDHCP::unpackOptions4(optsBuffer, options_);
 
-    return (true);
+    // TODO: check will need to be called separately, so hooks can be called after
+    // packet is parsed, but before its content is verified
+    check();
+}
+
+void Pkt4::check() {
+    boost::shared_ptr<Option> typeOpt = getOption(DHO_DHCP_MESSAGE_TYPE);
+    if (typeOpt) {
+        uint8_t msg_type = typeOpt->getUint8();
+        if (msg_type>DHCPLEASEACTIVE) {
+            isc_throw(BadValue, "Invalid DHCP message type received:" << msg_type);
+        }
+        msg_type_ = msg_type;
+
+    } else {
+        isc_throw(Unexpected, "Missing DHCP Message Type option");
+    }
+}
+
+void Pkt4::repack() {
+    cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
+
+    bufferOut_.writeData(&data_[0], data_.size());
 }
 
 std::string
 Pkt4::toText() {
     stringstream tmp;
-    tmp << "localAddr=[" << local_addr_.toText() << "]:" << local_port_
-        << " remoteAddr=[" << remote_addr_.toText()
-        << "]:" << remote_port_ << endl;
-    tmp << "msgtype=" << msg_type_
-        << ", transid=0x" << hex << transid_ << dec
-        << endl;
+    tmp << "localAddr=" << local_addr_.toText() << ":" << local_port_
+        << " remoteAddr=" << remote_addr_.toText()
+        << ":" << remote_port_ << ", msgtype=" << int(msg_type_)
+        << ", transid=0x" << hex << transid_ << dec << endl;
+
+    for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
+         opt != options_.end();
+         ++opt) {
+        tmp << "  " << opt->second->toText() << std::endl;
+    }
+
 
     return tmp.str();
 }
@@ -256,7 +305,6 @@ Pkt4::getOption(uint8_t type) {
     return boost::shared_ptr<isc::dhcp::Option>(); // NULL
 }
 
-
 } // end of namespace isc::dhcp
 
 } // end of namespace isc
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 9b6f726..c520747 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -74,9 +74,29 @@ public:
     /// Will create a collection of option objects that will
     /// be stored in options_ container.
     ///
-    /// @return true, if parsing was successful
-    bool
-    unpack();
+    /// Method with throw exception if packet parsing fails.
+    void unpack();
+
+    /// @brief performs sanity check on a packet.
+    ///
+    /// This is usually performed after unpack(). It checks if packet is sane:
+    /// required options are present, fields have sane content etc.
+    /// For example verifies that DHCP_MESSAGE_TYPE is present and have
+    /// reasonable value. This method is expected to grow significantly.
+    /// It makes sense to separate unpack() and check() for testing purposes.
+    ///
+    /// TODO: It is called from unpack() directly. It should be separated.
+    ///
+    /// Method will throw exception if anomaly is found.
+    void check();
+
+    /// @brief Copies content of input buffer to output buffer.
+    ///
+    /// This is mostly a diagnostic function. It is being used for sending
+    /// received packet. Received packet is stored in bufferIn_, but
+    /// transmitted data is stored in bufferOut_. If we want to send packet
+    /// that we just received, a copy between those two buffers is necessary.
+    void repack();
 
     /// @brief Returns text representation of the packet.
     ///
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index ef849f1..e86fa24 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -21,6 +21,8 @@ libdhcp___unittests_SOURCES  = run_unittests.cc
 libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc
 libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
 libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc
+libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc
 libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
 libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
@@ -33,6 +35,7 @@ libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCL
 libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
+
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
 # log4cplus's streams.h.
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index b647c18..9991e54 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -56,6 +56,10 @@ public:
         fakeifaces << LOOPBACK << " ::1";
         fakeifaces.close();
     }
+
+    ~IfaceMgrTest() {
+        unlink(INTERFACE_FILE);
+    }
 };
 
 // We need some known interface to work reliably. Loopback interface
@@ -147,6 +151,9 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
 
 TEST_F(IfaceMgrTest, basic) {
     // checks that IfaceManager can be instantiated
+    createLoInterfacesTxt();
+
+    createLoInterfacesTxt();
 
     IfaceMgr & ifacemgr = IfaceMgr::instance();
     ASSERT_TRUE(&ifacemgr != 0);
@@ -160,13 +167,14 @@ TEST_F(IfaceMgrTest, ifaceClass) {
     EXPECT_STREQ("eth5/7", iface->getFullName().c_str());
 
     delete iface;
-
 }
 
 // TODO: Implement getPlainMac() test as soon as interface detection
 // is implemented.
 TEST_F(IfaceMgrTest, getIface) {
 
+    createLoInterfacesTxt();
+
     cout << "Interface checks. Please ignore socket binding errors." << endl;
     NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
 
@@ -210,9 +218,11 @@ TEST_F(IfaceMgrTest, getIface) {
     EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
 
     delete ifacemgr;
+
 }
 
-TEST_F(IfaceMgrTest, detectIfaces) {
+#if !defined(OS_LINUX)
+TEST_F(IfaceMgrTest, detectIfaces_stub) {
 
     // test detects that interfaces can be detected
     // there is no code for that now, but interfaces are
@@ -240,8 +250,8 @@ TEST_F(IfaceMgrTest, detectIfaces) {
     EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
+#endif
 
 TEST_F(IfaceMgrTest, sockets6) {
     // testing socket operation in a portable way is tricky
@@ -273,7 +283,6 @@ TEST_F(IfaceMgrTest, sockets6) {
     close(socket2);
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
 // TODO: disabled due to other naming on various systems
@@ -360,9 +369,101 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
+TEST_F(IfaceMgrTest, sendReceive4) {
+
+    // testing socket operation in a portable way is tricky
+    // without interface detection implemented
+    createLoInterfacesTxt();
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+    // let's assume that every supported OS have lo interface
+    IOAddress loAddr("127.0.0.1");
+    int socket1 = 0, socket2 = 0;
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
+        socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000 + 1);
+    );
+
+    EXPECT_GE(socket1, 0);
+    EXPECT_GE(socket2, 0);
+
+    boost::shared_ptr<Pkt4> sendPkt(new Pkt4(DHCPDISCOVER, 1234) );
+
+    sendPkt->setLocalAddr(IOAddress("127.0.0.1"));
+
+    sendPkt->setLocalPort(DHCP4_SERVER_PORT + 10000 + 1);
+    sendPkt->setRemotePort(DHCP4_SERVER_PORT + 10000);
+    sendPkt->setRemoteAddr(IOAddress("127.0.0.1"));
+    sendPkt->setIndex(1);
+    sendPkt->setIface(string(LOOPBACK));
+    sendPkt->setHops(6);
+    sendPkt->setSecs(42);
+    sendPkt->setCiaddr(IOAddress("192.0.2.1"));
+    sendPkt->setSiaddr(IOAddress("192.0.2.2"));
+    sendPkt->setYiaddr(IOAddress("192.0.2.3"));
+    sendPkt->setGiaddr(IOAddress("192.0.2.4"));
+
+    // unpack() now checks if mandatory DHCP_MESSAGE_TYPE is present
+    boost::shared_ptr<Option> msgType(new Option(Option::V4,
+           static_cast<uint16_t>(DHO_DHCP_MESSAGE_TYPE)));
+    msgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
+    sendPkt->addOption(msgType);
+
+    uint8_t sname[] = "That's just a string that will act as SNAME";
+    sendPkt->setSname(sname, strlen((const char*)sname));
+    uint8_t file[] = "/another/string/that/acts/as/a/file_name.txt";
+    sendPkt->setFile(file, strlen((const char*)file));
+
+    ASSERT_NO_THROW(
+        sendPkt->pack();
+    );
+
+    boost::shared_ptr<Pkt4> rcvPkt;
+
+    EXPECT_EQ(true, ifacemgr->send(sendPkt));
+
+    rcvPkt = ifacemgr->receive4();
+
+    ASSERT_TRUE( rcvPkt ); // received our own packet
+
+    ASSERT_NO_THROW(
+        rcvPkt->unpack();
+    );
+
+    // let's check that we received what was sent
+    EXPECT_EQ(sendPkt->len(), rcvPkt->len());
+
+    EXPECT_EQ("127.0.0.1", rcvPkt->getRemoteAddr().toText());
+    EXPECT_EQ(sendPkt->getRemotePort(), rcvPkt->getLocalPort());
+
+    // now let's check content
+    EXPECT_EQ(sendPkt->getHops(), rcvPkt->getHops());
+    EXPECT_EQ(sendPkt->getOp(),   rcvPkt->getOp());
+    EXPECT_EQ(sendPkt->getSecs(), rcvPkt->getSecs());
+    EXPECT_EQ(sendPkt->getFlags(), rcvPkt->getFlags());
+    EXPECT_EQ(sendPkt->getCiaddr(), rcvPkt->getCiaddr());
+    EXPECT_EQ(sendPkt->getSiaddr(), rcvPkt->getSiaddr());
+    EXPECT_EQ(sendPkt->getYiaddr(), rcvPkt->getYiaddr());
+    EXPECT_EQ(sendPkt->getGiaddr(), rcvPkt->getGiaddr());
+    EXPECT_EQ(sendPkt->getTransid(), rcvPkt->getTransid());
+    EXPECT_EQ(sendPkt->getType(), rcvPkt->getType());
+    EXPECT_TRUE(sendPkt->getSname() == rcvPkt->getSname());
+    EXPECT_TRUE(sendPkt->getFile() == rcvPkt->getFile());
+    EXPECT_EQ(sendPkt->getHtype(), rcvPkt->getHtype());
+    EXPECT_EQ(sendPkt->getHlen(), rcvPkt->getHlen());
+
+    // since we opened 2 sockets on the same interface and none of them is multicast,
+    // none is preferred over the other for sending data, so we really should not
+    // assume the one or the other will always be choosen for sending data. We should
+    // skip checking source port of sent address.
+
+    delete ifacemgr;
+}
+
+
 TEST_F(IfaceMgrTest, socket4) {
 
     createLoInterfacesTxt();
@@ -388,7 +489,6 @@ TEST_F(IfaceMgrTest, socket4) {
     close(socket1);
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
 // Test the Iface structure itself
@@ -518,7 +618,268 @@ TEST_F(IfaceMgrTest, socketInfo) {
     );
 
     delete ifacemgr;
-    unlink(INTERFACE_FILE);
 }
 
+#if defined(OS_LINUX)
+
+/// @brief parses text representation of MAC address
+///
+/// This function parses text representation of a MAC address and stores
+/// it in binary format. Text format is expecte to be separate with
+/// semicolons, e.g. f4:6d:04:96:58:f2
+///
+/// TODO: IfaceMgr::Iface::mac_ uses uint8_t* type, should be vector<uint8_t>
+///
+/// @param textMac string with MAC address to parse
+/// @param mac pointer to output buffer
+/// @param macLen length of output buffer
+///
+/// @return number of bytes filled in output buffer
+size_t parse_mac(const std::string& textMac, uint8_t* mac, size_t macLen) {
+    stringstream tmp(textMac);
+    tmp.flags(ios::hex);
+    int i = 0;
+    uint8_t octet = 0; // output octet
+    uint8_t byte;  // parsed charater from text representation
+    while (!tmp.eof()) {
+
+        tmp >> byte; // hex value
+        if (byte == ':') {
+            mac[i++] = octet;
+
+            if (i == macLen) {
+                // parsing aborted. We hit output buffer size
+                return(i);
+            }
+            octet = 0;
+            continue;
+        }
+        if (isalpha(byte)) {
+            byte = toupper(byte) - 'A' + 10;
+        } else if (isdigit(byte)) {
+            byte -= '0';
+        } else {
+            // parse error. Let's return what we were able to parse so far
+            break;
+        }
+        octet <<= 4;
+        octet += byte;
+    }
+    mac[i++] = octet;
+
+    return (i);
+}
+
+/// @brief Parses 'ifconfig -a' output and creates list of interfaces
+///
+/// This method tries to parse ifconfig output. Note that there are some
+/// oddities in recent versions of ifconfig, like putting extra spaces
+/// after MAC address, inconsistent naming and spacing between inet and inet6.
+/// This is an attempt to find a balance between tight parsing of every piece
+/// of text that ifconfig prints and robustness to handle slight differences
+/// in ifconfig output.
+///
+/// @param textFile name of a text file that holds output of ifconfig -a
+/// @param ifaces empty list of interfaces to be filled
+void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifaces) {
+    fstream f(textFile.c_str());
+
+    bool first_line = true;
+    IfaceMgr::IfaceCollection::iterator iface;
+    while (!f.eof()) {
+        string line;
+        getline(f, line);
+
+        // interfaces are separated by empty line
+        if (line.length() == 0) {
+            first_line = true;
+            continue;
+        }
+
+        // uncomment this for ifconfig output debug
+        // cout << "line[" << line << "]" << endl;
+
+        // this is first line of a new interface
+        if (first_line) {
+            first_line = false;
+
+            size_t offset;
+            offset = line.find_first_of(" ");
+            if (offset == string::npos) {
+                isc_throw(BadValue, "Malformed output of ifconfig");
+            }
+            string name = line.substr(0, offset);
+
+            // sadly, ifconfig does not return ifindex
+            ifaces.push_back(IfaceMgr::Iface(name, 0));
+            iface = ifaces.end();
+            --iface; // points to the last element
+
+            offset = line.find(string("HWaddr"));
+
+            string mac = "";
+            if (offset != string::npos) { // some interfaces don't have MAC (e.g. lo)
+                offset += 7;
+                mac = line.substr(offset, string::npos);
+                mac = mac.substr(0, mac.find_first_of(" "));
+
+                iface->mac_len_ = parse_mac(mac, iface->mac_, IfaceMgr::MAX_MAC_LEN);
+            }
+        }
+
+        if (line.find("inet6") != string::npos) {
+            // IPv6 address
+            string addr = line.substr(line.find("inet6")+12, string::npos);
+            addr = addr.substr(0, addr.find("/"));
+            IOAddress a(addr);
+            iface->addAddress(a);
+        } else if(line.find("inet") != string::npos) {
+            // IPv4 address
+            string addr = line.substr(line.find("inet")+10, string::npos);
+            addr = addr.substr(0, addr.find_first_of(" "));
+            IOAddress a(addr);
+            iface->addAddress(a);
+        } else if(line.find("Metric")) {
+            // flags
+            if (line.find("UP") != string::npos) {
+                iface->flag_up_ = true;
+            }
+            if (line.find("LOOPBACK") != string::npos) {
+                iface->flag_loopback_ = true;
+            }
+            if (line.find("RUNNING") != string::npos) {
+                iface->flag_running_ = true;
+            }
+            if (line.find("BROADCAST") != string::npos) {
+                iface->flag_broadcast_ = true;
+            }
+            if (line.find("MULTICAST") != string::npos) {
+                iface->flag_multicast_ = true;
+            }
+        }
+    }
+}
+
+
+// This test compares implemented detection routines to output of "ifconfig -a" command.
+// It is far from perfect, but it is able to verify that interface names, flags,
+// MAC address, IPv4 and IPv6 addresses are detected properly. Interface list completeness
+// (check that each interface is reported, i.e. no missing or extra interfaces) and
+// address completeness is verified.
+//
+// Things that are not tested:
+// - ifindex (ifconfig does not print it out)
+// - address scopes and lifetimes (we don't need it, so it is not implemented in IfaceMgr)
+TEST_F(IfaceMgrTest, detectIfaces_linux) {
+
+    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    IfaceMgr::IfaceCollection& detectedIfaces = ifacemgr->getIfacesLst();
+
+    const std::string textFile = "ifconfig.txt";
+
+    unlink(textFile.c_str());
+    int result = system( ("/sbin/ifconfig -a > " + textFile).c_str());
+
+    ASSERT_EQ(0, result);
+
+    // list of interfaces parsed from ifconfig
+    IfaceMgr::IfaceCollection parsedIfaces;
+
+    EXPECT_NO_THROW(
+        parse_ifconfig(textFile, parsedIfaces);
+    );
+    unlink(textFile.c_str());
+
+    cout << "------Parsed interfaces---" << endl;
+    for (IfaceMgr::IfaceCollection::iterator i = parsedIfaces.begin();
+         i != parsedIfaces.end(); ++i) {
+        cout << i->getName() << ": ifindex=" << i->getIndex() << ", mac=" << i->getPlainMac();
+        cout << ", flags:";
+        if (i->flag_up_) {
+            cout << " UP";
+        }
+        if (i->flag_running_) {
+            cout << " RUNNING";
+        }
+        if (i->flag_multicast_) {
+            cout << " MULTICAST";
+        }
+        if (i->flag_broadcast_) {
+            cout << " BROADCAST";
+        }
+        cout << ", addrs:";
+        const IfaceMgr::AddressCollection& addrs = i->getAddresses();
+        for (IfaceMgr::AddressCollection::const_iterator a= addrs.begin();
+             a != addrs.end(); ++a) {
+            cout << a->toText() << " ";
+        }
+        cout << endl;
+    }
+
+    // Ok, now we have 2 lists of interfaces. Need to compare them
+    ASSERT_EQ(detectedIfaces.size(), parsedIfaces.size());
+
+    // TODO: This could could probably be written simple with find()
+    for (IfaceMgr::IfaceCollection::iterator detected = detectedIfaces.begin();
+         detected != detectedIfaces.end(); ++detected) {
+        // let's find out if this interface is
+
+        bool found = false;
+        for (IfaceMgr::IfaceCollection::iterator i = parsedIfaces.begin();
+             i != parsedIfaces.end(); ++i) {
+            if (detected->getName() != i->getName()) {
+                continue;
+            }
+            found = true;
+
+            cout << "Checking interface " << detected->getName() << endl;
+
+            // start with checking flags
+            EXPECT_EQ(detected->flag_loopback_, i->flag_loopback_);
+            EXPECT_EQ(detected->flag_up_, i->flag_up_);
+            EXPECT_EQ(detected->flag_running_, i->flag_running_);
+            EXPECT_EQ(detected->flag_multicast_, i->flag_multicast_);
+            EXPECT_EQ(detected->flag_broadcast_, i->flag_broadcast_);
+
+            // skip MAC comparison for loopback as netlink returns MAC
+            // 00:00:00:00:00:00 for lo
+            if (!detected->flag_loopback_) {
+                ASSERT_EQ(detected->mac_len_, i->mac_len_);
+                EXPECT_EQ(0, memcmp(detected->mac_, i->mac_, i->mac_len_));
+            }
+
+            EXPECT_EQ(detected->getAddresses().size(), i->getAddresses().size());
+
+            // now compare addresses
+            const IfaceMgr::AddressCollection& addrs = detected->getAddresses();
+            for (IfaceMgr::AddressCollection::const_iterator addr = addrs.begin();
+                 addr != addrs.end(); ++addr) {
+                bool addr_found = false;
+
+                const IfaceMgr::AddressCollection& addrs2 = detected->getAddresses();
+                for (IfaceMgr::AddressCollection::const_iterator a = addrs2.begin();
+                     a != addrs2.end(); ++a) {
+                    if (*addr != *a) {
+                        continue;
+                    }
+                    addr_found = true;
+                }
+                if (!addr_found) {
+                    cout << "ifconfig does not seem to report " << addr->toText()
+                         << " address on " << detected->getFullName() << " interface." << endl;
+                    FAIL();
+                }
+                cout << "Address " << addr->toText() << " on iterface " << detected->getFullName()
+                     << " matched with 'ifconfig -a' output." << endl;
+            }
+        }
+        if (!found) { // corresponding interface was not found
+            FAIL();
+        }
+    }
+
+    delete ifacemgr;
+}
+#endif
+
 }
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 66dce8f..c83a839 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -33,8 +33,14 @@ using namespace isc::util;
 namespace {
 class OptionTest : public ::testing::Test {
 public:
-    OptionTest() {
+    OptionTest(): outBuffer_(255) {
+        buf_ = boost::shared_array<uint8_t>(new uint8_t[255]);
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
     }
+    boost::shared_array<uint8_t> buf_;
+    OutputBuffer outBuffer_;
 };
 
 // v4 is not really implemented yet. A simple test will do for now
@@ -419,3 +425,78 @@ TEST_F(OptionTest, v6_toText) {
 
     EXPECT_EQ("type=258, len=3: 00:0f:ff", opt->toText());
 }
+
+TEST_F(OptionTest, getUintX) {
+
+    // TODO: Update this test to use buf_ instead of buf
+    boost::shared_array<uint8_t> buf(new uint8_t[5]);
+    buf[0] = 0x5;
+    buf[1] = 0x4;
+    buf[2] = 0x3;
+    buf[3] = 0x2;
+    buf[4] = 0x1;
+
+    // five options with varying lengths
+    boost::shared_ptr<Option> opt1(new Option(Option::V6, 258, buf, 0, 1));
+    boost::shared_ptr<Option> opt2(new Option(Option::V6, 258, buf, 0, 2));
+    boost::shared_ptr<Option> opt3(new Option(Option::V6, 258, buf, 0, 3));
+    boost::shared_ptr<Option> opt4(new Option(Option::V6, 258, buf, 0, 4));
+    boost::shared_ptr<Option> opt5(new Option(Option::V6, 258, buf, 0, 5));
+
+    EXPECT_EQ(5, opt1->getUint8());
+    EXPECT_THROW(opt1->getUint16(), OutOfRange);
+    EXPECT_THROW(opt1->getUint32(), OutOfRange);
+
+    EXPECT_EQ(5, opt2->getUint8());
+    EXPECT_EQ(0x0504, opt2->getUint16());
+    EXPECT_THROW(opt2->getUint32(), OutOfRange);
+
+    EXPECT_EQ(5, opt3->getUint8());
+    EXPECT_EQ(0x0504, opt3->getUint16());
+    EXPECT_THROW(opt3->getUint32(), OutOfRange);
+
+    EXPECT_EQ(5, opt4->getUint8());
+    EXPECT_EQ(0x0504, opt4->getUint16());
+    EXPECT_EQ(0x05040302, opt4->getUint32());
+
+    // the same as for 4-byte long, just get first 1,2 or 4 bytes
+    EXPECT_EQ(5, opt5->getUint8());
+    EXPECT_EQ(0x0504, opt5->getUint16());
+    EXPECT_EQ(0x05040302, opt5->getUint32());
+
+}
+
+TEST_F(OptionTest, setUintX) {
+    boost::shared_ptr<Option> opt1(new Option(Option::V4, 125));
+    boost::shared_ptr<Option> opt2(new Option(Option::V4, 125));
+    boost::shared_ptr<Option> opt4(new Option(Option::V4, 125));
+
+    // verify setUint8
+    opt1->setUint8(255);
+    EXPECT_EQ(255, opt1->getUint8());
+    opt1->pack4(outBuffer_);
+    EXPECT_EQ(3, opt1->len());
+    EXPECT_EQ(3, outBuffer_.getLength());
+    uint8_t exp1[] = {125, 1, 255};
+    EXPECT_TRUE(0 == memcmp(exp1, outBuffer_.getData(), 3));
+
+    // verify getUint16
+    outBuffer_.clear();
+    opt2->setUint16(12345);
+    opt2->pack4(outBuffer_);
+    EXPECT_EQ(12345, opt2->getUint16());
+    EXPECT_EQ(4, opt2->len());
+    EXPECT_EQ(4, outBuffer_.getLength());
+    uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
+    EXPECT_TRUE(0 == memcmp(exp2, outBuffer_.getData(), 4));
+
+    // verity getUint32
+    outBuffer_.clear();
+    opt4->setUint32(0x12345678);
+    opt4->pack4(outBuffer_);
+    EXPECT_EQ(0x12345678, opt4->getUint32());
+    EXPECT_EQ(6, opt4->len());
+    EXPECT_EQ(6, outBuffer_.getLength());
+    uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
+    EXPECT_TRUE(0 == memcmp(exp4, outBuffer_.getData(), 6));
+}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index fd28c5d..9936ca4 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -197,7 +197,7 @@ generateTestPacket2() {
 
 TEST(Pkt4Test, fixedFields) {
 
-    shared_ptr<Pkt4> pkt = generateTestPacket1();
+    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
 
     // ok, let's check packet values
     EXPECT_EQ(dummyOp, pkt->getOp());
@@ -224,7 +224,7 @@ TEST(Pkt4Test, fixedFields) {
 }
 
 TEST(Pkt4Test, fixedFieldsPack) {
-    shared_ptr<Pkt4> pkt = generateTestPacket1();
+    boost::shared_ptr<Pkt4> pkt = generateTestPacket1();
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
     EXPECT_NO_THROW(
@@ -244,8 +244,18 @@ TEST(Pkt4Test, fixedFieldsPack) {
 TEST(Pkt4Test, fixedFieldsUnpack) {
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
-    shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
-                                  Pkt4::DHCPV4_PKT_HDR_LEN));
+    expectedFormat.push_back(0x63); // magic cookie
+    expectedFormat.push_back(0x82);
+    expectedFormat.push_back(0x53);
+    expectedFormat.push_back(0x63);
+
+    expectedFormat.push_back(0x35); // message-type
+    expectedFormat.push_back(0x1);
+    expectedFormat.push_back(0x1);
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
+                                         expectedFormat.size()));;
+
 
     EXPECT_NO_THROW(
         pkt->unpack()
@@ -441,8 +451,9 @@ static uint8_t v4Opts[] = {
     12,  3, 0,   1,  2,
     13,  3, 10, 11, 12,
     14,  3, 20, 21, 22,
+    53, 1, 1, // DHCP_MESSAGE_TYPE (required to not throw exception during unpack)
     128, 3, 30, 31, 32,
-    254, 3, 40, 41, 42
+    254, 3, 40, 41, 42,
 };
 
 TEST(Pkt4Test, options) {
@@ -458,14 +469,17 @@ TEST(Pkt4Test, options) {
     boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
     boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
     boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
+    boost::shared_ptr<Option> optMsgType(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE));
     boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[3]));
     boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[4]));
+    optMsgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
 
     pkt->addOption(opt1);
     pkt->addOption(opt2);
     pkt->addOption(opt3);
     pkt->addOption(opt4);
     pkt->addOption(opt5);
+    pkt->addOption(optMsgType);
 
     EXPECT_TRUE(pkt->getOption(12));
     EXPECT_TRUE(pkt->getOption(13));
@@ -486,14 +500,14 @@ TEST(Pkt4Test, options) {
     );
 
     const OutputBuffer& buf = pkt->getBuffer();
-    // check that all options are stored, they should take sizeof(v4Opts)
-    // there also should be OPTION_END added (just one byte)
-    ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + sizeof(v4Opts) + 1,
-              buf.getLength());
+    // check that all options are stored, they should take sizeof(v4Opts),
+    // DHCP magic cookie (4 bytes), and OPTION_END added (just one byte)
+    ASSERT_EQ(static_cast<size_t>(Pkt4::DHCPV4_PKT_HDR_LEN) + sizeof(DHCP_OPTIONS_COOKIE)
+              + sizeof(v4Opts) + 1, buf.getLength());
 
     // that that this extra data actually contain our options
     const uint8_t* ptr = static_cast<const uint8_t*>(buf.getData());
-    ptr += Pkt4::DHCPV4_PKT_HDR_LEN; // rewind to end of fixed part
+    ptr += Pkt4::DHCPV4_PKT_HDR_LEN + sizeof(DHCP_OPTIONS_COOKIE); // rewind to end of fixed part
     EXPECT_EQ(0, memcmp(ptr, v4Opts, sizeof(v4Opts)));
     EXPECT_EQ(DHO_END, static_cast<uint8_t>(*(ptr + sizeof(v4Opts))));
 
@@ -506,14 +520,19 @@ TEST(Pkt4Test, unpackOptions) {
 
     vector<uint8_t> expectedFormat = generateTestPacket2();
 
+    expectedFormat.push_back(0x63);
+    expectedFormat.push_back(0x82);
+    expectedFormat.push_back(0x53);
+    expectedFormat.push_back(0x63);
+
     for (int i = 0; i < sizeof(v4Opts); i++) {
         expectedFormat.push_back(v4Opts[i]);
     }
 
     // now expectedFormat contains fixed format and 5 options
 
-    shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
-                                  expectedFormat.size()));
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
+                                expectedFormat.size()));
 
     EXPECT_NO_THROW(
         pkt->unpack()
@@ -525,7 +544,7 @@ TEST(Pkt4Test, unpackOptions) {
     EXPECT_TRUE(pkt->getOption(128));
     EXPECT_TRUE(pkt->getOption(254));
 
-    shared_ptr<Option> x = pkt->getOption(12);
+    boost::shared_ptr<Option> x = pkt->getOption(12);
     ASSERT_TRUE(x); // option 1 should exist
     EXPECT_EQ(12, x->getType());  // this should be option 12
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
@@ -551,14 +570,14 @@ TEST(Pkt4Test, unpackOptions) {
     EXPECT_EQ(128, x->getType());  // this should be option 254
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+17, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+20, 3)); // data len=3
 
     x = pkt->getOption(254);
     ASSERT_TRUE(x); // option 3 should exist
     EXPECT_EQ(254, x->getType());  // this should be option 254
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+22, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+25, 3)); // data len=3
 }
 
 // This test verifies methods that are used for manipulating meta fields
@@ -575,7 +594,6 @@ TEST(Pkt4Test, metaFields) {
     EXPECT_EQ(42, pkt->getIndex());
     EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
     EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
-
 }
 
 } // end of anonymous namespace
diff --git a/src/lib/dns/masterload.cc b/src/lib/dns/masterload.cc
index 5f7d5a0..a32d4b7 100644
--- a/src/lib/dns/masterload.cc
+++ b/src/lib/dns/masterload.cc
@@ -40,8 +40,11 @@ void
 masterLoad(const char* const filename, const Name& origin,
            const RRClass& zone_class, MasterLoadCallback callback)
 {
-    ifstream ifs;
+    if ((filename == NULL) || (*filename == '\0')) {
+        isc_throw(MasterLoadError, "Name of master file must not be null");
+    }
 
+    ifstream ifs;
     ifs.open(filename, ios_base::in);
     if (ifs.fail()) {
         isc_throw(MasterLoadError, "Failed to open master file: " << filename);
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 02f5519..1378ba9 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -117,7 +117,7 @@ private:
                           uint16_t pos, uint16_t& llen) const
     {
         if (llen == 0) {
-            int i = 0;
+            size_t i = 0;
 
             while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
                    Name::COMPRESS_POINTER_MARK8) {
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 0f0f873..8e623c5 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -13,7 +13,6 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <Python.h>
-#include <pydnspp_common.h>
 
 #include <exceptions/exceptions.h>
 
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index 27496a2..c1ece52 100644
--- a/src/lib/dns/rdata.cc
+++ b/src/lib/dns/rdata.cc
@@ -112,7 +112,9 @@ Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
     }
 
     vector<uint8_t> data(rdata_len);
-    buffer.readData(&data[0], rdata_len);
+    if (rdata_len > 0) {
+        buffer.readData(&data[0], rdata_len);
+    }
 
     impl_ = new GenericImpl(data);
 }
@@ -242,8 +244,10 @@ compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) {
     size_t len = (this_len < other_len) ? this_len : other_len;
     int cmp;
 
-    if ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len))
-        != 0) {
+    // TODO: is there a need to check len - should we just assert?
+    // (Depends if it is possible for rdata to have zero length)
+    if ((len != 0) &&
+        ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len)) != 0)) {
         return (cmp);
     } else {
         return ((this_len == other_len) ? 0 :
diff --git a/src/lib/dns/rdata/generic/dnskey_48.cc b/src/lib/dns/rdata/generic/dnskey_48.cc
index e0f5461..7bdbd05 100644
--- a/src/lib/dns/rdata/generic/dnskey_48.cc
+++ b/src/lib/dns/rdata/generic/dnskey_48.cc
@@ -181,7 +181,7 @@ DNSKEY::getTag() const {
     ac += impl_->algorithm_;
     
     size_t size = impl_->keydata_.size();
-    for (int i = 0; i < size; i ++) {
+    for (size_t i = 0; i < size; i ++) {
         ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8);
     }
     ac += (ac >> 16) & 0xffff;
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index 3bd0bb2..092695c 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -221,7 +221,7 @@ string
 NSEC3::toText() const {
     ostringstream s;
     int len = 0;
-    for (int i = 0; i < impl_->typebits_.size(); i += len) {
+    for (size_t i = 0; i < impl_->typebits_.size(); i += len) {
         assert(i + 2 <= impl_->typebits_.size());
         int window = impl_->typebits_[i];
         len = impl_->typebits_[i + 1];
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index 4723c23..a9a9f75 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/src/lib/dns/rdata/generic/nsec_47.cc
@@ -143,7 +143,7 @@ NSEC::toText() const {
     // and easier to find a bug (if any).  Note that this conversion method
     // is generally not expected to be very efficient, so the slight overhead
     // of at() should be acceptable.
-    for (int i = 0; i < impl_->typebits_.size(); i += len) {
+    for (size_t i = 0; i < impl_->typebits_.size(); i += len) {
         assert(i + 2 <= impl_->typebits_.size());
         const int block = impl_->typebits_.at(i);
         len = impl_->typebits_.at(i + 1);
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
index d3f282b..af9ba2e 100644
--- a/src/lib/dns/rdatafields.cc
+++ b/src/lib/dns/rdatafields.cc
@@ -171,7 +171,7 @@ RdataFields::RdataFields(const void* fields, const unsigned int fields_length,
     }
 
     size_t total_length = 0;
-    for (int i = 0; i < nfields_; ++i) {
+    for (unsigned int i = 0; i < nfields_; ++i) {
         total_length += fields_[i].len;
     }
     if (total_length != data_length_) {
@@ -198,7 +198,7 @@ void
 RdataFields::toWire(AbstractMessageRenderer& renderer) const {
     size_t offset = 0;
 
-    for (int i = 0; i < nfields_; ++i) {
+    for (unsigned int i = 0; i < nfields_; ++i) {
         if (fields_[i].type == DATA) {
             renderer.writeData(data_ + offset, fields_[i].len);
         } else {
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 5058ca3..62a9e34 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -77,7 +77,7 @@ struct RRTypeParam {
     static size_t UNKNOWN_MAXLEN();
 };
 
-typedef shared_ptr<RRTypeParam> RRTypeParamPtr;
+typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr;
 typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap;
 typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap;
 
@@ -119,7 +119,7 @@ struct RRClassParam {
     static size_t UNKNOWN_MAXLEN();
 };
 
-typedef shared_ptr<RRClassParam> RRClassParamPtr;
+typedef boost::shared_ptr<RRClassParam> RRClassParamPtr;
 typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap;
 typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap;
 
@@ -342,7 +342,7 @@ addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap)
         return (false);
     }
 
-    typedef shared_ptr<PT> ParamPtr;
+    typedef boost::shared_ptr<PT> ParamPtr;
     typedef pair<string, ParamPtr> StrParamPair;
     typedef pair<uint16_t, ParamPtr> CodeParamPair;
     ParamPtr param = ParamPtr(new PT(code_string, code));
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index f068791..c4d4984 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -207,7 +207,7 @@ TEST_F(MessageTest, fromWireWithTSIG) {
     EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation);
 
     factoryFromFile(message_parse, "message_toWire2.wire");
-    const char expected_mac[] = {
+    const uint8_t expected_mac[] = {
         0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
         0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
     };
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index c9c0982..ca9d347 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -116,7 +116,7 @@ UnitTestUtil::readWireData(const string& datastr,
             throw runtime_error(err_oss.str());
         }
 
-        for (int pos = 0; pos < bytes.size(); pos += 2) {
+        for (string::size_type pos = 0; pos < bytes.size(); pos += 2) {
             istringstream iss_byte(bytes.substr(pos, 2));
             unsigned int ch;
 
@@ -139,7 +139,7 @@ UnitTestUtil::matchWireData(const char*, const char*, const char*, const char*,
     ::testing::Message msg;
     size_t cmplen = min(len1, len2);
 
-    for (int i = 0; i < cmplen; i++) {
+    for (size_t i = 0; i < cmplen; i++) {
         int ch1 = static_cast<const uint8_t*>(data1)[i];
         int ch2 = static_cast<const uint8_t*>(data2)[i];
         if (ch1 != ch2) {
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index f74020a..3c8a3f2 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -224,7 +224,7 @@ writeOpeningNamespace(ostream& output, const vector<string>& ns) {
     if (!ns.empty()) {
 
         // Output namespaces in correct order
-        for (int i = 0; i < ns.size(); ++i) {
+        for (vector<string>::size_type i = 0; i < ns.size(); ++i) {
             output << "namespace " << ns[i] << " {\n";
         }
         output << "\n";
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index 70e0d6f..a04fffb 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -167,7 +167,7 @@ LoggerManager::readLocalMessageFile(const char* file) {
         // Log the variable number of arguments.  The actual message will be
         // logged when the error_message variable is destroyed.
         Formatter<isc::log::Logger> error_message = logger.error(ident);
-        for (int i = 0; i < args.size(); ++i) {
+        for (vector<string>::size_type i = 0; i < args.size(); ++i) {
             error_message = error_message.arg(args[i]);
         }
     }
diff --git a/src/lib/log/logger_manager_impl.h b/src/lib/log/logger_manager_impl.h
index f99f832..42a98f1 100644
--- a/src/lib/log/logger_manager_impl.h
+++ b/src/lib/log/logger_manager_impl.h
@@ -31,7 +31,7 @@ namespace log {
 
 // Forward declarations
 class LoggerSpecification;
-class OutputOption;
+struct OutputOption;
 
 /// \brief Logger Manager Implementation
 ///
diff --git a/src/lib/log/logger_specification.h b/src/lib/log/logger_specification.h
index 6805fdd..78df054 100644
--- a/src/lib/log/logger_specification.h
+++ b/src/lib/log/logger_specification.h
@@ -97,7 +97,7 @@ public:
     }
 
     /// \return Return additive flag.
-    int getAdditive() const {
+    bool getAdditive() const {
         return additive_;
     }
 
diff --git a/src/lib/log/output_option.h b/src/lib/log/output_option.h
index cbb7e95..8dfdd70 100644
--- a/src/lib/log/output_option.h
+++ b/src/lib/log/output_option.h
@@ -60,7 +60,7 @@ struct OutputOption {
 
     /// \brief Constructor
     OutputOption() : destination(DEST_CONSOLE), stream(STR_STDERR),
-                     flush(false), facility("LOCAL0"), filename(""),
+                     flush(true), facility("LOCAL0"), filename(""),
                      maxsize(0), maxver(0)
     {}
 
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
index 0bdfc74..9eb85ad 100644
--- a/src/lib/log/tests/logger_manager_unittest.cc
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -315,7 +315,7 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
     EXPECT_FALSE(file3.good());
 
     // Tidy up
-    for (int i = 0; i < prev_name.size(); ++i) {
+    for (vector<string>::size_type i = 0; i < prev_name.size(); ++i) {
        (void) unlink(prev_name[i].c_str());
     }
 }
diff --git a/src/lib/log/tests/output_option_unittest.cc b/src/lib/log/tests/output_option_unittest.cc
index 8f0e0de..ce00bb6 100644
--- a/src/lib/log/tests/output_option_unittest.cc
+++ b/src/lib/log/tests/output_option_unittest.cc
@@ -29,7 +29,7 @@ TEST(OutputOptionTest, Initialization) {
 
     EXPECT_EQ(OutputOption::DEST_CONSOLE, option.destination);
     EXPECT_EQ(OutputOption::STR_STDERR, option.stream);
-    EXPECT_FALSE(option.flush);
+    EXPECT_TRUE(option.flush);
     EXPECT_EQ(string("LOCAL0"), option.facility);
     EXPECT_EQ(string(""), option.filename);
     EXPECT_EQ(0, option.maxsize);
diff --git a/src/lib/nsas/hash_key.cc b/src/lib/nsas/hash_key.cc
index bf4676b..782e3d8 100644
--- a/src/lib/nsas/hash_key.cc
+++ b/src/lib/nsas/hash_key.cc
@@ -35,7 +35,7 @@ bool HashKey::operator==(const isc::nsas::HashKey& other) {
             // variation (stops on the first null byte).
             //
             // TODO: Use a lookup table to map upper to lower case (for speed)
-            for (int i = 0; i < other.keylen; ++i) {
+            for (uint32_t i = 0; i < other.keylen; ++i) {
                 if (tolower(static_cast<unsigned char>(other.key[i])) !=
                     tolower(static_cast<unsigned char>(key[i]))) {
                     return false;   // Mismatch
diff --git a/src/lib/nsas/tests/hash_table_unittest.cc b/src/lib/nsas/tests/hash_table_unittest.cc
index 7ba25b5..f4c28fd 100644
--- a/src/lib/nsas/tests/hash_table_unittest.cc
+++ b/src/lib/nsas/tests/hash_table_unittest.cc
@@ -30,7 +30,6 @@
 #include "nsas_test.h"
 
 using namespace std;
-using boost::shared_ptr;
 using namespace isc::dns;
 
 namespace isc {
@@ -180,8 +179,8 @@ TEST_F(HashTableTest, GetTest) {
     EXPECT_TRUE(value.get() == NULL);
 }
 
-shared_ptr<TestEntry>
-pass(shared_ptr<TestEntry> value) {
+boost::shared_ptr<TestEntry>
+pass(boost::shared_ptr<TestEntry> value) {
     return (value);
 }
 
@@ -190,7 +189,7 @@ TEST_F(HashTableTest, GetOrAddTest) {
     EXPECT_TRUE(table_.add(dummy1_, dummy1_->hashKey()));
 
     // Check it looks it up
-    std::pair<bool, shared_ptr<TestEntry> > result = table_.getOrAdd(
+    std::pair<bool, boost::shared_ptr<TestEntry> > result = table_.getOrAdd(
         dummy1_->hashKey(), boost::bind(pass, dummy3_));
     EXPECT_FALSE(result.first);
     EXPECT_EQ(dummy1_.get(), result.second.get());
diff --git a/src/lib/nsas/tests/nameserver_address_store_unittest.cc b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
index abbac1d..4785627 100644
--- a/src/lib/nsas/tests/nameserver_address_store_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
@@ -16,6 +16,8 @@
 ///
 /// This file contains tests for the nameserver address store as a whole.
 
+#include <config.h>
+
 #include <algorithm>
 #include <cassert>
 #include <string.h>
@@ -26,8 +28,6 @@
 #include <boost/shared_ptr.hpp>
 #include <gtest/gtest.h>
 
-#include <config.h>
-
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrset.h>
diff --git a/src/lib/python/isc/acl/dns_requestloader_python.cc b/src/lib/python/isc/acl/dns_requestloader_python.cc
index ab421c5..24ae599 100644
--- a/src/lib/python/isc/acl/dns_requestloader_python.cc
+++ b/src/lib/python/isc/acl/dns_requestloader_python.cc
@@ -36,7 +36,6 @@
 #include "dns_requestloader_python.h"
 
 using namespace std;
-using boost::shared_ptr;
 using namespace isc::util::python;
 using namespace isc::data;
 using namespace isc::acl::dns;
@@ -121,7 +120,7 @@ RequestLoader_load(PyObject* po_self, PyObject* args) {
             }
         }
         if (py_result) {
-            shared_ptr<RequestACL> acl(
+            boost::shared_ptr<RequestACL> acl(
                 self->cppobj->load(Element::fromJSON(acl_config)));
             s_RequestACL* py_acl = static_cast<s_RequestACL*>(
                 requestacl_type.tp_alloc(&requestacl_type, 0));
diff --git a/src/lib/python/isc/datasrc/finder_inc.cc b/src/lib/python/isc/datasrc/finder_inc.cc
index 82c5fdc..25910e7 100644
--- a/src/lib/python/isc/datasrc/finder_inc.cc
+++ b/src/lib/python/isc/datasrc/finder_inc.cc
@@ -46,9 +46,8 @@ Return the RR class of the zone.\n\
 // - Return type: use tuple instead of the dedicated FindResult type
 // - NULL->None
 // - exceptions
-// - description of the 'target' parameter (must be None for now)
 const char* const ZoneFinder_find_doc = "\
-find(name, type, target=None, options=FIND_DEFAULT) -> (integer, RRset)\n\
+find(name, type, options=FIND_DEFAULT) -> (integer, RRset)\n\
 \n\
 Search the zone for a given pair of domain name and RR type.\n\
 \n\
@@ -72,14 +71,6 @@ answer for the search key. Specifically,\n\
   and the code of SUCCESS will be returned.\n\
 - If the search name matches a delegation point of DNAME, it returns\n\
   the code of DNAME and that DNAME RR.\n\
-- If the target isn't None, all RRsets under the domain are inserted\n\
-  there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned\n\
-  instead of normall processing. This is intended to handle ANY query.\n\
-  (Note: the Python version doesn't support this feature yet)\n\
-\n\
-Note: This behavior is controversial as we discussed in\n\
-https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html We\n\
-should revisit the interface before we heavily rely on it.\n\
 \n\
 The options parameter specifies customized behavior of the search.\n\
 Their semantics is as follows (they are or bit-field):\n\
@@ -100,6 +91,10 @@ Their semantics is as follows (they are or bit-field):\n\
   of the non existence of any matching wildcard or non existence of an\n\
   exact match when a wildcard match is found.\n\
 \n\
+In general, name is expected to be included in the zone, that is, it\n\
+should be equal to or a subdomain of the zone origin. Otherwise this\n\
+method will return NXDOMAIN with an empty RRset. But such a case\n\
+should rather be considered a caller's bug.\n\
 \n\
 This method raises an isc.datasrc.Error exception if there is an\n\
 internal error in the datasource.\n\
@@ -107,13 +102,32 @@ internal error in the datasource.\n\
 Parameters:\n\
   name       The domain name to be searched for.\n\
   type       The RR type to be searched for.\n\
-  target     Must be None.\n\
   options    The search options.\n\
 \n\
 Return Value(s): A tuple of a result code (integer) and an RRset object\n\
 enclosing the search result (see above).\n\
 ";
 
+const char* const ZoneFinder_find_all_doc = "\
+find_all(isc.dns.Name, options=FIND_DEFAULT) -> (integer, RRset) | (integer, [RRset])\
+\n\
+This acts mostly the same as the find method. The main difference is,\n\
+when the lookup is successful (eg. the first part of the result is either\n\
+SUCCESS or WILDCARD), the second part is list of all RRsets in the given name\n\
+instead of a single RRset as in case of find.\n\
+\n\
+This method raises an isc.datasrc.Error exception if there is an\n\
+internal error in the datasource.\n\
+\n\
+Parameters:\n\
+  name       The domain name to be searched for.\n\
+  options    The search options.\n\
+\n\
+Return Value(s): A tuple of a result code (integer) and an either RRset object,\n\
+for cases where the result is some kind of delegation, CNAME or similar, or list\n\
+of RRset objects, containing all the results.\n\
+";
+
 const char* const ZoneFinder_find_previous_name_doc = "\
 find_previous_name(isc.dns.Name) -> isc.dns.Name\n\
 \n\
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
index 7f74133..5df944f 100644
--- a/src/lib/python/isc/datasrc/finder_python.cc
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -59,22 +59,16 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
     }
     PyObject* name;
     PyObject* rrtype;
-    PyObject* target = Py_None;
     unsigned int options_int = ZoneFinder::FIND_DEFAULT;
-    if (PyArg_ParseTuple(args, "O!O!|OI", &name_type, &name,
+    if (PyArg_ParseTuple(args, "O!O!|I", &name_type, &name,
                                          &rrtype_type, &rrtype,
-                                         &target, &options_int)) {
+                                         &options_int)) {
         try {
-            if (target != Py_None) {
-                PyErr_SetString(PyExc_TypeError,
-                                "find(): target must be None in this version");
-                return (NULL);
-            }
             ZoneFinder::FindOptions options =
                 static_cast<ZoneFinder::FindOptions>(options_int);
             const ZoneFinder::FindResult find_result(
                 finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
-                             NULL, options));
+                             options));
             const ZoneFinder::Result r = find_result.code;
             isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
             if (rrsp) {
@@ -97,7 +91,60 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
     } else {
         return (NULL);
     }
-    return Py_BuildValue("I", 1);
+}
+
+PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
+    if (finder == NULL) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Internal error in find_all() wrapper; "
+                        "finder object NULL");
+        return (NULL);
+    }
+    PyObject* name;
+    const unsigned int options_int = ZoneFinder::FIND_DEFAULT;
+    if (PyArg_ParseTuple(args, "O!|I", &name_type, &name, &options_int)) {
+        try {
+            ZoneFinder::FindOptions options =
+                static_cast<ZoneFinder::FindOptions>(options_int);
+            std::vector<isc::dns::ConstRRsetPtr> target;
+            const ZoneFinder::FindResult find_result(
+                finder->findAll(PyName_ToName(name), target, options));
+            const ZoneFinder::Result r = find_result.code;
+            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            if (r == ZoneFinder::SUCCESS || r == ZoneFinder::WILDCARD) {
+                // Copy all the RRsets to the result list
+                PyObjectContainer list_container(PyList_New(target.size()));
+                for (size_t i(0); i < target.size(); ++i) {
+                    PyList_SET_ITEM(list_container.get(), i,
+                                    createRRsetObject(*target[i]));
+                }
+                // Construct the result with the list. The Py_BuildValue
+                // increases the refcount and the container decreases it
+                // later. This way, it feels safer in case the build function
+                // would fail.
+                return (Py_BuildValue("IO", r, list_container.get()));
+            } else {
+                if (rrsp) {
+                    // Use N instead of O so the refcount isn't increased twice
+                    return (Py_BuildValue("IN", r, createRRsetObject(*rrsp)));
+                } else {
+                    return (Py_BuildValue("IO", r, Py_None));
+                }
+            }
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
 }
 
 } // end namespace internal
@@ -121,7 +168,7 @@ typedef CPPPyObjectContainer<s_ZoneFinder, ZoneFinder> ZoneFinderContainer;
 
 // General creation and destruction
 int
-ZoneFinder_init(s_ZoneFinder* self, PyObject* args) {
+ZoneFinder_init(PyObject*, PyObject*, PyObject*) {
     // can't be called directly
     PyErr_SetString(PyExc_TypeError,
                     "ZoneFinder cannot be constructed directly");
@@ -130,7 +177,8 @@ ZoneFinder_init(s_ZoneFinder* self, PyObject* args) {
 }
 
 void
-ZoneFinder_destroy(s_ZoneFinder* const self) {
+ZoneFinder_destroy(PyObject* po_self) {
+    s_ZoneFinder* self = static_cast<s_ZoneFinder*>(po_self);
     // cppobj is a shared ptr, but to make sure things are not destroyed in
     // the wrong order, we reset it here.
     self->cppobj.reset();
@@ -173,6 +221,13 @@ ZoneFinder_find(PyObject* po_self, PyObject* args) {
 }
 
 PyObject*
+ZoneFinder_find_all(PyObject* po_self, PyObject* args) {
+    s_ZoneFinder* const self = static_cast<s_ZoneFinder*>(po_self);
+    return (isc_datasrc_internal::ZoneFinder_helper_all(self->cppobj.get(),
+                                                        args));
+}
+
+PyObject*
 ZoneFinder_findPreviousName(PyObject* po_self, PyObject* args) {
     s_ZoneFinder* const self = static_cast<s_ZoneFinder*>(po_self);
     PyObject* name_obj;
@@ -208,6 +263,7 @@ PyMethodDef ZoneFinder_methods[] = {
        ZoneFinder_getOrigin_doc },
     { "get_class", ZoneFinder_getClass, METH_NOARGS, ZoneFinder_getClass_doc },
     { "find", ZoneFinder_find, METH_VARARGS, ZoneFinder_find_doc },
+    { "find_all", ZoneFinder_find_all, METH_VARARGS, ZoneFinder_find_all_doc },
     { "find_previous_name", ZoneFinder_findPreviousName, METH_VARARGS,
       ZoneFinder_find_previous_name_doc },
     { NULL, NULL, 0, NULL }
@@ -224,7 +280,7 @@ PyTypeObject zonefinder_type = {
     "datasrc.ZoneFinder",
     sizeof(s_ZoneFinder),               // tp_basicsize
     0,                                  // tp_itemsize
-    reinterpret_cast<destructor>(ZoneFinder_destroy),// tp_dealloc
+    ZoneFinder_destroy,                 // tp_dealloc
     NULL,                               // tp_print
     NULL,                               // tp_getattr
     NULL,                               // tp_setattr
@@ -255,7 +311,7 @@ PyTypeObject zonefinder_type = {
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_set
     0,                                  // tp_dictoffset
-    reinterpret_cast<initproc>(ZoneFinder_init),// tp_init
+    ZoneFinder_init,                    // tp_init
     NULL,                               // tp_alloc
     PyType_GenericNew,                  // tp_new
     NULL,                               // tp_free
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index 3e4a1d7..8b2a8c5 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -57,6 +57,61 @@ def create_soa(serial):
                         str(serial) + ' 3600 1800 2419200 7200'))
     return soa
 
+def test_findall_common(self, tested):
+    """
+    Common part of the find_all test. It tests a find_all method on the passed
+    object.
+    """
+    # Some "failure" responses
+    result, rrset = tested.find_all(isc.dns.Name("www.sql1.example.com"),
+                                    ZoneFinder.FIND_DEFAULT)
+    self.assertEqual(ZoneFinder.DELEGATION, result)
+    expected = RRset(Name('sql1.example.com.'), RRClass.IN(), RRType.NS(),
+                     RRTTL(3600))
+    expected.add_rdata(Rdata(RRType.NS(), RRClass.IN(),
+                             'dns01.example.com.'))
+    expected.add_rdata(Rdata(RRType.NS(), RRClass.IN(),
+                             'dns02.example.com.'))
+    expected.add_rdata(Rdata(RRType.NS(), RRClass.IN(),
+                             'dns03.example.com.'))
+    self.assertTrue(rrsets_equal(expected, rrset))
+
+    result, rrset = tested.find_all(isc.dns.Name("nxdomain.example.com"),
+                                     ZoneFinder.FIND_DEFAULT)
+    self.assertEqual(ZoneFinder.NXDOMAIN, result)
+    self.assertIsNone(None, rrset)
+
+    # A success. It should return the list now.
+    # This also tests we can ommit the options parameter
+    result, rrsets = tested.find_all(isc.dns.Name("mix.example.com."))
+    self.assertEqual(ZoneFinder.SUCCESS, result)
+    self.assertEqual(2, len(rrsets))
+    rrsets.sort(key=lambda rrset: rrset.get_type().to_text())
+    expected = [
+        RRset(Name('mix.example.com.'), RRClass.IN(), RRType.A(),
+              RRTTL(3600)),
+        RRset(Name('mix.example.com.'), RRClass.IN(), RRType.AAAA(),
+              RRTTL(3600))
+    ]
+    expected[0].add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.1"))
+    expected[0].add_rdata(Rdata(RRType.A(), RRClass.IN(), "192.0.2.2"))
+    expected[1].add_rdata(Rdata(RRType.AAAA(), RRClass.IN(),
+                                "2001:db8::1"))
+    expected[1].add_rdata(Rdata(RRType.AAAA(), RRClass.IN(),
+                                "2001:db8::2"))
+    for (rrset, exp) in zip(rrsets, expected):
+        self.assertTrue(rrsets_equal(exp, rrset))
+
+    # Check the reference counts on them. The getrefcount returns one more,
+    # as for the reference in its own parameter - see its docs.
+
+    # Two - one for the variable, one for parameter
+    self.assertEqual(2, sys.getrefcount(rrsets))
+    for rrset in rrsets:
+        # 3 - one as the element of list, one for the rrset variable
+        # and one for the parameter.
+        self.assertEqual(3, sys.getrefcount(rrset))
+
 class DataSrcClient(unittest.TestCase):
 
     def test_(self):
@@ -269,6 +324,19 @@ class DataSrcClient(unittest.TestCase):
         self.assertNotEqual(ZoneFinder.WILDCARD_CNAME,
                             ZoneFinder.WILDCARD_NXRRSET)
 
+    def test_findall(self):
+        """
+        A test for the find_all method.
+        """
+        dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
+        result, finder = dsc.find_zone(isc.dns.Name("example.com"))
+
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
+        self.assertEqual("example.com.", finder.get_origin().to_text())
+
+        test_findall_common(self, finder)
+
     def test_find(self):
         dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
 
@@ -279,7 +347,6 @@ class DataSrcClient(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -292,20 +359,8 @@ class DataSrcClient(unittest.TestCase):
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
 
-        result, rrset = finder.find(isc.dns.Name("www.example.com"),
-                                    isc.dns.RRType.A(), None)
-        self.assertEqual(finder.SUCCESS, result)
-        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
-                         rrset.to_text())
-
-        # Invalid value for the "target"
-        self.assertRaises(TypeError, finder.find,
-                          isc.dns.Name("www.example.com"),
-                          isc.dns.RRType.A(), True)
-
         result, rrset = finder.find(isc.dns.Name("www.sql1.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.DELEGATION, result)
         self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\n" +
@@ -315,28 +370,24 @@ class DataSrcClient(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
         result, rrset = finder.find(isc.dns.Name("www.some.other.domain"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.TXT(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.NXRRSET, result)
         self.assertEqual(None, rrset)
 
         result, rrset = finder.find(isc.dns.Name("cname-ext.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.CNAME, result)
         self.assertEqual(
@@ -345,7 +396,6 @@ class DataSrcClient(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("foo.wild.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.WILDCARD, result)
         self.assertEqual("foo.wild.example.com. 3600 IN A 192.0.2.255\n",
@@ -353,7 +403,6 @@ class DataSrcClient(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("foo.wild.example.com"),
                                     isc.dns.RRType.TXT(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.WILDCARD_NXRRSET, result)
         self.assertEqual(None, rrset)
@@ -361,17 +410,14 @@ class DataSrcClient(unittest.TestCase):
         self.assertRaises(TypeError, finder.find,
                           "foo",
                           isc.dns.RRType.A(),
-                          None,
                           finder.FIND_DEFAULT)
         self.assertRaises(TypeError, finder.find,
                           isc.dns.Name("cname-ext.example.com"),
                           "foo",
-                          None,
                           finder.FIND_DEFAULT)
         self.assertRaises(TypeError, finder.find,
                           isc.dns.Name("cname-ext.example.com"),
                           isc.dns.RRType.A(),
-                          None,
                           "foo")
 
     def test_find_previous(self):
@@ -399,6 +445,16 @@ class DataSrcUpdater(unittest.TestCase):
         # Make a fresh copy of the writable database with all original content
         shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
 
+    def test_findall(self):
+        """
+        The same test as DataSrcClient.test_findall, but on an updater
+        instead of a finder.
+        """
+        dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
+        updater = dsc.get_updater(isc.dns.Name("example.com"), False)
+
+        test_findall_common(self, updater)
+
     def test_construct(self):
         # can't construct directly
         self.assertRaises(TypeError, isc.datasrc.ZoneUpdater)
@@ -409,7 +465,6 @@ class DataSrcUpdater(unittest.TestCase):
         updater = dsc.get_updater(isc.dns.Name("example.com"), False)
         result, rrset = updater.find(isc.dns.Name("www.example.com"),
                                      isc.dns.RRType.A(),
-                                     None,
                                      ZoneFinder.FIND_DEFAULT)
         self.assertEqual(ZoneFinder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -422,17 +477,6 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
                          rrset.to_text())
 
-        result, rrset = updater.find(isc.dns.Name("www.example.com"),
-                                     isc.dns.RRType.A(), None)
-        self.assertEqual(ZoneFinder.SUCCESS, result)
-        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
-                         rrset.to_text())
-
-        # Invalid value for 'target'
-        self.assertRaises(TypeError, updater.find,
-                          isc.dns.Name("www.example.com"),
-                          isc.dns.RRType.A(), 1)
-
     def test_update_delete_commit(self):
 
         dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
@@ -445,7 +489,6 @@ class DataSrcUpdater(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -467,14 +510,12 @@ class DataSrcUpdater(unittest.TestCase):
         # finder (since we have not committed)
         result, rrset = updater.find(isc.dns.Name("www.example.com"),
                                      isc.dns.RRType.A(),
-                                     None,
                                      finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -487,7 +528,6 @@ class DataSrcUpdater(unittest.TestCase):
         # the record should be gone now in the 'real' finder as well
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
@@ -502,7 +542,6 @@ class DataSrcUpdater(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -527,7 +566,6 @@ class DataSrcUpdater(unittest.TestCase):
         self.assertEqual("example.com.", finder.get_origin().to_text())
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -551,7 +589,6 @@ class DataSrcUpdater(unittest.TestCase):
 
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
@@ -573,7 +610,6 @@ class DataSrcUpdater(unittest.TestCase):
         # finder (since we have not committed)
         result, rrset = updater.find(isc.dns.Name("www.example.com"),
                                      isc.dns.RRType.A(),
-                                     None,
                                      finder.FIND_DEFAULT)
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
@@ -584,7 +620,6 @@ class DataSrcUpdater(unittest.TestCase):
         # the record should still be available in the 'real' finder as well
         result, rrset = finder.find(isc.dns.Name("www.example.com"),
                                     isc.dns.RRType.A(),
-                                    None,
                                     finder.FIND_DEFAULT)
         self.assertEqual(finder.SUCCESS, result)
         self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc
index 29d2ffe..c528a0c 100644
--- a/src/lib/python/isc/datasrc/updater_python.cc
+++ b/src/lib/python/isc/datasrc/updater_python.cc
@@ -48,6 +48,7 @@ using namespace isc::datasrc::python;
 namespace isc_datasrc_internal {
 // See finder_python.cc
 PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args);
+PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args);
 }
 
 namespace {
@@ -74,7 +75,7 @@ typedef CPPPyObjectContainer<s_ZoneUpdater, ZoneUpdater> ZoneUpdaterContainer;
 
 // General creation and destruction
 int
-ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) {
+ZoneUpdater_init(PyObject*, PyObject*, PyObject*) {
     // can't be called directly
     PyErr_SetString(PyExc_TypeError,
                     "ZoneUpdater cannot be constructed directly");
@@ -83,7 +84,9 @@ ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) {
 }
 
 void
-ZoneUpdater_destroy(s_ZoneUpdater* const self) {
+ZoneUpdater_destroy(PyObject* po_self) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+
     // cppobj is a shared ptr, but to make sure things are not destroyed in
     // the wrong order, we reset it here.
     self->cppobj.reset();
@@ -185,6 +188,13 @@ ZoneUpdater_find(PyObject* po_self, PyObject* args) {
                                                     args));
 }
 
+PyObject*
+ZoneUpdater_find_all(PyObject* po_self, PyObject* args) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    return (isc_datasrc_internal::ZoneFinder_helper_all(
+        &self->cppobj->getFinder(), args));
+}
+
 // This list contains the actual set of functions we have in
 // python. Each entry has
 // 1. Python method name
@@ -192,22 +202,22 @@ ZoneUpdater_find(PyObject* po_self, PyObject* args) {
 // 3. Argument type
 // 4. Documentation
 PyMethodDef ZoneUpdater_methods[] = {
-    { "add_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_addRRset),
+    { "add_rrset", ZoneUpdater_addRRset,
       METH_VARARGS, ZoneUpdater_addRRset_doc },
-    { "delete_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_deleteRRset),
+    { "delete_rrset", ZoneUpdater_deleteRRset,
       METH_VARARGS, ZoneUpdater_deleteRRset_doc },
-    { "commit", reinterpret_cast<PyCFunction>(ZoneUpdater_commit), METH_NOARGS,
-      ZoneUpdater_commit_doc },
+    { "commit", ZoneUpdater_commit, METH_NOARGS, ZoneUpdater_commit_doc },
     // Instead of a getFinder, we implement the finder functionality directly
     // This is because ZoneFinder is non-copyable, and we should not create
     // a ZoneFinder object from a reference only (which is what is returned
     // by getFinder(). Apart from that
-    { "get_origin", reinterpret_cast<PyCFunction>(ZoneUpdater_getOrigin),
+    { "get_origin", ZoneUpdater_getOrigin,
       METH_NOARGS, ZoneFinder_getOrigin_doc },
-    { "get_class", reinterpret_cast<PyCFunction>(ZoneUpdater_getClass),
+    { "get_class", ZoneUpdater_getClass,
       METH_NOARGS, ZoneFinder_getClass_doc },
-    { "find", reinterpret_cast<PyCFunction>(ZoneUpdater_find), METH_VARARGS,
-      ZoneFinder_find_doc },
+    { "find", ZoneUpdater_find, METH_VARARGS, ZoneFinder_find_doc },
+    { "find_all", ZoneUpdater_find_all, METH_VARARGS,
+      ZoneFinder_find_all_doc },
     { NULL, NULL, 0, NULL }
 };
 
@@ -221,7 +231,7 @@ PyTypeObject zoneupdater_type = {
     "datasrc.ZoneUpdater",
     sizeof(s_ZoneUpdater),              // tp_basicsize
     0,                                  // tp_itemsize
-    reinterpret_cast<destructor>(ZoneUpdater_destroy),// tp_dealloc
+    ZoneUpdater_destroy,                // tp_dealloc
     NULL,                               // tp_print
     NULL,                               // tp_getattr
     NULL,                               // tp_setattr
@@ -252,7 +262,7 @@ PyTypeObject zoneupdater_type = {
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_set
     0,                                  // tp_dictoffset
-    reinterpret_cast<initproc>(ZoneUpdater_init),// tp_init
+    ZoneUpdater_init,                   // tp_init
     NULL,                               // tp_alloc
     PyType_GenericNew,                  // tp_new
     NULL,                               // tp_free
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index 2e4a28f..ed05398 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -237,10 +237,10 @@ logConfigUpdate(PyObject*, PyObject* args) {
     } catch (const isc::data::JSONError& je) {
         std::string error_msg = std::string("JSON format error: ") + je.what();
         PyErr_SetString(PyExc_TypeError, error_msg.c_str());
-    } catch (const isc::data::TypeError& de) {
+    } catch (const isc::data::TypeError&) {
         PyErr_SetString(PyExc_TypeError, "argument 1 of log_config_update "
                                          "is not a map of config data");
-    } catch (const isc::config::ModuleSpecError& mse) {
+    } catch (const isc::config::ModuleSpecError&) {
         PyErr_SetString(PyExc_TypeError, "argument 2 of log_config_update "
                                          "is not a correct module specification");
     } catch (const std::exception& e) {
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 30f8374..4b084cc 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS = work
 EXTRA_DIST = __init__.py
 EXTRA_DIST += bind10_messages.py
 EXTRA_DIST += cmdctl_messages.py
+EXTRA_DIST += ddns_messages.py
 EXTRA_DIST += stats_messages.py
 EXTRA_DIST += stats_httpd_messages.py
 EXTRA_DIST += xfrin_messages.py
@@ -16,6 +17,7 @@ EXTRA_DIST += libxfrin_messages.py
 CLEANFILES = __init__.pyc
 CLEANFILES += bind10_messages.pyc
 CLEANFILES += cmdctl_messages.pyc
+CLEANFILES += ddns_messages.pyc
 CLEANFILES += stats_messages.pyc
 CLEANFILES += stats_httpd_messages.pyc
 CLEANFILES += xfrin_messages.pyc
diff --git a/src/lib/python/isc/log_messages/ddns_messages.py b/src/lib/python/isc/log_messages/ddns_messages.py
new file mode 100644
index 0000000..38d83bb
--- /dev/null
+++ b/src/lib/python/isc/log_messages/ddns_messages.py
@@ -0,0 +1 @@
+from work.ddns_messages import *
diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am
index 140e221..703879a 100644
--- a/src/lib/python/isc/util/Makefile.am
+++ b/src/lib/python/isc/util/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = . io tests
 
 python_PYTHON = __init__.py process.py socketserver_mixin.py file.py
 
diff --git a/src/lib/python/isc/util/io/Makefile.am b/src/lib/python/isc/util/io/Makefile.am
new file mode 100644
index 0000000..6e2b263
--- /dev/null
+++ b/src/lib/python/isc/util/io/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+python_PYTHON = __init__.py
+pythondir = $(PYTHON_SITEPKG_DIR)/isc/util/io
+
+pyexec_LTLIBRARIES = socketsession.la
+pyexecdir = $(PYTHON_SITEPKG_DIR)/isc/util/io
+
+socketsession_la_SOURCES = socketsession_python.cc socketsession_python.h
+socketsession_la_SOURCES += socketsessionforwarder_python.cc
+socketsession_la_SOURCES += socketsessionforwarder_python.h
+socketsession_la_SOURCES += socketsessionreceiver_python.cc
+socketsession_la_SOURCES += socketsessionreceiver_python.h
+socketsession_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+socketsession_la_LDFLAGS = $(PYTHON_LDFLAGS)
+# Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
+# placed after -Wextra defined in AM_CXXFLAGS
+socketsession_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects.  -module is necessary to work this around.
+socketsession_la_LDFLAGS += -module
+socketsession_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
+socketsession_la_LIBADD += $(PYTHON_LIB)
+
+# This is not installed, it helps locate the module during tests
+EXTRA_DIST = __init__.py socketsession.py
+
+EXTRA_DIST += socketsession_inc.cc
+EXTRA_DIST += socketsessionforwarder_inc.cc socketsessionreceiver_inc.cc
+
+CLEANFILES = __init__.pyc socketsession.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/io/__init__.py b/src/lib/python/isc/util/io/__init__.py
new file mode 100644
index 0000000..935160a
--- /dev/null
+++ b/src/lib/python/isc/util/io/__init__.py
@@ -0,0 +1,3 @@
+"""
+Here are function and classes for forwarding socket sessions between processes.
+"""
diff --git a/src/lib/python/isc/util/io/socketsession.py b/src/lib/python/isc/util/io/socketsession.py
new file mode 100644
index 0000000..ccb16ec
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsession.py
@@ -0,0 +1,26 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed.  See python/isc/log/__init__.py for the trick.
+
+import os
+import sys
+
+for base in sys.path[:]:
+    libdir = os.path.join(base, 'isc/util/io/.libs')
+    if os.path.exists(libdir):
+        sys.path.insert(0, libdir)
+
+from socketsession import *
diff --git a/src/lib/python/isc/util/io/socketsession_inc.cc b/src/lib/python/isc/util/io/socketsession_inc.cc
new file mode 100644
index 0000000..e200063
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsession_inc.cc
@@ -0,0 +1,122 @@
+namespace {
+const char* const socketsession_doc = "\
+This module defines a set of classes that support forwarding a\n\
+\"socket session\" from one process to another.  A socket session is a\n\
+conceptual tuple of the following elements:\n\
+\n\
+- A network socket\n\
+- The local and remote endpoints of a (IP) communication taking place\n\
+  on the socket. In practice an endpoint is a pair of an IP address\n\
+  and TCP or UDP port number.\n\
+- Some amount of data sent from the remote endpoint and received on\n\
+  the socket. We call it (socket) session data in this documentation.\n\
+\n\
+Note that this is a conceptual definition. Depending on the underlying\n\
+implementation and/or the network protocol, some of the elements could\n\
+be part of others; for example, if it's an established TCP connection,\n\
+the local and remote endpoints would be able to be retrieved from the\n\
+socket using the standard getsockname() and getpeername() system\n\
+calls. But in this definition we separate these to be more generic.\n\
+Also, as a matter of fact our intended usage includes non-connected\n\
+UDP communications, in which case at least the remote endpoint should\n\
+be provided separately from the socket.\n\
+\n\
+In the actual implementation we represent a socket as a Python socket\n\
+object, which contains the information of the address family\n\
+(e.g. AF_INET6), socket type (e.g. SOCK_STREAM), and protocol\n\
+(e.g. IPPROTO_TCP).\n\
+\n\
+We use the Python socket address tuple to represent endpoints.\n\
+\n\
+Socket session data is an opaque blob in the form of a Python byte\n\
+object.\n\
+\n\
+To forward a socket session between processes, we use connected UNIX\n\
+domain sockets established between the processes. The file descriptor\n\
+will be forwarded through the sockets as an ancillary data item of\n\
+type SCM_RIGHTS. Other elements of the session will be transferred as\n\
+normal data over the connection.\n\
+\n\
+We provide two classes to help applications forward socket sessions:\n\
+SocketSessionForwarder is the sender of the UNIX domain connection,\n\
+while SocketSessionReceiver is the receiver (this interface assumes\n\
+one direction of forwarding).\n\
+\n\
+Note: this paragraph and following discussions on the internal\n\
+protocol are for reference purposes only; it's not necessary to\n\
+understand how to use the API.\n\
+SocketSessionForwarder and SocketSessionReceiver objects (internally)\n\
+use a straightforward protocol to pass elements of socket sessions.\n\
+Once the connection is established, the forwarder object first forwards\n\
+the file descriptor with 1-byte dummy data.  It then forwards a\n\
+\"(socket) session header\", which contains all other elements of\n\
+the session except the file descriptor (already forwarded) and session\n\
+data.  The wire format of the header is as follows:\n\
+\n\
+- The length of the header (16-bit unsigned integer)\n\
+- Address family\n\
+- Socket type\n\
+- Protocol\n\
+- Size of the local endpoint in bytes\n\
+- Local endpoint (a copy of the memory image of the corresponding\n\
+  sockaddr)\n\
+- Size of the remote endpoint in bytes\n\
+- Remote endpoint (same as local endpoint)\n\
+- Size of session data in bytes\n\
+\n\
+The type of the fields is 32-bit unsigned integer unless explicitly\n\
+noted, and all fields are formatted in the network byte order.\n\
+\n\
+The socket session data immediately follows the session header.\n\
+\n\
+Note that the fields do not necessarily be in the network byte order\n\
+because they are expected to be exchanged on the same machine.\n\
+Likewise, integer elements such as address family do not necessarily\n\
+be represented as an fixed-size value (i.e., 32-bit). But fixed size\n\
+fields are used in order to ensure maximum portability in such a\n\
+(rare) case where the forwarder and the receiver are built with\n\
+different compilers that have different definitions of int. Also,\n\
+since sockaddr fields are generally formatted in the network byte\n\
+order, other fields are defined so to be consistent.\n\
+\n\
+One basic assumption in the API of this module is socket sessions\n\
+should be forwarded without blocking, thus eliminating the need for\n\
+incremental read/write or blocking other important services such as\n\
+responding to requests from the application's clients. This assumption\n\
+should be held as long as both the forwarder and receiver have\n\
+sufficient resources to handle the forwarding process since the\n\
+communication is local. But a forward attempt could still block if the\n\
+receiver is busy (or even hang up) and cannot keep up with the volume\n\
+of incoming sessions.\n\
+\n\
+So, in this implementation, the forwarder uses non blocking writes to\n\
+forward sessions. If a write attempt could block, it immediately gives\n\
+up the operation with an exception. The corresponding application is\n\
+expected to catch it, close the connection, and perform any necessary\n\
+recovery for that application (that would normally be re-establish the\n\
+connection with a new receiver, possibly after confirming the\n\
+receiving side is still alive). On the other hand, the receiver\n\
+implementation assumes it's possible that it only receive incomplete\n\
+elements of a session (such as in the case where the forwarder writes\n\
+part of the entire session and gives up the connection). The receiver\n\
+implementation throws an exception when it encounters an incomplete\n\
+session. Like the case of the forwarder application, the receiver\n\
+application is expected to catch it, close the connection, and perform\n\
+any necessary recovery steps.\n\
+\n\
+Note that the receiver implementation uses blocking read. So it's\n\
+application's responsibility to ensure that there's at least some data\n\
+in the connection when the receiver object is requested to receive a\n\
+session (unless this operation can be blocking, e.g., by the use of a\n\
+separate thread). Also, if the forwarder implementation or application\n\
+is malicious or extremely buggy and intentionally sends partial\n\
+session and keeps the connection, the receiver could block in\n\
+receiving a session. In general, we assume the forwarder doesn't do\n\
+intentional blocking as it's a local node and is generally a module of\n\
+the same (BIND 10) system. The minimum requirement for the forwarder\n\
+implementation (and application) is to make sure the connection is\n\
+closed once it detects an error on it. Even a naive implementation\n\
+that simply dies due to the exception will meet this requirement.\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsession_python.cc b/src/lib/python/isc/util/io/socketsession_python.cc
new file mode 100644
index 0000000..7011b53
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsession_python.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include "socketsessionreceiver_python.h"
+#include "socketsessionforwarder_python.h"
+
+using namespace isc::util::io::python;
+using namespace isc::util::python;
+
+#include "socketsession_inc.cc"
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+PyObject* po_SocketSessionError;
+}
+}
+}
+}
+
+namespace {
+
+PyModuleDef socketsession = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "isc.util.io.socketsession",
+    socketsession_doc,
+    -1,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_socketsession(void) {
+    PyObject* mod = PyModule_Create(&socketsession);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    try {
+        po_SocketSessionError =
+            PyErr_NewException("isc.util.io.SocketSessionError", NULL, NULL);
+        PyObjectContainer(po_SocketSessionError).
+            installToModule(mod, "SocketSessionError");
+    } catch (...) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    if (!initModulePart_SocketSessionForwarder(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+    if (!initModulePart_SocketSessionReceiver(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    return (mod);
+}
diff --git a/src/lib/python/isc/util/io/socketsession_python.h b/src/lib/python/isc/util/io/socketsession_python.h
new file mode 100644
index 0000000..b0703ac
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsession_python.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSION_H
+#define __PYTHON_SOCKETSESSION_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+
+extern PyObject* po_SocketSessionError;
+
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSION_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc b/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc
new file mode 100644
index 0000000..6b9de01
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionforwarder_inc.cc
@@ -0,0 +1,136 @@
+namespace {
+// Modifications:
+//  reference to the module description (instead of "utility")
+//  exception description
+const char* const SocketSessionForwarder_doc = "\
+The forwarder of socket sessions.\n\
+\n\
+An object of this class maintains a UNIX domain socket (normally\n\
+expected to be connected to a SocketSessionReceiver object) and\n\
+forwards socket sessions to the receiver.\n\
+\n\
+See the description of socketsession module for other details of how\n\
+the session forwarding works.\n\
+\n\
+SocketSessionForwarder(unix_file)\n\
+\n\
+    The constructor.\n\
+\n\
+    It's constructed with path information of the intended receiver,\n\
+    but does not immediately establish a connection to the receiver;\n\
+    connect_to_receiver() must be called to establish it. These are\n\
+    separated so that an object of class can be initialized (possibly\n\
+    as an attribute of a higher level application class object)\n\
+    without knowing the receiver is ready for accepting new\n\
+    forwarders. The separate connect interface allows the object to be\n\
+    reused when it detects connection failure and tries to re-\n\
+    establish it after closing the failed one.\n\
+\n\
+    On construction, it also installs a signal filter for SIGPIPE to\n\
+    ignore it. Since this class uses a stream-type connected UNIX\n\
+    domain socket, if the receiver (abruptly) closes the connection a\n\
+    subsequent write operation on the socket would trigger a SIGPIPE\n\
+    signal, which kills the caller process by default. This behavior\n\
+    would be undesirable in many cases, so this implementation always\n\
+    disables the signal.\n\
+\n\
+    This approach has some drawbacks, however; first, since signal\n\
+    handling is process (or thread) wide, ignoring it may not what the\n\
+    application wants. On the other hand, if the application changes\n\
+    how the signal is handled after instantiating this class, the new\n\
+    behavior affects the class operation. Secondly, even if ignoring\n\
+    the signal is the desired operation, it's a waste to set the\n\
+    filter every time this class object is constructed. It's\n\
+    sufficient to do it once. We still adopt this behavior based on\n\
+    the observation that in most cases applications would like to\n\
+    ignore SIGPIPE (or simply doesn't care about it) and that this\n\
+    class is not instantiated so often (so the wasteful setting\n\
+    overhead should be marginal). On the other hand, doing it every\n\
+    time is beneficial if the application is threaded and different\n\
+    threads create different forwarder objects (and if signals work\n\
+    per thread).\n\
+\n\
+    Exceptions:\n\
+      SocketSessionError unix_file is invalid as a path name of a UNIX\n\
+                 domain socket or error happens in setting a filter for\n\
+                 SIGPIPE (see above)\n\
+      SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+    Parameters:\n\
+      unix_file  Path name of the receiver.\n\
+\n\
+";
+
+// Modifications:
+//  exception description
+const char* const SocketSessionForwarder_connectToReceiver_doc = "\
+connect_to_receiver()\n\
+\n\
+Establish a connection to the receiver.\n\
+\n\
+This method establishes a connection to the receiver at the path given\n\
+on construction. It makes the underlying UNIX domain socket non\n\
+blocking, so this method (or subsequent push() calls) does not block.\n\
+\n\
+Exceptions:\n\
+  TypeError  The method is called while an already established\n\
+             connection is still active.\n\
+  SocketSessionError A system error in socket operation.\n\
+  SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+";
+
+// Modifications:
+//  bullet description
+//  parameters
+//  exception description
+const char* const SocketSessionForwarder_push_doc = "\
+push(sock, family, type, protocol, local_end, remote_end, data)\n\
+\n\
+Forward a socket session to the receiver.\n\
+\n\
+This method takes a set of parameters that represent a single socket\n\
+session, renders them in the \"wire\" format according to the internal\n\
+protocol (see socketsession module) and forwards them to the\n\
+receiver through the UNIX domain connection.\n\
+\n\
+The connection must have been established by connect_to_receiver().\n\
+\n\
+For simplicity and for the convenience of detecting application\n\
+errors, this method imposes some restrictions on the parameters:\n\
+\n\
+- Socket family must be either AF_INET or AF_INET6\n\
+- The address family (sa_family) member of the local and remote end\n\
+  points must be equal to the family parameter\n\
+- Socket session data must not be empty\n\
+- Data length must not exceed 65535\n\
+\n\
+These are not architectural limitation, and might be loosened in future\n\
+versions as we see the need for flexibility.\n\
+\n\
+Since the underlying UNIX domain socket is non blocking (see the\n\
+description for the constructor), a call to this method should either\n\
+return immediately or result in exception (in case of \"would\n\
+block\").\n\
+\n\
+Exceptions:\n\
+  TypeError  The method is called before establishing a connection or\n\
+             given parameters are invalid, or the given socket address\n\
+             is valid.\n\
+  SocketSessionError A system error in socket operation, including the\n\
+             case where the write operation would block.\n\
+\n\
+Parameters:\n\
+  sock       (int) The socket file descriptor\n\
+  family     (int) The address family (such as socket.AF_INET6) of the\n\
+             socket\n\
+  type       (int) The socket type (such as socket.SOCK_DGRAM) of the\n\
+             socket\n\
+  protocol   (int) The transport protocol (such as socket.IPPROTO_UDP)\n\
+             of the socket\n\
+  local_end  (socket address) The local end point of the session\n\
+  remote_end (socket address) The remote end point of the session\n\
+  data       (byte) the session data\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_python.cc b/src/lib/python/isc/util/io/socketsessionforwarder_python.cc
new file mode 100644
index 0000000..bdc2bf7
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionforwarder_python.cc
@@ -0,0 +1,309 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/io/sockaddr_util.h>
+#include <util/io/socketsession.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "socketsession_python.h"
+#include "socketsessionforwarder_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::util::io;
+using namespace isc::util::io::internal;
+using namespace isc::util::io::python;
+using boost::lexical_cast;
+
+// Trivial constructor.
+s_SocketSessionForwarder::s_SocketSessionForwarder() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "socketsessionforwarder_inc.cc"
+
+// See python/isc/log/log.cc for the use of namespace
+namespace clang_unnamed_namespace_workaround {
+// Internal exception class thrown when address parsing fails
+class AddressParseError: public isc::Exception {
+public:
+    AddressParseError(const char *file, size_t line, const char *what):
+        isc::Exception(file, line, what) {}
+};
+}
+using namespace clang_unnamed_namespace_workaround;
+
+namespace {
+
+int
+SocketSessionForwarder_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_SocketSessionForwarder* self =
+        static_cast<s_SocketSessionForwarder*>(po_self);
+    try {
+        const char* unix_file;
+        if (PyArg_ParseTuple(args, "s", &unix_file)) {
+            self->cppobj = new SocketSessionForwarder(unix_file);
+            return (0);
+        }
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to construct SocketSessionForwarder object: " +
+            string(ex.what());
+        PyErr_SetString(po_SocketSessionError, ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (-1);
+    }
+
+    return (-1);
+}
+
+void
+SocketSessionForwarder_destroy(PyObject* po_self) {
+    s_SocketSessionForwarder* self =
+        static_cast<s_SocketSessionForwarder*>(po_self);
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// Convert a Python socket address object to an addrinfo structure by
+// getaddrinfo.
+void
+parsePySocketAddress(PyObject* obj, int type, int protocol,
+                     struct sockaddr_storage* ss)
+{
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_socktype = type;
+    hints.ai_protocol = protocol;
+    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+    const char* addr;
+    int port, flowinfo, scopeid;
+    struct addrinfo *res;
+    if (PyArg_ParseTuple(obj, "si", &addr, &port)) {
+        // Possibly an IPv4 address.
+        hints.ai_family = AF_INET;
+        const int error = getaddrinfo(addr,
+                                      lexical_cast<string>(port).c_str(),
+                                      &hints, &res);
+        if (error == 0) {
+            assert(res->ai_addrlen <= sizeof(*ss));
+            memcpy(ss, res->ai_addr, res->ai_addrlen);
+            return;
+        }
+        isc_throw(AddressParseError, "Invalid or unsupported socket address: "
+                  << gai_strerror(error));
+    }
+    PyErr_Clear();
+    if (PyArg_ParseTuple(obj, "siii", &addr, &port, &flowinfo, &scopeid)) {
+        // Possibly an IPv6 address.  We ignore flowinfo.
+        hints.ai_family = AF_INET6;
+        const int error = getaddrinfo(addr,
+                                      lexical_cast<string>(port).c_str(),
+                                      &hints, &res);
+        if (error == 0) {
+            assert(res->ai_addrlen <= sizeof(*ss));
+            memcpy(ss, res->ai_addr, res->ai_addrlen);
+            void* p = ss;
+            static_cast<struct sockaddr_in6*>(p)->sin6_scope_id = scopeid;
+            return;
+        }
+        isc_throw(AddressParseError, "Invalid or unsupported socket address: "
+                  << gai_strerror(error));
+    }
+    PyErr_Clear();
+    isc_throw(AddressParseError, "Invalid or unsupported socket address, must "
+              "be AF_INET or AF_INET6 socket address.");
+}
+
+PyObject*
+SocketSessionForwarder_connectToReceiver(PyObject* po_self, PyObject*) {
+    s_SocketSessionForwarder* const self =
+        static_cast<s_SocketSessionForwarder*>(po_self);
+
+    try {
+        self->cppobj->connectToReceiver();
+        Py_RETURN_NONE;
+    } catch (const isc::BadValue& ex) {
+        PyErr_SetString(PyExc_TypeError, ex.what());
+        return (NULL);
+    } catch (const SocketSessionError& ex) {
+        PyErr_SetString(po_SocketSessionError, ex.what());
+        return (NULL);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in connecting to receiver: " +
+            string(ex.what());
+        PyErr_SetString(PyExc_SystemError, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+SocketSessionForwarder_push(PyObject* po_self, PyObject* args) {
+    s_SocketSessionForwarder* const self =
+        static_cast<s_SocketSessionForwarder*>(po_self);
+
+    try {
+        int fd, family, type, protocol;
+        PyObject* po_local_end;
+        PyObject* po_remote_end;
+        Py_buffer py_buf;
+
+        if (!PyArg_ParseTuple(args, "iiiiOOy*", &fd, &family, &type, &protocol,
+                              &po_local_end, &po_remote_end, &py_buf)) {
+            return (NULL);
+        }
+        struct sockaddr_storage ss_local, ss_remote;
+        parsePySocketAddress(po_local_end, type, protocol, &ss_local);
+        parsePySocketAddress(po_remote_end, type, protocol, &ss_remote);
+        self->cppobj->push(fd, family, type, protocol,
+                           *convertSockAddr(&ss_local),
+                           *convertSockAddr(&ss_remote),
+                           py_buf.buf, py_buf.len);
+        Py_RETURN_NONE;
+    } catch (const AddressParseError& ex) {
+        PyErr_SetString(PyExc_TypeError, ex.what());
+        return (NULL);
+    } catch (const isc::BadValue& ex) {
+        PyErr_SetString(PyExc_TypeError, ex.what());
+        return (NULL);
+    } catch (const SocketSessionError& ex) {
+        PyErr_SetString(po_SocketSessionError, ex.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (NULL);
+    } 
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef SocketSessionForwarder_methods[] = {
+    { "push", SocketSessionForwarder_push, METH_VARARGS,
+      SocketSessionForwarder_push_doc },
+    { "connect_to_receiver", SocketSessionForwarder_connectToReceiver,
+      METH_NOARGS, SocketSessionForwarder_connectToReceiver_doc },
+    { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_SocketSessionForwarder
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject socketsessionforwarder_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.util.io.SocketSessionForwarder",
+    sizeof(s_SocketSessionForwarder),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    SocketSessionForwarder_destroy,                 // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    SocketSessionForwarder_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    SocketSessionForwarder_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    SocketSessionForwarder_init,        // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_SocketSessionForwarder(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&socketsessionforwarder_type) < 0) {
+        return (false);
+    }
+    void* p = &socketsessionforwarder_type;
+    if (PyModule_AddObject(mod, "SocketSessionForwarder",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&socketsessionforwarder_type);
+
+    return (true);
+}
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
diff --git a/src/lib/python/isc/util/io/socketsessionforwarder_python.h b/src/lib/python/isc/util/io/socketsessionforwarder_python.h
new file mode 100644
index 0000000..2ce220a
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionforwarder_python.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSIONFORWARDER_H
+#define __PYTHON_SOCKETSESSIONFORWARDER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+class SocketSessionForwarder;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_SocketSessionForwarder : public PyObject {
+public:
+    s_SocketSessionForwarder();
+    SocketSessionForwarder* cppobj;
+};
+
+extern PyTypeObject socketsessionforwarder_type;
+
+bool initModulePart_SocketSessionForwarder(PyObject* mod);
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSIONFORWARDER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc b/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc
new file mode 100644
index 0000000..ed29d3e
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionreceiver_inc.cc
@@ -0,0 +1,89 @@
+namespace {
+// Modifications
+//   - about return value
+//   - socket session "utility" => module
+const char* const SocketSessionReceiver_doc = "\
+The receiver of socket sessions.\n\
+\n\
+An object of this class holds a UNIX domain socket for an established\n\
+connection, receives socket sessions from the remote forwarder, and\n\
+provides the session to the application as a tuple of corresponding\n\
+elements.\n\
+\n\
+Note that this class is instantiated with an already connected socket;\n\
+it's not a listening socket that is accepting connection requests from\n\
+forwarders. It's application's responsibility to create the listening\n\
+socket, listen on it and accept connections. Once the connection is\n\
+established, the application would construct a SocketSessionReceiver\n\
+object with the socket for the newly established connection. This\n\
+behavior is based on the design decision that the application should\n\
+decide when it performs (possibly) blocking operations (see\n\
+socketsession module for more details).\n\
+\n\
+See the description of socketsession module for other details of how\n\
+the session forwarding works.\n\
+\n\
+SocketSessionReceiver(socket)\n\
+\n\
+    The constructor.\n\
+\n\
+    Exceptions:\n\
+      TypeError  The given parameter is not a valid socket object\n\
+      SocketSessionError Any error on an operation that is performed\n\
+                 on the given socket as part of initialization.\n\
+      SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+    Parameters:\n\
+      socket     A python socket object of a UNIX domain family for an\n\
+                 established connection with a forwarder.\n\
+\n\
+";
+
+// Modifications
+//  - socket session utility -> module
+//  - return value (not a SocketSession object, but a Python tuple)
+//  - remove the validity note (we copy it here, so there's no such
+//    restriction)
+//  - caller's responsibility: only responsible for closing the socket.
+//  - text around the bullets
+//  - exception
+const char* const SocketSessionReceiver_pop_doc = "\
+pop() -> (socket, socket address, socket address, byte)\n\
+\n\
+Receive a socket session from the forwarder.\n\
+\n\
+This method receives wire-format data (see socketsession module) for\n\
+a socket session on the UNIX domain socket, performs some validation\n\
+on the data, and returns the session information as a tuple.\n\
+\n\
+The caller is responsible for closing the received socket.\n\
+\n\
+It ensures the following:\n\
+\n\
+- The socket's address family is either AF_INET or AF_INET6\n\
+- The family element of the socket addresses for the local and remote\n\
+  end points must be equal to the socket's address family\n\
+- The socket session data is not empty and does not exceed 65535\n\
+  bytes.\n\
+\n\
+If the validation fails or an unexpected system error happens\n\
+(including a connection close in the meddle of reception), it throws\n\
+an SocketSessionError exception. When this happens, it's very\n\
+unlikely that a subsequent call to this method succeeds, so in\n\
+reality the application is expected to destruct it and close the\n\
+socket in such a case.\n\
+\n\
+Exceptions:\n\
+  SocketSessionError Invalid data is received or a system error on\n\
+             socket operation happens.\n\
+  SystemError Unexpected errors such as resource allocation failure\n\
+\n\
+Return Value(s): A tuple corresponding to the extracted socket session:\n\
+  socket     A Python socket object corresponding to the socket passed\n\
+             by the forwarder\n\
+  socket address A Python socket address (which is a tuple) for the local\n\
+             end point\n\
+  socket address A Python socket address for the remote endpoint\n\
+  data       A Python byte object that stores the session data\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_python.cc b/src/lib/python/isc/util/io/socketsessionreceiver_python.cc
new file mode 100644
index 0000000..24e3021
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionreceiver_python.cc
@@ -0,0 +1,327 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <boost/lexical_cast.hpp>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <util/io/socketsession.h>
+
+#include "socketsession_python.h"
+#include "socketsessionreceiver_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::util::io;
+using namespace isc::util::io::python;
+using boost::lexical_cast;
+
+// Trivial constructor.
+s_SocketSessionReceiver::s_SocketSessionReceiver() : cppobj(NULL) {
+}
+
+// Import pydoc text
+#include "socketsessionreceiver_inc.cc"
+
+namespace {
+// This C structure corresponds to a Python callable object for
+// socket.fromfd().
+// See json_dumps_obj in dns_requestloader_python.cc for background rationale
+// of this trick.
+PyObject* socket_fromfd_obj = NULL;
+
+int
+SocketSessionReceiver_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_SocketSessionReceiver* self =
+        static_cast<s_SocketSessionReceiver*>(po_self);
+    try {
+        // The constructor expects a Python socket object.  We'll extract
+        // the underlying file descriptor using the fileno method (in the
+        // duck typing manner) and pass it to the C++ constructor.
+        // PyObject_CallMethod() could return NULL (especially if the given
+        // object is of the wrong type and doesn't have the "fileno" method),
+        // in which case PyObjectContainer will detect it and throw
+        // PyCPPWrapperException, which will be converted to the Python
+        // TypeError below.
+        PyObject* po_sock;
+        if (PyArg_ParseTuple(args, "O", &po_sock)) {
+            PyObjectContainer fd_container(PyObject_CallMethod(
+                                               po_sock,
+                                               const_cast<char*>("fileno"),
+                                               NULL));
+            PyObjectContainer fdarg_container(
+                Py_BuildValue("(O)", fd_container.get()));
+            int fd;
+            if (PyArg_ParseTuple(fdarg_container.get(), "i", &fd)) {
+                self->cppobj = new SocketSessionReceiver(fd);
+                return (0);
+            }
+            PyErr_SetString(PyExc_TypeError, "Given object's fileno() doesn't "
+                            "return an integer, probably not a valid socket "
+                            "object");
+        }
+    } catch (const PyCPPWrapperException& ex) {
+        // This could happen due to memory allocation failure, but it's more
+        // likely that the object doesn't have the "fileno()" method or it
+        // returns an unexpected type of value.  So we adjust the error
+        // message accordingly.
+        PyErr_SetString(PyExc_TypeError, "Failed to parse parameter, "
+                        "probably not a valid socket object");
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to construct SocketSessionReceiver object: " +
+            string(ex.what());
+        PyErr_SetString(po_SocketSessionError, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+    }
+
+    return (-1);
+}
+
+PyObject*
+createPySocketAddress(const struct sockaddr& sa) {
+    socklen_t salen;
+    if (sa.sa_family == AF_INET) {
+        salen = sizeof(struct sockaddr_in);
+    } else if (sa.sa_family == AF_INET6) {
+        salen = sizeof(struct sockaddr_in6);
+    } else {
+        isc_throw(SocketSessionError, "Unsupported socket address family: "
+                  << static_cast<int>(sa.sa_family));
+    }
+
+    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+    const int error = getnameinfo(&sa, salen, hbuf, sizeof(hbuf), sbuf,
+                                  sizeof(sbuf),
+                                  NI_NUMERICHOST | NI_NUMERICSERV);
+    if (error != 0) {
+        isc_throw(SocketSessionError, "Unrecognized socket address format: "
+                  << gai_strerror(error));
+    }
+    if (sa.sa_family == AF_INET) {
+        return (Py_BuildValue("(si)", hbuf, lexical_cast<int>(sbuf)));
+    }
+    // We know it's AF_INET6 at this point.  We need some special trick for
+    // non-0 scope (zone) ID: getnameinfo() may convert the address to a
+    // textual representation using the extension described in RFC 4007,
+    // in which case it contains a delimiter character '%'.  We need to remove
+    // it before constructing the tuple.  The scope (zone) ID is preserved
+    // in the corresponding field of the tuple.
+    const void* p = &sa;
+    const struct sockaddr_in6* sin6 =
+        static_cast<const struct sockaddr_in6*>(p);
+    char* cp = strchr(hbuf, '%');
+    if (cp != NULL) {
+        *cp = '\0';
+    }
+    return (Py_BuildValue("(siii)", hbuf, lexical_cast<int>(sbuf), 0,
+                          sin6->sin6_scope_id));
+}
+
+void
+SocketSessionReceiver_destroy(PyObject* po_self) {
+    s_SocketSessionReceiver* self =
+        static_cast<s_SocketSessionReceiver*>(po_self);
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// A helper struct to automatically close a socket in an RAII manner.
+struct ScopedSocket : boost::noncopyable {
+    ScopedSocket(int fd) : fd_(fd) {}
+    ~ScopedSocket() {
+        close(fd_);
+    }
+    const int fd_;
+};
+
+PyObject*
+SocketSessionReceiver_pop(PyObject* po_self, PyObject*) {
+    s_SocketSessionReceiver* const self =
+        static_cast<s_SocketSessionReceiver*>(po_self);
+
+    try {
+        // retrieve the session, and the convert it to a corresponding
+        // Python tuple.
+        const SocketSession session = self->cppobj->pop();
+
+        // We need to immediately store the socket file descriptor in a
+        // ScopedSocket object.  socket.fromfd() will dup() the FD, so we need
+        // to close our copy even if an exception is thrown.
+        ScopedSocket sock(session.getSocket());
+
+        // Build Python socket object
+        PyObjectContainer c_args(Py_BuildValue("(iiii)", sock.fd_,
+                                               session.getFamily(),
+                                               session.getType(),
+                                               session.getProtocol()));
+        PyObjectContainer c_sock(PyObject_CallObject(socket_fromfd_obj,
+                                                     c_args.get()));
+        // Convert the local and remote sockaddr to Python socket address objs
+        PyObjectContainer c_local(createPySocketAddress(
+                                      session.getLocalEndpoint()));
+        PyObjectContainer c_remote(createPySocketAddress(
+                                       session.getRemoteEndpoint()));
+        // Convert the session data to Python byte object.
+        PyObjectContainer c_data(Py_BuildValue("y#", session.getData(),
+                                               session.getDataLength()));
+
+        // Build a tuple from them and return it.
+        return (Py_BuildValue("(OOOO)", c_sock.get(), c_local.get(),
+                              c_remote.get(), c_data.get()));
+    } catch (const SocketSessionError& ex) {
+        PyErr_SetString(po_SocketSessionError, ex.what());
+        return (NULL);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in receiving a socket session: " +
+            string(ex.what());
+        PyErr_SetString(PyExc_SystemError, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
+        return (NULL);
+    }
+}
+
+// These are the functions we export
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef SocketSessionReceiver_methods[] = {
+    { "pop", SocketSessionReceiver_pop, METH_NOARGS,
+      SocketSessionReceiver_pop_doc },
+    { NULL, NULL, 0, NULL }
+};
+} // end of unnamed namespace
+
+namespace isc {
+namespace util {
+namespace io {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_SocketSessionReceiver
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject socketsessionreceiver_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.util.io.SocketSessionReceiver",
+    sizeof(s_SocketSessionReceiver),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    SocketSessionReceiver_destroy,                 // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    SocketSessionReceiver_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    SocketSessionReceiver_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    SocketSessionReceiver_init,                    // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_SocketSessionReceiver(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&socketsessionreceiver_type) < 0) {
+        return (false);
+    }
+    void* p = &socketsessionreceiver_type;
+    if (PyModule_AddObject(mod, "SocketSessionReceiver",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+
+    PyObject* socket_module = PyImport_AddModule("socket");
+    if (socket_module != NULL) {
+        PyObject* socket_dict = PyModule_GetDict(socket_module);
+        if (socket_dict != NULL) {
+            socket_fromfd_obj = PyDict_GetItemString(socket_dict, "fromfd");
+        }
+    }
+    if (socket_fromfd_obj != NULL) {
+        Py_INCREF(socket_fromfd_obj);
+    } else {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "isc.util.io.SocketSessionReceiver needs "
+                        "socket.fromfd(), but it's missing");
+        return (false);
+    }
+
+    Py_INCREF(&socketsessionreceiver_type);
+
+    return (true);
+}
+
+} // namespace python
+} // namespace io
+} // namespace util
+} // namespace isc
diff --git a/src/lib/python/isc/util/io/socketsessionreceiver_python.h b/src/lib/python/isc/util/io/socketsessionreceiver_python.h
new file mode 100644
index 0000000..14e8a1b
--- /dev/null
+++ b/src/lib/python/isc/util/io/socketsessionreceiver_python.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_SOCKETSESSIONRECEIVER_H
+#define __PYTHON_SOCKETSESSIONRECEIVER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+namespace io {
+class SocketSessionReceiver;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_SocketSessionReceiver : public PyObject {
+public:
+    s_SocketSessionReceiver();
+    SocketSessionReceiver* cppobj;
+};
+
+extern PyTypeObject socketsessionreceiver_type;
+
+bool initModulePart_SocketSessionReceiver(PyObject* mod);
+
+} // namespace io
+} // namespace python
+} // namespace util
+} // namespace isc
+#endif // __PYTHON_SOCKETSESSIONRECEIVER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/util/io/tests/Makefile.am b/src/lib/python/isc/util/io/tests/Makefile.am
new file mode 100644
index 0000000..3429009
--- /dev/null
+++ b/src/lib/python/isc/util/io/tests/Makefile.am
@@ -0,0 +1,36 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = socketsession_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+	touch $(abs_top_srcdir)/.coverage
+	rm -f .coverage
+	${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+# Note: below we intentionally use a non absolute path for TESTDATAOBJDIR.
+# It will be used as part of the path for a UNIX domain socket.  Due to the
+# relatively lower limit on the length it's better to keep it as short as
+# possible.
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/isc/python/util/io/.libs \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	TESTDATAOBJDIR=$(builddir) \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
+
+CLEANFILES = $(builddir)/ssessiontest.unix
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/util/io/tests/socketsession_test.py b/src/lib/python/isc/util/io/tests/socketsession_test.py
new file mode 100644
index 0000000..9b5f12a
--- /dev/null
+++ b/src/lib/python/isc/util/io/tests/socketsession_test.py
@@ -0,0 +1,253 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import os, signal, socket, unittest
+from socket import AF_INET, AF_INET6, SOCK_STREAM, SOCK_DGRAM, IPPROTO_UDP, \
+    IPPROTO_TCP
+from isc.util.io.socketsession import *
+
+TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
+TEST_UNIX_FILE = TESTDATA_OBJDIR + '/ssessiontest.unix'
+TEST_DATA = b'BIND10 test'
+TEST_PORT = 53535
+
+class TestForwarder(unittest.TestCase):
+    '''In general, this is a straightforward port of the C++ counterpart.
+
+    In some cases test cases are simplified or have Python specific cases.
+
+    '''
+
+    def setUp(self):
+        self.forwarder = SocketSessionForwarder(TEST_UNIX_FILE)
+        if os.path.exists(TEST_UNIX_FILE):
+            os.unlink(TEST_UNIX_FILE)
+        self.large_text = b'a' * 65535
+
+    def tearDown(self):
+        if os.path.exists(TEST_UNIX_FILE):
+            os.unlink(TEST_UNIX_FILE)
+
+    def start_listen(self):
+        self.listen_sock = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
+        self.listen_sock.bind(TEST_UNIX_FILE)
+        self.listen_sock.listen(10)
+
+    def accept_forwarder(self):
+        self.listen_sock.setblocking(False)
+        s, _ = self.listen_sock.accept()
+        s.setblocking(True)
+        return s
+
+    def test_init(self):
+        # check bad arguments.  valid cases will covered in other tests.
+        self.assertRaises(TypeError, SocketSessionForwarder, 1)
+        self.assertRaises(TypeError, SocketSessionForwarder,
+                          'test.unix', 'test.unix')
+
+    def test_badpush(self):
+        # bad numbers of parameters
+        self.assertRaises(TypeError, self.forwarder.push, 1)
+        self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
+                          ('192.0.2.1', 5300), TEST_DATA, 0)
+        # contain a bad type of parameter
+        self.assertRaises(TypeError, self.forwarder.push, 0, 'AF_INET',
+                          SOCK_DGRAM, IPPROTO_UDP, ('127.0.0.1', 53),
+                          ('192.0.2.1', 5300), TEST_DATA)
+        # bad local address
+        self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('127.0.0..1', 53),
+                            ('192.0.2.1', 5300), TEST_DATA)
+        self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, '127.0.0.1',
+                            ('192.0.2.1', 5300), TEST_DATA)
+        # bad remote address
+        self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET6,
+                          SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53),
+                            ('2001:db8:::3', 5300), TEST_DATA)
+
+        # push before connect
+        self.assertRaises(TypeError, self.forwarder.push, 0, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+                          ('192.0.2.2', 53), TEST_DATA)
+
+        # Now connect the forwarder for the rest of tests
+        self.start_listen()
+        self.forwarder.connect_to_receiver()
+
+        # Inconsistent address family
+        self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
+                          ('192.0.2.2', 53), TEST_DATA)
+        self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET6,
+                          SOCK_DGRAM, IPPROTO_UDP, ('2001:db8::1', 53, 0, 1),
+                          ('192.0.2.2', 53), TEST_DATA)
+
+        # Empty data: we reject them at least for now
+        self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+                          ('192.0.2.2', 53), b'')
+
+        # Too big data: we reject them at least for now
+        self.assertRaises(TypeError, self.forwarder.push, 1, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+                          ('192.0.2.2', 53), b'd' * 65536)
+
+        # Close the receptor before push.  It will result in SIGPIPE (should be
+        # ignored) and EPIPE, which will be converted to SocketSessionError.
+        self.listen_sock.close()
+        self.assertRaises(SocketSessionError, self.forwarder.push, 1, AF_INET,
+                          SOCK_DGRAM, IPPROTO_UDP, ('192.0.2.1', 53),
+                          ('192.0.2.2', 53), TEST_DATA)
+
+    def create_socket(self, family, type, protocol, addr, do_listen):
+        s = socket.socket(family, type, protocol)
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        s.bind(addr)
+        if do_listen and protocol == IPPROTO_TCP:
+            s.listen(1)
+        return s
+
+    def check_push_and_pop(self, family, type, protocol, local, remote,
+                           data, new_connection):
+        sock = self.create_socket(family, type, protocol, local, True)
+        fwd_fd = sock.fileno()
+        if protocol == IPPROTO_TCP:
+            client_addr = ('::1', 0, 0, 0) if family == AF_INET6 \
+                else ('127.0.0.1', 0)
+            client_sock = self.create_socket(family, type, protocol,
+                                             client_addr, False)
+            client_sock.setblocking(False)
+            try:
+                client_sock.connect(local)
+            except socket.error:
+                pass
+            server_sock, _ = sock.accept()
+            fwd_fd = server_sock.fileno()
+
+        # If a new connection is required, start the "server", have the
+        # internal forwarder connect to it, and then internally accept it.
+        if new_connection:
+            self.start_listen()
+            self.forwarder.connect_to_receiver()
+            self.accept_sock = self.accept_forwarder()
+
+        # Then push one socket session via the forwarder.
+        self.forwarder.push(fwd_fd, family, type, protocol, local, remote,
+                            data)
+
+        # Pop the socket session we just pushed from a local receiver, and
+        # check the content.
+        receiver = SocketSessionReceiver(self.accept_sock)
+        signal.alarm(1)
+        sock_session = receiver.pop()
+        signal.alarm(0)
+        passed_sock = sock_session[0]
+        self.assertNotEqual(fwd_fd, passed_sock.fileno())
+        self.assertEqual(family, passed_sock.family)
+        self.assertEqual(type, passed_sock.type)
+        self.assertEqual(protocol, passed_sock.proto)
+        self.assertEqual(local, sock_session[1])
+        self.assertEqual(remote, sock_session[2])
+        self.assertEqual(data, sock_session[3])
+
+        # Check if the passed FD is usable by sending some data from it.
+        passed_sock.setblocking(True)
+        if protocol == IPPROTO_UDP:
+            self.assertEqual(len(TEST_DATA), passed_sock.sendto(TEST_DATA,
+                                                                local))
+            sock.settimeout(10)
+            self.assertEqual(TEST_DATA, sock.recvfrom(len(TEST_DATA))[0])
+        else:
+            server_sock.close()
+            self.assertEqual(len(TEST_DATA), passed_sock.send(TEST_DATA))
+            client_sock.setblocking(True)
+            client_sock.settimeout(10)
+            self.assertEqual(TEST_DATA, client_sock.recv(len(TEST_DATA)))
+
+    def test_push_and_pop(self):
+        # This is a straightforward port of C++ pushAndPop test.
+        local6 = ('::1', TEST_PORT, 0, 0)
+        remote6 = ('2001:db8::1', 5300, 0, 0)
+        self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+                                local6, remote6, TEST_DATA, True)
+        self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+                                local6, remote6, TEST_DATA, False)
+
+        local4 = ('127.0.0.1', TEST_PORT)
+        remote4 = ('192.0.2.2', 5300)
+        self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                local4, remote4, TEST_DATA, False)
+        self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+                                local4, remote4, TEST_DATA, False)
+
+        self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+                                local6, remote6, self.large_text, False)
+        self.check_push_and_pop(AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+                                local6, remote6, self.large_text, False)
+        self.check_push_and_pop(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                local4, remote4, self.large_text, False)
+        self.check_push_and_pop(AF_INET, SOCK_STREAM, IPPROTO_TCP,
+                                local4, remote4, self.large_text, False)
+
+        # Python specific: check for an IPv6 scoped address with non 0
+        # scope (zone) ID
+        scope6 = ('fe80::1', TEST_PORT, 0, 1)
+        self.check_push_and_pop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+                                local6, scope6, TEST_DATA, False)
+
+    def test_push_too_fast(self):
+        # A straightforward port of C++ pushTooFast test.
+        def multi_push(forwarder, addr, data):
+            for i in range(0, 10):
+                forwarder.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP, addr,
+                               addr, data)
+        self.start_listen()
+        self.forwarder.connect_to_receiver()
+        self.assertRaises(SocketSessionError, multi_push, self.forwarder,
+                          ('192.0.2.1', 53), self.large_text)
+
+    def test_bad_pop(self):
+        # This is a subset of C++ badPop test.  We only check pop() raises
+        # SocketSessionError when it internally fails to get the FD.
+        # Other cases would require passing a valid FD from the test,
+        # which would make the test too complicated.  As a wrapper checking
+        # one common failure case should be reasonably sufficient.
+
+        self.start_listen()
+        s = socket.socket(socket.AF_UNIX, SOCK_STREAM, 0)
+        s.setblocking(False)
+        s.connect(TEST_UNIX_FILE)
+        accept_sock = self.accept_forwarder()
+        receiver = SocketSessionReceiver(accept_sock)
+        s.close()
+        self.assertRaises(SocketSessionError, receiver.pop)
+
+class TestReceiver(unittest.TestCase):
+    # We only check a couple of failure cases on construction.  Valid cases
+    # are covered in TestForwarder.
+
+    def test_bad_init(self):
+        class FakeSocket:
+            # pretending to be th standard socket class, but its fileno() is
+            # bogus.
+            def fileno(self):
+                return None
+        self.assertRaises(TypeError, SocketSessionReceiver, 1)
+        self.assertRaises(TypeError, SocketSessionReceiver, FakeSocket())
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py
index 38b7f39..80fa909 100644
--- a/src/lib/python/isc/xfrin/diff.py
+++ b/src/lib/python/isc/xfrin/diff.py
@@ -162,19 +162,33 @@ class Diff:
         and do more merging, but such diffs should be rare in practice anyway,
         so we don't bother and do it this simple way.
         """
+        def same_type(rrset1, rrset2):
+            '''A helper routine to identify whether two RRsets are of the
+            same 'type'.  For RRSIGs we should consider type covered, too.
+            '''
+            if rrset1.get_type() != isc.dns.RRType.RRSIG() or \
+                    rrset2.get_type != isc.dns.RRType.RRSIG():
+                return rrset1.get_type() == rrset2.get_type()
+            # RR type of the both RRsets is RRSIG.  Compare type covered.
+            # We know they have exactly one RDATA.
+            sigdata1 = rrset1.get_rdata()[0].to_text().split()[0]
+            sigdata2 = rrset2.get_rdata()[0].to_text().split()[0]
+            return sigdata1 == sigdata2
+
         buf = []
         for (op, rrset) in self.__buffer:
             old = buf[-1][1] if len(buf) > 0 else None
             if old is None or op != buf[-1][0] or \
                 rrset.get_name() != old.get_name() or \
-                rrset.get_type() != old.get_type():
+                (not same_type(rrset, old)):
                 buf.append((op, isc.dns.RRset(rrset.get_name(),
                                               rrset.get_class(),
                                               rrset.get_type(),
                                               rrset.get_ttl())))
             if rrset.get_ttl() != buf[-1][1].get_ttl():
                 logger.warn(LIBXFRIN_DIFFERENT_TTL, rrset.get_ttl(),
-                            buf[-1][1].get_ttl())
+                            buf[-1][1].get_ttl(), rrset.get_name(),
+                            rrset.get_class(), rrset.get_type())
             for rdatum in rrset.get_rdata():
                 buf[-1][1].add_rdata(rdatum)
         self.__buffer = buf
diff --git a/src/lib/python/isc/xfrin/libxfrin_messages.mes b/src/lib/python/isc/xfrin/libxfrin_messages.mes
index 203e31f..b948e02 100644
--- a/src/lib/python/isc/xfrin/libxfrin_messages.mes
+++ b/src/lib/python/isc/xfrin/libxfrin_messages.mes
@@ -15,10 +15,10 @@
 # No namespace declaration - these constants go in the global namespace
 # of the libxfrin_messages python module.
 
-% LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4. Adjusting %2 -> %1.
+% LIBXFRIN_DIFFERENT_TTL multiple data with different TTLs (%1, %2) on %3/%4/%5. Adjusting %2 -> %1.
 The xfrin module received an update containing multiple rdata changes for the
 same RRset. But the TTLs of these don't match each other. As we combine them
-together, the later one get's overwritten to the earlier one in the sequence.
+together, the latter one gets overwritten to the earlier one in the sequence.
 
 % LIBXFRIN_NO_JOURNAL disabled journaling for updates to %1 on %2
 An attempt was made to create a Diff object with journaling enabled, but
diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py
index 9944404..7c1158a 100644
--- a/src/lib/python/isc/xfrin/tests/diff_tests.py
+++ b/src/lib/python/isc/xfrin/tests/diff_tests.py
@@ -99,6 +99,8 @@ class DiffTest(unittest.TestCase):
         in the tested module.
         """
         self.__warn_called = True
+        # Also log the message so we can check the log format (manually)
+        self.orig_logger.warn(*args)
 
     def commit(self):
         """
@@ -430,7 +432,7 @@ class DiffTest(unittest.TestCase):
         Test the TTL handling. A warn function should have been called if they
         differ, but that's all, it should not crash or raise.
         """
-        orig_logger = isc.xfrin.diff.logger
+        self.orig_logger = isc.xfrin.diff.logger
         try:
             isc.xfrin.diff.logger = self
             diff = Diff(self, Name('example.org.'))
@@ -451,7 +453,30 @@ class DiffTest(unittest.TestCase):
             self.assertEqual(self.__ttl, diff.get_buffer()[0][1].get_ttl())
             self.assertTrue(self.__warn_called)
         finally:
-            isc.xfrin.diff.logger = orig_logger
+            isc.xfrin.diff.logger = self.orig_logger
+
+    def test_rrsig_ttl(self):
+        '''Similar to the previous test, but for RRSIGs of different covered
+        types.
+
+        They shouldn't be compacted.
+
+        '''
+        diff = Diff(self, Name('example.org.'))
+        rrsig1 = RRset(Name('example.org'), self.__rrclass,
+                       RRType.RRSIG(), RRTTL(3600))
+        rrsig1.add_rdata(Rdata(RRType.RRSIG(), self.__rrclass,
+                               'A 5 3 3600 20000101000000 20000201000000 ' +
+                               '0 example.org. FAKEFAKEFAKE'))
+        diff.add_data(rrsig1)
+        rrsig2 = RRset(Name('example.org'), self.__rrclass,
+                       RRType.RRSIG(), RRTTL(1800))
+        rrsig2.add_rdata(Rdata(RRType.RRSIG(), self.__rrclass,
+                               'AAAA 5 3 3600 20000101000000 20000201000000 ' +
+                               '1 example.org. FAKEFAKEFAKE'))
+        diff.add_data(rrsig2)
+        diff.compact()
+        self.assertEqual(2, len(diff.get_buffer()))
 
     def test_relpace(self):
         """
@@ -463,4 +488,5 @@ class DiffTest(unittest.TestCase):
 
 if __name__ == "__main__":
     isc.log.init("bind10")
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 930b593..ea7d528 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
+
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <sys/socket.h>
@@ -21,8 +23,6 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/bind.hpp>
 
-#include <config.h>
-
 #include <dns/question.h>
 #include <dns/message.h>
 #include <dns/opcode.h>
@@ -229,6 +229,9 @@ private:
     // case of a TCP packet being returned with the TC bit set.
     IOFetch::Protocol protocol_;
 
+    // EDNS flag
+    bool edns_;
+
     // To prevent both unreasonably long cname chains and cname loops,
     // we simply keep a counter of the number of CNAMEs we have
     // followed so far (and error if it exceeds RESOLVER_MAX_CNAME_CHAIN
@@ -352,37 +355,40 @@ private:
             IOFetch query(protocol_, io_, question_,
                 test_server_.first,
                 test_server_.second, buffer_, this,
-                query_timeout_);
+                query_timeout_, edns_);
             io_.get_io_service().post(query);
         } else {
             IOFetch query(protocol_, io_, question_,
                 current_ns_address.getAddress(),
                 53, buffer_, this,
-                query_timeout_);
+                query_timeout_, edns_);
             io_.get_io_service().post(query);
         }
     }
     
     // 'general' send, ask the NSAS to give us an address.
-    void send(IOFetch::Protocol protocol = IOFetch::UDP) {
+    void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
         protocol_ = protocol;   // Store protocol being used for this
+        edns_ = edns;
         if (test_server_.second != 0) {
             // Send query to test server
-            LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
+            LOG_DEBUG(isc::resolve::logger,
+                      RESLIB_DBG_TRACE, RESLIB_TEST_UPSTREAM)
                 .arg(questionText(question_)).arg(test_server_.first);
             gettimeofday(&current_ns_qsent_time, NULL);
             ++outstanding_events_;
             IOFetch query(protocol, io_, question_,
                 test_server_.first,
                 test_server_.second, buffer_, this,
-                query_timeout_);
+                query_timeout_, edns_);
             io_.get_io_service().post(query);
 
         } else {
             // Ask the NSAS for an address for the current zone,
             // the callback will call the actual sendTo()
-            LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
-                      .arg(cur_zone_);
+            LOG_DEBUG(isc::resolve::logger,
+                      RESLIB_DBG_TRACE, RESLIB_NSAS_LOOKUP)
+                .arg(cur_zone_);
 
             // Can we have multiple calls to nsas_out? Let's assume not
             // for now
@@ -544,10 +550,28 @@ private:
             }
 
             // Was a TCP query so we have received a packet over TCP with the
-            // TC bit set: report an error by dropping down to the common
+            // TC bit set: report an error by going to the common
             // error code.
+            goto SERVFAIL;
+
+        case isc::resolve::ResponseClassifier::RCODE:
+            // see if it's a FORMERR and a potential EDNS problem
+            if (incoming.getRcode() == Rcode::FORMERR()) {
+                if (protocol_ == IOFetch::UDP && edns_) {
+                    // TODO: in case we absolutely need EDNS (i.e. for DNSSEC
+                    // aware queries), we might want to try TCP before we give
+                    // up. For now, just try UDP, no EDNS
+                    send(IOFetch::UDP, false);
+                    return (false);
+                }
 
+                // TC should take care of non-EDNS over UDP, fall through to
+                // SERVFAIL if we get FORMERR instead
+            }
+            goto SERVFAIL;
+            
         default:
+SERVFAIL:
             // Some error in received packet it.  Report it and return SERVFAIL
             // to the caller.
             if (logger.isDebugEnabled()) {
@@ -679,11 +703,14 @@ public:
         nsas_(nsas),
         cache_(cache),
         cur_zone_("."),
-        nsas_callback_(new ResolverNSASCallback(this)),
+        nsas_callback_(),
         nsas_callback_out_(false),
         outstanding_events_(0),
         rtt_recorder_(recorder)
     {
+        // Set here to avoid using "this" in initializer list.
+        nsas_callback_.reset(new ResolverNSASCallback(this));
+
         // Setup the timer to stop trying (lookup_timeout)
         if (lookup_timeout >= 0) {
             lookup_timer.expires_from_now(
diff --git a/src/lib/resolve/response_classifier.cc b/src/lib/resolve/response_classifier.cc
index 02808e4..27c5cfc 100644
--- a/src/lib/resolve/response_classifier.cc
+++ b/src/lib/resolve/response_classifier.cc
@@ -119,7 +119,7 @@ ResponseClassifier::Category ResponseClassifier::classify(
         if (authority.empty()) {
             return (EMPTY);
         }
-        for (int i = 0; i < authority.size(); ++i) {
+        for (vector<RRsetPtr>::size_type i = 0; i < authority.size(); ++i) {
             if (authority[i]->getType() == RRType::NS()) {
                 return (REFERRAL);
             }
@@ -161,7 +161,7 @@ ResponseClassifier::Category ResponseClassifier::classify(
 
     // There are multiple RRsets in the answer. They should all have the same
     // QCLASS, else there is some error in the response.
-    for (int i = 1; i < answer.size(); ++i) {
+    for (vector<RRsetPtr>::size_type i = 1; i < answer.size(); ++i) {
         if (answer[0]->getClass() != answer[i]->getClass()) {
             return (MULTICLASS);
         }
@@ -173,7 +173,8 @@ ResponseClassifier::Category ResponseClassifier::classify(
     // CNAME - in which case there should no other record types at that QNAME.
     if (question.getType() == RRType::ANY()) {
         bool all_same = true;
-        for (int i = 1; (i < answer.size()) && all_same; ++i) {
+        for (vector<RRsetPtr>::size_type i = 1; (i < answer.size()) && all_same;
+             ++i) {
             all_same = (answer[0]->getName() == answer[i]->getName());
         }
         if (all_same) {
@@ -211,7 +212,7 @@ ResponseClassifier::Category ResponseClassifier::cnameChase(
 {
     // Search through the vector of RRset pointers until we find one with the
     // right QNAME.
-    for (int i = 0; i < ansrrset.size(); ++i) {
+    for (vector<RRsetPtr>::size_type i = 0; i < ansrrset.size(); ++i) {
         if (present[i]) {
 
             // This entry has not been logically removed, so look at it.
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index cf05d9b..e7c59f4 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -23,6 +23,7 @@ run_unittests_SOURCES += resolver_callback_unittest.cc
 run_unittests_SOURCES += response_classifier_unittest.cc
 run_unittests_SOURCES += recursive_query_unittest.cc
 run_unittests_SOURCES += recursive_query_unittest_2.cc
+run_unittests_SOURCES += recursive_query_unittest_3.cc
 
 run_unittests_LDADD = $(GTEST_LDADD)
 run_unittests_LDADD +=  $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index 3e62336..a222240 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -275,7 +275,9 @@ public:
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     /// \param length Amount of data received.
-    void udpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+    void udpReceiveHandler(asio::error_code ec = asio::error_code(),
+                           size_t length = 0)
+    {
         if (debug_) {
             cout << "udpReceiveHandler(): error = " << ec.value() <<
                     ", length = " << length << ", last state = " << last_ <<
@@ -368,7 +370,9 @@ public:
     ///
     /// \param ec Completion error code of the send.
     /// \param length Actual number of bytes sent.
-    void udpSendHandler(error_code ec = error_code(), size_t length = 0) {
+    void udpSendHandler(asio::error_code ec = asio::error_code(),
+                        size_t length = 0)
+    {
         if (debug_) {
             cout << "udpSendHandler(): error = " << ec.value() <<
                     ", length = " << length << endl;
@@ -392,7 +396,9 @@ public:
     ///
     /// \param socket Socket on which data will be received
     /// \param ec Boost error code, value should be zero.
-    void tcpAcceptHandler(error_code ec = error_code(), size_t length = 0) {
+    void tcpAcceptHandler(asio::error_code ec = asio::error_code(),
+                          size_t length = 0)
+    {
         if (debug_) {
             cout << "tcpAcceptHandler(): error = " << ec.value() <<
                     ", length = " << length << endl;
@@ -419,7 +425,9 @@ public:
     /// \param ec ASIO error code, completion code of asynchronous I/O issued
     ///        by the "server" to receive data.
     /// \param length Amount of data received.
-    void tcpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+    void tcpReceiveHandler(asio::error_code ec = asio::error_code(),
+                           size_t length = 0)
+    {
         if (debug_) {
             cout << "tcpReceiveHandler(): error = " << ec.value() <<
                     ", length = " << length <<
@@ -506,7 +514,8 @@ public:
     /// \param expected_length Number of bytes that were expected to have been sent.
     /// \param ec Boost error code, value should be zero.
     /// \param length Number of bytes sent.
-    void tcpSendHandler(size_t expected_length = 0, error_code ec = error_code(),
+    void tcpSendHandler(size_t expected_length = 0,
+                        asio::error_code ec = asio::error_code(),
                         size_t length = 0)
     {
         if (debug_) {
@@ -689,7 +698,7 @@ TEST_F(RecursiveQueryTest2, Resolve) {
     // weren't, we would expect some absurdly high answers.
     vector<uint32_t> rtt = recorder->getRtt();
     EXPECT_GT(rtt.size(), 0);
-    for (int i = 0; i < rtt.size(); ++i) {
+    for (vector<uint32_t>::size_type i = 0; i < rtt.size(); ++i) {
         EXPECT_LT(rtt[i], 2000);
     }
 }
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
new file mode 100644
index 0000000..3602b03
--- /dev/null
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -0,0 +1,564 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <cstdlib>
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <boost/bind.hpp>
+
+#include <asio.hpp>
+
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+
+#include <util/io_utilities.h>
+#include <asiodns/dns_service.h>
+#include <asiodns/io_fetch.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_endpoint.h>
+#include <asiolink/io_service.h>
+#include <resolve/recursive_query.h>
+#include <resolve/resolver_interface.h>
+
+using namespace asio;
+using namespace asio::ip;
+using namespace isc::asiolink;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::util;
+using namespace isc::resolve;
+using namespace std;
+
+/// RecursiveQuery Test - 3
+///
+/// The second part of the RecursiveQuery unit tests, this attempts to get the
+/// RecursiveQuery object to follow a set of EDNS-induced errors, causing the
+/// resolver to follow the fallback logic.
+///
+/// - Send EDNS question over UDP - get FORMERR
+/// - Send EDNS question over TCP - get FORMERR
+/// - Send non-EDNS question over UDP - get RESPONSE
+///
+/// By using the "test_server_" element of RecursiveQuery, all queries are
+/// directed to one or other of the "servers" in the RecursiveQueryTest3 class.
+
+namespace isc {
+namespace asiodns {
+
+const std::string TEST_ADDRESS3 = "127.0.0.1"; 
+                                               ///< Servers are on this address
+const uint16_t TEST_PORT3 = 5303;              ///< ... and this port
+const size_t BUFFER_SIZE = 1024;              ///< For all buffers
+
+const std::string DUMMY_ADDR3 = "1.2.3.4";     ///< address to return as A
+
+class MockResolver3 : public isc::resolve::ResolverInterface {
+public:
+    virtual void resolve(const QuestionPtr& question,
+                 const ResolverInterface::CallbackPtr& callback) {
+    }
+
+    virtual ~MockResolver3() {}
+};
+
+
+
+/// \brief Test fixture for the RecursiveQuery Test
+class RecursiveQueryTest3 : public virtual ::testing::Test
+{
+public:
+
+    /// \brief Status of query
+    ///
+    /// Set before the query and then by each "server" when responding.
+    enum QueryStatus {
+        NONE = 0,                   ///< Default
+        EDNS_UDP = 1,               ///< EDNS query over UDP
+        NON_EDNS_UDP = 2,           ///< Non-EDNS query over UDP
+        COMPLETE = 6                ///< Query is complete
+    };
+
+    // Common stuff
+    IOService       service_;                   ///< Service to run everything
+    DNSService      dns_service_;               ///< Resolver is part of "server"
+    QuestionPtr     question_;                  ///< What to ask
+    QueryStatus     last_;                      ///< What was the last state
+    QueryStatus     expected_;                  ///< Expected next state
+    OutputBufferPtr question_buffer_;           ///< Question we expect to receive
+    boost::shared_ptr<MockResolver3> resolver_;  ///< Mock resolver
+    isc::nsas::NameserverAddressStore* nsas_;   ///< Nameserver address store
+    isc::cache::ResolverCache cache_;           ///< Resolver cache
+
+    // Data for TCP Server
+    size_t          tcp_cumulative_;            ///< Cumulative TCP data received
+    tcp::endpoint   tcp_endpoint_;              ///< Endpoint for TCP receives
+    size_t          tcp_length_;                ///< Expected length value
+    uint8_t         tcp_receive_buffer_[BUFFER_SIZE];   ///< Receive buffer for TCP I/O
+    OutputBufferPtr tcp_send_buffer_;           ///< Send buffer for TCP I/O
+    tcp::socket     tcp_socket_;                ///< Socket used by TCP server
+
+    /// Data for UDP
+    udp::endpoint   udp_remote_;                ///< Endpoint for UDP receives
+    size_t          udp_length_;                ///< Expected length value
+    uint8_t         udp_receive_buffer_[BUFFER_SIZE];   ///< Receive buffer for UDP I/O
+    OutputBufferPtr udp_send_buffer_;           ///< Send buffer for UDP I/O
+    udp::socket     udp_socket_;                ///< Socket used by UDP server
+
+    /// \brief Constructor
+    RecursiveQueryTest3() :
+        service_(),
+        dns_service_(service_, NULL, NULL, NULL),
+        question_(new Question(Name("ednsfallback"),
+                  RRClass::IN(), RRType::A())),
+        last_(NONE),
+        expected_(NONE),
+        question_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        resolver_(new MockResolver3()),
+        nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
+        tcp_cumulative_(0),
+        tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS3),
+                      TEST_PORT3),
+        tcp_length_(0),
+        tcp_receive_buffer_(),
+        tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        tcp_socket_(service_.get_io_service()),
+        udp_remote_(),
+        udp_length_(0),
+        udp_receive_buffer_(),
+        udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
+        udp_socket_(service_.get_io_service(), udp::v4())
+    {
+    }
+
+    /// \brief Set Common Message Bits
+    ///
+    /// Sets up the common bits of a response message returned by the handlers.
+    ///
+    /// \param message Message buffer in RENDER mode.
+    /// \param qid QID to set the message to
+    void setCommonMessage(isc::dns::Message& message, uint16_t qid) {
+        message.setQid(qid);
+        message.setHeaderFlag(Message::HEADERFLAG_QR);
+        message.setOpcode(Opcode::QUERY());
+        message.setHeaderFlag(Message::HEADERFLAG_AA);
+        message.addQuestion(*question_);
+    }
+
+    /// \brief Set FORMERR answer
+    ///
+    /// \param message Message to update with FORMERR status
+    void setFORMERR(isc::dns::Message& message) {
+        message.setRcode(Rcode::FORMERR());
+    }
+
+    /// \brief Set Answer
+    ///
+    /// \param message Message to update with FORMERR status
+    void setAnswer(isc::dns::Message& message) {
+        // Give a response
+        RRsetPtr answer(new RRset(Name("ednsfallback."), RRClass::IN(),
+                        RRType::A(), RRTTL(300)));
+        answer->addRdata(createRdata(RRType::A(), RRClass::IN(), DUMMY_ADDR3));
+        message.addRRset(Message::SECTION_ANSWER, answer);
+        message.setRcode(Rcode::NOERROR());
+    }
+
+    /// \brief UDP Receive Handler
+    ///
+    /// This is invoked when a message is received over UDP from the
+    /// RecursiveQuery object under test.  It formats an answer and sends it
+    /// asynchronously, with the UdpSendHandler method being specified as the
+    /// completion handler.
+    ///
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void udpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+        // Expected state should be one greater than the last state.
+        EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
+        last_ = expected_;
+
+        Message query(Message::PARSE);
+
+        // The QID in the incoming data is random so set it to 0 for the
+        // data comparison check. (It is set to 0 in the buffer containing
+        // the expected data.)
+        // And check that question we received is what was expected.
+        checkReceivedPacket(udp_receive_buffer_, length, query);
+
+        // The message returned depends on what state we are in.  Set up
+        // common stuff first: bits not mentioned are set to 0.
+        Message message(Message::RENDER);
+        setCommonMessage(message, query.getQid());
+
+        // Set up state-dependent bits:
+        switch (expected_) {
+        case EDNS_UDP:
+            EXPECT_TRUE(query.getEDNS());
+            // Return FORMERROR
+            setFORMERR(message);
+            expected_ = NON_EDNS_UDP;
+            break;
+
+        case NON_EDNS_UDP:
+            EXPECT_FALSE(query.getEDNS());
+            // Return the answer to the question.
+            setAnswer(message);
+            expected_ = COMPLETE;
+            break;
+
+         default:
+            FAIL() << "UdpReceiveHandler called with unknown state";
+        }
+
+        // Convert to wire format
+        udp_send_buffer_->clear();
+        MessageRenderer renderer(*udp_send_buffer_);
+        message.toWire(renderer);
+
+        // Return a message back to the IOFetch object (after setting the
+        // expected length of data for the check in the send handler).
+        udp_length_ = udp_send_buffer_->getLength();
+        udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
+                                               udp_send_buffer_->getLength()),
+                                  udp_remote_,
+                              boost::bind(&RecursiveQueryTest3::udpSendHandler,
+                                              this, _1, _2));
+    }
+
+    /// \brief UDP Send Handler
+    ///
+    /// Called when a send operation of the UDP server (i.e. a response
+    /// being sent to the RecursiveQuery) has completed, this re-issues
+    /// a read call.
+    ///
+    /// \param ec Completion error code of the send.
+    /// \param length Actual number of bytes sent.
+    void udpSendHandler(error_code ec = error_code(), size_t length = 0) {
+        // Check send was OK
+        EXPECT_EQ(0, ec.value());
+        EXPECT_EQ(udp_length_, length);
+
+        // Reissue the receive call to await the next message.
+        udp_socket_.async_receive_from(
+            asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
+            udp_remote_,
+            boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
+                        this, _1, _2));
+    }
+
+    /// \brief Completion Handler for Accepting TCP Data
+    ///
+    /// Called when the remote system connects to the "TCP server".  It issues
+    /// an asynchronous read on the socket to read data.
+    ///
+    /// \param socket Socket on which data will be received
+    /// \param ec Boost error code, value should be zero.
+    void tcpAcceptHandler(error_code ec = error_code(), size_t length = 0) {
+        // Expect that the accept completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // Initiate a read on the socket, indicating that nothing has yet been
+        // received.
+        tcp_cumulative_ = 0;
+        tcp_socket_.async_receive(
+            asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
+            boost::bind(&RecursiveQueryTest3::tcpReceiveHandler, this, _1, _2));
+    }
+
+    /// \brief Completion Handler for Receiving TCP Data
+    ///
+    /// Reads data from the RecursiveQuery object and loops, reissuing reads,
+    /// until all the message has been read.  It then returns an appropriate
+    /// response.
+    ///
+    /// \param socket Socket to use to send the answer
+    /// \param ec ASIO error code, completion code of asynchronous I/O issued
+    ///        by the "server" to receive data.
+    /// \param length Amount of data received.
+    void tcpReceiveHandler(error_code ec = error_code(), size_t length = 0) {
+        // Expect that the receive completed without a problem.
+        EXPECT_EQ(0, ec.value());
+
+        // Have we received all the data?  We know this by checking if the two-
+        // byte length count in the message is equal to the data received.
+        tcp_cumulative_ += length;
+        bool complete = false;
+        if (tcp_cumulative_ > 2) {
+            uint16_t dns_length = readUint16(tcp_receive_buffer_);
+            complete = ((dns_length + 2) == tcp_cumulative_);
+        }
+
+        if (!complete) {
+            // Not complete yet, issue another read.
+            tcp_socket_.async_receive(
+                asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
+                             sizeof(tcp_receive_buffer_) - tcp_cumulative_),
+                boost::bind(&RecursiveQueryTest3::tcpReceiveHandler,
+                            this, _1, _2));
+            return;
+        }
+
+        // Have received a TCP message.  Expected state should be one greater
+        // than the last state.
+        EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
+        last_ = expected_;
+
+        Message query(Message::PARSE);
+
+        // Check that question we received is what was expected.  Note that we
+        // have to ignore the two-byte header in order to parse the message.
+        checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2, query);
+
+        // Return a message back.  This is a referral to example.org, which
+        // should result in another query over UDP.  Note the setting of the
+        // QID in the returned message with what was in the received message.
+        Message message(Message::RENDER);
+        setCommonMessage(message, query.getQid());
+
+        // Set up state-dependent bits:
+        switch (expected_) {
+        default:
+            FAIL() << "TcpReceiveHandler called with unknown state";
+        }
+
+
+        // Convert to wire format
+        // Use a temporary buffer for the dns wire data (we copy it
+        // to the 'real' buffer below)
+        OutputBuffer msg_buf(BUFFER_SIZE);
+        MessageRenderer renderer(msg_buf);
+        message.toWire(renderer);
+
+        // Also, take this opportunity to clear the accumulated read count in
+        // readiness for the next read. (If any - at present, there is only
+        // one read in the test, although extensions to this test suite could
+        // change that.)
+        tcp_cumulative_ = 0;
+
+        // Unless we go through a callback loop we cannot simply use
+        // async_send() multiple times, so we cannot send the size first
+        // followed by the actual data. We copy them to a new buffer
+        // first
+        tcp_send_buffer_->clear();
+        tcp_send_buffer_->writeUint16(msg_buf.getLength());
+        tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+        tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
+                                            tcp_send_buffer_->getLength()),
+                           boost::bind(&RecursiveQueryTest3::tcpSendHandler,
+                               this, tcp_send_buffer_->getLength(), _1, _2));
+    }
+
+    /// \brief Completion Handler for Sending TCP data
+    ///
+    /// Called when the asynchronous send of data back to the RecursiveQuery
+    /// by the TCP "server" in this class has completed.  (This send has to
+    /// be asynchronous because control needs to return to the caller in order
+    /// for the IOService "run()" method to be called to run the handlers.)
+    ///
+    /// \param expected_length Number of bytes that were expected to have been
+    /// sent.
+    /// \param ec Boost error code, value should be zero.
+    /// \param length Number of bytes sent.
+    void tcpSendHandler(size_t expected_length = 0,
+                        error_code ec = error_code(),
+                        size_t length = 0)
+    {
+        EXPECT_EQ(0, ec.value());       // Expect no error
+        EXPECT_EQ(expected_length, length);    // And that amount sent is as
+                                               // expected
+    }
+
+    /// \brief Check Received Packet
+    ///
+    /// Checks the packet received from the RecursiveQuery object to ensure
+    /// that the question is what is expected.
+    ///
+    /// \param data Start of data.  This is the start of the received buffer in
+    ///        the case of UDP data, and an offset into the buffer past the
+    ///        count field for TCP data.
+    /// \param length Length of data.
+    /// \return The QID of the message
+    void checkReceivedPacket(uint8_t* data, size_t length, Message& message) {
+
+        // Decode the received buffer.
+        InputBuffer buffer(data, length);
+        message.fromWire(buffer);
+
+        // Check the packet.
+        EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
+
+        Question question = **(message.beginQuestion());
+        EXPECT_TRUE(question == *question_);
+    }
+};
+
+/// \brief Resolver Callback Object
+///
+/// Holds the success and failure callback methods for the resolver
+class ResolverCallback3 : public isc::resolve::ResolverInterface::Callback {
+public:
+    /// \brief Constructor
+    ResolverCallback3(IOService& service) :
+        service_(service), run_(false), status_(false)
+    {}
+
+    /// \brief Destructor
+    virtual ~ResolverCallback3()
+    {}
+
+    /// \brief Resolver Callback Success
+    ///
+    /// Called if the resolver detects that the call has succeeded.
+    ///
+    /// \param response Answer to the question.
+    virtual void success(const isc::dns::MessagePtr response) {
+        // There should be one RR each  in the question and answer sections,
+        // and two RRs in each of the the authority and additional sections.
+        EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
+        EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
+
+        // Check the answer - that the RRset is there...
+        EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
+                                       RRsetPtr(new RRset(Name("ednsfallback."),
+                                                RRClass::IN(),
+                                                RRType::A(),
+                                                RRTTL(300)))));
+        const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
+
+        // ... get iterator into the Rdata of this RRset and point to first
+        // element...
+        const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
+        rdata_i->first();
+
+        // ... and check it is what we expect.
+        EXPECT_EQ(string(DUMMY_ADDR3), rdata_i->getCurrent().toText());
+
+        // Flag completion
+        run_ = true;
+        status_ = true;
+
+        service_.stop();    // Cause run() to exit.
+    }
+
+    /// \brief Resolver Failure Completion
+    ///
+    /// Called if the resolver detects that the resolution has failed.
+    virtual void failure() {
+        FAIL() << "Resolver reported completion failure";
+
+        // Flag completion
+        run_ = true;
+        status_ = false;
+
+        service_.stop();    // Cause run() to exit.
+    }
+
+    /// \brief Return status of "run" flag
+    bool getRun() const {
+        return (run_);
+    }
+
+    /// \brief Return "status" flag
+    bool getStatus() const {
+        return (status_);
+    }
+
+private:
+    IOService&      service_;       ///< Service handling the run queue
+    bool            run_;           ///< Set true when completion handler run
+    bool            status_;        ///< Set true for success, false on error
+};
+
+// Sets up the UDP and TCP "servers", then tries a resolution.
+
+TEST_F(RecursiveQueryTest3, Resolve) {
+    // Set up the UDP server and issue the first read.  The endpoint from which
+    // the query is sent is put in udp_endpoint_ when the read completes, which
+    // is referenced in the callback as the place to which the response is
+    // sent.
+    udp_socket_.set_option(socket_base::reuse_address(true));
+    udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS3),
+                                   TEST_PORT3));
+    udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
+                                                sizeof(udp_receive_buffer_)),
+                                   udp_remote_,
+                           boost::bind(&RecursiveQueryTest3::udpReceiveHandler,
+                                               this, _1, _2));
+
+    // Set up the TCP server and issue the accept.  Acceptance will cause the
+    // read to be issued.
+    tcp::acceptor acceptor(service_.get_io_service(),
+                           tcp::endpoint(tcp::v4(), TEST_PORT3));
+    acceptor.async_accept(tcp_socket_,
+                          boost::bind(&RecursiveQueryTest3::tcpAcceptHandler,
+                                      this, _1, 0));
+
+    // Set up the RecursiveQuery object. We will also test that it correctly
+    // records RTT times by setting up a RTT recorder object as well.
+    std::vector<std::pair<std::string, uint16_t> > upstream;         // Empty
+    std::vector<std::pair<std::string, uint16_t> > upstream_root;    // Empty
+    RecursiveQuery query(dns_service_, *nsas_, cache_,
+                         upstream, upstream_root);
+    query.setTestServer(TEST_ADDRESS3, TEST_PORT3);
+
+    boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
+    query.setRttRecorder(recorder);
+
+    // Set up callback to receive notification that the query has completed.
+    isc::resolve::ResolverInterface::CallbackPtr
+        resolver_callback(new ResolverCallback3(service_));
+
+    // Kick off the resolution process.
+    expected_ = EDNS_UDP;
+    query.resolve(question_, resolver_callback);
+    service_.run();
+
+    // Check what ran. (We have to cast the callback to ResolverCallback3 as we
+    // lost the information on the derived class when we used a
+    // ResolverInterface::CallbackPtr to store a pointer to it.)
+    ResolverCallback3* rc
+                    = static_cast<ResolverCallback3*>(resolver_callback.get());
+    EXPECT_TRUE(rc->getRun());
+    EXPECT_TRUE(rc->getStatus());
+
+    // Finally, check that all the RTTs were "reasonable" (defined here as
+    // being below 2 seconds).  This is an explicit check to test that the
+    // variables in the RTT calculation are at least being initialized; if they
+    // weren't, we would expect some absurdly high answers.
+    vector<uint32_t> rtt = recorder->getRtt();
+    EXPECT_GT(rtt.size(), 0);
+    for (int i = 0; i < rtt.size(); ++i) {
+        EXPECT_LT(rtt[i], 2000);
+    }
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc
index 379a0a1..fba8e1a 100644
--- a/src/lib/server_common/portconfig.cc
+++ b/src/lib/server_common/portconfig.cc
@@ -59,7 +59,7 @@ parseAddresses(isc::data::ConstElementPtr addresses,
                     result.push_back(AddressPair(addr->stringValue(),
                         port->intValue()));
                 }
-                catch (const TypeError &e) { // Better error message
+                catch (const TypeError&) { // Better error message
                     LOG_ERROR(logger, SRVCOMM_ADDRESS_TYPE).
                         arg(addrPair->str());
                     isc_throw(TypeError,
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index cbcd54d..96b9d25 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -1,8 +1,12 @@
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
 lib_LTLIBRARIES = libutil_io.la
 libutil_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
-libutil_io_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
+libutil_io_la_SOURCES += socketsession.h socketsession.cc sockaddr_util.h
+libutil_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 
 CLEANFILES = *.gcno *.gcda
 
diff --git a/src/lib/util/io/fd.cc b/src/lib/util/io/fd.cc
index 04e64dc..f9b17a7 100644
--- a/src/lib/util/io/fd.cc
+++ b/src/lib/util/io/fd.cc
@@ -23,46 +23,63 @@ namespace io {
 
 bool
 write_data(const int fd, const void *buffer_v, const size_t length) {
-    const unsigned char *buffer(static_cast<const unsigned char *>(buffer_v));
-    size_t rest(length);
-    // Just keep writing until all is written
-    while (rest) {
-        ssize_t written(write(fd, buffer, rest));
-        if (rest == -1) {
-            if (errno == EINTR) { // Just keep going
-                continue;
-            } else {
+
+    const unsigned char* buffer(static_cast<const unsigned char*>(buffer_v));
+    size_t remaining = length;  // Amount remaining to be written
+
+    while (remaining > 0) {
+        ssize_t amount = write(fd, buffer, remaining);
+        if (amount == -1) {
+            // Some error.  Ignore interrupted system calls otherwise return
+            // an error indication.
+            if (errno != EINTR) {
                 return false;
             }
-        } else { // Wrote something
-            rest -= written;
-            buffer += written;
+
+        } else if (amount > 0) {
+            // Wrote "amount" bytes from the buffer
+            remaining -= amount;
+            buffer += amount;
+
+        } else {
+            // Wrote zero bytes from the buffer. We should not get here as any
+            // error that causes zero bytes to be written should have returned
+            // -1.  However, write(2) can return 0, and in this case we
+            // interpret it as an error.
+            return (false);
         }
     }
-    return true;
+    return (true);
 }
 
 ssize_t
 read_data(const int fd, void *buffer_v, const size_t length) {
-    unsigned char *buffer(static_cast<unsigned char *>(buffer_v));
-    size_t rest(length), already(0);
-    while (rest) { // Stil something to read
-        ssize_t amount(read(fd, buffer, rest));
-        if (rest == -1) {
-            if (errno == EINTR) { // Continue on interrupted call
-                continue;
-            } else {
+
+    unsigned char* buffer(static_cast<unsigned char*>(buffer_v));
+    size_t remaining = length;   // Amount remaining to be read
+
+    while (remaining > 0) {
+        ssize_t amount = read(fd, buffer, remaining);
+        if (amount == -1) {
+            // Some error.  Ignore interrupted system calls otherwise return
+            // an error indication.
+            if (errno != EINTR) {
                 return -1;
             }
-        } else if (amount) {
-            already += amount;
-            rest -= amount;
+
+        } else if (amount > 0) {
+            // Read "amount" bytes into the buffer
+            remaining -= amount;
             buffer += amount;
-        } else { // EOF
-            return already;
+
+        } else {
+            // EOF - end the read
+            break;
         }
     }
-    return already;
+
+    // Return total number of bytes read
+    return (static_cast<ssize_t>(length - remaining));
 }
 
 }
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
index 92576e0..9c02a33 100644
--- a/src/lib/util/io/fd_share.cc
+++ b/src/lib/util/io/fd_share.cc
@@ -18,6 +18,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
+#include <errno.h>
 #include <stdlib.h>             // for malloc and free
 #include "fd_share.h"
 
@@ -87,18 +88,22 @@ recv_fd(const int sock) {
     msghdr.msg_controllen = cmsg_space(sizeof(int));
     msghdr.msg_control = malloc(msghdr.msg_controllen);
     if (msghdr.msg_control == NULL) {
-        return (FD_OTHER_ERROR);
+        return (FD_SYSTEM_ERROR);
     }
 
-    if (recvmsg(sock, &msghdr, 0) < 0) {
+    const int cc = recvmsg(sock, &msghdr, 0);
+    if (cc <= 0) {
         free(msghdr.msg_control);
-        return (FD_COMM_ERROR);
+        if (cc == 0) {
+            errno = ECONNRESET;
+        }
+        return (FD_SYSTEM_ERROR);
     }
     const struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
     int fd = FD_OTHER_ERROR;
     if (cmsg != NULL && cmsg->cmsg_len == cmsg_len(sizeof(int)) &&
         cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
-        fd = *(const int*)CMSG_DATA(cmsg);
+        std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
     }
     free(msghdr.msg_control);
     return (fd);
@@ -127,11 +132,11 @@ send_fd(const int sock, const int fd) {
     cmsg->cmsg_len = cmsg_len(sizeof(int));
     cmsg->cmsg_level = SOL_SOCKET;
     cmsg->cmsg_type = SCM_RIGHTS;
-    *(int*)CMSG_DATA(cmsg) = fd;
+    std::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
 
     const int ret = sendmsg(sock, &msghdr, 0);
     free(msghdr.msg_control);
-    return (ret >= 0 ? 0 : FD_COMM_ERROR);
+    return (ret >= 0 ? 0 : FD_SYSTEM_ERROR);
 }
 
 } // End for namespace io
diff --git a/src/lib/util/io/fd_share.h b/src/lib/util/io/fd_share.h
index b74d4ef..2b30abd 100644
--- a/src/lib/util/io/fd_share.h
+++ b/src/lib/util/io/fd_share.h
@@ -25,7 +25,7 @@ namespace isc {
 namespace util {
 namespace io {
 
-const int FD_COMM_ERROR = -2;
+const int FD_SYSTEM_ERROR = -2;
 const int FD_OTHER_ERROR = -1;
 
 /**
@@ -33,8 +33,11 @@ const int FD_OTHER_ERROR = -1;
  * This receives a file descriptor sent over an unix domain socket. This
  * is the counterpart of send_fd().
  *
- * \return FD_COMM_ERROR when there's error receiving the socket, FD_OTHER_ERROR
- *     when there's a different error.
+ * \return FD_SYSTEM_ERROR when there's an error at the operating system
+ * level (such as a system call failure).  The global 'errno' variable
+ * indicates the specific error.  FD_OTHER_ERROR when there's a different
+ * error.
+ *
  * \param sock The unix domain socket to read from. Tested and it does
  *     not work with a pipe.
  */
@@ -45,8 +48,9 @@ int recv_fd(const int sock);
  * This sends a file descriptor over an unix domain socket. This is the
  * counterpart of recv_fd().
  *
- * \return FD_COMM_ERROR when there's error sending the socket, FD_OTHER_ERROR
- *     for all other possible errors.
+ * \return FD_SYSTEM_ERROR when there's an error at the operating system
+ * level (such as a system call failure).  The global 'errno' variable
+ * indicates the specific error.
  * \param sock The unix domain socket to send to. Tested and it does not
  *     work with a pipe.
  * \param fd The file descriptor to send. It should work with any valid
diff --git a/src/lib/util/io/fdshare_python.cc b/src/lib/util/io/fdshare_python.cc
index 0a41107..249f8b0 100644
--- a/src/lib/util/io/fdshare_python.cc
+++ b/src/lib/util/io/fdshare_python.cc
@@ -67,14 +67,15 @@ PyInit_libutil_io_python(void) {
         return (NULL);
     }
 
-    PyObject* FD_COMM_ERROR = Py_BuildValue("i", isc::util::io::FD_COMM_ERROR);
-    if (FD_COMM_ERROR == NULL) {
+    PyObject* FD_SYSTEM_ERROR = Py_BuildValue("i",
+                                              isc::util::io::FD_SYSTEM_ERROR);
+    if (FD_SYSTEM_ERROR == NULL) {
         Py_XDECREF(mod);
         return (NULL);
     }
-    int ret = PyModule_AddObject(mod, "FD_COMM_ERROR", FD_COMM_ERROR);
-    if (-1 == ret) {
-        Py_XDECREF(FD_COMM_ERROR);
+    int ret = PyModule_AddObject(mod, "FD_SYSTEM_ERROR", FD_SYSTEM_ERROR);
+    if (ret == -1) {
+        Py_XDECREF(FD_SYSTEM_ERROR);
         Py_XDECREF(mod);
         return (NULL);
     }
diff --git a/src/lib/util/io/sockaddr_util.h b/src/lib/util/io/sockaddr_util.h
new file mode 100644
index 0000000..4c9149e
--- /dev/null
+++ b/src/lib/util/io/sockaddr_util.h
@@ -0,0 +1,69 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SOCKADDR_UTIL_H_
+#define __SOCKADDR_UTIL_H_ 1
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <cassert>
+
+// This definitions in this file are for the convenience of internal
+// implementation and test code, and are not intended to be used publicly.
+// The namespace "internal" indicates the intent.
+
+namespace isc {
+namespace util {
+namespace io {
+namespace internal {
+
+inline socklen_t
+getSALength(const struct sockaddr& sa) {
+    if (sa.sa_family == AF_INET) {
+        return (sizeof(struct sockaddr_in));
+    } else {
+        assert(sa.sa_family == AF_INET6);
+        return (sizeof(struct sockaddr_in6));
+    }
+}
+
+// Lower level C-APIs require conversion between various variants of
+// sockaddr's, which is not friendly with C++.  The following templates
+// are a shortcut of common workaround conversion in such cases.
+
+template <typename SAType>
+const struct sockaddr*
+convertSockAddr(const SAType* sa) {
+    const void* p = sa;
+    return (static_cast<const struct sockaddr*>(p));
+}
+
+template <typename SAType>
+struct sockaddr*
+convertSockAddr(SAType* sa) {
+    void* p = sa;
+    return (static_cast<struct sockaddr*>(p));
+}
+
+}
+}
+}
+}
+
+#endif  // __SOCKADDR_UTIL_H_
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/io/socketsession.cc b/src/lib/util/io/socketsession.cc
new file mode 100644
index 0000000..4c50949
--- /dev/null
+++ b/src/lib/util/io/socketsession.cc
@@ -0,0 +1,434 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstring>
+#include <cassert>
+
+#include <string>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include "fd_share.h"
+#include "socketsession.h"
+#include "sockaddr_util.h"
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace io {
+
+using namespace internal;
+
+// The expected max size of the session header: 2-byte header length,
+// 6 32-bit fields, and 2 sockaddr structure. (see the SocketSessionUtility
+// overview description in the header file).  sizeof sockaddr_storage
+// should be the possible max of any sockaddr structure
+const size_t DEFAULT_HEADER_BUFLEN = sizeof(uint16_t) + sizeof(uint32_t) * 6 +
+    sizeof(struct sockaddr_storage) * 2;
+
+// The allowable maximum size of data passed with the socket FD.  For now
+// we use a fixed value of 65535, the largest possible size of valid DNS
+// messages.  We may enlarge it or make it configurable as we see the need
+// for more flexibility.
+const int MAX_DATASIZE = 65535;
+
+// The initial buffer size for receiving socket session data in the receiver.
+// This value is the maximum message size of DNS messages carried over UDP
+// (without EDNS).  In our expected usage (at the moment) this should be
+// sufficiently large (the expected data is AXFR/IXFR query or an UPDATE
+// requests.  The former should be generally quite small.  While the latter
+// could be large, it would often be small enough for a single UDP message).
+// If it turns out that there are many exceptions, we may want to extend
+// the class so that this value can be customized.  Note that the buffer
+// will be automatically extended for longer data and this is only about
+// efficiency.
+const size_t INITIAL_BUFSIZE = 512;
+
+// The (default) socket buffer size for the forwarder and receiver.  This is
+// chosen to be sufficiently large to store two full-size DNS messages.  We
+// may want to customize this value in future.
+const int SOCKSESSION_BUFSIZE = (DEFAULT_HEADER_BUFLEN + MAX_DATASIZE) * 2;
+
+struct SocketSessionForwarder::ForwarderImpl {
+    ForwarderImpl() : buf_(DEFAULT_HEADER_BUFLEN) {}
+    struct sockaddr_un sock_un_;
+    socklen_t sock_un_len_;
+    int fd_;
+    OutputBuffer buf_;
+};
+
+SocketSessionForwarder::SocketSessionForwarder(const std::string& unix_file) :
+    impl_(NULL)
+{
+    // We need to filter SIGPIPE for subsequent push().  See the class
+    // description.
+    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+        isc_throw(Unexpected, "Failed to filter SIGPIPE: " << strerror(errno));
+    }
+
+    ForwarderImpl impl;
+    if (sizeof(impl.sock_un_.sun_path) - 1 < unix_file.length()) {
+        isc_throw(SocketSessionError,
+                  "File name for a UNIX domain socket is too long: " <<
+                  unix_file);
+    }
+    impl.sock_un_.sun_family = AF_UNIX;
+    // the copy should be safe due to the above check, but we'd be rather
+    // paranoid about making it 100% sure even if the check has a bug (with
+    // triggering the assertion in the worse case)
+    strncpy(impl.sock_un_.sun_path, unix_file.c_str(),
+            sizeof(impl.sock_un_.sun_path));
+    assert(impl.sock_un_.sun_path[sizeof(impl.sock_un_.sun_path) - 1] == '\0');
+    impl.sock_un_len_ = offsetof(struct sockaddr_un, sun_path) +
+        unix_file.length();
+#ifdef HAVE_SA_LEN
+    impl.sock_un_.sun_len = impl.sock_un_len_;
+#endif
+    impl.fd_ = -1;
+
+    impl_ = new ForwarderImpl;
+    *impl_ = impl;
+}
+
+SocketSessionForwarder::~SocketSessionForwarder() {
+    if (impl_->fd_ != -1) {
+        close();
+    }
+    delete impl_;
+}
+
+void
+SocketSessionForwarder::connectToReceiver() {
+    if (impl_->fd_ != -1) {
+        isc_throw(BadValue, "Duplicate connect to UNIX domain "
+                  "endpoint " << impl_->sock_un_.sun_path);
+    }
+
+    impl_->fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (impl_->fd_ == -1) {
+        isc_throw(SocketSessionError, "Failed to create a UNIX domain socket: "
+                  << strerror(errno));
+    }
+    // Make the socket non blocking
+    int fcntl_flags = fcntl(impl_->fd_, F_GETFL, 0);
+    if (fcntl_flags != -1) {
+        fcntl_flags |= O_NONBLOCK;
+        fcntl_flags = fcntl(impl_->fd_, F_SETFL, fcntl_flags);
+    }
+    if (fcntl_flags == -1) {
+        close();   // note: this is the internal method, not ::close()
+        isc_throw(SocketSessionError,
+                  "Failed to make UNIX domain socket non blocking: " <<
+                  strerror(errno));
+    }
+    // Ensure the socket send buffer is large enough.  If we can't get the
+    // current size, simply set the sufficient size.
+    int sndbuf_size;
+    socklen_t sndbuf_size_len = sizeof(sndbuf_size);
+    if (getsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &sndbuf_size,
+                   &sndbuf_size_len) == -1 ||
+        sndbuf_size < SOCKSESSION_BUFSIZE) {
+        if (setsockopt(impl_->fd_, SOL_SOCKET, SO_SNDBUF, &SOCKSESSION_BUFSIZE,
+                       sizeof(SOCKSESSION_BUFSIZE)) == -1) {
+            close();
+            isc_throw(SocketSessionError, "Failed to set send buffer size");
+        }
+    }
+    if (connect(impl_->fd_, convertSockAddr(&impl_->sock_un_),
+                impl_->sock_un_len_) == -1) {
+        close();
+        isc_throw(SocketSessionError, "Failed to connect to UNIX domain "
+                  "endpoint " << impl_->sock_un_.sun_path << ": " <<
+                  strerror(errno));
+    }
+}
+
+void
+SocketSessionForwarder::close() {
+    if (impl_->fd_ == -1) {
+        isc_throw(BadValue, "Attempt of close before connect");
+    }
+    ::close(impl_->fd_);
+    impl_->fd_ = -1;
+}
+
+void
+SocketSessionForwarder::push(int sock, int family, int type, int protocol,
+                             const struct sockaddr& local_end,
+                             const struct sockaddr& remote_end,
+                             const void* data, size_t data_len)
+{
+    if (impl_->fd_ == -1) {
+        isc_throw(BadValue, "Attempt of push before connect");
+    }
+    if ((local_end.sa_family != AF_INET && local_end.sa_family != AF_INET6) ||
+        (remote_end.sa_family != AF_INET && remote_end.sa_family != AF_INET6))
+    {
+        isc_throw(BadValue, "Invalid address family: must be "
+                  "AF_INET or AF_INET6; " <<
+                  static_cast<int>(local_end.sa_family) << ", " <<
+                  static_cast<int>(remote_end.sa_family) << " given");
+    }
+    if (family != local_end.sa_family || family != remote_end.sa_family) {
+        isc_throw(BadValue, "Inconsistent address family: must be "
+                  << static_cast<int>(family) << "; "
+                  << static_cast<int>(local_end.sa_family) << ", "
+                  << static_cast<int>(remote_end.sa_family) << " given");
+    }
+    if (data_len == 0 || data == NULL) {
+        isc_throw(BadValue, "Data for a socket session must not be empty");
+    }
+    if (data_len > MAX_DATASIZE) {
+        isc_throw(BadValue, "Invalid socket session data size: " <<
+                  data_len << ", must not exceed " << MAX_DATASIZE);
+    }
+
+    if (send_fd(impl_->fd_, sock) != 0) {
+        isc_throw(SocketSessionError, "FD passing failed: " <<
+                  strerror(errno));
+    }
+
+    impl_->buf_.clear();
+    // Leave the space for the header length
+    impl_->buf_.skip(sizeof(uint16_t));
+    // Socket properties: family, type, protocol
+    impl_->buf_.writeUint32(static_cast<uint32_t>(family));
+    impl_->buf_.writeUint32(static_cast<uint32_t>(type));
+    impl_->buf_.writeUint32(static_cast<uint32_t>(protocol));
+    // Local endpoint
+    impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(local_end)));
+    impl_->buf_.writeData(&local_end, getSALength(local_end));
+    // Remote endpoint
+    impl_->buf_.writeUint32(static_cast<uint32_t>(getSALength(remote_end)));
+    impl_->buf_.writeData(&remote_end, getSALength(remote_end));
+    // Data length.  Must be fit uint32 due to the range check above.
+    const uint32_t data_len32 = static_cast<uint32_t>(data_len);
+    assert(data_len == data_len32); // shouldn't cause overflow.
+    impl_->buf_.writeUint32(data_len32);
+    // Write the resulting header length at the beginning of the buffer
+    impl_->buf_.writeUint16At(impl_->buf_.getLength() - sizeof(uint16_t), 0);
+
+    const struct iovec iov[2] = {
+        { const_cast<void*>(impl_->buf_.getData()), impl_->buf_.getLength() },
+        { const_cast<void*>(data), data_len }
+    };
+    const int cc = writev(impl_->fd_, iov, 2);
+    if (cc != impl_->buf_.getLength() + data_len) {
+        if (cc < 0) {
+            isc_throw(SocketSessionError,
+                      "Write failed in forwarding a socket session: " <<
+                      strerror(errno));
+        }
+        isc_throw(SocketSessionError,
+                  "Incomplete write in forwarding a socket session: " << cc <<
+                  "/" << (impl_->buf_.getLength() + data_len));
+    }
+}
+
+SocketSession::SocketSession(int sock, int family, int type, int protocol,
+                             const sockaddr* local_end,
+                             const sockaddr* remote_end,
+                             const void* data, size_t data_len) :
+    sock_(sock), family_(family), type_(type), protocol_(protocol),
+    local_end_(local_end), remote_end_(remote_end),
+    data_(data), data_len_(data_len)
+{
+    if (local_end == NULL || remote_end == NULL) {
+        isc_throw(BadValue, "sockaddr must be non NULL for SocketSession");
+    }
+    if (data_len == 0) {
+        isc_throw(BadValue, "data_len must be non 0 for SocketSession");
+    }
+    if (data == NULL) {
+        isc_throw(BadValue, "data must be non NULL for SocketSession");
+    }
+}
+
+struct SocketSessionReceiver::ReceiverImpl {
+    ReceiverImpl(int fd) : fd_(fd),
+                           sa_local_(convertSockAddr(&ss_local_)),
+                           sa_remote_(convertSockAddr(&ss_remote_)),
+                           header_buf_(DEFAULT_HEADER_BUFLEN),
+                           data_buf_(INITIAL_BUFSIZE)
+    {
+        if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &SOCKSESSION_BUFSIZE,
+                       sizeof(SOCKSESSION_BUFSIZE)) == -1) {
+            isc_throw(SocketSessionError,
+                      "Failed to set receive buffer size");
+        }
+    }
+
+    const int fd_;
+    struct sockaddr_storage ss_local_; // placeholder for local endpoint
+    struct sockaddr* const sa_local_;
+    struct sockaddr_storage ss_remote_; // placeholder for remote endpoint
+    struct sockaddr* const sa_remote_;
+
+    // placeholder for session header and data
+    vector<uint8_t> header_buf_;
+    vector<uint8_t> data_buf_;
+};
+
+SocketSessionReceiver::SocketSessionReceiver(int fd) :
+    impl_(new ReceiverImpl(fd))
+{
+}
+
+SocketSessionReceiver::~SocketSessionReceiver() {
+    delete impl_;
+}
+
+namespace {
+// A shortcut to throw common exception on failure of recv(2)
+void
+readFail(int actual_len, int expected_len) {
+    if (expected_len < 0) {
+        isc_throw(SocketSessionError, "Failed to receive data from "
+                  "SocketSessionForwarder: " << strerror(errno));
+    }
+    isc_throw(SocketSessionError, "Incomplete data from "
+              "SocketSessionForwarder: " << actual_len << "/" <<
+              expected_len);
+}
+
+// A helper container for a (socket) file descriptor used in
+// SocketSessionReceiver::pop that ensures the socket is closed unless it
+// can be safely passed to the caller via release().
+struct ScopedSocket : boost::noncopyable {
+    ScopedSocket(int fd) : fd_(fd) {}
+    ~ScopedSocket() {
+        if (fd_ >= 0) {
+            close(fd_);
+        }
+    }
+    int release() {
+        const int fd = fd_;
+        fd_ = -1;
+        return (fd);
+    }
+    int fd_;
+};
+}
+
+SocketSession
+SocketSessionReceiver::pop() {
+    ScopedSocket passed_sock(recv_fd(impl_->fd_));
+    if (passed_sock.fd_ == FD_SYSTEM_ERROR) {
+        isc_throw(SocketSessionError, "Receiving a forwarded FD failed: " <<
+                  strerror(errno));
+    } else if (passed_sock.fd_ < 0) {
+        isc_throw(SocketSessionError, "No FD forwarded");
+    }
+
+    uint16_t header_len;
+    const int cc_hlen = recv(impl_->fd_, &header_len, sizeof(header_len),
+                        MSG_WAITALL);
+    if (cc_hlen < sizeof(header_len)) {
+        readFail(cc_hlen, sizeof(header_len));
+    }
+    header_len = InputBuffer(&header_len, sizeof(header_len)).readUint16();
+    if (header_len > DEFAULT_HEADER_BUFLEN) {
+        isc_throw(SocketSessionError, "Too large header length: " <<
+                  header_len);
+    }
+    impl_->header_buf_.clear();
+    impl_->header_buf_.resize(header_len);
+    const int cc_hdr = recv(impl_->fd_, &impl_->header_buf_[0], header_len,
+                            MSG_WAITALL);
+    if (cc_hdr < header_len) {
+        readFail(cc_hdr, header_len);
+    }
+
+    InputBuffer ibuffer(&impl_->header_buf_[0], header_len);
+    try {
+        const int family = static_cast<int>(ibuffer.readUint32());
+        if (family != AF_INET && family != AF_INET6) {
+            isc_throw(SocketSessionError,
+                      "Unsupported address family is passed: " << family);
+        }
+        const int type = static_cast<int>(ibuffer.readUint32());
+        const int protocol = static_cast<int>(ibuffer.readUint32());
+        const socklen_t local_end_len = ibuffer.readUint32();
+        const socklen_t endpoint_minlen = (family == AF_INET) ?
+            sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+        if (local_end_len < endpoint_minlen ||
+            local_end_len > sizeof(impl_->ss_local_)) {
+            isc_throw(SocketSessionError, "Invalid local SA length: " <<
+                      local_end_len);
+        }
+        ibuffer.readData(&impl_->ss_local_, local_end_len);
+        const socklen_t remote_end_len = ibuffer.readUint32();
+        if (remote_end_len < endpoint_minlen ||
+            remote_end_len > sizeof(impl_->ss_remote_)) {
+            isc_throw(SocketSessionError, "Invalid remote SA length: " <<
+                      remote_end_len);
+        }
+        ibuffer.readData(&impl_->ss_remote_, remote_end_len);
+        if (family != impl_->sa_local_->sa_family ||
+            family != impl_->sa_remote_->sa_family) {
+            isc_throw(SocketSessionError, "SA family inconsistent: " <<
+                      static_cast<int>(impl_->sa_local_->sa_family) << ", " <<
+                      static_cast<int>(impl_->sa_remote_->sa_family) <<
+                      " given, must be " << family);
+        }
+        const size_t data_len = ibuffer.readUint32();
+        if (data_len == 0 || data_len > MAX_DATASIZE) {
+            isc_throw(SocketSessionError,
+                      "Invalid socket session data size: " << data_len <<
+                      ", must be > 0 and <= " << MAX_DATASIZE);
+        }
+
+        impl_->data_buf_.clear();
+        impl_->data_buf_.resize(data_len);
+        const int cc_data = recv(impl_->fd_, &impl_->data_buf_[0], data_len,
+                                 MSG_WAITALL);
+        if (cc_data < data_len) {
+            readFail(cc_data, data_len);
+        }
+
+        return (SocketSession(passed_sock.release(), family, type, protocol,
+                              impl_->sa_local_, impl_->sa_remote_,
+                              &impl_->data_buf_[0], data_len));
+    } catch (const InvalidBufferPosition& ex) {
+        // We catch the case where the given header is too short and convert
+        // the exception to SocketSessionError.
+        isc_throw(SocketSessionError, "bogus socket session header: " <<
+                  ex.what());
+    }
+}
+
+}
+}
+}
diff --git a/src/lib/util/io/socketsession.h b/src/lib/util/io/socketsession.h
new file mode 100644
index 0000000..77f18a3
--- /dev/null
+++ b/src/lib/util/io/socketsession.h
@@ -0,0 +1,466 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SOCKETSESSION_H_
+#define __SOCKETSESSION_H_ 1
+
+#include <string>
+
+#include <boost/noncopyable.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace util {
+namespace io {
+
+/// \page SocketSessionUtility Socket session utility
+///
+/// This utility defines a set of classes that support forwarding a
+/// "socket session" from one process to another.  A socket session is a
+/// conceptual tuple of the following elements:
+/// - A network socket
+/// - The local and remote endpoints of a (IP) communication taking place on
+///   the socket.  In practice an endpoint is a pair of an IP address and
+///   TCP or UDP port number.
+/// - Some amount of data sent from the remote endpoint and received on the
+///   socket.  We call it (socket) session data in this documentation.
+///
+/// Note that this is a conceptual definition.  Depending on the underlying
+/// implementation and/or the network protocol, some of the elements could be
+/// part of others; for example, if it's an established TCP connection,
+/// the local and remote endpoints would be able to be retrieved from the
+/// socket using the standard \c getsockname() and \c getpeername() system
+/// calls.  But in this definition we separate these to be more generic.
+/// Also, as a matter of fact our intended usage includes non-connected UDP
+/// communications, in which case at least the remote endpoint should be
+/// provided separately from the socket.
+///
+/// In the actual implementation we represent a socket as a tuple of
+/// socket's file descriptor, address family (e.g. \c AF_INET6),
+/// socket type (e.g. \c SOCK_STREAM), and protocol (e.g. \c IPPROTO_TCP).
+/// The latter three are included in the representation of a socket in order
+/// to provide complete information of how the socket would be created
+/// by the \c socket(2) system call.  More specifically in practice, these
+/// parameters could be used to construct a Python socket object from the
+/// file descriptor.
+///
+/// We use the standard \c sockaddr structure to represent endpoints.
+///
+/// Socket session data is an opaque memory region of an arbitrary length
+/// (possibly with some reasonable upper limit).
+///
+/// To forward a socket session between processes, we use connected UNIX
+/// domain sockets established between the processes.  The file descriptor
+/// will be forwarded through the sockets as an ancillary data item of
+/// type \c SCM_RIGHTS.  Other elements of the session will be transferred
+/// as normal data over the connection.
+///
+/// We provide three classes to help applications forward socket sessions:
+/// \c SocketSessionForwarder is the sender of the UNIX domain connection,
+/// while \c SocketSessionReceiver is the receiver (this interface assumes
+/// one direction of forwarding); \c SocketSession represents a single
+/// socket session.
+///
+/// \c SocketSessionForwarder and \c SocketSessionReceiver objects use a
+/// straightforward protocol to pass elements of socket sessions.
+/// Once the connection is established, the forwarder object first forwards
+/// the file descriptor with 1-byte dummy data.  It then forwards a
+/// "(socket) session header", which contains all other elements of the session
+/// except the file descriptor (already forwarded) and session data.
+/// The wire format of the header is as follows:
+/// - The length of the header (16-bit unsigned integer)
+/// - Address family
+/// - Socket type
+/// - Protocol
+/// - Size of the local endpoint in bytes
+/// - Local endpoint (a copy of the memory image of the corresponding
+///   \c sockaddr)
+/// - Size of the remote endpoint in bytes
+/// - Remote endpoint (same as local endpoint)
+/// - Size of session data in bytes
+///
+/// The type of the fields is 32-bit unsigned integer unless explicitly
+/// noted, and all fields are formatted in the network byte order.
+///
+/// The socket session data immediately follows the session header.
+///
+/// Note that the fields do not necessarily be in the network byte order
+/// because they are expected to be exchanged on the same machine.  Likewise,
+/// integer elements such as address family do not necessarily be represented
+/// as an fixed-size value (i.e., 32-bit).  But fixed size fields are used
+/// in order to ensure maximum portability in such a (rare) case where the
+/// forwarder and the receiver are built with different compilers that have
+/// different definitions of \c int.  Also, since \c sockaddr fields are
+/// generally formatted in the network byte order, other fields are defined
+/// so to be consistent.
+///
+/// One basic assumption in the API of this utility is socket sessions should
+/// be forwarded without blocking, thus eliminating the need for incremental
+/// read/write or blocking other important services such as responding to
+/// requests from the application's clients.  This assumption should be held
+/// as long as both the forwarder and receiver have sufficient resources
+/// to handle the forwarding process since the communication is local.
+/// But a forward attempt could still block if the receiver is busy (or even
+/// hang up) and cannot keep up with the volume of incoming sessions.
+///
+/// So, in this implementation, the forwarder uses non blocking writes to
+/// forward sessions.  If a write attempt could block, it immediately gives
+/// up the operation with an exception.  The corresponding application is
+/// expected to catch it, close the connection, and perform any necessary
+/// recovery for that application (that would normally be re-establish the
+/// connection with a new receiver, possibly after confirming the receiving
+/// side is still alive).  On the other hand, the receiver implementation
+/// assumes it's possible that it only receive incomplete elements of a
+/// session (such as in the case where the forwarder writes part of the
+/// entire session and gives up the connection).  The receiver implementation
+/// throws an exception when it encounters an incomplete session.  Like the
+/// case of the forwarder application, the receiver application is expected
+/// to catch it, close the connection, and perform any necessary recovery
+/// steps.
+///
+/// Note that the receiver implementation uses blocking read.  So it's
+/// application's responsibility to ensure that there's at least some data
+/// in the connection when the receiver object is requested to receive a
+/// session (unless this operation can be blocking, e.g., by the use of
+/// a separate thread).  Also, if the forwarder implementation or application
+/// is malicious or extremely buggy and intentionally sends partial session
+/// and keeps the connection, the receiver could block in receiving a session.
+/// In general, we assume the forwarder doesn't do intentional blocking
+/// as it's a local node and is generally a module of the same (BIND 10)
+/// system.  The minimum requirement for the forwarder implementation (and
+/// application) is to make sure the connection is closed once it detects
+/// an error on it.  Even a naive implementation that simply dies due to
+/// the exception will meet this requirement.
+
+/// An exception indicating general errors that takes place in the
+/// socket session related class objects.
+///
+/// In general the errors are unusual but possible failures such as unexpected
+/// connection reset, and suggest the application to close the connection and
+/// (if necessary) reestablish it.
+class SocketSessionError: public Exception {
+public:
+    SocketSessionError(const char *file, size_t line, const char *what):
+        isc::Exception(file, line, what) {}
+};
+
+/// The forwarder of socket sessions
+///
+/// An object of this class maintains a UNIX domain socket (normally expected
+/// to be connected to a \c SocketSessionReceiver object) and forwards
+/// socket sessions to the receiver.
+///
+/// See the description of \ref SocketSessionUtility for other details of how
+/// the session forwarding works.
+class SocketSessionForwarder : boost::noncopyable {
+public:
+    /// The constructor.
+    ///
+    /// It's constructed with path information of the intended receiver,
+    /// but does not immediately establish a connection to the receiver;
+    /// \c connectToReceiver() must be called to establish it.  These are
+    /// separated so that an object of class can be initialized (possibly
+    /// as an attribute of a higher level application class object) without
+    /// knowing the receiver is ready for accepting new forwarders.  The
+    /// separate connect interface allows the object to be reused when it
+    /// detects connection failure and tries to re-establish it after closing
+    /// the failed one.
+    ///
+    /// On construction, it also installs a signal filter for SIGPIPE to
+    /// ignore it.  Since this class uses a stream-type connected UNIX domain
+    /// socket, if the receiver (abruptly) closes the connection a subsequent
+    /// write operation on the socket would trigger a SIGPIPE signal, which
+    /// kills the caller process by default.   This behavior would be
+    /// undesirable in many cases, so this implementation always disables
+    /// the signal.
+    ///
+    /// This approach has some drawbacks, however; first, since signal handling
+    /// is process (or thread) wide, ignoring it may not what the application
+    /// wants.  On the other hand, if the application changes how the signal is
+    /// handled after instantiating this class, the new behavior affects the
+    /// class operation.  Secondly, even if ignoring the signal is the desired
+    /// operation, it's a waste to set the filter every time this class object
+    /// is constructed.  It's sufficient to do it once.  We still adopt this
+    /// behavior based on the observation that in most cases applications would
+    /// like to ignore SIGPIPE (or simply doesn't care about it) and that this
+    /// class is not instantiated so often (so the wasteful setting overhead
+    /// should be marginal).  On the other hand, doing it every time is
+    /// beneficial if the application is threaded and different threads
+    /// create different forwarder objects (and if signals work per thread).
+    ///
+    /// \exception SocketSessionError \c unix_file is invalid as a path name
+    /// of a UNIX domain socket.
+    /// \exception Unexpected Error in setting a filter for SIGPIPE (see above)
+    /// \exception std::bad_alloc resource allocation failure
+    ///
+    /// \param unix_file Path name of the receiver.
+    explicit SocketSessionForwarder(const std::string& unix_file);
+
+    /// The destructor.
+    ///
+    /// If a connection has been established, it's automatically closed in
+    /// the destructor.
+    ~SocketSessionForwarder();
+
+    /// Establish a connection to the receiver.
+    ///
+    /// This method establishes a connection to the receiver at the path
+    /// given on construction.  It makes the underlying UNIX domain socket
+    /// non blocking, so this method (or subsequent \c push() calls) does not
+    /// block.
+    ///
+    /// \exception BadValue The method is called while an already
+    /// established connection is still active.
+    /// \exception SocketSessionError A system error in socket operation.
+    void connectToReceiver();
+
+    /// Close the connection to the receiver.
+    ///
+    /// The connection must have been established by \c connectToReceiver().
+    /// As long as it's met this method is exception free.
+    ///
+    /// \exception BadValue The connection hasn't been established.
+    void close();
+
+    /// Forward a socket session to the receiver.
+    ///
+    /// This method takes a set of parameters that represent a single socket
+    /// session, renders them in the "wire" format according to the internal
+    /// protocol (see \ref SocketSessionUtility) and forwards them to
+    /// the receiver through the UNIX domain connection.
+    ///
+    /// The connection must have been established by \c connectToReceiver().
+    ///
+    /// For simplicity and for the convenience of detecting application
+    /// errors, this method imposes some restrictions on the parameters:
+    /// - Socket family must be either \c AF_INET or \c AF_INET6
+    /// - The address family (\c sa_family) member of the local and remote
+    ///   end points must be equal to the \c family parameter
+    /// - Socket session data must not be empty (\c data_len must not be 0
+    ///   and \c data must not be NULL)
+    /// - Data length must not exceed 65535
+    /// These are not architectural limitation, and might be loosened in
+    /// future versions as we see the need for flexibility.
+    ///
+    /// Since the underlying UNIX domain socket is non blocking
+    /// (see the description for the constructor), a call to this method
+    /// should either return immediately or result in exception (in case of
+    /// "would block").
+    ///
+    /// \exception BadValue The method is called before establishing a
+    /// connection or given parameters are invalid.
+    /// \exception SocketSessionError A system error in socket operation,
+    /// including the case where the write operation would block.
+    ///
+    /// \param sock The socket file descriptor
+    /// \param family The address family (such as AF_INET6) of the socket
+    /// \param type The socket type (such as SOCK_DGRAM) of the socket
+    /// \param protocol The transport protocol (such as IPPROTO_UDP) of the
+    ///        socket
+    /// \param local_end The local end point of the session in the form of
+    ///        \c sockaddr.
+    /// \param remote_end The remote end point of the session in the form of
+    ///        \c sockaddr.
+    /// \param data A pointer to the beginning of the memory region for the
+    ///             session data
+    /// \param data_len The size of the session data in bytes.
+    void push(int sock, int family, int type, int protocol,
+              const struct sockaddr& local_end,
+              const struct sockaddr& remote_end,
+              const void* data, size_t data_len);
+
+private:
+    struct ForwarderImpl;
+    ForwarderImpl* impl_;
+};
+
+/// Socket session object.
+///
+/// The \c SocketSession class provides a convenient encapsulation
+/// for the notion of a socket session.  It's instantiated with straightforward
+/// parameters corresponding to a socket session, and provides read only
+/// accessors to the parameters to ensure data integrity.
+///
+/// In the initial design and implementation it's only used as a return type
+/// of \c SocketSessionReceiver::pop(), but it could also be used by
+/// the \c SocketSessionForwarder class or for other purposes.
+///
+/// It is assumed that the original owner of a \c SocketSession object
+/// (e.g. a class or a function that constructs it) is responsible for validity
+/// of the data passed to the object.  See the description of
+/// \c SocketSessionReceiver::pop() for the specific case of that usage.
+class SocketSession {
+public:
+    /// The constructor.
+    ///
+    /// This is a trivial constructor, taking a straightforward representation
+    /// of session parameters and storing them internally to ensure integrity.
+    ///
+    /// As long as the given parameters are valid it never throws an exception.
+    ///
+    /// \exception BadValue Given parameters don't meet the requirement
+    /// (see the parameter descriptions).
+    ///
+    /// \param sock The socket file descriptor
+    /// \param family The address family (such as AF_INET6) of the socket
+    /// \param type The socket type (such as SOCK_DGRAM) of the socket
+    /// \param protocol The transport protocol (such as IPPROTO_UDP) of the
+    ///        socket.
+    /// \param local_end The local end point of the session in the form of
+    ///        \c sockaddr.  Must not be NULL.
+    /// \param remote_end The remote end point of the session in the form of
+    ///        \c sockaddr.  Must not be NULL.
+    /// \param data A pointer to the beginning of the memory region for the
+    /// session data.  Must not be NULL, and the subsequent \c data_len bytes
+    /// must be valid.
+    /// \param data_len The size of the session data in bytes.  Must not be 0.
+    SocketSession(int sock, int family, int type, int protocol,
+                  const sockaddr* local_end, const sockaddr* remote_end,
+                  const void* data, size_t data_len);
+
+    /// Return the socket file descriptor.
+    int getSocket() const { return (sock_); }
+
+    /// Return the address family (such as AF_INET6) of the socket.
+    int getFamily() const { return (family_); }
+
+    /// Return the socket type (such as SOCK_DGRAM) of the socket.
+    int getType() const { return (type_); }
+
+    /// Return the transport protocol (such as IPPROTO_UDP) of the socket.
+    int getProtocol() const { return (protocol_); }
+
+    /// Return the local end point of the session in the form of \c sockaddr.
+    const sockaddr& getLocalEndpoint() const { return (*local_end_); }
+
+    /// Return the remote end point of the session in the form of \c sockaddr.
+    const sockaddr& getRemoteEndpoint() const { return (*remote_end_); }
+
+    /// Return a pointer to the beginning of the memory region for the session
+    /// data.
+    ///
+    /// In the current implementation it should never be NULL, and the region
+    /// of the size returned by \c getDataLength() is expected to be valid.
+    const void* getData() const { return (data_); }
+
+    /// Return the size of the session data in bytes.
+    ///
+    /// In the current implementation it should be always larger than 0.
+    size_t getDataLength() const { return (data_len_); }
+
+private:
+    const int sock_;
+    const int family_;
+    const int type_;
+    const int protocol_;
+    const sockaddr* local_end_;
+    const sockaddr* remote_end_;
+    const void* const data_;
+    const size_t data_len_;
+};
+
+/// The receiver of socket sessions
+///
+/// An object of this class holds a UNIX domain socket for an
+/// <em>established connection</em>, receives socket sessions from
+/// the remote forwarder, and provides the session to the application
+/// in the form of a \c SocketSession object.
+///
+/// Note that this class is instantiated with an already connected socket;
+/// it's not a listening socket that is accepting connection requests from
+/// forwarders.  It's application's responsibility to create the listening
+/// socket, listen on it and accept connections.  Once the connection is
+/// established, the application would construct a \c SocketSessionReceiver
+/// object with the socket for the newly established connection.
+/// This behavior is based on the design decision that the application should
+/// decide when it performs (possibly) blocking operations (see \ref
+/// SocketSessionUtility for more details).
+///
+/// See the description of \ref SocketSessionUtility for other details of how
+/// the session forwarding works.
+class SocketSessionReceiver : boost::noncopyable {
+public:
+    /// The constructor.
+    ///
+    /// \exception SocketSessionError Any error on an operation that is
+    /// performed on the given socket as part of initialization.
+    /// \exception std::bad_alloc Resource allocation failure
+    ///
+    /// \param fd A UNIX domain socket for an established connection with
+    /// a forwarder.
+    explicit SocketSessionReceiver(int fd);
+
+    /// The destructor.
+    ///
+    /// The destructor does \c not close the socket given on construction.
+    /// It's up to the application what to do with it (note that the
+    /// application would have to maintain the socket itself for detecting
+    /// the existence of a new socket session asynchronously).
+    ~SocketSessionReceiver();
+
+    /// Receive a socket session from the forwarder.
+    ///
+    /// This method receives wire-format data (see \ref SocketSessionUtility)
+    /// for a socket session on the UNIX domain socket, performs some
+    /// validation on the data, and returns the session information in the
+    /// form of a \c SocketSession object.
+    ///
+    /// The returned SocketSession object is valid only until the next time
+    /// this method is called or until the \c SocketSessionReceiver object is
+    /// destructed.
+    ///
+    /// The caller is responsible for closing the received socket (whose
+    /// file descriptor is accessible via \c SocketSession::getSocket()).
+    /// If the caller copies the returned \c SocketSession object, it's also
+    /// responsible for making sure the descriptor is closed at most once.
+    /// On the other hand, the caller is not responsible for freeing the
+    /// socket session data (accessible via \c SocketSession::getData());
+    /// the \c SocketSessionReceiver object will clean it up automatically.
+    ///
+    /// It ensures the following:
+    /// - The address family is either \c AF_INET or \c AF_INET6
+    /// - The address family (\c sa_family) member of the local and remote
+    ///   end points must be equal to the \c family parameter
+    /// - The socket session data is not empty and does not exceed 65535
+    ///   bytes.
+    /// If the validation fails or an unexpected system error happens
+    /// (including a connection close in the meddle of reception), it throws
+    /// an SocketSessionError exception.  When this happens, it's very
+    /// unlikely that a subsequent call to this method succeeds, so in reality
+    /// the application is expected to destruct it and close the socket in
+    /// such a case.
+    ///
+    /// \exception SocketSessionError Invalid data is received or a system
+    /// error on socket operation happens.
+    /// \exception std::bad_alloc Resource allocation failure
+    ///
+    /// \return A \c SocketSession object corresponding to the extracted
+    /// socket session.
+    SocketSession pop();
+
+private:
+    struct ReceiverImpl;
+    ReceiverImpl* impl_;
+};
+
+}
+}
+}
+
+#endif  // __SOCKETSESSION_H_
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index 426ced5..1a67a88 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -52,56 +52,12 @@ namespace {
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_ at CPPCLASS@, @CPPCLASS@> @CPPCLASS at Container;
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
-
-// General creation and destruction
-int @CPPCLASS at _init(s_ at CPPCLASS@* self, PyObject* args);
-void @CPPCLASS at _destroy(s_ at CPPCLASS@* self);
-
-// These are the functions we export
-// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
-//
-PyObject* @CPPCLASS at _toText(const s_ at CPPCLASS@* const self);
-PyObject* @CPPCLASS at _str(PyObject* self);
-PyObject* @CPPCLASS at _richcmp(const s_ at CPPCLASS@* const self,
-                            const s_ at CPPCLASS@* const other, int op);
-
-// This is quite specific pydnspp.  For other wrappers this should probably
-// be removed.
-PyObject* @CPPCLASS at _toWire(const s_ at CPPCLASS@* self, PyObject* args);
-
-// These are the functions we export
-// For a minimal support, we don't need them.
-
-// This list contains the actual set of functions we have in
-// python. Each entry has
-// 1. Python method name
-// 2. Our static function here
-// 3. Argument type
-// 4. Documentation
-PyMethodDef @CPPCLASS at _methods[] = {
-    { "to_text", reinterpret_cast<PyCFunction>(@CPPCLASS at _toText), METH_NOARGS,
-      "Returns the text representation" },
-    // This is quite specific pydnspp.  For other wrappers this should probably
-    // be removed:
-    { "to_wire", reinterpret_cast<PyCFunction>(@CPPCLASS at _toWire), METH_VARARGS,
-      "Converts the @CPPCLASS@ object to wire format.\n"
-      "The argument can be either a MessageRenderer or an object that "
-      "implements the sequence interface. If the object is mutable "
-      "(for instance a bytearray()), the wire data is added in-place.\n"
-      "If it is not (for instance a bytes() object), a new object is "
-      "returned" },
-    { NULL, NULL, 0, NULL }
-};
-
 // This is a template of typical code logic of python class initialization
 // with C++ backend.  You'll need to adjust it according to details of the
 // actual C++ class.
 int
- at CPPCLASS@_init(s_ at CPPCLASS@* self, PyObject* args) {
+ at CPPCLASS@_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_ at CPPCLASS@* self = static_cast<s_ at CPPCLASS@*>(po_self);
     try {
         if (PyArg_ParseTuple(args, "REPLACE ME")) {
             // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE.
@@ -114,13 +70,14 @@ int
         PyErr_SetString(po_IscException, ex_what.c_str());
         return (-1);
     } catch (...) {
-        PyErr_SetString(po_IscException,
-                        "Unexpected exception in constructing @CPPCLASS@");
+        PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
         return (-1);
     }
 
-    PyErr_SetString(PyExc_TypeError,
-                    "Invalid arguments to @CPPCLASS@ constructor");
+    // If we are here PyArg_ParseTuple() failed and TypeError should have
+    // been set.  If the constructor is more complicated and the control
+    // could reach this point for other reasons, an appropriate Python
+    // exception should be set by PyErr_SetString.
 
     return (-1);
 }
@@ -128,7 +85,8 @@ int
 // This is a template of typical code logic of python object destructor.
 // In many cases you can use it without modification, but check that carefully.
 void
- at CPPCLASS@_destroy(s_ at CPPCLASS@* const self) {
+ at CPPCLASS@_destroy(PyObject* po_self) {
+    s_ at CPPCLASS@* self = static_cast<s_ at CPPCLASS@*>(po_self);
     delete self->cppobj;
     self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
@@ -137,7 +95,8 @@ void
 // This should be able to be used without modification as long as the
 // underlying C++ class has toText().
 PyObject*
- at CPPCLASS@_toText(const s_ at CPPCLASS@* const self) {
+ at CPPCLASS@_toText(PyObject* po_self) {
+    const s_ at CPPCLASS@* self = static_cast<const s_ at CPPCLASS@*>(po_self);
     try {
         // toText() could throw, so we need to catch any exceptions below.
         return (Py_BuildValue("s", self->cppobj->toText().c_str()));
@@ -160,11 +119,17 @@ PyObject*
                                 const_cast<char*>("")));
 }
 
+// This is quite specific isc.dns.  For other wrappers this should probably
+// be removed.
+PyObject* @CPPCLASS at _toWire(PyObject* self, PyObject* args) {
+}
+
 PyObject* 
- at CPPCLASS@_richcmp(const s_ at CPPCLASS@* const self,
-                   const s_ at CPPCLASS@* const other,
-                   const int op)
-{
+ at CPPCLASS@_richcmp(PyObject* po_self, PyObject* po_other, const int op) {
+    const s_ at CPPCLASS@* const self = static_cast<const s_ at CPPCLASS@*>(po_self);
+    const s_ at CPPCLASS@* const other =
+        static_cast<const s_ at CPPCLASS@*>(po_other);
+
     bool c = false;
 
     // Check for null and if the types match. If different type,
@@ -200,6 +165,22 @@ PyObject*
         Py_RETURN_FALSE;
     }
 }
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef @CPPCLASS at _methods[] = {
+    { "to_text", @CPPCLASS at _toText, METH_NOARGS,
+      @CPPCLASS at _toText_doc },
+    // This is quite specific isc.dns.  For other wrappers this should probably
+    // be removed:
+    { "to_wire", @CPPCLASS at _toWire, METH_VARARGS,
+      @CPPCLASS at _toWire_doc },
+    { NULL, NULL, 0, NULL }
+};
 } // end of unnamed namespace
 
 namespace isc {
@@ -213,7 +194,7 @@ PyTypeObject @cppclass at _type = {
     "@MODULE at .@CPPCLASS@",
     sizeof(s_ at CPPCLASS@),                 // tp_basicsize
     0,                                  // tp_itemsize
-    reinterpret_cast<destructor>(@CPPCLASS at _destroy),       // tp_dealloc
+    @CPPCLASS at _destroy,                 // tp_dealloc
     NULL,                               // tp_print
     NULL,                               // tp_getattr
     NULL,                               // tp_setattr
@@ -230,11 +211,11 @@ PyTypeObject @cppclass at _type = {
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
     Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The @CPPCLASS@ class objects is...(COMPLETE THIS)",
+    @CPPCLASS at _doc,
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
     // THIS MAY HAVE TO BE CHANGED TO NULL:
-    reinterpret_cast<richcmpfunc>(@CPPCLASS at _richcmp), // tp_richcompare
+    @CPPCLASS at _richcmp,                 // tp_richcompare
     0,                                  // tp_weaklistoffset
     NULL,                               // tp_iter
     NULL,                               // tp_iternext
@@ -246,7 +227,7 @@ PyTypeObject @cppclass at _type = {
     NULL,                               // tp_descr_get
     NULL,                               // tp_descr_set
     0,                                  // tp_dictoffset
-    reinterpret_cast<initproc>(@CPPCLASS at _init),            // tp_init
+    @CPPCLASS at _init,                    // tp_init
     NULL,                               // tp_alloc
     PyType_GenericNew,                  // tp_new
     NULL,                               // tp_free
diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc
index ed7fc9b..89edcc9 100644
--- a/src/lib/util/strutil.cc
+++ b/src/lib/util/strutil.cc
@@ -120,7 +120,7 @@ format(const std::string& format, const std::vector<std::string>& args) {
     // Iterate through replacing all tokens
     result = format;
     size_t tokenpos = 0;    // Position of last token replaced
-    int i = 0;              // Index into argument array
+    std::vector<std::string>::size_type i = 0; // Index into argument array
 
     while ((i < args.size()) && (tokenpos != string::npos)) {
         tokenpos = result.find(flag, tokenpos);
diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h
index 021c236..0dbbe96 100644
--- a/src/lib/util/strutil.h
+++ b/src/lib/util/strutil.h
@@ -191,7 +191,7 @@ tokenToNum(const std::string& num_token) {
     NumType num;
     try {
         num = boost::lexical_cast<NumType>(num_token);
-    } catch (const boost::bad_lexical_cast& ex) {
+    } catch (const boost::bad_lexical_cast&) {
         isc_throw(StringTokenError, "Invalid SRV numeric parameter: " <<
                   num_token);
     }
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index 47243f8..dc144bd 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -2,6 +2,11 @@ SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_builddir)\"
+# XXX: we'll pollute the top builddir for creating a temporary test file
+# used to bind a UNIX domain socket so we can minimize the risk of exceeding
+# the limit of file name path size.
+AM_CPPFLAGS += -DTEST_DATA_TOPBUILDDIR=\"$(abs_top_builddir)\"
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -26,6 +31,7 @@ run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += time_utilities_unittest.cc
 
diff --git a/src/lib/util/tests/base32hex_unittest.cc b/src/lib/util/tests/base32hex_unittest.cc
index fa4a290..89d441e 100644
--- a/src/lib/util/tests/base32hex_unittest.cc
+++ b/src/lib/util/tests/base32hex_unittest.cc
@@ -154,7 +154,7 @@ TEST_F(Base32HexTest, decodeMap) {
 }
 
 TEST_F(Base32HexTest, encodeMap) {
-    for (int i = 0; i < 32; ++i) {
+    for (uint8_t i = 0; i < 32; ++i) {
         decoded_data.assign(4, 0);
         decoded_data.push_back(i);
         EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
diff --git a/src/lib/util/tests/hex_unittest.cc b/src/lib/util/tests/hex_unittest.cc
index 52bccea..efb842f 100644
--- a/src/lib/util/tests/hex_unittest.cc
+++ b/src/lib/util/tests/hex_unittest.cc
@@ -110,7 +110,7 @@ TEST_F(HexTest, decodeMap) {
 }
 
 TEST_F(HexTest, encodeMap) {
-    for (int i = 0; i < 16; ++i) {
+    for (uint8_t i = 0; i < 16; ++i) {
         decoded_data.clear();
         decoded_data.push_back(i);
         EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
diff --git a/src/lib/util/tests/socketsession_unittest.cc b/src/lib/util/tests/socketsession_unittest.cc
new file mode 100644
index 0000000..b9f2667
--- /dev/null
+++ b/src/lib/util/tests/socketsession_unittest.cc
@@ -0,0 +1,846 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstring>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/io/fd_share.h>
+#include <util/io/socketsession.h>
+#include <util/io/sockaddr_util.h>
+
+using namespace std;
+using namespace isc;
+using boost::scoped_ptr;
+using namespace isc::util::io;
+using namespace isc::util::io::internal;
+
+namespace {
+
+const char* const TEST_UNIX_FILE = TEST_DATA_TOPBUILDDIR "/test.unix";
+const char* const TEST_PORT = "53535";
+const char TEST_DATA[] = "BIND10 test";
+
+// A simple helper structure to automatically close test sockets on return
+// or exception in a RAII manner.  non copyable to prevent duplicate close.
+struct ScopedSocket : boost::noncopyable {
+    ScopedSocket() : fd(-1) {}
+    ScopedSocket(int sock) : fd(sock) {}
+    ~ScopedSocket() {
+        closeSocket();
+    }
+    void reset(int sock) {
+        closeSocket();
+        fd = sock;
+    }
+    int fd;
+private:
+    void closeSocket() {
+        if (fd >= 0) {
+            close(fd);
+        }
+    }
+};
+
+// A helper function that makes a test socket non block so that a certain
+// kind of test failure (such as missing send) won't cause hangup.
+void
+setNonBlock(int s, bool on) {
+    int fcntl_flags = fcntl(s, F_GETFL, 0);
+    if (on) {
+        fcntl_flags |= O_NONBLOCK;
+    } else {
+        fcntl_flags &= ~O_NONBLOCK;
+    }
+    if (fcntl(s, F_SETFL, fcntl_flags) == -1) {
+        isc_throw(isc::Unexpected, "fcntl(O_NONBLOCK) failed: " <<
+                  strerror(errno));
+    }
+}
+
+// A helper to impose some reasonable amount of wait on recv(from)
+// if possible.  It returns an option flag to be set for the system call
+// (when necessary).
+int
+setRecvDelay(int s) {
+    const struct timeval timeo = { 10, 0 };
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)) == -1) {
+        if (errno == ENOPROTOOPT) {
+            // Workaround for Solaris: see recursive_query_unittest
+            return (MSG_DONTWAIT);
+        } else {
+            isc_throw(isc::Unexpected, "set RCVTIMEO failed: " <<
+                      strerror(errno));
+        }
+    }
+    return (0);
+}
+
+// A shortcut type that is convenient to be used for socket related
+// system calls, which generally require this pair
+typedef pair<const struct sockaddr*, socklen_t> SockAddrInfo;
+
+// A helper class to convert textual representation of IP address and port
+// to a pair of sockaddr and its length (in the form of a SockAddrInfo
+// pair).  Its get method uses getaddrinfo(3) for the conversion and stores
+// the result in the addrinfo_list_ vector until the object is destructed.
+// The allocated resources will be automatically freed in an RAII manner.
+class SockAddrCreator {
+public:
+    ~SockAddrCreator() {
+        vector<struct addrinfo*>::const_iterator it;
+        for (it = addrinfo_list_.begin(); it != addrinfo_list_.end(); ++it) {
+            freeaddrinfo(*it);
+        }
+    }
+    SockAddrInfo get(const string& addr_str, const string& port_str) {
+        struct addrinfo hints, *res;
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+        hints.ai_family = AF_UNSPEC;
+        hints.ai_socktype = SOCK_DGRAM; // could be either DGRAM or STREAM here
+        const int error = getaddrinfo(addr_str.c_str(), port_str.c_str(),
+                                      &hints, &res);
+        if (error != 0) {
+            isc_throw(isc::Unexpected, "getaddrinfo failed for " <<
+                      addr_str << ", " << port_str << ": " <<
+                      gai_strerror(error));
+        }
+
+        // Technically, this is not entirely exception safe; if push_back
+        // throws, the resources allocated for 'res' will leak.  We prefer
+        // brevity here and ignore the minor failure mode.
+        addrinfo_list_.push_back(res);
+
+        return (SockAddrInfo(res->ai_addr, res->ai_addrlen));
+    }
+private:
+    vector<struct addrinfo*> addrinfo_list_;
+};
+
+class ForwardTest : public ::testing::Test {
+protected:
+    ForwardTest() : listen_fd_(-1), forwarder_(TEST_UNIX_FILE),
+                    large_text_(65535, 'a'),
+                    test_un_len_(2 + strlen(TEST_UNIX_FILE))
+    {
+        unlink(TEST_UNIX_FILE);
+        test_un_.sun_family = AF_UNIX;
+        strncpy(test_un_.sun_path, TEST_UNIX_FILE, sizeof(test_un_.sun_path));
+#ifdef HAVE_SA_LEN
+        test_un_.sun_len = test_un_len_;
+#endif
+    }
+
+    ~ForwardTest() {
+        if (listen_fd_ != -1) {
+            close(listen_fd_);
+        }
+        unlink(TEST_UNIX_FILE);
+    }
+
+    // Start an internal "socket session server".
+    void startListen() {
+        if (listen_fd_ != -1) {
+            isc_throw(isc::Unexpected, "duplicate call to startListen()");
+        }
+        listen_fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (listen_fd_ == -1) {
+            isc_throw(isc::Unexpected, "failed to create UNIX domain socket" <<
+                      strerror(errno));
+        }
+        if (bind(listen_fd_, convertSockAddr(&test_un_), test_un_len_) == -1) {
+            isc_throw(isc::Unexpected, "failed to bind UNIX domain socket" <<
+                      strerror(errno));
+        }
+        // 10 is an arbitrary choice, should be sufficient for a single test
+        if (listen(listen_fd_, 10) == -1) {
+            isc_throw(isc::Unexpected, "failed to listen on UNIX domain socket"
+                      << strerror(errno));
+        }
+    }
+
+    int dummyConnect() const {
+        const int s = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (s == -1) {
+            isc_throw(isc::Unexpected,
+                      "failed to create a test UNIX domain socket");
+        }
+        setNonBlock(s, true);
+        if (connect(s, convertSockAddr(&test_un_), sizeof(test_un_)) == -1) {
+            isc_throw(isc::Unexpected,
+                      "failed to connect to the test SocketSessionForwarder");
+        }
+        return (s);
+    }
+
+    // Accept a new connection from a SocketSessionForwarder and return
+    // the socket FD of the new connection.  This assumes startListen()
+    // has been called.
+    int acceptForwarder() {
+        setNonBlock(listen_fd_, true); // prevent the test from hanging up
+        struct sockaddr_un from;
+        socklen_t from_len = sizeof(from);
+        const int s = accept(listen_fd_, convertSockAddr(&from), &from_len);
+        if (s == -1) {
+            isc_throw(isc::Unexpected, "accept failed: " << strerror(errno));
+        }
+        // Make sure the socket is *blocking*.  We may pass large data, through
+        // it, and apparently non blocking read could cause some unexpected
+        // partial read on some systems.
+        setNonBlock(s, false);
+        return (s);
+    }
+
+    // A convenient shortcut for the namespace-scope version of getSockAddr
+    SockAddrInfo getSockAddr(const string& addr_str, const string& port_str) {
+        return (addr_creator_.get(addr_str, port_str));
+    }
+
+    // A helper method that creates a specified type of socket that is
+    // supposed to be passed via a SocketSessionForwarder.  It will bound
+    // to the specified address and port in sainfo.  If do_listen is true
+    // and it's a TCP socket, it will also start listening to new connection
+    // requests.
+    int createSocket(int family, int type, int protocol,
+                     const SockAddrInfo& sainfo, bool do_listen) 
+    {
+        int s = socket(family, type, protocol);
+        if (s < 0) {
+            isc_throw(isc::Unexpected, "socket(2) failed: " <<
+                      strerror(errno));
+        }
+        const int on = 1;
+        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+            isc_throw(isc::Unexpected, "setsockopt(SO_REUSEADDR) failed: " <<
+                      strerror(errno));
+        }
+        if (bind(s, sainfo.first, sainfo.second) < 0) {
+            close(s);
+            isc_throw(isc::Unexpected, "bind(2) failed: " <<
+                      strerror(errno));
+        }
+        if (do_listen && protocol == IPPROTO_TCP) {
+            if (listen(s, 1) == -1) {
+                isc_throw(isc::Unexpected, "listen(2) failed: " <<
+                          strerror(errno));
+            }
+        }
+        return (s);
+    }
+
+    // A helper method to push some (normally bogus) socket session header
+    // via a Unix domain socket that pretends to be a valid
+    // SocketSessionForwarder.  It first opens the Unix domain socket,
+    // and connect to the test receiver server (startListen() is expected to
+    // be called beforehand), forwards a valid file descriptor ("stdin" is
+    // used for simplicity), the pushed a 2-byte header length field of the
+    // session header.  The internal receiver_ pointer will be set to a
+    // newly created receiver object for the connection.
+    //
+    // \param hdrlen: The header length to be pushed.  It may or may not be
+    //                valid.
+    // \param hdrlen_len: The length of the actually pushed data as "header
+    //                    length".  Normally it should be 2 (the default), but
+    //                    could be a bogus value for testing.
+    // \param push_fd: Whether to forward the FD.  Normally it should be true,
+    //                 but can be false for testing.
+    void pushSessionHeader(uint16_t hdrlen,
+                           size_t hdrlen_len = sizeof(uint16_t),
+                           bool push_fd = true,
+                           int fd = 0)
+    {
+        isc::util::OutputBuffer obuffer(0);
+        obuffer.clear();
+
+        dummy_forwarder_.reset(dummyConnect());
+        if (push_fd && send_fd(dummy_forwarder_.fd, fd) != 0) {
+            isc_throw(isc::Unexpected, "Failed to pass FD");
+        }
+        obuffer.writeUint16(hdrlen);
+        if (hdrlen_len > 0) {
+            if (send(dummy_forwarder_.fd, obuffer.getData(), hdrlen_len, 0) !=
+                hdrlen_len) {
+                isc_throw(isc::Unexpected,
+                          "Failed to pass session header len");
+            }
+        }
+        accept_sock_.reset(acceptForwarder());
+        receiver_.reset(new SocketSessionReceiver(accept_sock_.fd));
+    }
+
+    // A helper method to push some (normally bogus) socket session via a
+    // Unix domain socket pretending to be a valid SocketSessionForwarder.
+    // It internally calls pushSessionHeader() for setup and pushing the
+    // header, and pass (often bogus) header data and session data based
+    // on the function parameters.  The parameters are generally compatible
+    // to those for SocketSessionForwarder::push, but could be invalid for
+    // testing purposes.  For session data, we use TEST_DATA and its size
+    // by default for simplicity, but the size can be tweaked for testing.
+    void pushSession(int family, int type, int protocol, socklen_t local_len,
+                     const sockaddr& local, socklen_t remote_len,
+                     const sockaddr& remote,
+                     size_t data_len = sizeof(TEST_DATA))
+    {
+        isc::util::OutputBuffer obuffer(0);
+        obuffer.writeUint32(static_cast<uint32_t>(family));
+        obuffer.writeUint32(static_cast<uint32_t>(type));
+        obuffer.writeUint32(static_cast<uint32_t>(protocol));
+        obuffer.writeUint32(static_cast<uint32_t>(local_len));
+        obuffer.writeData(&local, min(local_len, getSALength(local)));
+        obuffer.writeUint32(static_cast<uint32_t>(remote_len));
+        obuffer.writeData(&remote, min(remote_len, getSALength(remote)));
+        obuffer.writeUint32(static_cast<uint32_t>(data_len));
+        pushSessionHeader(obuffer.getLength());
+        if (send(dummy_forwarder_.fd, obuffer.getData(), obuffer.getLength(),
+                 0) != obuffer.getLength()) {
+            isc_throw(isc::Unexpected, "Failed to pass session header");
+        }
+        if (send(dummy_forwarder_.fd, TEST_DATA, sizeof(TEST_DATA), 0) !=
+            sizeof(TEST_DATA)) {
+            isc_throw(isc::Unexpected, "Failed to pass session data");
+        }
+    }
+
+    // See below
+    void checkPushAndPop(int family, int type, int protocoal,
+                         const SockAddrInfo& local,
+                         const SockAddrInfo& remote, const void* const data,
+                         size_t data_len, bool new_connection);
+
+protected:
+    int listen_fd_;
+    SocketSessionForwarder forwarder_;
+    ScopedSocket dummy_forwarder_; // forwarder "like" socket to pass bad data
+    scoped_ptr<SocketSessionReceiver> receiver_;
+    ScopedSocket accept_sock_;
+    const string large_text_;
+
+private:
+    struct sockaddr_un test_un_;
+    const socklen_t test_un_len_;
+    SockAddrCreator addr_creator_;
+};
+
+TEST_F(ForwardTest, construct) {
+    // On construction the existence of the file doesn't matter.
+    SocketSessionForwarder("some_file");
+
+    // But too long a path should be rejected
+    struct sockaddr_un s;     // can't be const; some compiler complains
+    EXPECT_THROW(SocketSessionForwarder(string(sizeof(s.sun_path), 'x')),
+                 SocketSessionError);
+    // If it's one byte shorter it should be okay
+    SocketSessionForwarder(string(sizeof(s.sun_path) - 1, 'x'));
+}
+
+TEST_F(ForwardTest, connect) {
+    // File doesn't exist (we assume the file "no_such_file" doesn't exist)
+    SocketSessionForwarder forwarder("no_such_file");
+    EXPECT_THROW(forwarder.connectToReceiver(), SocketSessionError);
+    // The socket should be closed internally, so close() should result in
+    // error.
+    EXPECT_THROW(forwarder.close(), BadValue);
+
+    // Set up the receiver and connect.  It should succeed.
+    SocketSessionForwarder forwarder2(TEST_UNIX_FILE);
+    startListen();
+    forwarder2.connectToReceiver();
+    // And it can be closed successfully.
+    forwarder2.close();
+    // Duplicate close should fail
+    EXPECT_THROW(forwarder2.close(), BadValue);
+    // Once closed, reconnect is okay.
+    forwarder2.connectToReceiver();
+    forwarder2.close();
+
+    // Duplicate connect should be rejected
+    forwarder2.connectToReceiver();
+    EXPECT_THROW(forwarder2.connectToReceiver(), BadValue);
+
+    // Connect then destroy.  Should be internally closed, but unfortunately
+    // it's not easy to test it directly.  We only check no disruption happens.
+    SocketSessionForwarder* forwarderp =
+        new SocketSessionForwarder(TEST_UNIX_FILE);
+    forwarderp->connectToReceiver();
+    delete forwarderp;
+}
+
+TEST_F(ForwardTest, close) {
+    // can't close before connect
+    EXPECT_THROW(SocketSessionForwarder(TEST_UNIX_FILE).close(), BadValue);
+}
+
+void
+checkSockAddrs(const sockaddr& expected, const sockaddr& actual) {
+    char hbuf_expected[NI_MAXHOST], sbuf_expected[NI_MAXSERV],
+        hbuf_actual[NI_MAXHOST], sbuf_actual[NI_MAXSERV];
+    EXPECT_EQ(0, getnameinfo(&expected, getSALength(expected),
+                             hbuf_expected, sizeof(hbuf_expected),
+                             sbuf_expected, sizeof(sbuf_expected),
+                             NI_NUMERICHOST | NI_NUMERICSERV));
+    EXPECT_EQ(0, getnameinfo(&actual, getSALength(actual),
+                             hbuf_actual, sizeof(hbuf_actual),
+                             sbuf_actual, sizeof(sbuf_actual),
+                             NI_NUMERICHOST | NI_NUMERICSERV));
+    EXPECT_EQ(string(hbuf_expected), string(hbuf_actual));
+    EXPECT_EQ(string(sbuf_expected), string(sbuf_actual));
+}
+
+// This is a commonly used test case that confirms normal behavior of
+// session passing.  It first creates a "local" socket (which is supposed
+// to act as a "server") bound to the 'local' parameter.  It then forwards
+// the descriptor of the FD of the local socket along with given data.
+// Next, it creates an Receiver object to receive the forwarded FD itself,
+// receives the FD, and sends test data from the received FD.  The
+// test finally checks if it can receive the test data from the local socket
+// at the Forwarder side.  In the case of TCP it's a bit complicated because
+// it first needs to establish a new connection, but essentially the test
+// scenario is the same.  See the diagram below for more details.
+//
+// UDP:
+//   Forwarder          Receiver
+//   sock -- (pass) --> passed_sock
+//   (check)  <-------- send TEST_DATA
+//
+// TCP:
+//   Forwarder               Receiver
+//   server_sock---(pass)--->passed_sock
+//     ^                        |
+//     |(connect)               |
+//   client_sock                |
+//      (check)<---------send TEST_DATA
+void
+ForwardTest::checkPushAndPop(int family, int type, int protocol,
+                             const SockAddrInfo& local,
+                             const SockAddrInfo& remote,
+                             const void* const data,
+                             size_t data_len, bool new_connection)
+{
+    // Create an original socket to be passed
+    const ScopedSocket sock(createSocket(family, type, protocol, local, true));
+    int fwd_fd = sock.fd;       // default FD to be forwarded
+    ScopedSocket client_sock;   // for TCP test we need a separate "client"..
+    ScopedSocket server_sock;   // ..and a separate socket for the connection
+    if (protocol == IPPROTO_TCP) {
+        // Use unspecified port for the "client" to avoid bind(2) failure
+        const SockAddrInfo client_addr = getSockAddr(family == AF_INET6 ?
+                                                     "::1" : "127.0.0.1", "0");
+        client_sock.reset(createSocket(family, type, protocol, client_addr,
+                                       false));
+        setNonBlock(client_sock.fd, true);
+        // This connect would "fail" due to EINPROGRESS.  Ignore it for now.
+        connect(client_sock.fd, local.first, local.second);
+        sockaddr_storage ss;
+        socklen_t salen = sizeof(ss);
+        server_sock.reset(accept(sock.fd, convertSockAddr(&ss), &salen));
+        if (server_sock.fd == -1) {
+            isc_throw(isc::Unexpected, "internal accept failed: " <<
+                      strerror(errno));
+        }
+        fwd_fd = server_sock.fd;
+    }
+
+    // If a new connection is required, start the "server", have the
+    // internal forwarder connect to it, and then internally accept it.
+    if (new_connection) {
+        startListen();
+        forwarder_.connectToReceiver();
+        accept_sock_.reset(acceptForwarder());
+    }
+
+    // Then push one socket session via the forwarder.
+    forwarder_.push(fwd_fd, family, type, protocol, *local.first,
+                    *remote.first, data, data_len);
+
+    // Pop the socket session we just pushed from a local receiver, and
+    // check the content.  Since we do blocking read on the receiver's socket,
+    // we set up an alarm to prevent hangup in case there's a bug that really
+    // makes the blocking happen.
+    SocketSessionReceiver receiver(accept_sock_.fd);
+    alarm(1);                   // set up 1-sec timer, an arbitrary choice.
+    const SocketSession sock_session = receiver.pop();
+    alarm(0);                   // then cancel it.
+    const ScopedSocket passed_sock(sock_session.getSocket());
+    EXPECT_LE(0, passed_sock.fd);
+    // The passed FD should be different from the original FD
+    EXPECT_NE(fwd_fd, passed_sock.fd);
+    EXPECT_EQ(family, sock_session.getFamily());
+    EXPECT_EQ(type, sock_session.getType());
+    EXPECT_EQ(protocol, sock_session.getProtocol());
+    checkSockAddrs(*local.first, sock_session.getLocalEndpoint());
+    checkSockAddrs(*remote.first, sock_session.getRemoteEndpoint());
+    ASSERT_EQ(data_len, sock_session.getDataLength());
+    EXPECT_EQ(0, memcmp(data, sock_session.getData(), data_len));
+
+    // Check if the passed FD is usable by sending some data from it.
+    setNonBlock(passed_sock.fd, false);
+    if (protocol == IPPROTO_UDP) {
+        EXPECT_EQ(sizeof(TEST_DATA),
+                  sendto(passed_sock.fd, TEST_DATA, sizeof(TEST_DATA), 0,
+                         convertSockAddr(local.first), local.second));
+    } else {
+        server_sock.reset(-1);
+        EXPECT_EQ(sizeof(TEST_DATA),
+                  send(passed_sock.fd, TEST_DATA, sizeof(TEST_DATA), 0));
+    }
+    // We don't use non blocking read below as it doesn't seem to be always
+    // reliable.  Instead we impose some reasonably large upper time limit of
+    // blocking (normally it shouldn't even block at all; the limit is to
+    // force the test to stop even if there's some bug and recv fails).
+    char recvbuf[sizeof(TEST_DATA)];
+    sockaddr_storage ss;
+    socklen_t sa_len = sizeof(ss);
+    if (protocol == IPPROTO_UDP) {
+        EXPECT_EQ(sizeof(recvbuf),
+                  recvfrom(fwd_fd, recvbuf, sizeof(recvbuf),
+                           setRecvDelay(fwd_fd), convertSockAddr(&ss),
+                           &sa_len));
+    } else {
+        setNonBlock(client_sock.fd, false);
+        EXPECT_EQ(sizeof(recvbuf),
+                  recv(client_sock.fd, recvbuf, sizeof(recvbuf),
+                       setRecvDelay(client_sock.fd)));
+    }
+    EXPECT_EQ(string(TEST_DATA), string(recvbuf));
+}
+
+TEST_F(ForwardTest, pushAndPop) {
+    // Pass a UDP/IPv6 session.
+    const SockAddrInfo sai_local6(getSockAddr("::1", TEST_PORT));
+    const SockAddrInfo sai_remote6(getSockAddr("2001:db8::1", "5300"));
+    {
+        SCOPED_TRACE("Passing UDP/IPv6 session");
+        checkPushAndPop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, sai_local6,
+                        sai_remote6, TEST_DATA, sizeof(TEST_DATA), true);
+    }
+    // Pass a TCP/IPv6 session.
+    {
+        SCOPED_TRACE("Passing TCP/IPv6 session");
+        checkPushAndPop(AF_INET6, SOCK_STREAM, IPPROTO_TCP, sai_local6,
+                        sai_remote6, TEST_DATA, sizeof(TEST_DATA), false);
+    }
+
+    // Pass a UDP/IPv4 session.  This reuses the same pair of forwarder and
+    // receiver, which should be usable for multiple attempts of passing,
+    // regardless of family of the passed session
+    const SockAddrInfo sai_local4(getSockAddr("127.0.0.1", TEST_PORT));
+    const SockAddrInfo sai_remote4(getSockAddr("192.0.2.2", "5300"));
+    {
+        SCOPED_TRACE("Passing UDP/IPv4 session");
+        checkPushAndPop(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local4,
+                        sai_remote4, TEST_DATA, sizeof(TEST_DATA), false);
+    }
+    // Pass a TCP/IPv4 session.
+    {
+        SCOPED_TRACE("Passing TCP/IPv4 session");
+        checkPushAndPop(AF_INET, SOCK_STREAM, IPPROTO_TCP, sai_local4,
+                        sai_remote4, TEST_DATA, sizeof(TEST_DATA), false);
+    }
+
+    // Also try large data
+    {
+        SCOPED_TRACE("Passing UDP/IPv6 session with large data");
+        checkPushAndPop(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, sai_local6,
+                        sai_remote6, large_text_.c_str(), large_text_.length(),
+                        false);
+    }
+    {
+        SCOPED_TRACE("Passing TCP/IPv6 session with large data");
+        checkPushAndPop(AF_INET6, SOCK_STREAM, IPPROTO_TCP, sai_local6,
+                        sai_remote6, large_text_.c_str(), large_text_.length(),
+                        false);
+    }
+    {
+        SCOPED_TRACE("Passing UDP/IPv4 session with large data");
+        checkPushAndPop(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local4,
+                        sai_remote4, large_text_.c_str(), large_text_.length(),
+                        false);
+    }
+    {
+        SCOPED_TRACE("Passing TCP/IPv4 session with large data");
+        checkPushAndPop(AF_INET, SOCK_STREAM, IPPROTO_TCP, sai_local4,
+                        sai_remote4, large_text_.c_str(), large_text_.length(),
+                        false);
+    }
+}
+
+TEST_F(ForwardTest, badPush) {
+    // push before connect
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, sizeof(TEST_DATA)),
+                 BadValue);
+
+    // Now connect the forwarder for the rest of tests
+    startListen();
+    forwarder_.connectToReceiver();
+
+    // Invalid address family
+    struct sockaddr sockaddr_unspec;
+    sockaddr_unspec.sa_family = AF_UNSPEC;
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 sockaddr_unspec,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, sizeof(TEST_DATA)),
+                 BadValue);
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 sockaddr_unspec, TEST_DATA,
+                                 sizeof(TEST_DATA)),
+                 BadValue);
+
+    // Inconsistent address family
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("2001:db8::1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, sizeof(TEST_DATA)),
+                 BadValue);
+    EXPECT_THROW(forwarder_.push(1, AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("2001:db8::1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, sizeof(TEST_DATA)),
+                 BadValue);
+
+    // Empty data: we reject them at least for now
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, 0),
+                 BadValue);
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 NULL, sizeof(TEST_DATA)),
+                 BadValue);
+
+    // Too big data: we reject them at least for now
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 string(65536, 'd').c_str(), 65536),
+                 BadValue);
+
+    // Close the receiver before push.  It will result in SIGPIPE (should be
+    // ignored) and EPIPE, which will be converted to SocketSessionError.
+    const int receiver_fd = acceptForwarder();
+    close(receiver_fd);
+    EXPECT_THROW(forwarder_.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                 *getSockAddr("192.0.2.1", "53").first,
+                                 *getSockAddr("192.0.2.2", "53").first,
+                                 TEST_DATA, sizeof(TEST_DATA)),
+                 SocketSessionError);
+}
+
+// A subroutine for pushTooFast, continuously pushing socket sessions
+// with full-size DNS messages (65535 bytes) without receiving them.  
+// the push attempts will eventually fill the socket send buffer and trigger
+// an exception.  Unfortunately exactly how many we can forward depends on
+// the internal system implementation; it should be close to 3, because
+// in our current implementation it sets the send buffer to a size that
+// is sufficiently large to hold 2 sessions (but not much larger than that),
+// but (for example) Linux internally doubles the specified upper limit.
+// Experimentally we know 10 is enough to produce a reliable result, but
+// if it turns out to be not the case, we should do it a bit harder, e.g.,
+// by probing the actual buffer size by getsockopt(SO_SNDBUF).
+void
+multiPush(SocketSessionForwarder& forwarder, const struct sockaddr& sa,
+          const void* data, size_t data_len)
+{
+    for (int i = 0; i < 10; ++i) {
+        forwarder.push(1, AF_INET, SOCK_DGRAM, IPPROTO_UDP, sa, sa,
+                       data, data_len);
+    }
+}
+
+TEST_F(ForwardTest, pushTooFast) {
+    // Emulate the situation where the forwarder is pushing sessions too fast.
+    // It should eventually fail without blocking.
+    startListen();
+    forwarder_.connectToReceiver();
+    EXPECT_THROW(multiPush(forwarder_, *getSockAddr("192.0.2.1", "53").first,
+                           large_text_.c_str(), large_text_.length()),
+                 SocketSessionError);
+}
+
+TEST_F(ForwardTest, badPop) {
+    startListen();
+
+    // Close the forwarder socket before pop() without sending anything.
+    pushSessionHeader(0, 0, false);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pretending to be a forwarder but don't actually pass FD.
+    pushSessionHeader(0, 1, false);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pass a valid FD (stdin), but provide short data for the hdrlen
+    pushSessionHeader(0, 1);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pass a valid FD, but provides too large hdrlen
+    pushSessionHeader(0xffff);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Don't provide full header
+    pushSessionHeader(sizeof(uint32_t));
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pushed header is too short
+    const uint8_t dummy_data = 0;
+    pushSessionHeader(1);
+    send(dummy_forwarder_.fd, &dummy_data, 1, 0);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // socket addresses commonly used below (the values don't matter).
+    const SockAddrInfo sai_local(getSockAddr("192.0.2.1", "53535"));
+    const SockAddrInfo sai_remote(getSockAddr("192.0.2.2", "53536"));
+    const SockAddrInfo sai6(getSockAddr("2001:db8::1", "53537"));
+
+    // Pass invalid address family (AF_UNSPEC)
+    pushSession(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sai_remote.second, *sai_remote.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pass inconsistent address family for local
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai6.second,
+                *sai6.first, sai_remote.second, *sai_remote.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Same for remote
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sai6.second, *sai6.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pass too big sa length for local
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                sizeof(struct sockaddr_storage) + 1, *sai_local.first,
+                sai_remote.second, *sai_remote.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Same for remote
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sizeof(struct sockaddr_storage) + 1,
+                *sai_remote.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Pass too small sa length for local
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                sizeof(struct sockaddr_in) - 1, *sai_local.first,
+                sai_remote.second, *sai_remote.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Same for remote
+    pushSession(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
+                sai6.second, *sai6.first, sizeof(struct sockaddr_in6) - 1,
+                *sai6.first);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Data length is too large
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sai_remote.second,
+                *sai_remote.first, 65536);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Empty data
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sai_remote.second,
+                *sai_remote.first, 0);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Not full data are passed
+    pushSession(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sai_local.second,
+                *sai_local.first, sai_remote.second,
+                *sai_remote.first, sizeof(TEST_DATA) + 1);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+
+    // Check the forwarded FD is closed on failure
+    ScopedSocket sock(createSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                                   getSockAddr("127.0.0.1", TEST_PORT),
+                                   false));
+    pushSessionHeader(0, 1, true, sock.fd);
+    dummy_forwarder_.reset(-1);
+    EXPECT_THROW(receiver_->pop(), SocketSessionError);
+    // Close the original socket
+    sock.reset(-1);
+    // The passed one should have been closed, too, so we should be able
+    // to bind a new socket to the same port.
+    sock.reset(createSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                            getSockAddr("127.0.0.1", TEST_PORT),
+                            false));
+}
+
+TEST(SocketSessionTest, badValue) {
+    // normal cases are confirmed in ForwardTest.  We only check some
+    // abnormal cases here.
+
+    SockAddrCreator addr_creator;
+
+    EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
+                               addr_creator.get("192.0.2.1", "53").first,
+                               TEST_DATA, sizeof(TEST_DATA)),
+                 BadValue);
+    EXPECT_THROW(SocketSession(42, AF_INET6, SOCK_STREAM, IPPROTO_TCP,
+                               addr_creator.get("2001:db8::1", "53").first,
+                               NULL, TEST_DATA , sizeof(TEST_DATA)), BadValue);
+    EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                               addr_creator.get("192.0.2.1", "53").first,
+                               addr_creator.get("192.0.2.2", "5300").first,
+                               TEST_DATA, 0), BadValue);
+    EXPECT_THROW(SocketSession(42, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
+                               addr_creator.get("192.0.2.1", "53").first,
+                               addr_creator.get("192.0.2.2", "5300").first,
+                               NULL, sizeof(TEST_DATA)), BadValue);
+}
+}
diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc
index 9303ab5..c756382 100644
--- a/src/lib/util/time_utilities.cc
+++ b/src/lib/util/time_utilities.cc
@@ -158,7 +158,7 @@ uint64_t
 timeFromText64(const string& time_txt) {
     // Confirm the source only consists digits.  sscanf() allows some
     // minor exceptions.
-    for (int i = 0; i < time_txt.length(); ++i) {
+    for (string::size_type i = 0; i < time_txt.length(); ++i) {
         if (!isdigit(time_txt.at(i))) {
             isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
                       << time_txt);
diff --git a/tests/lettuce/setup_intree_bind10.sh.in b/tests/lettuce/setup_intree_bind10.sh.in
index 40fd82d..b1f17bc 100755
--- a/tests/lettuce/setup_intree_bind10.sh.in
+++ b/tests/lettuce/setup_intree_bind10.sh.in
@@ -20,7 +20,7 @@ export PYTHON_EXEC
 
 BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 
-PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
+PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
 PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
diff --git a/tests/tools/Makefile.am b/tests/tools/Makefile.am
index 2f07494..b4c1394 100644
--- a/tests/tools/Makefile.am
+++ b/tests/tools/Makefile.am
@@ -1 +1,5 @@
-SUBDIRS = badpacket
+# perfdhcp uses getifaddrs, which is not a standard library and is not
+# portable (at least not exist on our Solaris buildbot).  For a short term
+# workaround we stop building it until it's resolved.
+#SUBDIRS = badpacket perfdhcp
+SUBDIRS = badpacket perfdhcp
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
new file mode 100644
index 0000000..bbad595
--- /dev/null
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = .
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+AM_LDFLAGS = $(CLOCK_GETTIME_LDFLAGS)
+AM_LDFLAGS += -lm
+if USE_STATIC_LINK
+AM_LDFLAGS += -static
+endif
+
+pkglibexec_PROGRAMS  = perfdhcp
+perfdhcp_SOURCES  = perfdhcp.c
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
new file mode 100644
index 0000000..6e02b7e
--- /dev/null
+++ b/tests/tools/perfdhcp/perfdhcp.c
@@ -0,0 +1,3523 @@
+/*
+ * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#ifndef HAVE_GETIFADDRS
+
+/*
+ * Solaris 10 does not have the getifaddrs() function available (although it
+ * is present on Solaris 11 and later).  For the present we will not implement
+ * a replacement (as we do with clock_gettime) as the implementation is
+ * relatively complex.  Just output a message saying that the utility is not
+ * supported on this operating system.
+ */
+
+#include <stdio.h>
+
+int
+main(const int argc, char* const argv[])
+{
+    fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
+    return (1);
+}
+
+#else
+
+/* getifaddrs() present, so the code should compile */
+
+#ifdef __linux__
+#define _GNU_SOURCE
+#endif
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
+#include <math.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+/* DHCPv4 defines (to be moved/shared) */
+
+#define DHCP_OFF_OPCODE		0
+#define DHCP_OFF_HTYPE		1
+#define DHCP_OFF_HLEN		2
+#define DHCP_OFF_HOPS		3
+#define DHCP_OFF_XID		4
+#define DHCP_OFF_SECS		8
+#define DHCP_OFF_FLAGS		10
+#define DHCP_OFF_CIADDR		12
+#define DHCP_OFF_YIADDR		16
+#define DHCP_OFF_SADDR		20
+#define DHCP_OFF_GIADDR		24
+#define DHCP_OFF_CHADDR		28
+#define DHCP_OFF_SNAME		44
+#define DHCP_OFF_FILE		108
+#define DHCP_OFF_COOKIE		236
+#define DHCP_OFF_OPTIONS	240
+
+#define BOOTP_OP_REQUEST	1
+#define BOOTP_OP_REPLY		2
+#define BOOTP_MIN_LEN		300
+
+#define DHCP_OP_DISCOVER	1
+#define DHCP_OP_OFFER		2
+#define DHCP_OP_REQUEST		3
+#define DHCP_OP_DECLINE		4
+#define DHCP_OP_ACK		5
+#define DHCP_OP_NAK		6
+#define DHCP_OP_RELEASE		7
+#define DHCP_OP_INFORM		8
+
+#define DHCP_HTYPE_ETHER	1
+
+#define DHCP_OPT_PAD		0
+#define DHCP_OPT_SUBNET_MASK	1
+#define DHCP_OPT_TIME_OFFSET	2
+#define DHCP_OPT_ROUTERS	3
+#define DHCP_OPT_DNS_SERVERS	6
+#define DHCP_OPT_HOST_NAME	12
+#define DHCP_OPT_DOMAIN_NAME	15
+#define DHCP_OPT_BROADCAST	28
+#define DHCP_OPT_DHCP_ADDRESS	50
+#define DHCP_OPT_DHCP_LEASE	51
+#define DHCP_OPT_DHCP_MSGTYPE	53
+#define DHCP_OPT_DHCP_SRVID	54
+#define DHCP_OPT_DHCP_PRL	55
+#define DHCP_OPT_END		255
+
+#define DHCP_OPTLEN_SRVID	6
+
+/* DHCPv6 defines  (to be moved/shared) */
+
+#define DHCP6_OFF_MSGTYP	0
+#define DHCP6_OFF_XID		1
+#define DHCP6_OFF_OPTIONS	4
+
+#define DHCP6_OP_SOLICIT	1
+#define DHCP6_OP_ADVERTISE	2
+#define DHCP6_OP_REQUEST	3
+#define DHCP6_OP_REPLY		7
+
+#define DHCP6_OPT_CLIENTID	1
+#define DHCP6_OPT_SERVERID	2
+#define DHCP6_OPT_IA_NA		3
+#define DHCP6_OPT_ORO		6
+#define DHCP6_OPT_ELAPSED_TIME	8
+#define DHCP6_OPT_STATUS_CODE	13
+#define DHCP6_OPT_RAPID_COMMIT	14
+#define DHCP6_OPT_NAME_SERVERS	23
+#define DHCP6_OPT_DOMAIN_SEARCH	24
+
+#define DHCP6_ST_SUCCESS	0
+#define DHCP6_ST_NOADDRSAVAIL	2
+
+#define DHCP6_DUID_LLT		1
+#define DHCP6_DUID_EPOCH	946684800
+
+/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
+
+#define ISC_TAILQ_HEAD(name, type)					\
+struct name {								\
+	struct type *tqh_first;						\
+	struct type **tqh_last;						\
+}
+
+#define ISC_TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;						\
+	struct type **tqe_prev;						\
+}
+
+#define ISC_TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
+
+#define ISC_TAILQ_FIRST(head)	((head)->tqh_first)
+
+#define ISC_TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define ISC_TAILQ_NEXT(elm, field)	((elm)->field.tqe_next)
+
+#define ISC_TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define ISC_TAILQ_INIT(head)	do {					\
+	ISC_TAILQ_FIRST((head)) = NULL;					\
+	(head)->tqh_last = &ISC_TAILQ_FIRST((head));			\
+} while (0)
+
+#define ISC_TAILQ_INSERT_HEAD(head, elm, field)	do {			\
+	ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head));		\
+	if (!ISC_TAILQ_EMPTY((head)))					\
+		ISC_TAILQ_FIRST((head))->field.tqe_prev =		\
+			&ISC_TAILQ_NEXT((elm), field);			\
+	else								\
+		(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);	\
+	ISC_TAILQ_FIRST((head)) = (elm);				\
+	(elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head));		\
+} while (0)
+
+#define ISC_TAILQ_INSERT_TAIL(head, elm, field)	do {			\
+	ISC_TAILQ_NEXT((elm), field) = NULL;				\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define ISC_TAILQ_REMOVE(head, elm, field)	do {			\
+	if ((ISC_TAILQ_NEXT((elm), field)) != NULL)			\
+		ISC_TAILQ_NEXT((elm), field)->field.tqe_prev =		\
+			(elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define ISC_TAILQ_FOREACH(var, head, field)				\
+	for ((var) = ISC_TAILQ_FIRST((head));				\
+	     (var);							\
+	     (var) = ISC_TAILQ_NEXT((var), field))
+
+#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = ISC_TAILQ_FIRST((head));				\
+	     (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1);	\
+	     (var) = (tvar))
+
+/*
+ * Data structures
+ */
+
+/*
+ * exchange:
+ *    - per exchange values:
+ *	* order (for debugging)
+ *	* xid (even/odd for 4 packet exchanges)
+ *	* random (for debugging)
+ *	* time-stamps
+ *	* server ID (for 3rd packet)
+ *	* IA_NA (for IPv6 3rd packet)
+ *
+ * sent/rcvd global chains, "next to be received" on entry cache,
+ * and hash table for xid -> data structure fast matching
+ * (using the assumption collisions are unlikely, cf birthday problem)
+ */
+
+struct exchange {				/* per exchange structure */
+	ISC_TAILQ_ENTRY(exchange) gchain;	/* global chaining */
+	ISC_TAILQ_ENTRY(exchange) hchain;	/* hash table chaining */
+	uint64_t order0, order2;		/* number of this exchange */
+	uint32_t xid;				/* transaction ID */
+	uint32_t rnd;				/* random part */
+	struct timespec ts0, ts1, ts2, ts3;	/* timespecs */
+	uint8_t *sid;				/* server ID */
+	size_t sidlen;				/* server ID length */
+	uint8_t *iana;				/* (IPv6) IA_NA */
+	size_t ianalen;				/* (IPv6) IA_NA length */
+};
+struct exchange *xnext0, *xnext2;		/* next to be received */
+ISC_TAILQ_HEAD(xlist, exchange);		/* exchange list */
+struct xlist xsent0, xsent2, xrcvd0, xrcvd2;	/* sent and received lists */
+uint64_t xscount0, xscount2;			/* sent counters */
+uint64_t xrcount0, xrcount2;			/* received counters */
+caddr_t exchanges0, exchanges2;			/* hash tables */
+uint32_t hashsize0, hashsize2;			/* hash table sizes */
+
+/*
+ * statictics counters and accumulators
+ */
+
+uint64_t tooshort, orphans, locallimit;		/* error counters */
+uint64_t latesent, compsend, latercvd;		/* rate stats */
+uint64_t multrcvd, shortwait, collected[2];	/* rate stats (cont) */
+double dmin0 = 999999999., dmin2 = 999999999.;	/* minimum delays */
+double dmax0 = 0., dmax2 = 0.;			/* maximum delays */
+double dsum0 = 0., dsum2 = 0.;			/* delay sums */
+double dsumsq0 = 0., dsumsq2 = 0.;		/* square delay sums */
+
+/*
+ * command line parameters
+ */
+
+int ipversion = 0;			/* IP version */
+int simple;				/* DO/SA in place of DORR/SARR */
+int rate;				/* rate in exchange per second */
+int report;				/* delay between two reports */
+uint32_t range;				/* randomization range */
+uint32_t maxrandom;			/* maximum random value */
+int basecnt;				/* base count */
+char *base[4];				/* bases */
+int gotnumreq;				/* numreq[0] was set */
+int numreq[2];				/* number of exchange */
+int period;				/* test period */
+int gotlosttime;			/* losttime[0] was set */
+double losttime[2] = {1., 1.};		/* time after a request is lost  */
+int gotmaxdrop;				/* max{p}drop[0] was set */
+int maxdrop[2];				/* maximum number of lost requests */
+double maxpdrop[2] = { 0., 0.};		/* maximum percentage */
+char *localname;			/* local address or interface */
+int isinterface;			/* interface vs local address */
+int preload;				/* preload exchanges */
+int aggressivity = 1;			/* back to back exchanges */
+int localport;				/* local port number (host endian) */
+int seeded;				/* is a seed provided */
+unsigned int seed;			/* randomization seed */
+int isbroadcast;			/* use broadcast */
+int rapidcommit;			/* add rapid commit option */
+int usefirst;				/* where to take the server-ID */
+char *templatefile[2];			/* template file name */
+int xidoffset[2] = {-1, -1};		/* template offsets (xid)*/
+int rndoffset[2] = {-1, -1};		/* template offsets (random) */
+int elpoffset = -1;			/* template offset (elapsed time) */
+int sidoffset = -1;			/* template offset (server ID) */
+int ripoffset = -1;			/* template offset (requested IP) */
+char *diags;				/* diagnostic selectors */
+char *wrapped;				/* wrapped command */
+char *servername;			/* server */
+
+/*
+ * global variables
+ */
+
+struct sockaddr_storage localaddr;	/* local socket address */
+struct sockaddr_storage serveraddr;	/* server socket address */
+
+int sock;				/* socket descriptor */
+int interrupted, fatal;			/* to finish flags */
+
+uint8_t obuf[4096], ibuf[4096];		/* I/O buffers */
+char tbuf[8200];			/* template buffer */
+
+struct timespec boot;			/* the date of boot */
+struct timespec last;			/* the date of last send */
+struct timespec due;			/* the date of next send */
+struct timespec dreport;		/* the date of next reporting */
+struct timespec finished;		/* the date of finish */
+
+uint8_t *gsrvid;			/* global server id */
+size_t gsrvidlen;			/*  and its length */
+uint8_t gsrvidbuf[64];			/*  and its storage */
+
+/* MAC address */
+uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
+
+/* DUID prefix */
+uint8_t *duid_prefix;
+int duid_length;
+
+/* magic cookie for BOOTP/DHCPv4 */
+uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
+
+/*
+ * templates
+ *
+ * note: the only hard point is what are the offsets:
+ *   - xid_discover4 and xid_request4: first of the 4 octet long
+ *     transaction ID (default DHCP_OFF_XID = 4)
+ *   - random_discover4 and random_request4: last of the 6 octet long
+ *     MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
+ *   - elapsed_request4: first of the 2 octet long secs field
+ *     (default DHCP_OFF_SECS = 8, 0 means disabled)
+ *   - serverid_request4: first of the 6 octet long server ID option
+ *     (no default, required)
+ *   - reqaddr_request4: first of the 4 octet long requested IP address
+ *     option content (i.e., the address itself, btw OFFER yiaddr)
+ *     (no default, required)
+ *   - xid_solicit6 and xid_request6: first of the 3 octet long
+ *     transaction ID (default DHCP6_OFF_XID = 1)
+ *   - random_solicit6 and random_request6: last of the DUID in the
+ *     client ID option (no default, required when rate is set)
+ *   - elapsed_request6: first of the 2 octet long content of
+ *     the option elapsed time option (no default, 0 means disabled)
+ *   - serverid_request6: position where the variable length server ID
+ *     option is inserted (no default, required, set to length means append)
+ *   - reqaddr_request6: position where of the variable length requested
+ *     IP address option is inserted (no default, required, set to
+ *     length means append)
+ */
+
+size_t length_discover4;
+uint8_t template_discover4[4096];
+size_t xid_discover4;
+size_t random_discover4;
+size_t length_request4;
+uint8_t template_request4[4096];
+size_t xid_request4;
+size_t elapsed_request4;
+size_t random_request4;
+size_t serverid_request4;
+size_t reqaddr_request4;
+size_t length_solicit6;
+uint8_t template_solicit6[4096];
+size_t xid_solicit6;
+size_t random_solicit6;
+size_t length_request6;
+uint8_t template_request6[4096];
+size_t xid_request6;
+size_t elapsed_request6;
+size_t random_request6;
+size_t serverid_request6;
+size_t reqaddr_request6;
+
+
+// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
+// if the code is being compiled or Linux (or somewhere else)
+// Perhaps this should be based on OS_LINUX define?
+
+#if !defined (CLOCK_REALTIME)
+#define CLOCK_REALTIME 0
+
+/// @brief clock_gettime implementation for non-Linux systems
+///
+/// This implementation lacks nanosecond resolution. It is intended
+/// to be used on non-Linux systems that does not provide clock_gettime
+/// implementation.
+///
+/// @param clockid ignored (kept for Linux prototype compatibility)
+/// @param tp timespec structure
+///
+/// @return always zero (kept for compatibility reasons)
+int clock_gettime(int clockid, struct timespec *tp) {
+
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    tp->tv_sec = tv.tv_sec;
+    tp->tv_nsec = tv.tv_usec*1000;
+
+    return (0);
+}
+
+#endif
+
+/*
+ * initialize data structures handling exchanges
+ */
+
+void
+inits(void)
+{
+	struct xlist *bucket;
+	caddr_t p;
+	size_t len, i;
+
+	ISC_TAILQ_INIT(&xsent0);
+	ISC_TAILQ_INIT(&xsent2);
+	ISC_TAILQ_INIT(&xrcvd0);
+	ISC_TAILQ_INIT(&xrcvd2);
+
+	/// compute hashsizes
+	hashsize0 = 1024;
+	len = sizeof(*bucket) * hashsize0;
+	exchanges0 = malloc(len);
+	if (exchanges0 == NULL) {
+		perror("malloc(exchanges0)");
+		exit(1);
+	}
+	for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
+		bucket = (struct xlist *) p;
+		ISC_TAILQ_INIT(bucket);
+	}
+	if (simple != 0)
+		return;
+	hashsize2 = 1024;
+	len = sizeof(*bucket) * hashsize2;
+	exchanges2 = malloc(len);
+	if (exchanges2 == NULL) {
+		perror("malloc(exchanges2)");
+		exit(1);
+	}
+	for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
+		bucket = (struct xlist *) p;
+		ISC_TAILQ_INIT(bucket);
+	}
+}
+
+/*
+ * randomize the value of the given field:
+ *   - offset of the field
+ *   - random seed (used as it when suitable)
+ *   - returns the random value which was used
+ */
+
+uint32_t
+randomize(size_t offset, uint32_t r)
+{
+	uint32_t v;
+
+	if (range == 0)
+		return 0;
+	if (range == UINT32_MAX)
+		return r;
+	if (maxrandom != 0)
+		while (r >= maxrandom)
+			r = (uint32_t) random();
+	r %= range + 1;
+	v = r;
+	v += obuf[offset];
+	obuf[offset] = v;
+	if (v < 256)
+		return r;
+	v >>= 8;
+	v += obuf[offset - 1];
+	obuf[offset - 1] = v;
+	if (v < 256)
+		return r;
+	v >>= 8;
+	v += obuf[offset - 2];
+	obuf[offset - 2] = v;
+	if (v < 256)
+		return r;
+	v >>= 8;
+	v += obuf[offset - 3];
+	obuf[offset - 3] = v;
+	return r;
+}
+
+/*
+ * receive a reply (4th packet), shared between IPv4 and IPv6:
+ *   - transaction ID xid
+ *   - receiving time-stamp now
+ * called from receive[46]() when the xid is odd
+ */
+
+void
+receive_reply(uint32_t xid, struct timespec *now)
+{
+	struct exchange *x, *t;
+	struct xlist *bucket;
+	uint32_t hash;
+	int checklost;
+	double delta;
+
+	/* bucket is needed even when the next cache matches */
+	hash = (xid >> 1) & (hashsize2 - 1);
+	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
+	/* try the 'next to be received' cache */
+	if ((xnext2 != NULL) && (xnext2->xid == xid)) {
+		x = xnext2;
+		goto found;
+	}
+	/* usually the lost probability is low for request/reply */
+	checklost = 1;
+	/* look for the exchange */
+	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
+		double waited;
+
+		if (x->xid == xid)
+			goto found;
+		if (checklost <= 0)
+			continue;
+		checklost = 0;
+		/* check for a timed-out exchange */
+		waited = now->tv_sec - x->ts2.tv_sec;
+		waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
+		if (waited < losttime[1])
+			continue;
+		/* garbage collect timed-out exchange */
+		ISC_TAILQ_REMOVE(bucket, x, hchain);
+		ISC_TAILQ_REMOVE(&xsent2, x, gchain);
+		free(x);
+		collected[1] += 1;
+	}
+	/* no match? very late or not for us */
+	orphans++;
+	return;
+
+	/* got it: update stats and move to the received queue */
+    found:
+	xrcount2++;
+	x->ts3 = *now;
+	delta = x->ts3.tv_sec - x->ts2.tv_sec;
+	delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
+	if (delta < dmin2)
+		dmin2 = delta;
+	if (delta > dmax2)
+		dmax2 = delta;
+	dsum2 += delta;
+	dsumsq2 += delta * delta;
+	xnext2 = ISC_TAILQ_NEXT(x, gchain);
+	ISC_TAILQ_REMOVE(bucket, x, hchain);
+	ISC_TAILQ_REMOVE(&xsent2, x, gchain);
+	ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
+}
+
+/*
+ * get the DHCPv4 socket descriptor
+ * (the only complexity is broadcast enabling: there is no easy way to
+ *  recognize broadcast addresses, so the command line -B flag)
+ */
+
+void
+getsock4(void)
+{
+	int ret;
+
+	/* update local port */
+	if (localport != 0) {
+		uint16_t lp = htons((uint16_t) localport);
+
+		((struct sockaddr_in *) &localaddr)->sin_port = lp;
+	}
+	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (sock < 0) {
+		perror("socket");
+		exit(1);
+	}
+	ret = bind(sock,
+		   (struct sockaddr *) &localaddr,
+		   sizeof(struct sockaddr_in));
+	if (ret < 0) {
+		perror("bind");
+		exit(1);
+	}
+	/* enable broadcast if needed or required */
+	if (isbroadcast != 0) {
+		int on = 1;
+
+		ret = setsockopt(sock,
+				 SOL_SOCKET, SO_BROADCAST,
+				 &on, sizeof(on));
+		if (ret < 0) {
+			perror("setsockopt(SO_BROADCAST)");
+			exit(1);
+		}
+	}
+}
+
+/*
+ * build a DHCPv4 DISCOVER from a relay template
+ * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
+ * (assume the link is Ethernet)
+ */
+
+void
+build_template_discover4(void)
+{
+	uint8_t *p = template_discover4;
+
+	length_discover4 = BOOTP_MIN_LEN;
+	xid_discover4 = DHCP_OFF_XID;
+	random_discover4 = DHCP_OFF_CHADDR + 6;
+	/* opcode */
+	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
+	/* hardware address type */
+	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
+	/* hardware address length */
+	p[DHCP_OFF_HLEN] = 6;
+	/* hops */
+	p[DHCP_OFF_HOPS] = 1;
+	/* gateway address */
+	memcpy(p + DHCP_OFF_GIADDR,
+	       &((struct sockaddr_in *) &localaddr)->sin_addr,
+	       4);
+	/* hardware address */
+	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
+	/* cookie */
+	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
+	/* options */
+	p += DHCP_OFF_OPTIONS;
+	/* inline DHCP message type */
+	*p++ = DHCP_OPT_DHCP_MSGTYPE;
+	*p++ = 1;
+	*p++ = DHCP_OP_DISCOVER;
+	/* inline DHCP parameter request list (default) */
+	*p++ = DHCP_OPT_DHCP_PRL;
+	*p++ = 7;
+	*p++ = DHCP_OPT_SUBNET_MASK;
+	*p++ = DHCP_OPT_BROADCAST;
+	*p++ = DHCP_OPT_TIME_OFFSET;
+	*p++ = DHCP_OPT_ROUTERS;
+	*p++ = DHCP_OPT_DOMAIN_NAME;
+	*p++ = DHCP_OPT_DNS_SERVERS;
+	*p++ = DHCP_OPT_HOST_NAME;
+	/* end */
+	*p = DHCP_OPT_END;
+}
+
+/*
+ * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
+ * from the file given in the command line (-T<template-file>)
+ * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
+ */
+
+void
+get_template_discover4(void)
+{
+	uint8_t *p = template_discover4;
+	int fd, cc, i, j;
+
+	fd = open(templatefile[0], O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(%s): %s\n",
+			templatefile[0], strerror(errno));
+		exit(2);
+	}
+	cc = read(fd, tbuf, sizeof(tbuf));
+	(void) close(fd);
+	if (cc < 0) {
+		fprintf(stderr, "read(%s): %s\n",
+			templatefile[0], strerror(errno));
+		exit(1);
+	}
+	if (cc < 100) {
+		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
+		exit(2);
+	}
+	if (cc > 8193) {
+		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
+		exit(2);
+	}
+	j = 0;
+	for (i = 0; i < cc; i++) {
+		if (isspace((int) tbuf[i]))
+			continue;
+		if (!isxdigit((int) tbuf[i])) {
+			fprintf(stderr,
+				"illegal char[%d]='%c' in file '%s'\n",
+				i, (int) tbuf[i], templatefile[0]);
+			exit(2);
+		}
+		tbuf[j] = tbuf[i];
+		j++;
+	}
+	cc = j;
+	if ((cc & 1) != 0) {
+		fprintf(stderr,
+			"odd number of hexadecimal digits in file '%s'\n",
+			templatefile[0]);
+		exit(2);
+	}
+	length_discover4 = cc >> 1;
+	for (i = 0; i < cc; i += 2)
+		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
+	if (xidoffset[0] >= 0)
+		xid_discover4 = (size_t) xidoffset[0];
+	else
+		xid_discover4 = DHCP_OFF_XID;
+	if (xid_discover4 + 4 > length_discover4) {
+		fprintf(stderr,
+			"xid (at %zu) outside the template (length %zu)?\n",
+			xid_discover4, length_discover4);
+		exit(2);
+	}
+	if (rndoffset[0] >= 0)
+		random_discover4 = (size_t) rndoffset[0];
+	else
+		random_discover4 = DHCP_OFF_CHADDR + 6;
+	if (random_discover4 > length_discover4) {
+		fprintf(stderr,
+			"random (at %zu) outside the template (length %zu)?\n",
+			random_discover4, length_discover4);
+		exit(2);
+	}
+}
+
+/*
+ * build a DHCPv4 REQUEST from a relay template
+ * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
+ * (assume the link is Ethernet)
+ */
+
+void
+build_template_request4(void)
+{
+	uint8_t *p = template_request4;
+
+	length_request4 = BOOTP_MIN_LEN;
+	xid_request4 = DHCP_OFF_XID;
+	elapsed_request4 = DHCP_OFF_SECS;
+	random_request4 = DHCP_OFF_CHADDR + 6;
+	/* opcode */
+	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
+	/* hardware address type */
+	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
+	/* hardware address length */
+	p[DHCP_OFF_HLEN] = 6;
+	/* hops */
+	p[DHCP_OFF_HOPS] = 1;
+	/* gateway address */
+	memcpy(p + DHCP_OFF_GIADDR,
+	       &((struct sockaddr_in *) &localaddr)->sin_addr,
+	       4);
+	/* hardware address */
+	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
+	/* cookie */
+	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
+	/* options */
+	p += DHCP_OFF_OPTIONS;
+	/* inline DHCP message type */
+	*p++ = DHCP_OPT_DHCP_MSGTYPE;
+	*p++ = 1;
+	*p++ = DHCP_OP_REQUEST;
+	/* place for DHCP server id (option) */
+	serverid_request4 = p - template_request4;
+	p += DHCP_OPTLEN_SRVID;
+	/* place for DHCP requested IP address (address) */
+	*p++ = DHCP_OPT_DHCP_ADDRESS;
+	*p++ = 4;
+	reqaddr_request4 = p - template_request4;
+	p += 4;
+	/* inline DHCP parameter request list (default) */
+	*p++ = DHCP_OPT_DHCP_PRL;
+	*p++ = 7;
+	*p++ = DHCP_OPT_SUBNET_MASK;
+	*p++ = DHCP_OPT_BROADCAST;
+	*p++ = DHCP_OPT_TIME_OFFSET;
+	*p++ = DHCP_OPT_ROUTERS;
+	*p++ = DHCP_OPT_DOMAIN_NAME;
+	*p++ = DHCP_OPT_DNS_SERVERS;
+	*p++ = DHCP_OPT_HOST_NAME;
+	/* end */
+	*p = DHCP_OPT_END;
+}
+
+/*
+ * get a DHCPv4 client/relay third packet (usually a REQUEST) template
+ * from the file given in the command line (-T<template-file>)
+ * and offsets (-X,-O,-E,-S,-I).
+ */
+
+void
+get_template_request4(void)
+{
+	uint8_t *p = template_request4;
+	int fd, cc, i, j;
+
+	fd = open(templatefile[1], O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(%s): %s\n",
+			templatefile[1], strerror(errno));
+		exit(2);
+	}
+	cc = read(fd, tbuf, sizeof(tbuf));
+	(void) close(fd);
+	if (cc < 0) {
+		fprintf(stderr, "read(%s): %s\n",
+			templatefile[1], strerror(errno));
+		exit(1);
+	}
+	if (cc < 100) {
+		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
+		exit(2);
+	}
+	if (cc > 8193) {
+		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
+		exit(2);
+	}
+	j = 0;
+	for (i = 0; i < cc; i++) {
+		if (isspace((int) tbuf[i]))
+			continue;
+		if (!isxdigit((int) tbuf[i])) {
+			fprintf(stderr,
+				"illegal char[%d]='%c' in file '%s'\n",
+				i, (int) tbuf[i], templatefile[1]);
+			exit(2);
+		}
+		tbuf[j] = tbuf[i];
+		j++;
+	}
+	cc = j;
+	if ((cc & 1) != 0) {
+		fprintf(stderr,
+			"odd number of hexadecimal digits in file '%s'\n",
+			templatefile[1]);
+		exit(2);
+	}
+	length_request4 = cc >> 1;
+	for (i = 0; i < cc; i += 2)
+		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
+	if (xidoffset[1] >= 0)
+		xid_request4 = (size_t) xidoffset[1];
+	else
+		xid_request4 = DHCP_OFF_XID;
+	if (xid_request4 + 4 > length_request4) {
+		fprintf(stderr,
+			"xid (at %zu) outside the template (length %zu)?\n",
+			xid_request4, length_request4);
+		exit(2);
+	}
+	if (rndoffset[1] >= 0)
+		random_request4 = (size_t) rndoffset[1];
+	else
+		random_request4 = DHCP_OFF_CHADDR + 6;
+	if (random_request4 > length_request4) {
+		fprintf(stderr,
+			"random (at %zu) outside the template (length %zu)?\n",
+			random_request4, length_request4);
+		exit(2);
+	}
+	if (elpoffset >= 0)
+		elapsed_request4 = (size_t) elpoffset;
+	else
+		elapsed_request4 = DHCP_OFF_SECS;
+	if (elapsed_request4 + 2 > length_request4) {
+		fprintf(stderr,
+			"secs (at %zu) outside the template (length %zu)?\n",
+			elapsed_request4, length_request4);
+		exit(2);
+	}
+	serverid_request4 = (size_t) sidoffset;
+	if (serverid_request4 + 6 > length_request4) {
+		fprintf(stderr,
+			"server-id option (at %zu) outside the template "
+			"(length %zu)?\n",
+			serverid_request4, length_request4);
+		exit(2);
+	}
+	reqaddr_request4 = (size_t) ripoffset;
+	if (reqaddr_request4 + 4 > length_request4) {
+		fprintf(stderr,
+			"requested-ip-address option (at %zu) outside "
+			"the template (length %zu)?\n",
+			reqaddr_request4, length_request4);
+		exit(2);
+	}
+}
+
+/*
+ * send the DHCPv4 REQUEST third packet
+ * (the transaction ID is odd)
+ * (TODO: check for errors in the OFFER)
+ */
+
+void
+send_request4(struct exchange *x0)
+{
+	struct exchange *x;
+	struct xlist *bucket;
+	uint32_t hash;
+	ssize_t ret;
+
+	x = (struct exchange *) malloc(sizeof(*x));
+	if (x == NULL) {
+		locallimit++;
+		perror("send2");
+		return;
+	}
+
+	memcpy(x, x0, sizeof(*x));
+	x->order2 = xscount2++;
+	x->xid |= 1;
+	hash = x->xid >> 1;
+
+	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
+	hash &= hashsize2 - 1;
+	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
+	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
+
+	memcpy(obuf, template_request4, length_request4);
+	/* xid */
+	memcpy(obuf + xid_request4, &x->xid, 4);
+	/* random */
+	randomize(random_request4, x->rnd);
+	/* secs */
+	if (elapsed_request4 > 0) {
+		int secs;
+
+		secs = x->ts1.tv_sec - x->ts0.tv_sec;
+		if (x->ts1.tv_nsec < x->ts0.tv_nsec)
+			secs += 1;
+		if (secs > 0) {
+			obuf[elapsed_request4] = secs >> 8;
+			obuf[elapsed_request4 + 1] = secs & 0xff;
+		}
+	}
+	/* server ID */
+	memcpy(obuf + serverid_request4, x->sid, x->sidlen);
+	/* requested IP address */
+	memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
+
+	/* timestamp */
+	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
+	if (ret < 0) {
+		perror("clock_gettime(send2)");
+		fatal = 1;
+		return;
+	}
+	ret = sendto(sock, obuf, length_request4, 0,
+		     (struct sockaddr *) &serveraddr,
+		     sizeof(struct sockaddr_in));
+	if (ret >= 0)
+		return;
+	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
+	    (errno == ENOBUFS) || (errno == ENOMEM))
+		locallimit++;
+	perror("send2");
+}
+
+/*
+ * send the DHCPv4 DISCOVER first packet
+ * (for 4-exchange, the transaction ID xid is even)
+ */
+
+int
+send4(void)
+{
+	struct exchange *x;
+	struct xlist *bucket;
+	uint32_t hash;
+	ssize_t ret;
+
+	x = (struct exchange *) malloc(sizeof(*x));
+	if (x == NULL)
+		return -ENOMEM;
+
+	memset(x, 0, sizeof(*x));
+	x->order0 = xscount0++;
+	hash = x->rnd = (uint32_t) random();
+	if (simple == 0)
+		x->xid = hash << 1;
+	else
+		x->xid = hash;
+
+	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
+	hash &= hashsize0 - 1;
+	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
+	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
+
+	memcpy(obuf, template_discover4, length_discover4);
+	/* xid */
+	memcpy(obuf + xid_discover4, &x->xid, 4);
+	/* random */
+	x->rnd = randomize(random_discover4, x->rnd);
+	/* timestamp */
+	ret = clock_gettime(CLOCK_REALTIME, &last);
+	if (ret < 0) {
+		perror("clock_gettime(send)");
+		fatal = 1;
+		return -errno;
+	}
+	x->ts0 = last;
+	errno = 0;
+	ret = sendto(sock, obuf, length_discover4, 0,
+		     (struct sockaddr *) &serveraddr,
+		     sizeof(struct sockaddr_in));
+	if (ret == (ssize_t) length_discover4)
+		return 0;
+	return -errno;
+}	
+
+/*
+ * scan a DHCPv4 OFFER to get its server-id option
+ */
+
+int
+scan_for_srvid4(struct exchange *x, size_t cc)
+{
+	size_t off = DHCP_OFF_OPTIONS;
+
+	for (;;) {
+		if (off + DHCP_OPTLEN_SRVID > cc) {
+			fprintf(stderr, "truncated\n");
+			return -1;
+		}
+		if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
+			break;
+		if (ibuf[off] == DHCP_OPT_END) {
+			fprintf(stderr, "server-id not found\n");
+			return -1;
+		}
+		if (ibuf[off] == DHCP_OPT_PAD) {
+			off++;
+			continue;
+		}
+		off += 2 + ibuf[off + 1];
+	}
+	/* check length */
+	if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
+		fprintf(stderr,
+			"bad server-id length (%hhu)\n",
+			ibuf[off + 1]);
+		return -1;
+	}
+	/* cache it in the global variables when required and not yet done */
+	if ((usefirst != 0) && (gsrvid == NULL)) {
+		memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
+		gsrvid = gsrvidbuf;
+		gsrvidlen = DHCP_OPTLEN_SRVID;
+	}
+	x->sid = ibuf + off;
+	x->sidlen = DHCP_OPTLEN_SRVID;
+	return 0;
+}
+
+/*
+ * receive a DHCPv4 packet
+ */
+
+void
+receive4(void)
+{
+	struct exchange *x, *t;
+	struct xlist *bucket;
+	struct timespec now;
+	ssize_t cc;
+	uint32_t xid, hash;
+	int checklost = 0;
+	double delta;
+
+	cc = recv(sock, ibuf, sizeof(ibuf), 0);
+	if (cc < 0) {
+		if ((errno == EAGAIN) ||
+		    (errno == EWOULDBLOCK) ||
+		    (errno == EINTR))
+			return;
+		perror("recv");
+		fatal = 1;
+		return;
+	}
+	/* enforce a reasonable length */
+	if (cc < BOOTP_MIN_LEN) {
+		tooshort++;
+		return;
+	}
+	/* must be a BOOTP REPLY */
+	if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
+		return;
+	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
+		perror("clock_gettime(receive)");
+		fatal = 1;
+		return;
+	}
+	memcpy(&xid, ibuf + xid_discover4, 4);
+	/* 4-packet exchange even/odd xid */
+	if (simple == 0) {
+		if ((xid & 1) != 0) {
+			receive_reply(xid, &now);
+			return;
+		}
+		hash = (xid >> 1) & (hashsize0 - 1);
+	} else
+		hash = xid & (hashsize0 - 1);
+	/* now it is the second packet, get the bucket which is needed */
+	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
+	/* try the 'next to be received' cache */
+	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
+		x = xnext0;
+		goto found;
+	}
+	/* if the rate is not illimited, garbage collect up to 3
+	   timed-out exchanges */
+	if (rate != 0)
+		checklost = 3;
+	/* look for the exchange */
+	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
+		double waited;
+
+		if (x->xid == xid)
+			goto found;
+		if (checklost <= 0)
+			continue;
+		/* check for a timed-out exchange */
+		waited = now.tv_sec - x->ts0.tv_sec;
+		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
+		if (waited < losttime[0]) {
+			checklost = 0;
+			continue;
+		}
+		/* garbage collect timed-out exchange */
+		checklost--;
+		ISC_TAILQ_REMOVE(bucket, x, hchain);
+		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
+		free(x);
+		collected[0] += 1;
+	}
+	/* no match? very late or not for us */
+	orphans++;
+	return;
+
+	/* got it: update stats and move to the received queue */
+    found:
+	xrcount0++;
+	x->ts1 = now;
+	delta = x->ts1.tv_sec - x->ts0.tv_sec;
+	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
+	if (delta < dmin0)
+		dmin0 = delta;
+	if (delta > dmax0)
+		dmax0 = delta;
+	dsum0 += delta;
+	dsumsq0 += delta * delta;
+	xnext0 = ISC_TAILQ_NEXT(x, gchain);
+	ISC_TAILQ_REMOVE(bucket, x, hchain);
+	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
+	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
+	/* if the exchange is not finished, go to the second part */
+	if (simple == 0) {
+		int ret = 0;
+
+		/* the server-ID option is needed */
+		if ((usefirst != 0) && (gsrvid != NULL)) {
+			x->sid = gsrvid;
+			x->sidlen = gsrvidlen;
+		} else
+			ret = scan_for_srvid4(x, cc);
+		if (ret >= 0)
+			send_request4(x);
+	}
+}
+
+/*
+ * get the DHCPv6 socket descriptor
+ */
+
+void
+getsock6(void)
+{
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
+	int ret;
+
+	/* update local port */
+	if (localport != 0) {
+		uint16_t lp = htons((uint16_t) localport);
+
+		((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
+	}
+	sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+	if (sock < 0) {
+		perror("socket");
+		exit(1);
+	}
+	/* perform the multicast stuff when the destination is multicast */
+	if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
+		int hops = 1;
+
+		ret = setsockopt(sock,
+				 IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+				 &hops, sizeof(hops));
+		if (ret < 0) {
+			perror("setsockopt(IPV6_MULTICAST_HOPS)");
+			exit(1);
+		}
+	}
+	if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
+		unsigned idx = if_nametoindex(localname);
+
+		if (idx == 0) {
+			fprintf(stderr,
+				"if_nametoindex(%s) failed\n",
+				localname);
+			exit(1);
+		}
+		ret = setsockopt(sock,
+				 IPPROTO_IPV6, IPV6_MULTICAST_IF,
+				 &idx, sizeof(idx));
+		if (ret < 0) {
+			perror("setsockopt(IPV6_MULTICAST_IF)");
+			exit(1);
+		}
+	}
+}
+
+/*
+ * build a DHCPv6 SOLICIT template
+ * (implicit parameter is the DUID, don't assume an Ethernet link)
+ */
+
+void
+build_template_solicit6(void)
+{
+	uint8_t *p = template_solicit6;
+
+	xid_solicit6 = DHCP6_OFF_XID;
+	/* message type */
+	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
+	/* options */
+	p += DHCP6_OFF_OPTIONS;
+	/* elapsed time */
+	p[1] = DHCP6_OPT_ELAPSED_TIME;
+	p[3] = 2;
+	p += 6;
+	/* rapid commit */
+	if (rapidcommit != 0) {
+		p[1] = DHCP6_OPT_RAPID_COMMIT;
+		p += 4;
+	}
+	/* client ID */
+	p[1] = DHCP6_OPT_CLIENTID;
+	p[3] = duid_length;
+	memcpy(p + 4, duid_prefix, duid_length);
+	p += 4 + duid_length;
+	random_solicit6 = p - template_solicit6;
+	/* option request option */
+	p[1] = DHCP6_OPT_ORO;
+	p[3] = 4;
+	p[5] = DHCP6_OPT_NAME_SERVERS;
+	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
+	p += 8;
+	/* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
+	p[1] = DHCP6_OPT_IA_NA;
+	p[3] = 12;
+	p[7] = 1;
+	p[10] = 3600 >> 8;
+	p[11] = 3600 & 0xff;
+	p[14] = 5400 >> 8;
+	p[15] = 5400 & 0xff;
+	p += 16;
+	/* set length */
+	length_solicit6 = p - template_solicit6;
+}
+
+/*
+ * get a DHCPv6 first packet (usually a SOLICIT) template
+ * from the file given in the command line (-T<template-file>)
+ * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
+ */
+
+void
+get_template_solicit6(void)
+{
+	uint8_t *p = template_solicit6;
+	int fd, cc, i, j;
+
+	fd = open(templatefile[0], O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(%s): %s\n",
+			templatefile[0], strerror(errno));
+		exit(2);
+	}
+	cc = read(fd, tbuf, sizeof(tbuf));
+	(void) close(fd);
+	if (cc < 0) {
+		fprintf(stderr, "read(%s): %s\n",
+			templatefile[0], strerror(errno));
+		exit(1);
+	}
+	if (cc < 10) {
+		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
+		exit(2);
+	}
+	if (cc > 8193) {
+		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
+		exit(2);
+	}
+	j = 0;
+	for (i = 0; i < cc; i++) {
+		if (isspace((int) tbuf[i]))
+			continue;
+		if (!isxdigit((int) tbuf[i])) {
+			fprintf(stderr,
+				"illegal char[%d]='%c' in file '%s'\n",
+				i, (int) tbuf[i], templatefile[0]);
+			exit(2);
+		}
+		tbuf[j] = tbuf[i];
+		j++;
+	}
+	cc = j;
+	if ((cc & 1) != 0) {
+		fprintf(stderr,
+			"odd number of hexadecimal digits in file '%s'\n",
+			templatefile[0]);
+		exit(2);
+	}
+	length_solicit6 = cc >> 1;
+	for (i = 0; i < cc; i += 2)
+		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
+	if (xidoffset[0] >= 0)
+		xid_solicit6 = (size_t) xidoffset[0];
+	else
+		xid_solicit6 = DHCP6_OFF_XID;
+	if (xid_solicit6 + 3 > length_solicit6) {
+		fprintf(stderr,
+			"xid (at %zu) is outside the template (length %zu)?\n",
+			xid_solicit6, length_solicit6);
+		exit(2);
+	}
+	if (rndoffset[0] >= 0)
+		random_solicit6 = (size_t) rndoffset[0];
+	else
+		random_solicit6 = 0;
+	if (random_solicit6 > length_solicit6) {
+		fprintf(stderr,
+			"random (at %zu) outside the template (length %zu)?\n",
+			random_solicit6, length_solicit6);
+		exit(2);
+	}
+}
+
+/*
+ * build a DHCPv6 REQUEST template
+ * (implicit parameter is the DUID, don't assume an Ethernet link)
+ */
+
+void
+build_template_request6(void)
+{
+	uint8_t *p = template_request6;
+
+	xid_request6 = DHCP6_OFF_XID;
+	/* message type */
+	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
+	/* options */
+	p += DHCP6_OFF_OPTIONS;
+	/* elapsed time */
+	p[1] = DHCP6_OPT_ELAPSED_TIME;
+	p[3] = 2;
+	p += 4;
+	elapsed_request6 = p - template_request6;
+	p += 2;
+	/* client ID */
+	p[1] = DHCP6_OPT_CLIENTID;
+	p[3] = duid_length;
+	memcpy(p + 4, duid_prefix, duid_length);
+	p += 4 + duid_length;
+	random_request6 = p - template_request6;
+	/* option request option */
+	p[1] = DHCP6_OPT_ORO;
+	p[3] = 4;
+	p[5] = DHCP6_OPT_NAME_SERVERS;
+	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
+	p += 8;
+	/* server ID and IA_NA */
+	serverid_request6 = p - template_request6;
+	reqaddr_request6 = p - template_request6;
+	/* set length */
+	length_request6 = p - template_request6;
+}
+
+/*
+ * get a DHCPv6 third packet (usually a REQUEST) template
+ * from the file given in the command line (-T<template-file>)
+ * and offsets (-X,-O,-E,-S,-I).
+ */
+
+void
+get_template_request6(void)
+{
+	uint8_t *p = template_request6;
+	int fd, cc, i, j;
+
+	fd = open(templatefile[1], O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "open(%s): %s\n",
+			templatefile[1], strerror(errno));
+		exit(2);
+	}
+	cc = read(fd, tbuf, sizeof(tbuf));
+	(void) close(fd);
+	if (cc < 0) {
+		fprintf(stderr, "read(%s): %s\n",
+			templatefile[1], strerror(errno));
+		exit(1);
+	}
+	if (cc < 10) {
+		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
+		exit(2);
+	}
+	if (cc > 8193) {
+		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
+		exit(2);
+	}
+	j = 0;
+	for (i = 0; i < cc; i++) {
+		if (isspace((int) tbuf[i]))
+			continue;
+		if (!isxdigit((int) tbuf[i])) {
+			fprintf(stderr,
+				"illegal char[%d]='%c' in file '%s'\n",
+				i, (int) tbuf[i], templatefile[1]);
+			exit(2);
+		}
+		tbuf[j] = tbuf[i];
+		j++;
+	}
+	cc = j;
+	if ((cc & 1) != 0) {
+		fprintf(stderr,
+			"odd number of hexadecimal digits in file '%s'\n",
+			templatefile[1]);
+		exit(2);
+	}
+	length_request6 = cc >> 1;
+	for (i = 0; i < cc; i += 2)
+		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
+	if (xidoffset[1] >= 0)
+		xid_request6 = (size_t) xidoffset[1];
+	else
+		xid_request6 = DHCP6_OFF_XID;
+	if (xid_request6 + 3 > length_request6) {
+		fprintf(stderr,
+			"xid (at %zu) is outside the template (length %zu)?\n",
+			xid_request6, length_request6);
+		exit(2);
+	}
+	if (rndoffset[1] >= 0)
+		random_request6 = (size_t) rndoffset[1];
+	else
+		random_request6 = 0;
+	if (random_request6 > length_request6) {
+		fprintf(stderr,
+			"random (at %zu) outside the template (length %zu)?\n",
+			random_request6, length_request6);
+		exit(2);
+	}
+	if (elpoffset >= 0)
+		elapsed_request6 = (size_t) elpoffset;
+	if (elapsed_request6 + 2 > length_request6) {
+		fprintf(stderr,
+			"secs (at %zu) outside the template (length %zu)?\n",
+			elapsed_request6, length_request6);
+		exit(2);
+	}
+	serverid_request6 = (size_t) sidoffset;
+	if (serverid_request6 > length_request6) {
+		fprintf(stderr,
+			"server-id option (at %zu) outside the template "
+			"(length %zu)?\n",
+			serverid_request6, length_request6);
+		exit(2);
+	}
+	reqaddr_request6 = (size_t) ripoffset;
+	if (reqaddr_request6 > length_request6) {
+		fprintf(stderr,
+			"requested-ip-address option (at %zu) outside "
+			"the template (length %zu)?\n",
+			reqaddr_request6, length_request6);
+		exit(2);
+	}
+}
+
+/*
+ * send the DHCPv6 REQUEST third packet
+ * (the transaction ID is odd)
+ * (TODO: check for errors in the ADVERTISE)
+ */
+
+void
+send_request6(struct exchange *x0)
+{
+	struct exchange *x;
+	struct xlist *bucket;
+	size_t len;
+	uint32_t hash;
+	ssize_t ret;
+
+	x = (struct exchange *) malloc(sizeof(*x));
+	if (x == NULL) {
+		locallimit++;
+		perror("send2");
+		return;
+	}
+
+	memcpy(x, x0, sizeof(*x));
+	x->order2 = xscount2++;
+	x->xid |= 1;
+	hash = x->xid >> 1;
+
+	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
+	hash &= hashsize2 - 1;
+	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
+	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
+
+	len = length_request6;
+	memcpy(obuf, template_request6, len);
+	/* xid */
+	obuf[xid_request6] = x->xid >> 16;
+	obuf[xid_request6 + 1] = x->xid >> 8;
+	obuf[xid_request6 + 2] = x->xid;
+	/* random */
+	randomize(random_request6, x->rnd);
+	/* elapsed time */
+	if (elapsed_request6 > 0) {
+		int et;
+
+		et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
+		et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
+		if (et > 65535) {
+			obuf[elapsed_request6] = 0xff;
+			obuf[elapsed_request6 + 1] = 0xff;
+		} else if (et > 0) {
+			obuf[elapsed_request6] = et >> 8;
+			obuf[elapsed_request6 + 1] = et & 0xff;
+		}
+	}
+	/* server ID */
+	if (serverid_request6 < length_request6)
+		memmove(obuf + serverid_request6 + x->sidlen,
+			obuf + serverid_request6,
+			x->sidlen);
+	memcpy(obuf + serverid_request6, x->sid, x->sidlen);
+	len += x->sidlen;
+	/* IA_NA */
+	if (reqaddr_request6 < serverid_request6) {
+		memmove(obuf + reqaddr_request6 + x->ianalen,
+			obuf + reqaddr_request6,
+			x->ianalen);
+		memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
+	} else if (reqaddr_request6 < length_request6) {
+		memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
+			obuf + reqaddr_request6 + x->sidlen,
+			x->ianalen);
+		memcpy(obuf + reqaddr_request6+ x->sidlen,
+		       x->iana,
+		       x->ianalen);
+	} else
+		memcpy(obuf + len, x->iana, x->ianalen);
+	len += x->ianalen;
+
+	/* timestamp */
+	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
+	if (ret < 0) {
+		perror("clock_gettime(send2)");
+		fatal = 1;
+		return;
+	}
+	ret = sendto(sock, obuf, len, 0,
+		     (struct sockaddr *) &serveraddr,
+		     sizeof(struct sockaddr_in6));
+	if (ret >= 0)
+		return;
+	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
+	    (errno == ENOBUFS) || (errno == ENOMEM))
+		locallimit++;
+	perror("send2");
+}
+
+/*
+ * send the DHCPv6 SOLICIT first packet
+ * (for 4-exchange, the transaction ID xid is even)
+ */
+
+int
+send6(void)
+{
+	struct exchange *x;
+	struct xlist *bucket;
+	uint32_t hash;
+	ssize_t ret;
+
+	x = (struct exchange *) malloc(sizeof(*x));
+	if (x == NULL)
+		return -ENOMEM;
+
+	memset(x, 0, sizeof(*x));
+	x->order0 = xscount0++;
+	hash = x->rnd = (uint32_t) random();
+	if (simple == 0)
+		x->xid = (hash << 1) & 0x00ffffff;
+	else
+		x->xid = hash & 0x00ffffff;
+
+	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
+	hash &= hashsize0 - 1;
+	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
+	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
+
+	memcpy(obuf, template_solicit6, length_solicit6);
+	/* xid */
+	obuf[xid_solicit6] = x->xid >> 16;
+	obuf[xid_solicit6 + 1] = x->xid >> 8;
+	obuf[xid_solicit6 + 2] = x->xid;
+	/* random */
+	x->rnd = randomize(random_solicit6, x->rnd);
+
+	/* timestamp */
+	ret = clock_gettime(CLOCK_REALTIME, &last);
+	if (ret < 0) {
+		perror("clock_gettime(send)");
+		fatal = 1;
+		return -errno;
+	}
+	x->ts0 = last;
+	errno = 0;
+	ret = sendto(sock, obuf, length_solicit6, 0,
+		     (struct sockaddr *) &serveraddr,
+		     sizeof(struct sockaddr_in6));
+	if (ret == (ssize_t) length_solicit6)
+		return 0;
+	return -errno;
+}
+
+/*
+ * scan a DHCPv6 ADVERTISE to get its server-id option
+ */
+
+int
+scan_for_srvid6(struct exchange *x, size_t cc)
+{
+	size_t off = DHCP6_OFF_OPTIONS, len;
+
+	for (;;) {
+		if (off + 4 > cc) {
+			fprintf(stderr, "server-id not found\n");
+			return -1;
+		}
+		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
+			break;
+		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
+	}
+	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
+	/* cache it in the global variables when required and not yet done */
+	if ((usefirst != 0) && (gsrvid == NULL)) {
+		if (len <= sizeof(gsrvidbuf)) {
+			memcpy(gsrvidbuf, ibuf + off, len);
+			gsrvid = gsrvidbuf;
+			gsrvidlen = len;
+		} else {
+			gsrvid = (uint8_t *) malloc(len);
+			if (gsrvid == NULL) {
+				perror("malloc(gsrvid");
+				return -1;
+			}
+			memcpy(gsrvid, ibuf + off, len);
+			gsrvidlen = len;
+		}
+	}
+	x->sid = ibuf + off;
+	x->sidlen = len;
+	return 0;
+}
+
+/*
+ * scan a DHCPv6 ADVERTISE to get its IA_NA option
+ * (TODO: check for errors)
+ */
+
+int
+scan_for_ia_na(struct exchange *x, size_t cc)
+{
+	size_t off = DHCP6_OFF_OPTIONS, len;
+
+	for (;;) {
+		if (off + 4 > cc) {
+			fprintf(stderr, "ia-na not found\n");
+			return -1;
+		}
+		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
+			break;
+		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
+	}
+	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
+	x->iana = ibuf + off;
+	x->ianalen = len;
+	return 0;
+}
+
+/*
+ * receive a DHCPv6 packet
+ */
+
+void
+receive6(void)
+{
+	struct exchange *x, *t;
+	struct xlist *bucket;
+	struct timespec now;
+	ssize_t cc;
+	uint32_t xid, hash;
+	int checklost = 0;
+	double delta;
+
+	cc = recv(sock, ibuf, sizeof(ibuf), 0);
+	if (cc < 0) {
+		if ((errno == EAGAIN) ||
+		    (errno == EWOULDBLOCK) ||
+		    (errno == EINTR))
+			return;
+		perror("recv");
+		fatal = 1;
+		return;
+	}
+	/* enforce a reasonable length */
+	if (cc < 22) {
+		tooshort++;
+		return;
+	}
+	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
+		perror("clock_gettime(receive)");
+		fatal = 1;
+		return;
+	}
+	xid = ibuf[xid_solicit6] << 16;
+	xid |= ibuf[xid_solicit6 + 1] << 8;
+	xid |= ibuf[xid_solicit6 + 2];
+	/* 4-packet exchange even/odd xid */
+	if (simple == 0) {
+		if ((xid & 1) != 0) {
+			receive_reply(xid, &now);
+			return;
+		}
+		hash = (xid >> 1) & (hashsize0 - 1);
+	} else
+		hash = xid & (hashsize0 - 1);
+	/* now it is the second packet, get the bucket which is needed */
+	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
+	/* try the 'next to be received' cache */
+	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
+		x = xnext0;
+		goto found;
+	}
+	/* if the rate is not illimited, garbage collect up to 3
+	   timed-out exchanges */
+	if (rate != 0)
+		checklost = 3;
+	/* look for the exchange */
+	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
+		double waited;
+
+		if (x->xid == xid)
+			goto found;
+		if (checklost <= 0)
+			continue;
+		/* check for a timed-out exchange */
+		waited = now.tv_sec - x->ts0.tv_sec;
+		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
+		if (waited < losttime[0]) {
+			checklost = 0;
+			continue;
+		}
+		/* garbage collect timed-out exchange */
+		checklost--;
+		ISC_TAILQ_REMOVE(bucket, x, hchain);
+		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
+		free(x);
+		collected[0] += 1;
+	}
+	/* no match? very late or not for us */
+	orphans++;
+	return;
+
+	/* got it: update stats and move to the received queue */
+    found:
+	xrcount0++;
+	x->ts1 = now;
+	delta = x->ts1.tv_sec - x->ts0.tv_sec;
+	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
+	if (delta < dmin0)
+		dmin0 = delta;
+	if (delta > dmax0)
+		dmax0 = delta;
+	dsum0 += delta;
+	dsumsq0 += delta * delta;
+	xnext0 = ISC_TAILQ_NEXT(x, gchain);
+	ISC_TAILQ_REMOVE(bucket, x, hchain);
+	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
+	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
+	/* if the exchange is not finished, go to the second part */
+	if (simple == 0) {
+		int ret = 0;
+
+		/* the server-ID option is needed */
+		if ((usefirst != 0) && (gsrvid != NULL)) {
+			x->sid = gsrvid;
+			x->sidlen = gsrvidlen;
+		} else
+			ret = scan_for_srvid6(x, cc);
+		/* and the IA_NA option too */
+		if (ret >= 0)
+			ret = scan_for_ia_na(x, cc);
+		if (ret >= 0)
+			send_request6(x);
+	}
+}
+
+/*
+ * decode a base command line parameter
+ * (currently only MAC address and DUID are supported)
+ */
+
+void
+decodebase(void)
+{
+	char *b0 = base[basecnt];
+
+	/* MAC address (alias Ethernet address) */
+	if ((strncasecmp(b0, "mac=", 4) == 0) ||
+	    (strncasecmp(b0, "ether=", 6) == 0)) {
+		char *b;
+
+		b = strchr(b0, '=') + 1;
+		if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+			   &mac_prefix[0], &mac_prefix[1],
+			   &mac_prefix[2], &mac_prefix[3],
+			   &mac_prefix[4], &mac_prefix[5]) == 6) {
+			if ((diags != NULL) && (strchr(diags, 'a') != NULL))
+				printf("set MAC to %s\n", b);
+			return;
+		}
+		fprintf(stderr,
+			"expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
+			(int) (b - b0 - 1), b0);
+			exit(2);
+	}
+	/* DUID */
+	if (strncasecmp(b0, "duid=", 5) == 0) {
+		char *b;
+		size_t i, l;
+
+		if (duid_prefix != NULL) {
+			fprintf(stderr, "duid was already set?\n");
+			exit(2);
+		}
+		b = strchr(b0, '=') + 1;
+		l = 0;
+		while (*b != '\0') {
+			if (!isxdigit((int) *b)) {
+				fprintf(stderr,
+					"illegal char '%c' in duid\n",
+					(int) *b);
+				exit(2);
+			}
+			b++;
+			l++;
+		}
+		if ((l & 1) != 0) {
+			fprintf(stderr,
+				"odd number of hexadecimal digits in duid\n");
+			exit(2);
+		}
+		l /= 2;
+		if (l > 64) {
+			fprintf(stderr, "duid too large\n");
+			exit(2);
+		}
+		duid_prefix = (uint8_t *) malloc(l);
+		if (duid_prefix == NULL) {
+			perror("malloc(duid)");
+			exit(1);
+		}
+		b = strchr(b0, '=') + 1;
+		for (i = 0; i < l; i++, b += 2)
+			(void) sscanf(b, "%02hhx", &duid_prefix[i]);
+		duid_length = l;
+		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
+			b = strchr(b0, '=') + 1;
+			printf("set DUID[%d] to %s\n",
+			       duid_length, b);
+		}
+		return;
+	}
+	/* other */
+	fprintf(stderr, "not yet supported base '%s'\n", b0);
+	exit(2);
+}
+			   
+/*
+ * get the server socket address from the command line:
+ *  - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
+ */
+
+void
+getserveraddr(const int flags)
+{
+	struct addrinfo hints, *res;
+	char *service;
+	int ret;
+
+	memset(&hints, 0, sizeof(hints));
+	if (ipversion == 4) {
+		hints.ai_family = AF_INET;
+		service = "67";
+	} else {
+		hints.ai_family = AF_INET6;
+		service = "547";
+	}
+	hints.ai_socktype = SOCK_DGRAM;
+
+	hints.ai_flags = AI_NUMERICSERV | flags;
+#if defined(AI_ADDRCONFIG)
+	hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+	hints.ai_protocol = IPPROTO_UDP;
+	
+	ret = getaddrinfo(servername, service, &hints, &res);
+	if (ret != 0) {
+		fprintf(stderr, "bad server=%s: %s\n",
+			servername, gai_strerror(ret));
+		exit(2);
+	}
+	if (res->ai_next != NULL) {
+		fprintf(stderr, "ambiguous server=%s\n", servername);
+		exit(2);
+	}
+	memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
+	freeaddrinfo(res);
+}
+
+/*
+ * get the interface from the command line:
+ *   - name of the interface
+ *   - socket address to fill
+ * (in IPv6, get the first link-local address)
+ */
+
+void
+getinterface(const char *name, struct sockaddr_storage *ss)
+{
+	struct ifaddrs *ifaddr, *ifa;
+	int ret;
+	size_t len;
+
+	ret = getifaddrs(&ifaddr);
+	if (ret < 0) {
+		perror("getifaddrs");
+		exit(1);
+	}
+
+	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+		if (ifa->ifa_name == NULL)
+			continue;
+		if (strcmp(ifa->ifa_name, name) != 0)
+			continue;
+		if ((ifa->ifa_flags & IFF_UP) == 0)
+			continue;
+		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
+			continue;
+		if (ifa->ifa_addr == NULL)
+			continue;
+		if ((ipversion == 4) &&
+		    (ifa->ifa_addr->sa_family != AF_INET))
+			continue;
+		if (ipversion == 6) {
+			struct sockaddr_in6 *a6;
+
+			a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+			if (a6->sin6_family != AF_INET6)
+				continue;
+			if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
+				continue;
+		}
+		if (ipversion == 4)
+			len = sizeof(struct sockaddr_in);
+		else
+			len = sizeof(struct sockaddr_in6);
+		memcpy(ss, ifa->ifa_addr, len);
+		/* fill the server port */
+		if (ipversion == 4)
+			((struct sockaddr_in *) ss)->sin_port = htons(67);
+		else
+			((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
+		return;
+	}
+	fprintf(stderr, "can't find interface %s\n", name);
+	exit(1);
+}
+
+/*
+ * get the local socket address from the command line
+ * (if it doesn't work, try an interface name)
+ */
+
+void
+getlocaladdr(void)
+{
+	struct addrinfo hints, *res;
+	char *service;
+	int ret;
+
+	memset(&hints, 0, sizeof(hints));
+	if (ipversion == 4) {
+		hints.ai_family = AF_INET;
+		service = "67";
+	} else {
+		hints.ai_family = AF_INET6;
+		service = "546";
+	}
+	hints.ai_socktype = SOCK_DGRAM;
+	hints.ai_flags =  AI_NUMERICSERV;
+#if defined(AI_ADDRCONFIG)
+	hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+	hints.ai_protocol = IPPROTO_UDP;
+	
+	ret = getaddrinfo(localname, service, &hints, &res);
+	if ((ret == EAI_NONAME)
+#ifdef EAI_NODATA
+	    || (ret == EAI_NODATA)
+#endif
+	   ) {
+		isinterface = 1;
+		getinterface(localname, &localaddr);
+		return;
+	}
+	if (ret != 0) {
+		fprintf(stderr,
+			"bad -l<local-addr=%s>: %s\n",
+			localname,
+			gai_strerror(ret));
+		exit(2);
+	}
+	/* refuse multiple addresses */
+	if (res->ai_next != NULL) {
+		fprintf(stderr,
+			"ambiguous -l<local-addr=%s>\n",
+			localname);
+		exit(2);
+	}
+	memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
+	freeaddrinfo(res);
+	return;
+}
+
+/*
+ * get the local socket address from the server one
+ */
+
+void
+getlocal(void)
+{
+	int ret, s;
+	socklen_t l;
+
+	if (ipversion == 4) {
+		l = sizeof(struct sockaddr_in);
+		s = socket(PF_INET, SOCK_DGRAM, 0);
+	} else {
+		l = sizeof(struct sockaddr_in6);
+		s = socket(PF_INET6, SOCK_DGRAM, 0);
+	}
+	if (s < 0) {
+		perror("socket");
+		exit(1);
+	}
+	if ((ipversion == 4) && (isbroadcast != 0)) {
+		int on = 1;
+
+		ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+		if (ret < 0) {
+			perror("setsockopt(SO_BROADCAST)");
+			exit(1);
+		}
+	}
+	ret = connect(s, (struct sockaddr *) &serveraddr, l);
+	if (ret < 0) {
+		perror("connect");
+		exit(1);
+	}
+	l = sizeof(localaddr);
+	ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
+	if (ret < 0) {
+		perror("getsockname");
+		exit(1);
+	}
+	(void) close(s);
+	/* fill the local port */
+	if (ipversion == 4)
+		((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
+	else
+		((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
+}
+
+/*
+ * intermediate reporting
+ * (note: an in-transit packet can be reported as dropped)
+ */
+
+void
+reporting(void)
+{
+	dreport.tv_sec += report;
+
+	if (xscount2 == 0) {
+		printf("sent: %llu, received: %llu (drops: %lld)",
+		       (unsigned long long) xscount0,
+		       (unsigned long long) xrcount0,
+		       (long long) (xscount0 - xrcount0));
+		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
+			double avg;
+
+			avg = dsum0 / xrcount0;
+			printf(" average: %.3f ms", avg * 1e3);
+		}
+	} else {
+		printf("sent: %llu/%llu received: %llu/%llu "
+		       "(drops: %lld/%lld)",
+		       (unsigned long long) xscount0,
+		       (unsigned long long) xscount2,
+		       (unsigned long long) xrcount0,
+		       (unsigned long long) xrcount2,
+		       (long long) (xscount0 - xrcount0),
+		       (long long) (xscount2 - xrcount2));
+		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
+			double avg0, avg2;
+
+			avg0 = dsum0 / xrcount0;
+			if (xrcount2 != 0)
+				avg2 = dsum2 / xrcount2;
+			else
+				avg2 = 0.;
+			printf(" average: %.3f/%.3f ms",
+			       avg0 * 1e3, avg2 * 1e3);
+		}
+	}
+	printf("\n");
+}
+
+/*
+ * SIGCHLD handler
+ */
+
+void
+reapchild(int sig)
+{
+	int status;
+
+	sig = sig;
+	while (wait3(&status, WNOHANG, NULL) > 0)
+		/* continue */;
+}
+
+/*
+ * SIGINT handler
+ */
+
+void
+interrupt(int sig)
+{
+	sig = sig;
+	interrupted = 1;
+}
+
+/*
+ * '-v' handler
+ */
+
+void
+version(void)
+{
+	fprintf(stderr, "version 0.01\n");
+}
+
+/*
+ * usage (from the wiki)
+ */
+
+void
+usage(void)
+{
+	fprintf(stderr, "%s",
+"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+"    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+"    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+"    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+"    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+"    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+"    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+"\f\n"
+"The [server] argument is the name/address of the DHCP server to\n"
+"contact.  For DHCPv4 operation, exchanges are initiated by\n"
+"transmitting a DHCP DISCOVER to this address.\n"
+"\n"
+"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
+"SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
+"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
+"multicast address FF02::1:2), or the special name 'servers' to refer\n"
+"to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
+"argument is optional only in the case that -l is used to specify an\n"
+"interface, in which case [server] defaults to 'all'.\n"
+"\n"
+"The default is to perform a single 4-way exchange, effectively pinging\n"
+"the server.\n"
+"The -r option is used to set up a performance test, without\n"
+"it exchanges are initiated as fast as possible.\n"
+"\n"
+"Options:\n"
+"-1: Take the server-ID option from the first received message.\n"
+"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
+"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
+"-a<aggressivity>: When the target sending rate is not yet reached,\n"
+"    control how many exchanges are initiated before the next pause.\n"
+"-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
+"    clients.  This can be specified multiple times, each instance is\n"
+"    in the <type>=<value> form, for instance:\n"
+"    (and default) MAC=00:0c:01:02:03:04.\n"
+"-d<drop-time>: Specify the time after which a request is treated as\n"
+"    having been lost.  The value is given in seconds and may contain a\n"
+"    fractional component.  The default is 1 second.\n"
+"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
+"    elapsed-time option in the (second/request) template.\n"
+"    The value 0 disables it.\n"
+"-h: Print this help.\n"
+"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
+"    whether -6 is given.\n"
+"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
+"    option / (DHCPv6) IA_NA option in the (second/request) template.\n"
+"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
+"    hostname/address to use when communicating with the server.  By\n"
+"    default, the interface address through which traffic would\n"
+"    normally be routed to the server is used.\n"
+"    For DHCPv6 operation, specify the name of the network interface\n"
+"    via which exchanges are initiated.\n"
+"-L<local-port>: Specify the local port to use\n"
+"    (the value 0 means to use the default).\n"
+"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
+"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
+"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
+"    exchanges per second.  A periodic report is generated showing the\n"
+"    number of exchanges which were not completed, as well as the\n"
+"    average response latency.  The program continues until\n"
+"    interrupted, at which point a final report is generated.\n"
+"-R<range>: Specify how many different clients are used. With 1\n"
+"    (the default), all requests seem to come from the same client.\n"
+"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
+"-S<srvid-offset>: Offset of the server-ID option in the\n"
+"    (second/request) template.\n"
+"-T<template-file>: The name of a file containing the template to use\n"
+"    as a stream of hexadecimal digits.\n"
+"-v: Report the version number of this program.\n"
+"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
+"    the program.\n"
+"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
+"    <diagnostic-selector> is a string of single-keywords specifying\n"
+"    the operations for which verbose output is desired.  The selector\n"
+"    keyletters are:\n"
+"   * 'a': print the decoded command line arguments\n"
+"   * 'e': print the exit reason\n"
+"   * 'i': print rate processing details\n"
+"   * 'r': print randomization details\n"
+"   * 's': print first server-id\n"
+"   * 't': when finished, print timers of all successful exchanges\n"
+"   * 'T': when finished, print templates\n"
+"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
+"\n"
+"DHCPv4 only options:\n"
+"-B: Force broadcast handling.\n"
+"\n"
+"DHCPv6 only options:\n"
+"-c: Add a rapid commit option (exchanges will be SA).\n"
+"\n"
+"The remaining options are used only in conjunction with -r:\n"
+"\n"
+"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
+"    been dropped.  Use -D0 to abort if even a single request has been\n"
+"    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
+"    maximum percentage of requests that may be dropped before abort.\n"
+"    In this case, testing of the threshold begins after 10 requests\n"
+"    have been expected to be received.\n"
+"-n<num-request>: Initiate <num-request> transactions.  No report is\n"
+"    generated until all transactions have been initiated/waited-for,\n"
+"    after which a report is generated and the program terminates.\n"
+"-p<test-period>: Send requests for the given test period, which is\n"
+"    specified in the same manner as -d.  This can be used as an\n"
+"    alternative to -n, or both options can be given, in which case the\n"
+"    testing is completed when either limit is reached.\n"
+"-t<report>: Delay in seconds between two periodic reports.\n"
+"\n"
+"Errors:\n"
+"- tooshort: received a too short message\n"
+"- orphans: received a message which doesn't match an exchange\n"
+"   (duplicate, late or not related)\n"
+"- locallimit: reached to local system limits when sending a message.\n"
+"\n"
+"Exit status:\n"
+"The exit status is:\n"
+"0 on complete success.\n"
+"1 for a general error.\n"
+"2 if an error is found in the command line arguments.\n"
+"3 if there are no general failures in operation, but one or more\n"
+"  exchanges are not successfully completed.\n");
+}
+
+/*
+ * main function / entry point
+ */
+
+int
+main(const int argc, char * const argv[])
+{
+	int opt, flags = 0, ret, i;
+	long long r;
+	char *pc;
+	extern char *optarg;
+	extern int optind;
+
+#define OPTIONS	"hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
+
+	/* decode options */
+	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
+	switch (opt) {
+	case 'h':
+		usage();
+		exit(0);
+
+	case 'v':
+		version();
+		exit(0);
+
+	case '4':
+		if (ipversion == 6) {
+			fprintf(stderr, "IP version already set to 6\n");
+			usage();
+			exit(2);
+		}
+		ipversion = 4;
+		break;
+
+	case '6':
+		if (ipversion == 4) {
+			fprintf(stderr, "IP version already set to 4\n");
+			usage();
+			exit(2);
+		}
+		ipversion = 6;
+		break;
+
+	case 'r':
+		rate = atoi(optarg);
+		if (rate <= 0) {
+			fprintf(stderr, "rate must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 't':
+		report = atoi(optarg);
+		if (report <= 0) {
+			fprintf(stderr, "report must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'R':
+		r = atoll(optarg);
+		if (r < 0) {
+			fprintf(stderr,
+				"range must not be a negative integer\n");
+			usage();
+			exit(2);
+		}
+		range = (uint32_t) r;
+		if ((range != 0) && (range != UINT32_MAX)) {
+			uint32_t s = range + 1;
+			uint64_t b = UINT32_MAX + 1, m;
+
+			m = (b / s) * s;
+			if (m == b)
+				maxrandom = 0;
+			else
+				maxrandom = (uint32_t) m;
+		}
+		break;
+
+	case 'b':
+		if (basecnt > 3) {
+			fprintf(stderr, "too many bases\n");
+			usage();
+			exit(2);
+		}
+		base[basecnt] = optarg;
+		decodebase();
+		basecnt++;
+		break;
+
+	case 'n':
+		numreq[gotnumreq] = atoi(optarg);
+		if (numreq[gotnumreq] <= 0) {
+			fprintf(stderr,
+				"num-request must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		gotnumreq = 1;
+		break;
+
+	case 'p':
+		period = atoi(optarg);
+		if (period <= 0) {
+			fprintf(stderr,
+				"test-period must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'd':
+		losttime[gotlosttime] = atof(optarg);
+		if (losttime[gotlosttime] <= 0.) {
+			fprintf(stderr,
+				"drop-time must be a positive number\n");
+			usage();
+			exit(2);
+		}
+		gotlosttime = 1;
+		break;
+
+	case 'D':
+		pc = strchr(optarg, '%');
+		if (pc != NULL) {
+			*pc = '\0';
+			maxpdrop[gotmaxdrop] = atof(optarg);
+			if ((maxpdrop[gotmaxdrop] <= 0) ||
+			    (maxpdrop[gotmaxdrop] >= 100)) {
+				fprintf(stderr,
+					"invalid drop-time percentage\n");
+				usage();
+				exit(2);
+			}
+			gotmaxdrop = 1;
+			break;
+		}
+		maxdrop[gotmaxdrop] = atoi(optarg);
+		if (maxdrop[gotmaxdrop] <= 0) {
+			fprintf(stderr,
+				"max-drop must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		gotmaxdrop = 1;
+		break;
+
+	case 'l':
+		localname = optarg;
+		break;
+
+	case 'P':
+		preload = atoi(optarg);
+		if (preload < 0) {
+			fprintf(stderr,
+				"preload must not be a negative integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'a':
+		aggressivity = atoi(optarg);
+		if (aggressivity <= 0) {
+			fprintf(stderr,
+				"aggressivity must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'L':
+		localport = atoi(optarg);
+		if (localport < 0) {
+			fprintf(stderr,
+				"local-port must not be a negative integer\n");
+			usage();
+			exit(2);
+		}
+		if (localport > (int) UINT16_MAX) {
+			fprintf(stderr,
+				"local-port must be lower than %d\n",
+				(int) UINT16_MAX);
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 's':
+		seeded = 1;
+		seed = (unsigned int) atol(optarg);
+		break;
+
+	case 'i':
+		simple = 1;
+		break;
+
+	case 'B':
+		isbroadcast = 1;
+		break;
+
+	case 'c':
+		rapidcommit = 1;
+		break;
+
+	case '1':
+		usefirst = 1;
+		break;
+
+	case 'T':
+		if (templatefile[0] != NULL) {
+			if (templatefile[1] != NULL) {
+				fprintf(stderr,
+					"template-files are already set\n");
+				usage();
+				exit(2);
+			}
+			templatefile[1] = optarg;
+		} else
+			templatefile[0] = optarg;
+		break;
+
+	case 'X':
+		if (xidoffset[0] >= 0)
+			i = 1;
+		else
+			i = 0;
+		xidoffset[i] = atoi(optarg);
+		if (xidoffset[i] <= 0) {
+			fprintf(stderr,
+				"xid-offset must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'O':
+		if (rndoffset[0] >= 0)
+			i = 1;
+		else
+			i = 0;
+		rndoffset[i] = atoi(optarg);
+		if (rndoffset[i] < 3) {
+			fprintf(stderr,
+				"random-offset must be greater than 3\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'E':
+		elpoffset = atoi(optarg);
+		if (elpoffset < 0) {
+			fprintf(stderr,
+				"time-offset must not be a "
+				"negative integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'S':
+		sidoffset = atoi(optarg);
+		if (sidoffset <= 0) {
+			fprintf(stderr,
+				"srvid-offset must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'I':
+		ripoffset = atoi(optarg);
+		if (ripoffset <= 0) {
+			fprintf(stderr,
+				"ip-offset must be a positive integer\n");
+			usage();
+			exit(2);
+		}
+		break;
+
+	case 'x':
+		diags = optarg;
+		break;
+
+	case 'w':
+		wrapped = optarg;
+		break;
+
+	default:
+		usage();
+		exit(2);
+	}
+
+	/* adjust some global variables */
+	if (ipversion == 0)
+		ipversion = 4;
+	if (templatefile[1] != NULL) {
+		if (xidoffset[1] < 0)
+			xidoffset[1] = xidoffset[0];
+		if (rndoffset[1] < 0)
+			rndoffset[1] = rndoffset[0];
+	}
+
+	/* when required, print the internal view of the command line */
+	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
+		printf("IPv%d", ipversion);
+		if (simple != 0) {
+			if (ipversion == 4)
+				printf(" DO only");
+			else
+				printf(" SA only");
+		}
+		if (rate != 0)
+			printf(" rate=%d", rate);
+		if (report != 0)
+			printf(" report=%d", report);
+		if (range != 0) {
+			if (strchr(diags, 'r') != NULL)
+				printf(" range=0..%d [0x%x]",
+				       range,
+				       (unsigned int) maxrandom);
+			else
+				printf(" range=0..%d", range);
+		}
+		if (basecnt != 0)
+			for (i = 0; i < basecnt; i++)
+				printf(" base[%d]='%s'", i, base[i]);
+		if (gotnumreq != 0)
+			printf(" num-request=%d,%d", numreq[0], numreq[1]);
+		if (period != 0)
+			printf(" test-period=%d", period);
+		printf(" drop-time=%g,%g", losttime[0], losttime[1]);
+		if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
+			printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
+		if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
+			printf(" max-drop=%2.2f%%,%2.2f%%",
+			       maxpdrop[0], maxpdrop[1]);
+		if (preload != 0)
+			printf(" preload=%d", preload);
+		printf(" aggressivity=%d", aggressivity);
+		if (localport != 0)
+			printf(" local-port=%d", localport);
+		if (seeded)
+			printf(" seed=%u", seed);
+		if (isbroadcast != 0)
+			printf(" broadcast");
+		if (rapidcommit != 0)
+			printf(" rapid-commit");
+		if (usefirst != 0)
+			printf(" use-first");
+		if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
+			printf(" template-file='%s'", templatefile[0]);
+		else if (templatefile[1] != NULL)
+			printf(" template-file='%s','%s'",
+			       templatefile[0], templatefile[1]);
+		if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
+			printf(" xid-offset=%d", xidoffset[0]);
+		else if (xidoffset[1] >= 0)
+			printf(" xid-offset=%d,%d",
+			       xidoffset[0], xidoffset[1]);
+		if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
+			printf(" xid-offset=%d", rndoffset[0]);
+		else if (rndoffset[1] >= 0)
+			printf(" xid-offset=%d,%d",
+			       rndoffset[0], rndoffset[1]);
+		if (elpoffset >= 0)
+			printf(" time-offset=%d", elpoffset);
+		if (sidoffset >= 0)
+			printf(" srvid-offset=%d", sidoffset);
+		if (ripoffset >= 0)
+			printf(" ip-offset=%d", ripoffset);
+		printf(" diagnotic-selectors='%s'", diags);
+		if (wrapped != NULL)
+			printf(" wrapped='%s'", wrapped);
+		printf("\n");
+	}
+
+	/* check DHCPv4 only options */
+	if ((ipversion != 4) && (isbroadcast != 0)) {
+		fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
+		usage();
+		exit(2);
+	}
+
+	/* check DHCPv6 only options */
+	if ((ipversion != 6) && (rapidcommit != 0)) {
+		fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
+		usage();
+		exit(2);
+	}
+
+	/* check 4-packet (aka not simple) mode options */
+	if ((simple != 0) && (numreq[1] != 0)) {
+		fprintf(stderr,
+			"second -n<num-request> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (losttime[1] != 1.)) {
+		fprintf(stderr,
+			"second -d<drop-time> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) &&
+	    ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
+		fprintf(stderr,
+			"second -D<max-drop> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (usefirst != 0)) {
+		fprintf(stderr,
+			"-1 is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (templatefile[1] != NULL)) {
+		fprintf(stderr,
+			"second -T<template-file> is not "
+			"compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (xidoffset[1] >= 0)) {
+		fprintf(stderr,
+			"second -X<xid-offset> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (rndoffset[1] >= 0)) {
+		fprintf(stderr,
+			"second -O<random-offset is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (elpoffset >= 0)) {
+		fprintf(stderr,
+			"-E<time-offset> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (sidoffset >= 0)) {
+		fprintf(stderr,
+			"-S<srvid-offset> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+	if ((simple != 0) && (ripoffset >= 0)) {
+		fprintf(stderr,
+			"-I<ip-offset> is not compatible with -i\n");
+		usage();
+		exit(2);
+	}
+
+
+	/* check simple mode options */
+	if ((simple == 0) && (rapidcommit != 0)) {
+		fprintf(stderr, "-i must be set to use -c\n");
+		usage();
+		exit(2);
+	}
+
+	/* check rate '-r' options */
+	if ((rate == 0) && (report != 0)) {
+		fprintf(stderr,
+			"-r<rate> must be set to use -t<report>\n");
+		usage();
+		exit(2);
+	}
+	if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
+		fprintf(stderr,
+			"-r<rate> must be set to use -n<num-request>\n");
+		usage();
+		exit(2);
+	}
+	if ((rate == 0) && (period != 0)) {
+		fprintf(stderr,
+			"-r<rate> must be set to use -p<test-period>\n");
+		usage();
+		exit(2);
+	}
+	if ((rate == 0) &&
+	    ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
+	     (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
+		fprintf(stderr,
+			"-r<rate> must be set to use -D<max-drop>\n");
+		usage();
+		exit(2);
+	}
+
+	/* check (first) template file options */
+	if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
+		fprintf(stderr,
+			"-T<template-file> must be set to "
+			"use -X<xid-offset>\n");
+		usage();
+		exit(2);
+	}
+	if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
+		fprintf(stderr,
+			"-T<template-file> must be set to "
+			"use -O<random-offset>\n");
+		usage();
+		exit(2);
+	}
+
+	/* check (second) template file options */
+	if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
+		fprintf(stderr,
+			"second/request -T<template-file> must be set to "
+			"use -E<time-offset>\n");
+		usage();
+		exit(2);
+	}
+	if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
+		fprintf(stderr,
+			"second/request -T<template-file> must be set to "
+			"use -S<srvid-offset>\n");
+		usage();
+		exit(2);
+	}
+	if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
+		fprintf(stderr,
+			"second/request -T<template-file> must be set to "
+			"use -I<ip-offset>\n");
+		usage();
+		exit(2);
+	}
+
+	/* check various template file(s) and other condition(s) options */
+	if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
+		fprintf(stderr,
+			"-O<random-offset> must be set when "
+			"-T<template-file> and -R<range> are used\n");
+		usage();
+		exit(2);
+	}
+	if ((templatefile[1] != NULL) && (sidoffset < 0)) {
+		fprintf(stderr,
+			"-S<srvid-offset> must be set when second "
+			"-T<template-file> is used\n");
+		usage();
+		exit(2);
+	}
+	if ((templatefile[1] != NULL) && (ripoffset < 0)) {
+		fprintf(stderr,
+			"-I<ip-offset> must be set when second "
+			"-T<template-file> is used\n");
+		usage();
+		exit(2);
+	}
+
+	/* get the server argument */
+	if (optind < argc - 1) {
+		fprintf(stderr, "extra arguments?\n");
+		usage();
+		exit(2);
+	}
+	if (optind == argc - 1) {
+		servername = argv[optind];
+		/* decode special cases */
+		if ((ipversion == 4) &&
+		    (strcmp(servername, "all") == 0)) {
+			flags = AI_NUMERICHOST;
+			isbroadcast = 1;
+			servername = "255.255.255.255";
+		} else if ((ipversion == 6) &&
+			   (strcmp(servername, "all") == 0)) {
+			flags = AI_NUMERICHOST;
+			servername = "FF02::1:2";
+		} else if ((ipversion == 6) &&
+			   (strcmp(servername, "servers") == 0)) {
+			flags = AI_NUMERICHOST;
+			servername = "FF05::1:3";
+		}
+	}
+
+	/* handle the local '-l' address/interface */
+	if (localname != NULL) {
+		/* given */
+		getlocaladdr();
+		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
+			if (isinterface)
+				printf("interface='%s'\n", localname);
+			else
+				printf("local-addr='%s'\n", localname);
+		}
+		/* get the not given server from it */
+		if (servername == NULL) {
+			if (isinterface && (ipversion == 4)) {
+				flags = AI_NUMERICHOST;
+				isbroadcast = 1;
+				servername = "255.255.255.255";
+			} else if (isinterface && (ipversion == 6)) {
+				flags = AI_NUMERICHOST;
+				servername = "FF02::1:2";
+			} else {
+				fprintf(stderr,
+					"without an interface "
+					"server is required\n");
+				usage();
+				exit(2);
+			}
+		}
+	} else if (servername == NULL) {
+		fprintf(stderr, "without an interface server is required\n");
+		usage();
+		exit(2);
+	}
+	/* get the server socket address */
+	getserveraddr(flags);
+	/* finish local/server socket address stuff and print it */
+	if ((diags != NULL) && (strchr(diags, 'a') != NULL))
+		printf("server='%s'\n", servername);
+	if (localname == NULL)
+		getlocal();
+	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
+		char addr[NI_MAXHOST];
+
+		ret = getnameinfo((struct sockaddr *) &localaddr,
+				  sizeof(localaddr),
+				  addr,
+				  NI_MAXHOST,
+				  NULL,
+				  0,
+				  NI_NUMERICHOST);
+		if (ret != 0) {
+			fprintf(stderr,
+				"can't get the local address: %s\n",
+				gai_strerror(ret));
+			exit(1);
+		}
+		printf("local address='%s'\n", addr);
+	}
+
+	/* initialize exchange structures */
+	inits();
+
+	/* get the socket descriptor and template(s) */
+	if (ipversion == 4) {
+		getsock4();
+		if (templatefile[0] == NULL)
+			build_template_discover4();
+		else
+			get_template_discover4();
+		if (simple == 0) {
+			if (templatefile[1] == NULL)
+				build_template_request4();
+			else
+				get_template_request4();
+		}
+	} else {
+		getsock6();
+		if (duid_prefix != NULL) {
+			if (templatefile[0] == NULL)
+				build_template_solicit6();
+			else
+				get_template_solicit6();
+			if (simple == 0) {
+				if (templatefile[1] == NULL)
+					build_template_request6();
+				else
+					get_template_request6();
+			}
+		}
+	}
+	/* sanity check */
+	if ((unsigned) sock > FD_SETSIZE) {
+		fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
+		exit(1);
+	}
+	/* make the socket descriptor not blocking */
+	flags = fcntl(sock, F_GETFL, 0);
+	if (flags < 0) {
+		perror("fcntl(F_GETFL)");
+		exit(1);
+	}
+	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
+		perror("fcntl(F_SETFL)");
+		exit(1);
+	}
+
+	/* wrapped start */
+	if (wrapped != NULL) {
+		pid_t pid;
+
+		(void) signal(SIGCHLD, reapchild);
+		pid = fork();
+		if (pid < 0) {
+			perror("fork");
+			exit(1);
+		} else if (pid == 0)
+			(void) execlp(wrapped, "start", (char *) NULL);
+	}
+
+	/* boot is done! */
+	if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
+		perror("clock_gettime(boot)");
+		exit(1);
+	}
+
+	/* compute the next intermediate reporting date */
+	if (report != 0) {
+		dreport.tv_sec = boot.tv_sec + report;
+		dreport.tv_nsec = boot.tv_nsec;
+	}
+
+	/* compute the DUID (the current date is needed) */
+	if ((ipversion == 6) && (duid_prefix == NULL)) {
+		uint32_t curdate;
+
+		duid_length = 14;
+		duid_prefix = (uint8_t *) malloc(duid_length);
+		if (duid_prefix == NULL) {
+			perror("malloc(duid)");
+			exit(1);
+		}
+		duid_prefix[0] = DHCP6_DUID_LLT >> 8;
+		duid_prefix[1] = DHCP6_DUID_LLT;
+		duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
+		duid_prefix[3] = DHCP_HTYPE_ETHER;
+		curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
+		memcpy(duid_prefix + 4, &curdate, 4);
+		memcpy(duid_prefix + 8, mac_prefix, 6);
+		/* the DUID is in template(s) */
+		if (templatefile[0] == NULL)
+			build_template_solicit6();
+		else
+			get_template_solicit6();
+		if (simple == 0) {
+			if (templatefile[1] == NULL)
+				build_template_request6();
+			else
+				get_template_request6();
+		}
+	}
+		
+	/* seed the random generator */
+	if (seeded == 0)
+		seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
+	srandom(seed);
+
+	/* preload the server with at least one packet */
+	compsend = preload + 1;
+	for (i = 0; i <= preload; i++) {
+		if (ipversion == 4)
+			ret = send4();
+		else
+			ret = send6();
+		if (ret < 0) {
+			/* failure at the first packet is fatal */
+			if (i == 0) {
+				fprintf(stderr,
+					"initial send failed: %s\n",
+					strerror(-ret));
+				exit(1);
+			}
+			if ((errno == EAGAIN) ||
+			    (errno == EWOULDBLOCK) ||
+			    (errno == ENOBUFS) ||
+			    (errno == ENOMEM))
+				locallimit++;
+			fprintf(stderr, "preload send: %s\n", strerror(-ret));
+			break;
+		}
+	}
+
+	/* required only before the interrupted flag check */
+	(void) signal(SIGINT, interrupt);
+
+	/* main loop */
+	for (;;) {
+		struct timespec now, ts;
+		fd_set rfds;
+
+		/* immediate loop exit conditions */
+		if (interrupted) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("interrupted\n");
+			break;
+		}
+		if (fatal) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("got a fatal error\n");
+			break;
+		}
+
+		/* get the date and use it */
+		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
+			perror("clock_gettime(now)");
+			fatal = 1;
+			continue;
+		}
+		if ((period != 0) &&
+		    ((boot.tv_sec + period < now.tv_sec) ||
+		     ((boot.tv_sec + period == now.tv_sec) &&
+		      (boot.tv_nsec < now.tv_nsec)))) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached test-period\n");
+			break;
+		}
+		if ((report != 0) &&
+		    ((dreport.tv_sec < now.tv_sec) ||
+		     ((dreport.tv_sec == now.tv_sec) &&
+		      (dreport.tv_nsec < now.tv_nsec))))
+			reporting();
+
+		/* compute the delay for the next send */
+		due = last;
+		if (rate == 1)
+			due.tv_sec += 1;
+		else if (rate != 0)
+			due.tv_nsec += 1010000000 / rate;
+		else
+			due.tv_nsec += 1;
+		while (due.tv_nsec >= 1000000000) {
+			due.tv_sec += 1;
+			due.tv_nsec -= 1000000000;
+		}
+		ts = due;
+		ts.tv_sec -= now.tv_sec;
+		ts.tv_nsec -= now.tv_nsec;
+		while (ts.tv_nsec < 0) {
+			ts.tv_sec -= 1;
+			ts.tv_nsec += 1000000000;
+		}
+		/* the send was already due? */
+		if (ts.tv_sec < 0) {
+			ts.tv_sec = ts.tv_nsec = 0;
+			latesent++;
+		}
+
+		/* pselect() */
+		FD_ZERO(&rfds);
+		FD_SET(sock, &rfds);
+		ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("pselect");
+			fatal = 1;
+			continue;
+		}
+
+		/* packet(s) to receive */
+		while (ret == 1) {
+			if (ipversion == 4)
+				receive4();
+			else
+				receive6();
+			if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
+				ret = 0;
+			else
+				multrcvd++;
+		}
+		if (fatal)
+			continue;
+
+		/* check receive loop exit conditions */
+		if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached num-request0\n");
+			break;
+		}
+		if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached num-request2\n");
+			break;
+		}
+		if ((maxdrop[0] != 0) &&
+		    ((int) (xscount0 - xrcount0) > maxdrop[0])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached max-drop%s (absolute)\n",
+				       simple != 0 ? "" : "0");
+			break;
+		}
+		if ((maxdrop[1] != 0) &&
+		    ((int) (xscount2 - xrcount2) > maxdrop[1])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached max-drop2 (absolute)\n");
+			break;
+		}
+		if ((maxpdrop[0] != 0.) &&
+		    (xscount0 > 10) &&
+		    (((100. * (xscount0 - xrcount0)) / xscount0)
+			> maxpdrop[0])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached max-drop%s (percent)\n",
+				       simple != 0 ? "" : "0");
+			break;
+		}
+		if ((maxpdrop[1] != 0.) &&
+		    (xscount2 > 10) &&
+		    (((100. * (xscount2 - xrcount2)) / xscount2)
+			> maxpdrop[1])) {
+			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
+				printf("reached max-drop2 (percent)\n");
+			break;
+		}
+
+		/* compute how many packets to send */
+		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
+			perror("clock_gettime(now2)");
+			fatal = 1;
+			continue;
+		}
+		if ((now.tv_sec > due.tv_sec) ||
+		    ((now.tv_sec == due.tv_sec) &&
+		     (now.tv_nsec >= due.tv_nsec))) {
+			double tosend;
+
+			if (rate != 0) {
+				tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
+				tosend += now.tv_sec - due.tv_sec;
+				tosend *= rate;
+				tosend += 1;
+				if (tosend > (double) aggressivity)
+					i = aggressivity;
+				else
+					i = (int) tosend;
+			} else
+				i = aggressivity;
+			compsend += i;
+			/* send packets */
+			for (;;) {
+				if (ipversion == 4)
+					ret = send4();
+				else
+					ret = send6();
+				if (ret < 0) {
+					if ((errno == EAGAIN) ||
+					    (errno == EWOULDBLOCK) ||
+					    (errno == ENOBUFS) ||
+					    (errno == ENOMEM))
+						locallimit++;
+					fprintf(stderr,
+						"send: %s\n", strerror(-ret));
+					break;
+				}
+				i--;
+				if (i == 0)
+					break;
+				/* check for late packets to receive */
+				if (recv(sock, ibuf, sizeof(ibuf),
+					 MSG_PEEK) > 0) {
+					latercvd++;
+					if (ipversion == 4)
+						receive4();
+					else
+						receive6();
+				}
+			}
+		} else
+			/* there was no packet to send */
+			shortwait++;
+	}
+
+	/* after main loop: finished */
+	if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
+		perror("clock_gettime(finished)");
+
+	/* wrapped stop */
+	if (wrapped != NULL) {
+		pid_t pid;
+
+		pid = fork();
+		if (pid == 0)
+			(void) execlp(wrapped, "stop", (char *) NULL);
+	}
+
+	/* main statictics */
+	if (xscount2 == 0)
+		printf("sent: %llu, received: %llu (drops: %lld)\n",
+		       (unsigned long long) xscount0,
+		       (unsigned long long) xrcount0,
+		       (long long) (xscount0 - xrcount0));
+	else
+		printf("sent: %llu/%llu, received: %llu/%llu "
+		       "(drops: %lld/%lld)\n",
+		       (unsigned long long) xscount0,
+		       (unsigned long long) xscount2,
+		       (unsigned long long) xrcount0,
+		       (unsigned long long) xrcount2,
+		       (long long) (xscount0 - xrcount0),
+		       (long long) (xscount2 - xrcount2));
+	printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
+	       (unsigned long long) tooshort,
+	       (unsigned long long) orphans,
+	       (unsigned long long) locallimit);
+
+	/* print the rate */
+	if (finished.tv_sec != 0) {
+		double dall, erate;
+
+		dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
+		dall += finished.tv_sec - boot.tv_sec;
+		erate = xrcount0 / dall;
+		if (rate != 0)
+			printf("rate: %f (expected %d)\n", erate, rate);
+		else
+			printf("rate: %f\n", erate);
+	}
+
+	/* rate processing instrumentation */
+	if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
+		printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
+		       "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
+		       (unsigned long long) latesent,
+		       (unsigned long long) compsend,
+		       (unsigned long long) shortwait,
+		       (unsigned long long) multrcvd,
+		       (unsigned long long) latercvd,
+		       (unsigned long long) collected[0],
+		       (unsigned long long) collected[1]);
+	}
+
+	/* round-time trip statistics */
+	if (xrcount2 != 0) {
+		double avg0, avg2, stddev0, stddev2;
+		
+		avg0 = dsum0 / xrcount0;
+		avg2 = dsum2 / xrcount2;
+		stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
+		stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
+		printf("RTT0: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
+		       dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
+		printf("RTT2: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
+		       dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
+	} else if (xrcount0 != 0) {
+		double avg, stddev;
+		
+		avg = dsum0 / xrcount0;
+		stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
+		printf("RTT%s: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
+		       simple != 0 ? "" : "0",
+		       dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
+	}
+
+	/* (first) server-ID option content */
+	if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
+	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
+		struct exchange *x;
+		size_t n;
+
+		printf("server-id: ");
+		x = ISC_TAILQ_FIRST(&xrcvd0);
+		if (ipversion == 4)
+			n = 2;
+		else
+			n = 4;
+		for (; n < x->sidlen; n++)
+			printf("%02hhx", x->sid[n]);
+		printf("\n");
+	}
+
+	/* all time-stamps */
+	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
+	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
+		struct exchange *x;
+
+		printf("\n\n");
+		ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
+			printf("%ld.%09ld %ld.%09ld\n",
+			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
+			       (long) x->ts1.tv_sec, x->ts1.tv_nsec);
+
+	}
+	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
+	    !ISC_TAILQ_EMPTY(&xrcvd2)) {
+		struct exchange *x;
+
+		printf("--\n");
+		ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
+			printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
+			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
+			       (long) x->ts1.tv_sec, x->ts1.tv_nsec,
+			       (long) x->ts2.tv_sec, x->ts2.tv_nsec,
+			       (long) x->ts3.tv_sec, x->ts3.tv_nsec);
+
+	}
+	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
+	    !ISC_TAILQ_EMPTY(&xrcvd0))
+		printf("\n\n");
+
+	/* template(s) */
+	if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
+		size_t n;
+
+		if (ipversion == 4) {
+			printf("length = %zu\n", length_discover4);
+			printf("xid offset = %d\n", DHCP_OFF_XID);
+			printf("xid length = 4\n");
+			printf("random offset = %zu\n", random_discover4);
+			printf("content:\n");
+			for (n = 0; n < length_discover4; n++) {
+				printf("%s%02hhx",
+				       (n & 15) == 0 ? "" : " ",
+				       template_discover4[n]);
+				if ((n & 15) == 15)
+					printf("\n");
+			}
+			if ((n & 15) != 15)
+				printf("\n");
+			if (simple != 0)
+				goto doneT;
+			printf("--\n");
+			printf("length = %zu\n", length_request4);
+			printf("xid offset = %d\n", DHCP_OFF_XID);
+			printf("xid length = 4\n");
+			printf("random offset = %zu\n", random_request4);
+			if (elapsed_request4 > 0)
+				printf("secs offset = %zu\n",
+				       elapsed_request4);
+			printf("server-id offset = %zu\n", serverid_request4);
+			printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
+			printf("content:\n");
+			printf("requested-ip-address offset = %zu\n",
+			       reqaddr_request4);
+			printf("requested-ip-address length = %d\n", 4);
+			for (n = 0; n < length_request4; n++) {
+				printf("%s%02hhx",
+				       (n & 15) == 0 ? "" : " ",
+				       template_request4[n]);
+				if ((n & 15) == 15)
+					printf("\n");
+			}
+			printf("\n");
+		} else {
+			printf("length = %zu\n", length_solicit6);
+			printf("xid offset = %d\n", DHCP6_OFF_XID);
+			printf("xid length = 3\n");
+			printf("random offset = %zu\n", random_solicit6);
+			for (n = 0; n < length_solicit6; n++) {
+				printf("%s%02hhx",
+				       (n & 15) == 0 ? "" : " ",
+				       template_solicit6[n]);
+				if ((n & 15) == 15)
+					printf("\n");
+			}
+			if ((n & 15) != 15)
+				printf("\n");
+			if (simple != 0)
+				goto doneT;
+			printf("--\n");
+			printf("length = %zu\n", length_request6);
+			printf("xid offset = %d\n", DHCP_OFF_XID);
+			printf("xid length = 4\n");
+			printf("random offset = %zu\n", random_request6);
+			if (elapsed_request6 > 0)
+				printf("secs offset = %zu\n",
+				       elapsed_request6);
+			printf("server-id offset = %zu\n", serverid_request6);
+			printf("content:\n");
+			printf("requested-ip-address offset = %zu\n",
+			       reqaddr_request6);
+			for (n = 0; n < length_request6; n++) {
+				printf("%s%02hhx",
+				       (n & 15) == 0 ? "" : " ",
+				       template_request6[n]);
+				if ((n & 15) == 15)
+					printf("\n");
+			}
+			printf("\n");
+		}
+	}
+    doneT:
+
+	/* compute the exit code (and exit) */
+	if (fatal)
+		exit(1);
+	else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
+		exit(0);
+	else
+		exit(3);
+}
+
+#endif  /* HAVE_GETIFADDRS */
diff --git a/tools/reorder_message_file.py b/tools/reorder_message_file.py
index 31f4941..2ba4d7c 100644
--- a/tools/reorder_message_file.py
+++ b/tools/reorder_message_file.py
@@ -191,6 +191,6 @@ if __name__ == "__main__":
 
     # Read the files and load the data
     if len(sys.argv) != 2:
-        print "Usage: python reorder.py message_file"
+        print("Usage: python reorder.py message_file")
     else:
         process_file(sys.argv[1])




More information about the bind10-changes mailing list