diff --git a/transport/transwarptls/address.go b/transport/transwarptls/address.go new file mode 100644 index 00000000..2ba1b1b8 --- /dev/null +++ b/transport/transwarptls/address.go @@ -0,0 +1,107 @@ +/* + Copyright NetFoundry, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package transwarptls + +import ( + "fmt" + "github.com/openziti/foundation/identity/identity" + "github.com/openziti/foundation/transport" + "github.com/pkg/errors" + "io" + "net" + "strconv" + "strings" +) + +var _ transport.Address = (*address) nil // enforce that address implements transport.Address + +type address struct { + hostname string + port uint16 +} + +func (self address) Dial(name string, id *identity.TokenId, tcfg transport.Configuration) (transport.Connection, error) { + endpoint, err := net.ResolveUDPAddr("udp", self.bindableAddress()) + if err != nil { + return nil, errors.Wrap(err, "resolve udp") + } + var subc map[interface{}]interface{} + if tcfg != nil { + if v, found := tcfg["westworld3"]; found { + if subv, ok := v.(map[interface{}]interface{}); ok { + subc = subv + } + } + } + return Dial(endpoint, name, id, subc) +} + +func (self address) Listen(name string, id *identity.TokenId, incoming chan transport.Connection, tcfg transport.Configuration) (io.Closer, error) { + bind, err := net.ResolveUDPAddr("udp", self.bindableAddress()) + if err != nil { + return nil, errors.Wrap(err, "resolve udp") + } + var subc map[interface{}]interface{} + if tcfg != nil { + if v, found := tcfg["westworld3"]; found { + if subv, ok := v.(map[interface{}]interface{}); ok { + subc = subv + } + } + } + return Listen(bind, name, id, incoming, subc) +} + +func (self address) MustListen(name string, id *identity.TokenId, incoming chan transport.Connection, tcfg transport.Configuration) io.Closer { + closer, err := self.Listen(name, id, incoming, tcfg) + if err != nil { + panic(err) + } + return closer +} + +func (self address) String() string { + return fmt.Sprintf("transwarptls:%s", self.bindableAddress()) +} + +func (self address) bindableAddress() string { + return fmt.Sprintf("%s:%d", self.hostname, self.port) +} + +type AddressParser struct{} + +func (self AddressParser) Parse(s string) (transport.Address, error) { + tokens := strings.Split(s, ":") + if len(tokens) < 2 { + return nil, errors.New("invalid format") + } + + if tokens[0] == "transwarptls" { + if len(tokens) != 3 { + return nil, errors.New("invalid format") + } + + port, err := strconv.ParseUint(tokens[2], 10, 16) + if err != nil { + return nil, err + } + + return &address{hostname: tokens[1], port: uint16(port)}, nil + } + + return nil, errors.New("invalid format") +} \ No newline at end of file diff --git a/transport/transwarptls/dialer.go b/transport/transwarptls/dialer.go new file mode 100644 index 00000000..f93b8195 --- /dev/null +++ b/transport/transwarptls/dialer.go @@ -0,0 +1,57 @@ +/* + Copyright NetFoundry, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package transwarptls + +import ( + "github.com/openziti/dilithium/cf" + "github.com/openziti/dilithium/protocol/westlsworld3" + "github.com/openziti/dilithium/protocol/westworld3" + "github.com/openziti/foundation/identity/identity" + "github.com/openziti/foundation/transport" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "net" +) + +func Dial(endpoint *net.UDPAddr, name string, id *identity.TokenId, tcfg transport.Configuration) (transport.Connection, error) { + profileId := byte(0) + if tcfg != nil { + profile := &westworld3.Profile{} + if err := profile.Load(cf.MapIToMapS(tcfg)); err != nil { + return nil, errors.Wrap(err, "load profile") + } + newProfileId, err := westworld3.AddProfile(profile) + if err != nil { + return nil, errors.Wrap(err, "register profile") + } + profileId = newProfileId + } + logrus.Infof("westworld3 profile = [\n%s\n]", westworld3.GetProfile(profileId).Dump()) + socket, err := westlsworld3.Dial(endpoint, id.ClientTLSConfig(), profileId) + if err != nil { + return nil, errors.Wrap(err, "dial") + } + + return &Connection{ + detail: &transport.ConnectionDetail{ + Address: "transwarptls:"+endpoint.String(), + InBound: false, + Name: name, + }, + socket: socket, + }, nil +} \ No newline at end of file diff --git a/transport/transwarptls/listener.go b/transport/transwarptls/listener.go new file mode 100644 index 00000000..371188a3 --- /dev/null +++ b/transport/transwarptls/listener.go @@ -0,0 +1,85 @@ +/* + Copyright NetFoundry, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package transwarptls + +import ( + "github.com/michaelquigley/pfxlog" + "github.com/openziti/dilithium/cf" + "github.com/openziti/dilithium/protocol/westlsworld3" + "github.com/openziti/dilithium/protocol/westworld3" + "github.com/openziti/foundation/identity/identity" + "github.com/openziti/foundation/transport" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "io" + "net" +) + +func Listen(bind *net.UDPAddr, name string, id *identity.TokenId, incoming chan transport.Connection, tcfg transport.Configuration) (io.Closer, error) { + log := pfxlog.ContextLogger(name + "/transwarptls:" + bind.String()) + + profileId := byte(0) + if tcfg != nil { + profile := &westworld3.Profile{} + if err := profile.Load(cf.MapIToMapS(tcfg)); err != nil { + return nil, errors.Wrap(err, "load profile") + } + newProfileId, err := westworld3.AddProfile(profile) + if err != nil { + return nil, errors.Wrap(err, "register profile") + } + profileId = newProfileId + } + logrus.Infof("westworld3 profile = [\n%s\n]", westworld3.GetProfile(profileId).Dump()) + + listener, err := westlsworld3.Listen(bind, id.ServerTLSConfig(), profileId) + if err != nil { + return nil, err + } + + go acceptLoop(log, name, listener, incoming) + + return listener, nil +} + +func acceptLoop(log *logrus.Entry, name string, listener net.Listener, incoming chan transport.Connection) { + defer log.Error("exited") + + for { + socket, err := listener.Accept() + if err != nil { + if netErr, ok := err.(net.Error); ok && !netErr.Temporary() { + log.WithField("err", err).Error("accept failed. failure not recoverable. exiting listen loop") + return + } + log.WithField("err", err).Error("accept failed") + + } else { + connection := &Connection{ + detail: &transport.ConnectionDetail{ + Address: "transwarptls:" + socket.RemoteAddr().String(), + InBound: true, + Name: name, + }, + socket: socket, + } + incoming <- connection + + log.WithField("addr", socket.RemoteAddr().String()).Info("accepted connection") + } + } +} \ No newline at end of file