func AuthDeny(domain string, addresses ...string) {
	if auth_isIP(domain) {
		auth_deny_for_domain("*", domain)
		auth_deny_for_domain("*", addresses...)
	} else {
		auth_deny_for_domain(domain, addresses...)
	}
}
Deny (blacklist) some addresses for a domain.

An address can be a single IP address, or an IP address and mask in CIDR notation.

For all security mechanisms, this rejects the connection without any further authentication.

Use either a whitelist for a domain, or a blacklist for a domain, not both.
If you define both a whitelist and a blacklist for a domain, only the whitelist takes effect.

Use domain "*" for all domains.

For backward compatibility: if domain can be parsed as an IP address, it will be
interpreted as another address, and it and all remaining addresses will be added
to all domains.
package zmq4
import "C"
import (
	"errors"
	"log"
	"net"
	"strings"
	"sync"
	"unsafe"
)
const CURVE_ALLOW_ANY = "*"
var (
	auth_handler *Socket
	auth_quit    *Socket
	auth_init          = false
	auth_verbose_value = false
	auth_verbose_lock  sync.RWMutex
	auth_allow     = make(map[string]map[string]bool)
	auth_deny      = make(map[string]map[string]bool)
	auth_allow_net = make(map[string][]*net.IPNet)
	auth_deny_net  = make(map[string][]*net.IPNet)
	auth_users = make(map[string]map[string]string)
	auth_pubkeys = make(map[string]map[string]bool)
	auth_meta_handler = auth_meta_handler_default
)
func auth_verbose() bool {
	auth_verbose_lock.RLock()
	value := auth_verbose_value
	auth_verbose_lock.RUnlock()
	return value
}
func auth_verbose_set(value bool) {
	auth_verbose_lock.Lock()
	auth_verbose_value = value
	auth_verbose_lock.Unlock()
}
func auth_meta_handler_default(version, request_id, domain, address, identity, mechanism string, credentials ...string) (metadata map[string]string) {
	return map[string]string{}
}
func auth_isIP(addr string) bool {
	if net.ParseIP(addr) != nil {
		return true
	}
	if _, _, err := net.ParseCIDR(addr); err == nil {
		return true
	}
	return false
}
func auth_is_allowed(domain, address string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_allow[d]; ok {
			if a[address] {
				return true
			}
		}
	}
	addr := net.ParseIP(address)
	if addr != nil {
		for _, d := range []string{domain, "*"} {
			if a, ok := auth_allow_net[d]; ok {
				for _, m := range a {
					if m.Contains(addr) {
						return true
					}
				}
			}
		}
	}
	return false
}
func auth_is_denied(domain, address string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_deny[d]; ok {
			if a[address] {
				return true
			}
		}
	}
	addr := net.ParseIP(address)
	if addr != nil {
		for _, d := range []string{domain, "*"} {
			if a, ok := auth_deny_net[d]; ok {
				for _, m := range a {
					if m.Contains(addr) {
						return true
					}
				}
			}
		}
	}
	return false
}
func auth_has_allow(domain string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_allow[d]; ok {
			if len(a) > 0 || len(auth_allow_net[d]) > 0 {
				return true
			}
		}
	}
	return false
}
func auth_has_deny(domain string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_deny[d]; ok {
			if len(a) > 0 || len(auth_deny_net[d]) > 0 {
				return true
			}
		}
	}
	return false
}
func auth_do_handler() {
	for {
		msg, err := auth_handler.RecvMessage(0)
		if err != nil {
			if auth_verbose() {
				log.Println("AUTH: Quitting:", err)
			}
			break
		}
		if msg[0] == "QUIT" {
			if auth_verbose() {
				log.Println("AUTH: Quitting: received QUIT message")
			}
			_, err := auth_handler.SendMessage("QUIT")
			if err != nil && auth_verbose() {
				log.Println("AUTH: Quitting: bouncing QUIT message:", err)
			}
			break
		}
		version := msg[0]
		if version != "1.0" {
			panic("AUTH: version != 1.0")
		}
		request_id := msg[1]
		domain := msg[2]
		address := msg[3]
		identity := msg[4]
		mechanism := msg[5]
		credentials := msg[6:]
		username := ""
		password := ""
		client_key := ""
		if mechanism == "PLAIN" {
			username = msg[6]
			password = msg[7]
		} else if mechanism == "CURVE" {
			s := msg[6]
			if len(s) != 32 {
				panic("AUTH: len(client_key) != 32")
			}
			client_key = Z85encode(s)
		}
		allowed := false
		denied := false
		if auth_has_allow(domain) {
			if auth_is_allowed(domain, address) {
				allowed = true
				if auth_verbose() {
					log.Printf("AUTH: PASSED (whitelist) domain=%q address=%q\n", domain, address)
				}
			} else {
				denied = true
				if auth_verbose() {
					log.Printf("AUTH: DENIED (not in whitelist) domain=%q address=%q\n", domain, address)
				}
			}
		} else if auth_has_deny(domain) {
			if auth_is_denied(domain, address) {
				denied = true
				if auth_verbose() {
					log.Printf("AUTH: DENIED (blacklist) domain=%q address=%q\n", domain, address)
				}
			} else {
				allowed = true
				if auth_verbose() {
					log.Printf("AUTH: PASSED (not in blacklist) domain=%q address=%q\n", domain, address)
				}
			}
		}
		if !denied {
			if mechanism == "NULL" && !allowed {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (NULL)\n")
				}
				allowed = true
			} else if mechanism == "PLAIN" {
				allowed = authenticate_plain(domain, username, password)
			} else if mechanism == "CURVE" {
				allowed = authenticate_curve(domain, client_key)
			}
		}
		if allowed {
			m := auth_meta_handler(version, request_id, domain, address, identity, mechanism, credentials...)
			user_id := ""
			if uid, ok := m["User-Id"]; ok {
				user_id = uid
				delete(m, "User-Id")
			}
			metadata := make([]byte, 0)
			for key, value := range m {
				if len(key) < 256 {
					metadata = append(metadata, auth_meta_blob(key, value)...)
				}
			}
			auth_handler.SendMessage(version, request_id, "200", "OK", user_id, metadata)
		} else {
			auth_handler.SendMessage(version, request_id, "400", "NO ACCESS", "", "")
		}
	}
	err := auth_handler.Close()
	if err != nil && auth_verbose() {
		log.Println("AUTH: Quitting: Close:", err)
	}
	if auth_verbose() {
		log.Println("AUTH: Quit")
	}
}
func authenticate_plain(domain, username, password string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_users[dom]; ok {
			if m[username] == password {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (PLAIN) domain=%q username=%q password=%q\n", dom, username, password)
				}
				return true
			}
		}
	}
	if auth_verbose() {
		log.Printf("AUTH: DENIED (PLAIN) domain=%q username=%q password=%q\n", domain, username, password)
	}
	return false
}
func authenticate_curve(domain, client_key string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_pubkeys[dom]; ok {
			if m[CURVE_ALLOW_ANY] {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (CURVE any client) domain=%q\n", dom)
				}
				return true
			}
			if m[client_key] {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (CURVE) domain=%q client_key=%q\n", dom, client_key)
				}
				return true
			}
		}
	}
	if auth_verbose() {
		log.Printf("AUTH: DENIED (CURVE) domain=%q client_key=%q\n", domain, client_key)
	}
	return false
}
func AuthStart() (err error) {
	if auth_init {
		if auth_verbose() {
			log.Println("AUTH: Already running")
		}
		return errors.New("Auth is already running")
	}
	auth_handler, err = NewSocket(REP)
	if err != nil {
		return
	}
	auth_handler.SetLinger(0)
	err = auth_handler.Bind("inproc://zeromq.zap.01")
	if err != nil {
		auth_handler.Close()
		return
	}
	auth_quit, err = NewSocket(REQ)
	if err != nil {
		auth_handler.Close()
		return
	}
	auth_quit.SetLinger(0)
	err = auth_quit.Connect("inproc://zeromq.zap.01")
	if err != nil {
		auth_handler.Close()
		auth_quit.Close()
		return
	}
	go auth_do_handler()
	if auth_verbose() {
		log.Println("AUTH: Starting")
	}
	auth_init = true
	return
}
func AuthStop() {
	if !auth_init {
		if auth_verbose() {
			log.Println("AUTH: Not running, can't stop")
		}
		return
	}
	if auth_verbose() {
		log.Println("AUTH: Stopping")
	}
	_, err := auth_quit.SendMessageDontwait("QUIT")
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: SendMessageDontwait(\"QUIT\"):", err)
	}
	_, err = auth_quit.RecvMessage(0)
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: RecvMessage:", err)
	}
	err = auth_quit.Close()
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: Close:", err)
	}
	if auth_verbose() {
		log.Println("AUTH: Stopped")
	}
	auth_init = false
}
func AuthAllow(domain string, addresses ...string) {
	if auth_isIP(domain) {
		auth_allow_for_domain("*", domain)
		auth_allow_for_domain("*", addresses...)
	} else {
		auth_allow_for_domain(domain, addresses...)
	}
}
func auth_allow_for_domain(domain string, addresses ...string) {
	if _, ok := auth_allow[domain]; !ok {
		auth_allow[domain] = make(map[string]bool)
		auth_allow_net[domain] = make([]*net.IPNet, 0)
	}
	for _, address := range addresses {
		if _, ipnet, err := net.ParseCIDR(address); err == nil {
			auth_allow_net[domain] = append(auth_allow_net[domain], ipnet)
		} else if net.ParseIP(address) != nil {
			auth_allow[domain][address] = true
		} else {
			if auth_verbose() {
				log.Printf("AUTH: Allow for domain %q: %q is not a valid address or network\n", domain, address)
			}
		}
	}
}