Adjusted to remove excessive packages.
Moved XLATE into generic listener.
This commit is contained in:
parent
b11f6c54bc
commit
d7e01e8b40
3
main.go
3
main.go
|
@ -11,7 +11,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/genericlistener"
|
"git.daplie.com/Daplie/go-rvpn-server/rvpn/genericlistener"
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/xlate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -25,7 +24,7 @@ var (
|
||||||
argServerExternalBinding string
|
argServerExternalBinding string
|
||||||
argDeadTime int
|
argDeadTime int
|
||||||
connectionTable *genericlistener.Table
|
connectionTable *genericlistener.Table
|
||||||
wssMapping *xlate.WssMapping
|
wssMapping *genericlistener.WssMapping
|
||||||
secretKey = "abc123"
|
secretKey = "abc123"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: admin: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: admin:", logFlags)
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
|
|
||||||
)
|
|
||||||
|
|
||||||
//LaunchClientListener - starts up http listeners and handles various URI paths
|
|
||||||
func LaunchClientListener(connectionTable *connection.Table, secretKey *string, serverBinding *string) (err error) {
|
|
||||||
loginfo.Println("starting WebRequestExternal Listener ", *serverBinding)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch url := r.URL.Path; url {
|
|
||||||
case "/":
|
|
||||||
handleConnectionWebSocket(connectionTable, w, r, *secretKey, false)
|
|
||||||
|
|
||||||
default:
|
|
||||||
http.Error(w, "Not Found", 404)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
s := &http.Server{
|
|
||||||
Addr: *serverBinding,
|
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.ListenAndServeTLS("certs/fullchain.pem", "certs/privkey.pem")
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("ListenAndServeTLS: ", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleConnectionWebSocket handles websocket requests from the peer.
|
|
||||||
func handleConnectionWebSocket(connectionTable *connection.Table, w http.ResponseWriter, r *http.Request, secretKey string, admin bool) {
|
|
||||||
loginfo.Println("websocket opening ", r.RemoteAddr, " ", r.Host)
|
|
||||||
|
|
||||||
tokenString := r.URL.Query().Get("access_token")
|
|
||||||
result, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
return []byte(secretKey), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil || !result.Valid {
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
w.Write([]byte("Not Authorized"))
|
|
||||||
loginfo.Println("access_token invalid...closing connection")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("help access_token valid")
|
|
||||||
|
|
||||||
claims := result.Claims.(jwt.MapClaims)
|
|
||||||
domains, ok := claims["domains"].([]interface{})
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
|
||||||
ReadBufferSize: 1024,
|
|
||||||
WriteBufferSize: 1024,
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("WebSocket upgrade failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("before connection table")
|
|
||||||
|
|
||||||
//newConnection := connection.NewConnection(connectionTable, conn, r.RemoteAddr, domains)
|
|
||||||
|
|
||||||
newRegistration := connection.NewRegistration(conn, r.RemoteAddr, domains)
|
|
||||||
connectionTable.Register() <- newRegistration
|
|
||||||
ok = <-newRegistration.CommCh()
|
|
||||||
if !ok {
|
|
||||||
loginfo.Println("connection registration failed ", newRegistration)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("connection registration accepted ", newRegistration)
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: client: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: client:", logFlags)
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package connection
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: connection: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: connection:", logFlags)
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package connectiontrack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: connectiontrack: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: connectiontrack:", logFlags)
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
package external
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
//LaunchWebRequestExternalListener - starts up extern http listeners, gets request and prep's to hand it off inside.
|
|
||||||
func LaunchWebRequestExternalListener(serverBinding *string, connectionTable *connection.Table) {
|
|
||||||
loginfo.Println("starting WebRequestExternal Listener ", *serverBinding)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch url := r.URL.Path; url {
|
|
||||||
default:
|
|
||||||
loginfo.Println("handlerWebRequestExternal")
|
|
||||||
|
|
||||||
dump, err := httputil.DumpRequest(r, true)
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println(err)
|
|
||||||
} else {
|
|
||||||
loginfo.Printf("%q", dump)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname := r.Host
|
|
||||||
|
|
||||||
if strings.Contains(hostname, ":") {
|
|
||||||
arr := strings.Split(hostname, ":")
|
|
||||||
hostname = arr[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteSplit := strings.Split(r.RemoteAddr, ":")
|
|
||||||
rAddr := remoteSplit[0]
|
|
||||||
rPort := remoteSplit[1]
|
|
||||||
|
|
||||||
if conn, ok := connectionTable.ConnByDomain(hostname); !ok {
|
|
||||||
//matching connection can not be found based on ConnByDomain
|
|
||||||
loginfo.Println("unable to match ", hostname, " to an existing connection")
|
|
||||||
http.Error(w, "Domain not supported", http.StatusBadRequest)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
loginfo.Println("Domain Accepted")
|
|
||||||
loginfo.Println(conn, rAddr, rPort)
|
|
||||||
p := packer.NewPacker()
|
|
||||||
p.Header.SetAddress(rAddr)
|
|
||||||
p.Header.Port, err = strconv.Atoi(rPort)
|
|
||||||
p.Header.Port = 8080
|
|
||||||
p.Header.Service = "http"
|
|
||||||
p.Data.AppendBytes(dump)
|
|
||||||
buf := p.PackV1()
|
|
||||||
|
|
||||||
sendTrack := connection.NewSendTrack(buf.Bytes(), hostname)
|
|
||||||
conn.SendCh() <- sendTrack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
s := &http.Server{
|
|
||||||
Addr: *serverBinding,
|
|
||||||
Handler: mux,
|
|
||||||
ConnState: connState,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("ListenAndServe: ", err)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connState(conn net.Conn, state http.ConnState) {
|
|
||||||
loginfo.Println("connState")
|
|
||||||
fmt.Println(conn, conn.LocalAddr(), conn.RemoteAddr())
|
|
||||||
fmt.Println(state)
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
package external
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"bufio"
|
|
||||||
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/connection"
|
|
||||||
"git.daplie.com/Daplie/go-rvpn-server/rvpn/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
//LaunchExternalServer -- used to listen for external connections destin for WSS
|
|
||||||
func LaunchExternalServer(serverBinding string, connectionTable *connection.Table) {
|
|
||||||
addr, err := net.ResolveTCPAddr("tcp4", serverBinding)
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("Unabled to resolve ", serverBinding, " in launchExternalServer")
|
|
||||||
loginfo.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("passed ResolveTCPAddr")
|
|
||||||
|
|
||||||
listener, err := net.ListenTCP("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("unable to bind ", serverBinding)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("listening")
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := listener.AcceptTCP()
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("Bad accept ", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleConnection(conn, connectionTable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type protocol int
|
|
||||||
|
|
||||||
//Family -- ENUM for Address Family
|
|
||||||
const (
|
|
||||||
protoHTTP protocol = iota + 1
|
|
||||||
protoHTTPS
|
|
||||||
protoSSLV3
|
|
||||||
protoTLSV1
|
|
||||||
protoTLSV11
|
|
||||||
protoTLSV2
|
|
||||||
)
|
|
||||||
|
|
||||||
//State -- state of connection
|
|
||||||
type State struct {
|
|
||||||
Protocol protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
//NewState -- Constructor
|
|
||||||
func NewState() (p *State) {
|
|
||||||
p = new(State)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleConnection(conn net.Conn, connectionTable *connection.Table) {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
state := NewState()
|
|
||||||
|
|
||||||
var buffer [512]byte
|
|
||||||
|
|
||||||
for {
|
|
||||||
cnt, err := conn.Read(buffer[0:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
loginfo.Println("state ", state, " ", state.Protocol)
|
|
||||||
loginfo.Println("conn ", conn)
|
|
||||||
loginfo.Println("byte read", cnt)
|
|
||||||
//loginfo.Println("buffer")
|
|
||||||
//loginfo.Println(hex.Dump(buffer[0:cnt]))
|
|
||||||
|
|
||||||
if state.Protocol == 0 {
|
|
||||||
//attempt to discover protocol
|
|
||||||
|
|
||||||
// HTTP Identifcation
|
|
||||||
if bytes.Contains(buffer[:], []byte{0x0d, 0x0a}) {
|
|
||||||
//string protocol
|
|
||||||
if bytes.ContainsAny(buffer[:], "HTTP/") {
|
|
||||||
loginfo.Println("identifed HTTP")
|
|
||||||
state.Protocol = protoHTTP
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if bytes.Contains(buffer[:], []byte{0x16, 0x03, 0x00}) {
|
|
||||||
loginfo.Println("identifed SSLV3")
|
|
||||||
state.Protocol = protoSSLV3
|
|
||||||
|
|
||||||
} else if bytes.Contains(buffer[:], []byte{0x16, 0x03, 0x01}) {
|
|
||||||
loginfo.Println("identifed TLSV1")
|
|
||||||
state.Protocol = protoTLSV1
|
|
||||||
|
|
||||||
} else if bytes.Contains(buffer[:], []byte{0x16, 0x03, 0x02}) {
|
|
||||||
loginfo.Println("identifed TLSV1.1")
|
|
||||||
state.Protocol = protoTLSV11
|
|
||||||
|
|
||||||
} else if bytes.Contains(buffer[:], []byte{0x16, 0x03, 0x03}) {
|
|
||||||
loginfo.Println("identifed TLSV2")
|
|
||||||
state.Protocol = protoTLSV2
|
|
||||||
|
|
||||||
} else {
|
|
||||||
loginfo.Println("Protocol not identified", conn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.Protocol == 0 {
|
|
||||||
loginfo.Println("Making sure protocol is set")
|
|
||||||
loginfo.Println(state)
|
|
||||||
return
|
|
||||||
} else if state.Protocol == protoHTTP {
|
|
||||||
readBuffer := bytes.NewBuffer(buffer[0:cnt])
|
|
||||||
reader := bufio.NewReader(readBuffer)
|
|
||||||
r, err := http.ReadRequest(reader)
|
|
||||||
|
|
||||||
loginfo.Println(r)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
loginfo.Println("error parsing request")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname := r.Host
|
|
||||||
loginfo.Println("Host: ", hostname)
|
|
||||||
|
|
||||||
if strings.Contains(hostname, ":") {
|
|
||||||
arr := strings.Split(hostname, ":")
|
|
||||||
hostname = arr[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
loginfo.Println("Remote: ", conn.RemoteAddr().String())
|
|
||||||
|
|
||||||
remoteSplit := strings.Split(conn.RemoteAddr().String(), ":")
|
|
||||||
rAddr := remoteSplit[0]
|
|
||||||
rPort := remoteSplit[1]
|
|
||||||
|
|
||||||
if conn, ok := connectionTable.ConnByDomain(hostname); !ok {
|
|
||||||
//matching connection can not be found based on ConnByDomain
|
|
||||||
loginfo.Println("unable to match ", hostname, " to an existing connection")
|
|
||||||
//http.Error(, "Domain not supported", http.StatusBadRequest)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
loginfo.Println("Domain Accepted")
|
|
||||||
loginfo.Println(conn, rAddr, rPort)
|
|
||||||
p := packer.NewPacker()
|
|
||||||
p.Header.SetAddress(rAddr)
|
|
||||||
p.Header.Port, err = strconv.Atoi(rPort)
|
|
||||||
p.Header.Port = 8080
|
|
||||||
p.Header.Service = "http"
|
|
||||||
p.Data.AppendBytes(buffer[0:cnt])
|
|
||||||
buf := p.PackV1()
|
|
||||||
|
|
||||||
sendTrack := connection.NewSendTrack(buf.Bytes(), hostname)
|
|
||||||
conn.SendCh() <- sendTrack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package external
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: external: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: external:", logFlags)
|
|
||||||
}
|
|
|
@ -1,12 +1,7 @@
|
||||||
package xlate
|
package genericlistener
|
||||||
|
|
||||||
import "golang.org/x/net/websocket"
|
import "golang.org/x/net/websocket"
|
||||||
|
|
||||||
const (
|
|
||||||
initialDomains = 10
|
|
||||||
incrementDomains = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
type domain string
|
type domain string
|
||||||
|
|
||||||
//WssRegistration --
|
//WssRegistration --
|
|
@ -1,17 +0,0 @@
|
||||||
package instrumentation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
loginfo *log.Logger
|
|
||||||
logdebug *log.Logger
|
|
||||||
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
loginfo = log.New(os.Stdout, "INFO: instrumentation: ", logFlags)
|
|
||||||
logdebug = log.New(os.Stdout, "DEBUG: instrumentation:", logFlags)
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package matching
|
|
||||||
|
|
||||||
type headerTerm int
|
|
||||||
type matchType int
|
|
||||||
type protocolType int
|
|
||||||
|
|
||||||
//headerTerm -- ENUM for header terminator
|
|
||||||
const (
|
|
||||||
CRLF2 headerTerm = iota
|
|
||||||
ZER0
|
|
||||||
)
|
|
||||||
|
|
||||||
//Family -- ENUM for Address Family
|
|
||||||
const (
|
|
||||||
BYTES matchType = iota
|
|
||||||
REGEX
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
pHTTP = iota + 1
|
|
||||||
pTLS
|
|
||||||
pSSH
|
|
||||||
)
|
|
||||||
|
|
||||||
//Protocol --
|
|
||||||
type Protocol struct {
|
|
||||||
HeaderTerm headerTerm
|
|
||||||
MatchType matchType
|
|
||||||
Type protocolType
|
|
||||||
SearchRegex string
|
|
||||||
SearchBytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
//NewProtocol -- Constructor
|
|
||||||
func NewProtocol() (p *Protocol) {
|
|
||||||
p = new(Protocol)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//Protocols --
|
|
||||||
type Protocols struct {
|
|
||||||
protocols []*Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Protocols) add(protocol *Protocol) []*Protocol {
|
|
||||||
p.protocols = append(p.protocols, protocol)
|
|
||||||
return p.protocols
|
|
||||||
}
|
|
||||||
|
|
||||||
//NewProtocols --
|
|
||||||
func NewProtocols() (p *Protocols) {
|
|
||||||
p = new(Protocols)
|
|
||||||
p.protocols = make([]*Protocol, 0)
|
|
||||||
|
|
||||||
newp := NewProtocol()
|
|
||||||
newp.MatchType = REGEX
|
|
||||||
newp.HeaderTerm = CRLF2
|
|
||||||
newp.MatchType = pHTTP
|
|
||||||
p.add(newp)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package matching
|
|
||||||
|
|
||||||
import "log"
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
logFlags := log.Ldate | log.Lmicroseconds | log.Lshortfile
|
|
||||||
loginfo := log.New(os.Stdout, "INFO: matching: ", logFlags)
|
|
||||||
logdebug := log.New(os.Stdout, "DEBUG: matching:", logFlags)
|
|
||||||
|
|
||||||
loginfo.Println("")
|
|
||||||
logdebug.Println("")
|
|
||||||
}
|
|
Loading…
Reference in New Issue