2018-08-09 18:34:09 +00:00
// Package mnet extends the standard package with extra functionality which is
// commonly useful
package mnet
import (
"net"
2019-02-03 00:55:42 +00:00
"strings"
2019-01-11 22:47:30 +00:00
"github.com/mediocregopher/mediocre-go-lib/mcfg"
"github.com/mediocregopher/mediocre-go-lib/mctx"
2019-01-24 21:54:38 +00:00
"github.com/mediocregopher/mediocre-go-lib/merr"
2019-01-11 22:47:30 +00:00
"github.com/mediocregopher/mediocre-go-lib/mlog"
"github.com/mediocregopher/mediocre-go-lib/mrun"
2018-08-09 18:34:09 +00:00
)
2019-01-24 21:54:38 +00:00
// MListener is returned by MListen and simply wraps a net.Listener.
type MListener struct {
2019-01-13 02:01:16 +00:00
net . Listener
log * mlog . Logger
2019-01-25 00:19:57 +00:00
// If set to true before mrun's stop event is run, the stop event will not
// cause the MListener to be closed.
NoCloseOnStop bool
2019-01-13 02:01:16 +00:00
}
2019-01-24 21:54:38 +00:00
// MListen returns an MListener which will be initialized when the start event
// is triggered on ctx (see mrun.Start), and closed when the stop event is
2019-01-13 01:11:22 +00:00
// triggered on ctx (see mrun.Stop).
2019-01-11 22:47:30 +00:00
//
// network defaults to "tcp" if empty. defaultAddr defaults to ":0" if empty,
// and will be configurable via mcfg.
2019-01-24 21:54:38 +00:00
func MListen ( ctx mctx . Context , network , defaultAddr string ) * MListener {
2019-01-11 22:47:30 +00:00
if network == "" {
network = "tcp"
}
if defaultAddr == "" {
defaultAddr = ":0"
}
2019-02-03 00:55:42 +00:00
addr := mcfg . String ( ctx , "listen-addr" , defaultAddr , strings . ToUpper ( network ) + " address to listen on in format [host]:port. If port is 0 then a random one will be chosen" )
2019-01-11 22:47:30 +00:00
2019-01-24 21:54:38 +00:00
l := new ( MListener )
2019-01-30 21:06:24 +00:00
l . log = mlog . From ( ctx )
l . log . SetKV ( l )
2019-01-13 02:01:16 +00:00
2019-01-11 22:47:30 +00:00
mrun . OnStart ( ctx , func ( mctx . Context ) error {
var err error
if l . Listener , err = net . Listen ( network , * addr ) ; err != nil {
return err
}
2019-01-13 02:01:16 +00:00
l . log . Info ( "listening" )
2019-01-11 22:47:30 +00:00
return nil
} )
2019-01-13 01:11:22 +00:00
// TODO track connections and wait for them to complete before shutting
// down?
mrun . OnStop ( ctx , func ( mctx . Context ) error {
2019-01-25 00:19:57 +00:00
if l . NoCloseOnStop {
return nil
}
2019-01-13 02:01:16 +00:00
l . log . Info ( "stopping listener" )
2019-01-13 01:11:22 +00:00
return l . Close ( )
} )
2019-01-13 02:01:16 +00:00
return l
}
2019-01-24 21:54:38 +00:00
// Accept wraps a call to Accept on the underlying net.Listener, providing debug
// logging.
func ( l * MListener ) Accept ( ) ( net . Conn , error ) {
conn , err := l . Listener . Accept ( )
if err != nil {
return conn , err
}
l . log . Debug ( "connection accepted" , mlog . KV { "remoteAddr" : conn . RemoteAddr ( ) } )
return conn , nil
}
// Close wraps a call to Close on the underlying net.Listener, providing debug
// logging.
func ( l * MListener ) Close ( ) error {
l . log . Debug ( "listener closing" )
err := l . Listener . Close ( )
l . log . Debug ( "listener closed" , merr . KV ( err ) )
return err
}
// KV implements the mlog.KVer interface.
func ( l * MListener ) KV ( ) map [ string ] interface { } {
2019-01-15 03:32:26 +00:00
return map [ string ] interface { } { "addr" : l . Addr ( ) . String ( ) }
2019-01-11 22:47:30 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2018-08-09 18:34:09 +00:00
func mustGetCIDRNetwork ( cidr string ) * net . IPNet {
_ , n , err := net . ParseCIDR ( cidr )
if err != nil {
panic ( err )
}
return n
}
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
var reservedCIDRs4 = [ ] * net . IPNet {
mustGetCIDRNetwork ( "0.0.0.0/8" ) , // current network
mustGetCIDRNetwork ( "10.0.0.0/8" ) , // private network
mustGetCIDRNetwork ( "100.64.0.0/10" ) , // private network
mustGetCIDRNetwork ( "127.0.0.0/8" ) , // localhost
mustGetCIDRNetwork ( "169.254.0.0/16" ) , // link-local
mustGetCIDRNetwork ( "172.16.0.0/12" ) , // private network
mustGetCIDRNetwork ( "192.0.0.0/24" ) , // IETF protocol assignments
mustGetCIDRNetwork ( "192.0.2.0/24" ) , // documentation and examples
mustGetCIDRNetwork ( "192.88.99.0/24" ) , // 6to4 Relay
mustGetCIDRNetwork ( "192.168.0.0/16" ) , // private network
mustGetCIDRNetwork ( "198.18.0.0/15" ) , // private network
mustGetCIDRNetwork ( "198.51.100.0/24" ) , // documentation and examples
mustGetCIDRNetwork ( "203.0.113.0/24" ) , // documentation and examples
mustGetCIDRNetwork ( "224.0.0.0/4" ) , // IP multicast
mustGetCIDRNetwork ( "240.0.0.0/4" ) , // reserved
mustGetCIDRNetwork ( "255.255.255.255/32" ) , // limited broadcast address
}
var reservedCIDRs6 = [ ] * net . IPNet {
mustGetCIDRNetwork ( "::/128" ) , // unspecified address
mustGetCIDRNetwork ( "::1/128" ) , // loopback address
mustGetCIDRNetwork ( "100::/64" ) , // discard prefix
mustGetCIDRNetwork ( "2001::/32" ) , // Teredo tunneling
mustGetCIDRNetwork ( "2001:20::/28" ) , // ORCHID v2
mustGetCIDRNetwork ( "2001:db8::/32" ) , // documentation and examples
mustGetCIDRNetwork ( "2002::/16" ) , // 6to4 addressing
mustGetCIDRNetwork ( "fc00::/7" ) , // unique local
mustGetCIDRNetwork ( "fe80::/10" ) , // link local
mustGetCIDRNetwork ( "ff00::/8" ) , // multicast
}
// IsReservedIP returns true if the given valid IP is part of a reserved IP
// range.
func IsReservedIP ( ip net . IP ) bool {
containedBy := func ( cidrs [ ] * net . IPNet ) bool {
for _ , cidr := range cidrs {
if cidr . Contains ( ip ) {
return true
}
}
return false
}
if ip . To4 ( ) != nil {
return containedBy ( reservedCIDRs4 )
}
return containedBy ( reservedCIDRs6 )
}