Commit 6887a4a2 authored by Marek Vavrusa's avatar Marek Vavrusa

modules: deprecated tinyweb, superseded by http

this module is superseded by http module, removing
parent 55520347
...@@ -55,4 +55,3 @@ kresd.amalg.c ...@@ -55,4 +55,3 @@ kresd.amalg.c
libkres.amalg.c libkres.amalg.c
/doc/kresd.8 /doc/kresd.8
/libkres.pc /libkres.pc
/modules/tinyweb/tinyweb.h
...@@ -28,7 +28,6 @@ $(eval $(call find_lib,hiredis)) ...@@ -28,7 +28,6 @@ $(eval $(call find_lib,hiredis))
$(eval $(call find_lib,socket_wrapper)) $(eval $(call find_lib,socket_wrapper))
$(eval $(call find_lib,libdnssec)) $(eval $(call find_lib,libdnssec))
$(eval $(call find_lib,libsystemd)) $(eval $(call find_lib,libsystemd))
$(eval $(call find_gopkg,geoip,github.com/abh/geoip))
# Find Go version and platform # Find Go version and platform
GO_VERSION := $(shell $(GO) version 2>/dev/null) GO_VERSION := $(shell $(GO) version 2>/dev/null)
...@@ -94,7 +93,6 @@ info: ...@@ -94,7 +93,6 @@ info:
$(info --------) $(info --------)
$(info [$(HAS_doxygen)] doxygen (doc)) $(info [$(HAS_doxygen)] doxygen (doc))
$(info [$(HAS_go)] go (modules/go, Go buildmode=c-shared support)) $(info [$(HAS_go)] go (modules/go, Go buildmode=c-shared support))
$(info [$(HAS_geoip)] geoip (modules/tinyweb, github.com/abh/geoip))
$(info [$(HAS_libmemcached)] libmemcached (modules/memcached)) $(info [$(HAS_libmemcached)] libmemcached (modules/memcached))
$(info [$(HAS_hiredis)] hiredis (modules/redis)) $(info [$(HAS_hiredis)] hiredis (modules/redis))
$(info [$(HAS_cmocka)] cmocka (tests/unit)) $(info [$(HAS_cmocka)] cmocka (tests/unit))
......
...@@ -49,7 +49,6 @@ There are also *optional* packages that enable specific functionality in Knot DN ...@@ -49,7 +49,6 @@ There are also *optional* packages that enable specific functionality in Knot DN
"luasec_", "``trust anchors``", "TLS for Lua." "luasec_", "``trust anchors``", "TLS for Lua."
"libmemcached_", "``modules/memcached``", "To build memcached backend module." "libmemcached_", "``modules/memcached``", "To build memcached backend module."
"hiredis_", "``modules/redis``", "To build redis backend module." "hiredis_", "``modules/redis``", "To build redis backend module."
"geoip_", "``modules/tinyweb``", "To build a proof-of-concept web interface (needs Go as well)."
"Go_ 1.5+", "``modules``", "Build modules written in Go." "Go_ 1.5+", "``modules``", "Build modules written in Go."
"cmocka_", "``unit tests``", "Unit testing framework." "cmocka_", "``unit tests``", "Unit testing framework."
"Doxygen_", "``documentation``", "Generating API documentation." "Doxygen_", "``documentation``", "Generating API documentation."
......
...@@ -19,6 +19,5 @@ Knot DNS Resolver modules ...@@ -19,6 +19,5 @@ Knot DNS Resolver modules
.. include:: ../modules/kmemcached/README.rst .. include:: ../modules/kmemcached/README.rst
.. include:: ../modules/redis/README.rst .. include:: ../modules/redis/README.rst
.. include:: ../modules/ketcd/README.rst .. include:: ../modules/ketcd/README.rst
.. include:: ../modules/tinyweb/README.rst
.. include:: ../modules/dns64/README.rst .. include:: ../modules/dns64/README.rst
.. include:: ../modules/renumber/README.rst .. include:: ../modules/renumber/README.rst
...@@ -24,13 +24,6 @@ modules_TARGETS += ketcd \ ...@@ -24,13 +24,6 @@ modules_TARGETS += ketcd \
daf daf
endif endif
# List of Golang modules
ifeq ($(HAS_go),yes)
ifeq ($(HAS_geoip),yes)
modules_TARGETS += tinyweb
endif
endif
# Make C module # Make C module
define make_c_module define make_c_module
$(1)-install: $(DESTDIR)$(MODULEDIR) $(1)-install: $(DESTDIR)$(MODULEDIR)
......
.. _mod-tinyweb:
Web interface
-------------
This module provides an embedded web interface for resolver. It plots current performance in real-time,
including a feed of recent iterative queries. It also includes bindings_ to `MaxMind GeoIP`_, and presents a world map coloured by frequency of queries, so you can see where do your queries go.
The *stats* module is required for plotting query rate.
By default, it listens on ``localhost:8053``.
.. warning:: This is a proof of concept module for embedding Go, which has several drawbacks - it runs in separate threads, is relatively heavy-weight due to the nature of Go, and is opaque for other modules. Look at :ref:`http module <mod-http>` if you want to expose services over HTTP from other modules.
Examples
^^^^^^^^
.. code-block:: lua
-- Load web interface
modules = { 'tinyweb' }
-- Listen on specific address/port
modules = {
tinyweb = {
addr = 'localhost:8080', -- Custom address
geoip = '/usr/local/var/GeoIP' -- Different path to GeoIP DB
}
}
Dependencies
^^^^^^^^^^^^
It depends on Go 1.5+, `github.com/abh/geoip <bindings>`_ package.
.. code-block:: bash
$ <install> libgeoip
$ go get github.com/abh/geoip
.. _`MaxMind GeoIP`: https://www.maxmind.com/en/home
.. _bindings: https://github.com/abh/geoip
package main
/*
#include "lib/layer.h"
#include "lib/module.h"
#include "lib/utils.h"
#include <libknot/descriptor.h>
int consume(knot_layer_t *, knot_pkt_t *);
static inline const char *module_path(void)
{ return MODULEDIR; }
static inline const knot_layer_api_t *_layer(void)
{ static const knot_layer_api_t api = { .consume = &consume, }; return &api; }
*/
import "C"
import (
"os"
"runtime"
"unsafe"
"fmt"
"net"
"net/http"
"html"
"html/template"
"encoding/json"
"github.com/abh/geoip"
)
type QueryInfo struct {
Qname string
Qtype string
Addr string
Secure bool
Country string
}
// Global context
var resolver *C.struct_kr_context
// FIFO of last-seen metrics
var fifo_metrics [10] QueryInfo
var fifo_metrics_i = 0
// Geo frequency table
var geo_freq map[string] int
var geo_db *geoip.GeoIP
var geo_db6 *geoip.GeoIP
/*
* Callbacks for serving static content.
*/
func resource_path(filename string) string {
return C.GoString(C.module_path()) + "/tinyweb" + filename;
}
func serve_page(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles(resource_path("/tinyweb.tpl"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
host, err := os.Hostname()
t.Execute(w, struct {
Title string
}{
Title: "kresd @ " + host,
})
w.Header().Set("Content-Type", "text/html; charset=utf-8")
}
func serve_file(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, resource_path(html.EscapeString(r.URL.Path)))
}
/*
* Serving dynamic contents.
*/
func serve_json(w http.ResponseWriter, r *http.Request, v interface{}) {
js, err := json.Marshal(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
}
func serve_geo(w http.ResponseWriter, r *http.Request) {
serve_json(w, r, geo_freq)
}
func serve_feed(w http.ResponseWriter, r *http.Request) {
// Walk back FIFO to preserve ordering
const nsamples = len(fifo_metrics)
var samples [nsamples] QueryInfo
for i := 0; i < nsamples; i++ {
samples[i] = fifo_metrics[(nsamples + (fifo_metrics_i - i - 1)) % nsamples]
}
serve_json(w, r, samples)
}
func serve_stats(w http.ResponseWriter, r *http.Request) {
mod_name := C.CString("stats")
defer C.free(unsafe.Pointer(mod_name))
prop_name := C.CString("list")
defer C.free(unsafe.Pointer(prop_name))
out := C.kr_module_call(resolver, mod_name, prop_name, nil)
defer C.free(unsafe.Pointer(out))
if out != nil {
fmt.Fprintf(w, C.GoString(out))
} else {
http.Error(w, "No stats module", http.StatusInternalServerError)
}
}
/*
* Module implementation.
*/
func process_sample(qname string, qtype int, addr net.IP, secure bool) {
var qtype_str [16] byte
C.knot_rrtype_to_string(C.uint16_t(qtype), (*C.char)(unsafe.Pointer(&qtype_str[0])), C.size_t(16))
// Sample NS country code
var cc string
switch len(addr) {
case 4: if (geo_db != nil) { cc, _ = geo_db.GetCountry(addr.String()) }
case 16: if (geo_db6 != nil) { cc, _ = geo_db6.GetCountry_v6(addr.String()) }
default: return
}
// Count occurences
if freq, exists := geo_freq[cc]; exists {
geo_freq[cc] = freq + 1
} else {
geo_freq[cc] = 1
}
fifo_metrics[fifo_metrics_i] = QueryInfo{qname, string(qtype_str[:]), addr.String(), secure, cc}
fifo_metrics_i = (fifo_metrics_i + 1) % len(fifo_metrics)
}
//export tinyweb_init
func tinyweb_init(module *C.struct_kr_module) int {
resolver = (*C.struct_kr_context)(module.data)
geo_freq = make(map[string]int)
return 0
}
//export tinyweb_config
func tinyweb_config(module *C.struct_kr_module, conf *C.char) int {
var err error
var config map[string] interface{}
addr := "localhost:8053"
if err = json.Unmarshal([]byte(C.GoString(conf)), &config); err != nil {
fmt.Printf("[tinyweb] %s\n", err)
} else {
if v, ok := config["addr"]; ok {
addr = v.(string)
}
if v, ok := config["geoip"]; ok {
geoip.SetCustomDirectory(v.(string))
}
}
geo_db, err = geoip.OpenTypeFlag(geoip.GEOIP_COUNTRY_EDITION, geoip.GEOIP_MEMORY_CACHE)
if err != nil {
fmt.Printf("[tinyweb] couldn't open GeoIP IPv4 Country Edition\n");
}
geo_db6, err = geoip.OpenTypeFlag(geoip.GEOIP_COUNTRY_EDITION_V6, geoip.GEOIP_MEMORY_CACHE)
if err != nil {
fmt.Printf("[tinyweb] couldn't open GeoIP IPv6 Country Edition\n");
}
// Start web interface
http.HandleFunc("/feed", serve_feed)
http.HandleFunc("/stats", serve_stats)
http.HandleFunc("/geo", serve_geo)
http.HandleFunc("/tinyweb.js", serve_file)
http.HandleFunc("/datamaps.world.min.js", serve_file)
http.HandleFunc("/topojson.js", serve_file)
http.HandleFunc("/jquery.js", serve_file)
http.HandleFunc("/epoch.css", serve_file)
http.HandleFunc("/favicon.ico", serve_file)
http.HandleFunc("/epoch.js", serve_file)
http.HandleFunc("/d3.js", serve_file)
http.HandleFunc("/", serve_page)
// @todo Not sure how to cancel this routine yet
fmt.Printf("[tinyweb] listening on %s\n", addr)
go http.ListenAndServe(addr, nil)
return 0
}
//export tinyweb_deinit
func tinyweb_deinit(module *C.struct_kr_module) int {
geo_db = nil
geo_db6 = nil
runtime.GC()
return 0
}
//export consume
func consume(ctx *C.knot_layer_t, pkt *C.knot_pkt_t) C.int {
req := (*C.struct_kr_request)(ctx.data)
qry := req.current_query
state := (C.int)(ctx.state)
if qry.flags & C.QUERY_CACHED != 0 {
return state
}
// Parse answer source address
sa := (*C.struct_sockaddr)(unsafe.Pointer(&qry.ns.addr[0]))
var ip net.IP
if sa.sa_family == C.AF_INET {
sa_v4 := (*C.struct_sockaddr_in)(unsafe.Pointer(sa))
ip = net.IP(C.GoBytes(unsafe.Pointer(&sa_v4.sin_addr), 4))
} else if sa.sa_family == C.AF_INET6 {
sa_v6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(sa))
ip = net.IP(C.GoBytes(unsafe.Pointer(&sa_v6.sin6_addr), 16))
}
// Parse metadata
qname := C.knot_dname_to_str_alloc(C.knot_pkt_qname(pkt))
defer C.free(unsafe.Pointer(qname))
qtype := C.knot_pkt_qtype(pkt)
secure := (bool)(C.knot_pkt_has_dnssec(pkt))
// Sample metric
process_sample(C.GoString(qname), int(qtype), ip, secure)
return state
}
//export tinyweb_layer
func tinyweb_layer(module *C.struct_kr_module) *C.knot_layer_api_t {
return C._layer()
}
//export tinyweb_api
func tinyweb_api() C.uint32_t {
return C.KR_MODULE_API
}
func main() {}
tinyweb_SOURCES := modules/tinyweb/tinyweb.go
tinyweb_INSTALL := $(wildcard modules/tinyweb/tinyweb/*)
tinyweb_DEPEND := $(libkres)
tinyweb_LIBS := $(libkres_TARGET) $(libkres_LIBS)
$(call make_go_module,tinyweb)
jQuery is provided under MIT license <https://jquery.org/license/>
D3 under BSD license <https://github.com/mbostock/d3/blob/master/LICENSE>
Epoch under MIT license <https://github.com/epochjs/epoch/blob/master/LICENSE>
TopoJSON under BSD license <https://github.com/mbostock/topojson/blob/master/LICENSE>
DataMaps under MIT license <https://github.com/markmarkoh/datamaps/blob/master/LICENSE>
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Country code conversion
var iso2_to_iso3 = {
"AF": "AFG", "AL": "ALB", "DZ": "DZA", "AS": "ASM", "AD": "AND", "AO": "AGO", "AI": "AIA", "AQ": "ATA", "AG": "ATG", "AR": "ARG", "AM": "ARM", "AW": "ABW", "AU": "AUS", "AT": "AUT", "AZ": "AZE", "BS": "BHS", "BH": "BHR", "BD": "BGD", "BB": "BRB", "BY": "BLR", "BE": "BEL", "BZ": "BLZ", "BJ": "BEN", "BM": "BMU", "BT": "BTN", "BO": "BOL", "BA": "BIH", "BW": "BWA", "BV": "BVT", "BR": "BRA", "IO": "IOT", "VG": "VGB", "BN": "BRN", "BG": "BGR", "BF": "BFA", "BI": "BDI", "KH": "KHM", "CM": "CMR", "CA": "CAN", "CV": "CPV", "KY": "CYM", "CF": "CAF", "TD": "TCD", "CL": "CHL", "CN": "CHN", "CX": "CXR", "CC": "CCK", "CO": "COL", "KM": "COM", "CD": "COD", "CG": "COG", "CK": "COK", "CR": "CRI", "CI": "CIV", "CU": "CUB", "CY": "CYP", "CZ": "CZE", "DK": "DNK", "DJ": "DJI", "DM": "DMA", "DO": "DOM", "EC": "ECU", "EG": "EGY", "SV": "SLV", "GQ": "GNQ", "ER": "ERI", "EE": "EST", "ET": "ETH", "FO": "FRO", "FK": "FLK", "FJ": "FJI", "FI": "FIN", "FR": "FRA", "GF": "GUF", "PF": "PYF", "TF": "ATF", "GA": "GAB", "GM": "GMB", "GE": "GEO", "DE": "DEU", "GH": "GHA", "GI": "GIB", "GR": "GRC", "GL": "GRL", "GD": "GRD", "GP": "GLP", "GU": "GUM", "GT": "GTM", "GN": "GIN", "GW": "GNB", "GY": "GUY", "HT": "HTI", "HM": "HMD", "VA": "VAT", "HN": "HND", "HK": "HKG", "HR": "HRV", "HU": "HUN", "IS": "ISL", "IN": "IND", "ID": "IDN", "IR": "IRN", "IQ": "IRQ", "IE": "IRL", "IL": "ISR", "IT": "ITA", "JM": "JAM", "JP": "JPN", "JO": "JOR", "KZ": "KAZ", "KE": "KEN", "KI": "KIR", "KP": "PRK", "KR": "KOR", "KW": "KWT", "KG": "KGZ", "LA": "LAO", "LV": "LVA", "LB": "LBN", "LS": "LSO", "LR": "LBR", "LY": "LBY", "LI": "LIE", "LT": "LTU", "LU": "LUX", "MO": "MAC", "MK": "MKD", "MG": "MDG", "MW": "MWI", "MY": "MYS", "MV": "MDV", "ML": "MLI", "MT": "MLT", "MH": "MHL", "MQ": "MTQ", "MR": "MRT", "MU": "MUS", "YT": "MYT", "MX": "MEX", "FM": "FSM", "MD": "MDA", "MC": "MCO", "MN": "MNG", "MS": "MSR", "MA": "MAR", "MZ": "MOZ", "MM": "MMR", "NA": "NAM", "NR": "NRU", "NP": "NPL", "AN": "ANT", "NL": "NLD", "NC": "NCL", "NZ": "NZL", "NI": "NIC", "NE": "NER", "NG": "NGA", "NU": "NIU", "NF": "NFK", "MP": "MNP", "NO": "NOR", "OM": "OMN", "PK": "PAK", "PW": "PLW", "PS": "PSE", "PA": "PAN", "PG": "PNG", "PY": "PRY", "PE": "PER", "PH": "PHL", "PN": "PCN", "PL": "POL", "PT": "PRT", "PR": "PRI", "QA": "QAT", "RE": "REU", "RO": "ROU", "RU": "RUS", "RW": "RWA", "SH": "SHN", "KN": "KNA", "LC": "LCA", "PM": "SPM", "VC": "VCT", "WS": "WSM", "SM": "SMR", "ST": "STP", "SA": "SAU", "SN": "SEN", "CS": "SCG", "SC": "SYC", "SL": "SLE", "SG": "SGP", "SK": "SVK", "SI": "SVN", "SB": "SLB", "SO": "SOM", "ZA": "ZAF", "GS": "SGS", "ES": "ESP", "LK": "LKA", "SD": "SDN", "SR": "SUR", "SJ": "SJM", "SZ": "SWZ", "SE": "SWE", "CH": "CHE", "SY": "SYR", "TW": "TWN", "TJ": "TJK", "TZ": "TZA", "TH": "THA", "TL": "TLS", "TG": "TGO", "TK": "TKL", "TO": "TON", "TT": "TTO", "TN": "TUN", "TR": "TUR", "TM": "TKM", "TC": "TCA", "TV": "TUV", "VI": "VIR", "UG": "UGA", "UA": "UKR", "AE": "ARE", "GB": "GBR", "UM": "UMI", "US": "USA", "UY": "URY", "UZ": "UZB", "VU": "VUT", "VE": "VEN", "VN": "VNM", "WF": "WLF", "EH": "ESH", "YE": "YEM", "ZM": "ZMB", "ZW": "ZWE",
};
// Unit conversion
function tounit(d) {
d = parseInt(d);
if (d < 1000) return d.toFixed(0);
else if (d < 1000000) return (d / 1000.0).toFixed(1) + 'K';
else return (d / 1000000.0).toFixed(1) + 'M';
}
// Set up UI and pollers
window.onload = function() {
var statsLabels = ['cached', '10ms', '100ms', '1000ms', 'slow'];
var statsHistory = [];
var now = Date.now();
for (i = 0; i < statsLabels.length; ++i) {
statsHistory.push({ label: 'Layer ' + statsLabels[i], values: [{time: now, y:0}] });
$('.stats-legend').append('<li class="l-' + statsLabels[i] + '">' + statsLabels[i]);
}
var statsChart = $('#stats').epoch({
type: 'time.area',
axes: ['right', 'bottom'],
ticks: { right: 2 },
margins: { right: 60 },
tickFormats: {
right: function(d) { return tounit(d) + ' pps'; },
bottom: function(d) { return new Date(d).toTimeString().split(' ')[0]; },
},
data: statsHistory
});
var statsPrev = null;
/* Map colour brackets. */
var colours = [
'rgb(198,219,239)',
'rgb(158,202,225)',
'rgb(107,174,214)',
'rgb(66,146,198)',
'rgb(33,113,181)',
'rgb(8,81,156)',
'rgb(8,48,107)',
];
var fills = { defaultFill: '#F5F5F5' };
for (var i in colours) {
fills['q' + i] = colours[i];
}
var map = new Datamap({
element: document.getElementById('map'),
fills: fills,
data: {},
height: 400,
geographyConfig: {
highlightOnHover: false,
borderColor: '#ccc',
borderWidth: 0.5,
popupTemplate: function(geo, data) {
return ['<div class="hoverinfo">',
'<strong>', geo.properties.name, '</strong>',
'<br>Queries: <strong>', data ? data.queries : '0', '</strong>',
'</div>'].join('');
}
},
done: function(datamap) {
datamap.svg.call(d3.behavior.zoom().on("zoom", redraw));
function redraw() {
datamap.svg.selectAll("g")
.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
}
});
/* Draw map legend */
var legendBarWidth = 30, legendBarHeight = 10, legendOffset = 150;
d3.select('#map svg').append('g').attr('class', 'map-legend');
d3.select('.map-legend').selectAll('.map-legend')
.data(colours).enter()
.append('rect')
.attr('y', function(d,i) { return legendOffset + legendBarHeight*i; })
.attr('width', legendBarWidth)
.attr('height', legendBarHeight)
.attr('fill', function(d){ return d; });
/* Realtime updates */
function poller(feed, interval, cb) {
var func = function() {
$.ajax({
url: feed,
type: 'get',
dataType: 'json',
success: cb
});
}
setInterval(func, interval);
func();
}
poller('stats', 1000, function(resp) {
var now = Date.now();
var next = [];
for (i = 0; i < statsLabels.length; ++i) {
next.push(resp['answer.' + statsLabels[i]]);
}
if (statsPrev) {
var delta = [];
for (i = 0; i < statsLabels.length; ++i) {
delta.push({time: now, y: next[i]-statsPrev[i]});
}
statsChart.push(delta);
}
statsPrev = next;
});
poller('feed', 2000, function(resp) {
var feed = $('#feed');
feed.children().remove();
feed.append('<tr><th>Type</th><th>Query</th><th>Nameserver</th><th>DNSSEC</th></tr>')
for (i = 0; i < resp.length; ++i) {
if (resp[i].Qname != "") {
var row = $('<tr />');
row.append('<td>' + resp[i].Qtype + '</td>');
row.append('<td>' + resp[i].Qname + '</td>');
row.append('<td>' + resp[i].Addr + '</td>');
if (resp[i].Secure) {
row.append('<td class="secure">SECURE</td>');
} else {
row.append('<td></td>');
}
feed.append(row);
}
}
});
poller('geo', 2000, function(resp) {
var max = 0.0;
var series = [];
/* Calculate dataset limits. */
for (var key in resp) {
if (resp.hasOwnProperty(key)) {
max = Math.max(max, resp[key]);
series.push(resp[key]);
}
}
/* Map frequency to palette. */
var dataset = {};
var quantile = d3.scale.quantile()
.domain(series)
.range(d3.range(colours.length).map(function(i) { return "q" + i; }));
for (var key in resp) {
if (resp.hasOwnProperty(key)) {
var iso3_key = iso2_to_iso3[key];
if (iso3_key) {
var val = resp[key];
dataset[iso3_key] = { queries: val, fillKey: quantile(val) };
}
}
}
map.updateChoropleth(dataset);
/* Update legend */
d3.select('.map-legend').selectAll('text').remove();
d3.select('.map-legend').selectAll('.map-legend')
.data(colours).enter()
.append('text')
.text(function(d, i) {
var range = quantile.invertExtent('q' + i);
if (parseInt(range[1]) > 0) {
return tounit(range[0]) + ' - ' + tounit(range[1]);
}
})
.attr('x', (legendBarWidth*1.25))
.attr('y', function(d, i){
return legendOffset + (legendBarHeight*0.9) + legendBarHeight*i;
})
});
}
<!DOCTYPE html>
<title>{{.Title}}</title>
<style>
body { font-family: 'Gill Sans', 'Gill Sans MT', Verdana, sans-serif; color: #555; }
h1, h2, h3 { line-height: 2em; color: #000; text-align: center; border-bottom: 1px solid #ccc; }
h1, h2, h3 { font-weight: 300; }
th { text-align: left; font-weight: normal; margin-bottom: 0.5em; }
#page { font-weight: 300; }
#page { width: 900px; margin: 0 auto; }
#stats { height: 300px; }
#stats .layer-cached .area, .l-cached { fill: #2CA02C; color: #2CA02C; }
#stats .layer-10ms .area , .l-10ms { fill: #165683; color: #165683; }
#stats .layer-100ms .area , .l-100ms { fill: #258FDA; color: #258FDA; }
#stats .layer-1000ms .area, .l-1000ms { fill: #51A5E1; color: #51A5E1; }
#stats .layer-slow .area , .l-slow { fill: #E1AC51; color: #E1AC51; }
#feed { width: 100%; }
#feed .secure { color: #74c476; }
.stats-legend { text-align: center; }
.stats-legend li { display: inline; list-style-type: none; padding-right: 20px; }
.map-legend { font-size: 10px; }
</style>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="d3.js"></script>
<script type="text/javascript" src="epoch.js"></script>
<script type="text/javascript" src="topojson.js"></script>
<script type="text/javascript" src="datamaps.world.min.js"></script>
<script type="text/javascript" src="tinyweb.js"></script>
<link rel="icon" type="image/ico" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="epoch.css">
<div id="page">
<h1>{{.Title}}</h1>
<div class="epoch" id="stats"></div>
<ul class="stats-legend"></ul>
<h2>Queried servers</h2>
<div id="map" style="position: relative;"></div>
<h2>Last queries</h2>
<table id="feed"></table>
</div>
!function(){function t(n,t){function r(t){var r,e=n.arcs[0>t?~t:t],o=e[0];return n.transform?(r=[0,0],e.forEach(function(n){r[0]+=n[0],r[1]+=n[1]})):r=e[e.length-1],0>t?[r,o]:[o,r]}function e(n,t){for(var r in n){var e=n[r];delete t[e.start],delete e.start,delete e.end,e.forEach(function(n){o[0>n?~n:n]=1}),f.push(e)}}var o={},i={},u={},f=[],c=-1;return t.forEach(function(r,e){var o,i=n.arcs[0>r?~r:r];i.length<3&&!i[1][0]&&!i[1][1]&&(o=t[++c],t[c]=r,t[e]=o)}),t.forEach(function(n){var t,e,o=r(n),f=o[0],c=o[1];if(t=u[f])if(delete u[t.end],t.push(n),t.end=c,e=i[c]){delete i[e.start];var a=e===t?t:t.concat(e);i[a.start=t.start]=u[a.end=e.end]=a}else i[t.start]=u[t.end]=t;else if(t=i[c])if(delete i[t.start],t.unshift(n),t.start=f,e=u[f]){delete u[e.end];var s=e===t?t:e.concat(t);i[s.start=e.start]=u[s.end=t.end]=s}else i[t.start]=u[t.end]=t;else t=[n],i[t.start=f]=u[t.end=c]=t}),e(u,i),e(i,u),t.forEach(function(n){o[0>n?~n:n]||f.push([n])}),f}function r(n,r,e){function o(n){var t=0>n?~n:n;(s[t]||(s[t]=[])).push({i:n,g:a})}function i(n){n.forEach(o)}function u(n){n.forEach(i)}function f(n){"GeometryCollection"===n.type?n.geometries.forEach(f):n.type in l&&(a=n,l[n.type](n.arcs))}var c=[];if(arguments.length>1){var a,s=[],l={LineString:i,MultiLineString:u,Polygon:u,MultiPolygon:function(n){n.forEach(u)}};f(r),s.forEach(arguments.length<3?function(n){c.push(n[0].i)}:function(n){e(n[0].g,n[n.length-1].g)&&c.push(n[0].i)})}else for(var h=0,p=n.arcs.length;p>h;++h)c.push(h);return{type:"MultiLineString",arcs:t(n,c)}}function e(r,e){function o(n){n.forEach(function(t){t.forEach(function(t){(f[t=0>t?~t:t]||(f[t]=[])).push(n)})}),c.push(n)}function i(n){return l(u(r,{type:"Polygon",arcs:[n]}).coordinates[0])>0}var f={},c=[],a=[];return e.forEach(function(n){"Polygon"===n.type?o(n.arcs):"MultiPolygon"===n.type&&n.arcs.forEach(o)}),c.forEach(function(n){if(!n._){var t=[],r=[n];for(n._=1,a.push(t);n=r.pop();)t.push(n),n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].forEach(function(n){n._||(n._=1,r.push(n))})})})}}),c.forEach(function(n){delete n._}),{type:"MultiPolygon",arcs:a.map(function(e){var o=[];if(e.forEach(function(n){n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].length<2&&o.push(n)})})}),o=t(r,o),(n=o.length)>1)for(var u,c=i(e[0][0]),a=0;n>a;++a)if(c===i(o[a])){u=o[0],o[0]=o[a],o[a]=u;break}return o})}}function o(n,t){return"GeometryCollection"===t.type?{type:"FeatureCollection",features:t.geometries.map(function(t){return i(n,t)})}:i(n,t)}function i(n,t){var r={type:"Feature",id:t.id,properties:t.properties||{},geometry:u(n,t)};return null==t.id&&delete r.id,r}function u(n,t){function r(n,t){t.length&&t.pop();for(var r,e=s[0>n?~n:n],o=0,i=e.length;i>o;++o)t.push(r=e[o].slice()),a(r,o);0>n&&f(t,i)}function e(n){return n=n.slice(),a(n,0),n}function o(n){for(var t=[],e=0,o=n.length;o>e;++e)r(n[e],t);return t.length<2&&t.push(t[0].slice()),t}function i(n){for(var t=o(n);t.length<4;)t.push(t[0].slice());return t}function u(n){return n.map(i)}function c(n){var t=n.type;return"GeometryCollection"===t?{type:t,geometries:n.geometries.map(c)}:t in l?{type:t,coordinates:l[t](n)}:null}var a=g(n.transform),s=n.arcs,l={Point:function(n){return e(n.coordinates)},MultiPoint:function(n){return n.coordinates.map(e)},LineString:function(n){return o(n.arcs)},MultiLineString:function(n){return n.arcs.map(o)},Polygon:function(n){return u(n.arcs)},MultiPolygon:function(n){return n.arcs.map(u)}};return c(t)}function f(n,t){for(var r,e=n.length,o=e-t;o<--e;)r=n[o],n[o++]=n[e],n[e]=r}function c(n,t){for(var r=0,e=n.length;e>r;){var o=r+e>>>1;n[o]<t?r=o+1:e=o}return r}function a(n){function t(n,t){n.forEach(function(n){0>n&&(n=~n);var r=o[n];r?r.push(t):o[n]=[t]})}function r(n,r){n.forEach(function(n){t(n,r)})}function e(n,t){"GeometryCollection"===n.type?n.geometries.forEach(function(n){e(n,t)}):n.type in u&&u[n.type](n.arcs,t)}var o={},i=n.map(function(){return[]}),u={LineString:t,MultiLineString:r,Polygon:r,MultiPolygon:function(n,t){n.forEach(function(n){r(n,t)})}};n.forEach(e);for(var f in o)for(var a=o[f],s=a.length,l=0;s>l;++l)for(var h=l+1;s>h;++h){var p,v=a[l],g=a[h];(p=i[v])[f=c(p,g)]!==g&&p.splice(f,0,g),(p=i[g])[f=c(p,v)]!==v&&p.splice(f,0,v)}return i}function s(n,t){function r(n){u.remove(n),n[1][2]=t(n),u.push(n)}var e,o=g(n.transform),i=m(n.transform),u=v(),f=0;for(t||(t=h),n.arcs.forEach(function(n){var r=[];n.forEach(o);for(var i=1,f=n.length-1;f>i;++i)e=n.slice(i-1,i+2),e[1][2]=t(e),r.push(e),u.push(e);n[0][2]=n[f][2]=1/0;for(var i=0,f=r.length;f>i;++i)e=r[i],e.previous=r[i-1],e.next=r[i+1]});e=u.pop();){var c=e.previous,a=e.next;e[1][2]<f?e[1][2]=f:f=e[1][2],c&&(c.next=a,c[2]=e[2],r(c)),a&&(a.previous=c,a[0]=e[0],r(a))}return n.arcs.forEach(function(n){n.forEach(i)}),n}function l(n){for(var t,r=-1,e=n.length,o=n[e-1],i=0;++r<e;)t=o,o=n[r],i+=t[0]*o[1]-t[1]*o[0];return.5*i}function h(n){var t=n[0],r=n[1],e=n[2];return Math.abs((t[0]-e[0])*(r[1]-t[1])-(t[0]-r[0])*(e[1]-t[1]))}function p(n,t){return n[1][2]-t[1][2]}function v(){function n(n,t){for(;t>0;){var r=(t+1>>1)-1,o=e[r];if(p(n,o)>=0)break;e[o._=t]=o,e[n._=t=r]=n}}function t(n,t){for(;;){var r=t+1<<1,i=r-1,u=t,f=e[u];if(o>i&&p(e[i],f)<0&&(f=e[u=i]),o>r&&p(e[r],f)<0&&(f=e[u=r]),u===t)break;e[f._=t]=f,e[n._=t=u]=n}}var r={},e=[],o=0;return r.push=function(t){return n(e[t._=o]=t,o++),o},r.pop=function(){if(!(0>=o)){var n,r=e[0];return--o>0&&(n=e[o],t(e[n._=0]=n,0)),r}},r.remove=function(r){var i,u=r._;if(e[u]===r)return u!==--o&&(i=e[o],(p(i,r)<0?n:t)(e[i._=u]=i,u)),u},r}function g(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0),n[0]=(t+=n[0])*e+i,n[1]=(r+=n[1])*o+u}}function m(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0);var c=(n[0]-i)/e|0,a=(n[1]-u)/o|0;n[0]=c-t,n[1]=a-r,t=c,r=a}}function y(){}var d={version:"1.6.9",mesh:function(n){return u(n,r.apply(this,arguments))},meshArcs:r,merge:function(n){return u(n,e.apply(this,arguments))},mergeArcs:e,feature:o,neighbors:a,presimplify:s};"function"==typeof define&&define.amd?define(d):"object"==typeof module&&module.exports?module.exports=d:this.topojson=d}();
\ No newline at end of file
...@@ -23,10 +23,6 @@ ...@@ -23,10 +23,6 @@
/usr/lib{,64}/kdns_modules/*.lua r, /usr/lib{,64}/kdns_modules/*.lua r,
/usr/lib{,64}/kdns_modules/*.so rm, /usr/lib{,64}/kdns_modules/*.so rm,
# for tinyweb
/usr/lib{,64}/kdns_modules/tinyweb/ r,
/usr/lib{,64}/kdns_modules/tinyweb/* r,
/var/lib/GeoIP/* r,
# Site-specific additions and overrides. See local/README for details. # Site-specific additions and overrides. See local/README for details.
#include <local/usr.sbin.kresd> #include <local/usr.sbin.kresd>
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment