536 lines
13 KiB
Go
536 lines
13 KiB
Go
|
package main
|
||
|
|
||
|
|
||
|
//import "bufio"
|
||
|
import "context"
|
||
|
import "crypto/tls"
|
||
|
import "crypto/x509"
|
||
|
//import "encoding/binary"
|
||
|
import "fmt"
|
||
|
import "io"
|
||
|
import "log"
|
||
|
import "net"
|
||
|
//import "os"
|
||
|
import "sync"
|
||
|
import "sync/atomic"
|
||
|
//import "syscall"
|
||
|
//import "time"
|
||
|
|
||
|
//import "github.com/google/uuid"
|
||
|
import "google.golang.org/grpc"
|
||
|
import "google.golang.org/grpc/credentials/insecure"
|
||
|
|
||
|
const PTC_LIMIT = 8192
|
||
|
|
||
|
type PacketStreamClient grpc.BidiStreamingClient[Packet, Packet]
|
||
|
|
||
|
type ServerConnMap = map[*net.TCPAddr]*ServerConn
|
||
|
type ClientPeerConnMap = map[uint32]*ClientPeerConn
|
||
|
type ClientRouteMap = map[uint32]*ClientRoute
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
type ClientConfig struct {
|
||
|
server_addr string
|
||
|
peer_addrs []string
|
||
|
}
|
||
|
|
||
|
type Client struct {
|
||
|
cfg *ClientConfig
|
||
|
tlscfg *tls.Config
|
||
|
saddr *net.TCPAddr
|
||
|
|
||
|
sc *grpc.ClientConn // main control connection to the server
|
||
|
sg HoduClient
|
||
|
psc PacketStreamClient
|
||
|
psc_mtx sync.Mutex
|
||
|
|
||
|
cts_mtx sync.Mutex
|
||
|
cts_map ServerConnMap
|
||
|
wg sync.WaitGroup
|
||
|
stop_req atomic.Bool
|
||
|
}
|
||
|
|
||
|
|
||
|
type ClientPeerConn struct {
|
||
|
route *ClientRoute
|
||
|
conn_id uint32
|
||
|
conn *net.TCPConn
|
||
|
remot_conn_id uint32
|
||
|
|
||
|
addr string // peer address
|
||
|
stop_req atomic.Bool
|
||
|
}
|
||
|
|
||
|
// client connection to server
|
||
|
type ServerConn struct {
|
||
|
cli *Client
|
||
|
saddr *net.TCPAddr // server address that is connected to
|
||
|
psc Hodu_PacketStreamClient
|
||
|
|
||
|
route_mtx sync.Mutex
|
||
|
routes ClientRouteMap
|
||
|
//route_wg sync.WaitGroup
|
||
|
|
||
|
//cw_mtx sync.Mutex
|
||
|
|
||
|
wg sync.WaitGroup
|
||
|
stop_req atomic.Bool
|
||
|
greeted bool
|
||
|
}
|
||
|
|
||
|
type ClientRoute struct {
|
||
|
cts *ServerConn
|
||
|
id uint32
|
||
|
peer_addr *net.TCPAddr
|
||
|
proto ROUTE_PROTO
|
||
|
|
||
|
ptc_mtx sync.Mutex
|
||
|
ptc_map ClientPeerConnMap
|
||
|
ptc_limit int
|
||
|
ptc_last_id uint32
|
||
|
ptc_wg sync.WaitGroup
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
func NewClientRoute(cts *ServerConn, id uint32, addr *net.TCPAddr, proto ROUTE_PROTO) *ClientRoute {
|
||
|
var r ClientRoute
|
||
|
|
||
|
r.cts = cts
|
||
|
r.id = id
|
||
|
r.ptc_limit = PTC_LIMIT
|
||
|
r.ptc_map = make(ClientPeerConnMap)
|
||
|
r.ptc_last_id = 0
|
||
|
r.proto = proto
|
||
|
r.peer_addr = addr
|
||
|
|
||
|
return &r;
|
||
|
}
|
||
|
|
||
|
func (r *ClientRoute) RunTask() {
|
||
|
// this task on the route object isn't actually necessary.
|
||
|
}
|
||
|
|
||
|
func (r *ClientRoute) StopTask() {
|
||
|
// TODO:
|
||
|
fmt.Printf ("ClientRoute StopTask not implemented yet\n")
|
||
|
// TOOD: stop all peer connection jobs
|
||
|
}
|
||
|
|
||
|
func (r* ClientRoute) ConnectToPeer(pts_id uint32) {
|
||
|
var err error
|
||
|
var conn *net.TCPConn
|
||
|
var ptc *ClientPeerConn
|
||
|
|
||
|
// MAKE thesse into a separte go rountine... so it doesn't block
|
||
|
conn, err = net.DialTCP("tcp", nil, r.peer_addr);
|
||
|
if err != nil {
|
||
|
fmt.Printf ("failed to connect to %s - %s\n", r.peer_addr.String(), err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ptc, err = r.AddNewClientPeerConn(conn)
|
||
|
if err != nil {
|
||
|
// TODO: logging
|
||
|
fmt.Printf("YYYYYYYY - %s\n", err.Error())
|
||
|
conn.Close()
|
||
|
return
|
||
|
}
|
||
|
fmt.Printf("STARTED NEW SERVER PEER STAK\n")
|
||
|
|
||
|
r.ptc_wg.Add(1)
|
||
|
go ptc.RunTask()
|
||
|
r.ptc_wg.Wait()
|
||
|
conn.Close() // don't care about double close. it could have been closed in StopTask
|
||
|
}
|
||
|
|
||
|
func (r* ClientRoute) ReportEvent (pts_id uint32, event_type PACKET_KIND, event_data []byte) error {
|
||
|
switch event_type {
|
||
|
case PACKET_KIND_PEER_STARTED:
|
||
|
go r.ConnectToPeer(pts_id)
|
||
|
|
||
|
// TODO: other types
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
func (cts *ServerConn) AddNewClientRoute(route_id uint32, addr *net.TCPAddr, proto ROUTE_PROTO) (*ClientRoute, error) {
|
||
|
var r *ClientRoute
|
||
|
|
||
|
cts.route_mtx.Lock()
|
||
|
if cts.routes[route_id] != nil {
|
||
|
cts.route_mtx.Unlock()
|
||
|
return nil, fmt.Errorf ("existent route id - %d", route_id)
|
||
|
}
|
||
|
r = NewClientRoute(cts, route_id, addr, proto)
|
||
|
cts.routes[route_id] = r
|
||
|
cts.route_mtx.Unlock()
|
||
|
|
||
|
fmt.Printf ("added client route.... %d -> %d\n", route_id, len(cts.routes))
|
||
|
go r.RunTask()
|
||
|
return r, nil
|
||
|
}
|
||
|
|
||
|
func (cts *ServerConn) RemoveClientRoute (route_id uint32) error {
|
||
|
var r *ClientRoute
|
||
|
var ok bool
|
||
|
|
||
|
cts.route_mtx.Lock()
|
||
|
r, ok = cts.routes[route_id]
|
||
|
if (!ok) {
|
||
|
cts.route_mtx.Unlock()
|
||
|
return fmt.Errorf ("non-existent route id - %d", route_id)
|
||
|
}
|
||
|
delete(cts.routes, route_id)
|
||
|
cts.route_mtx.Unlock()
|
||
|
|
||
|
r.StopTask() // TODO: make this unblocking or blocking?
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
func (cts *ServerConn) AddClientRoutes (peer_addrs []string) error {
|
||
|
var i int
|
||
|
var v string
|
||
|
var addr *net.TCPAddr
|
||
|
var proto ROUTE_PROTO
|
||
|
var r *ClientRoute
|
||
|
var err error
|
||
|
|
||
|
for i, v = range peer_addrs {
|
||
|
addr, err = net.ResolveTCPAddr(NET_TYPE_TCP, v)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("unable to resovle %s - %s", v, err.Error())
|
||
|
}
|
||
|
|
||
|
if addr.IP.To4() != nil {
|
||
|
proto = ROUTE_PROTO_TCP4
|
||
|
} else {
|
||
|
proto = ROUTE_PROTO_TCP6
|
||
|
}
|
||
|
|
||
|
_, err = cts.AddNewClientRoute(uint32(i), addr, proto)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("unable to add client route for %s", addr)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, r = range cts.routes {
|
||
|
err = cts.cli.psc.Send(MakeRouteStartPacket(r.id, r.proto, addr.String()))
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("unable to send route-start packet - %s", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
func (cts *ServerConn) ReportEvent (route_id uint32, pts_id uint32, event_type PACKET_KIND, event_data []byte) error {
|
||
|
var r *ClientRoute
|
||
|
var ok bool
|
||
|
|
||
|
cts.route_mtx.Lock()
|
||
|
r, ok = cts.routes[route_id]
|
||
|
if (!ok) {
|
||
|
cts.route_mtx.Unlock()
|
||
|
return fmt.Errorf ("non-existent route id - %d", route_id)
|
||
|
}
|
||
|
cts.route_mtx.Unlock()
|
||
|
|
||
|
return r.ReportEvent(pts_id, event_type, event_data)
|
||
|
}
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
func (r *ClientRoute) AddNewClientPeerConn (c* net.TCPConn) (*ClientPeerConn, error) {
|
||
|
var ptc *ClientPeerConn
|
||
|
var ok bool
|
||
|
var start_id uint32
|
||
|
|
||
|
r.ptc_mtx.Lock()
|
||
|
defer r.ptc_mtx.Unlock()
|
||
|
|
||
|
if len(r.ptc_map) >= r.ptc_limit {
|
||
|
return nil, fmt.Errorf("peer-to-client connection table full")
|
||
|
}
|
||
|
|
||
|
start_id = r.ptc_last_id
|
||
|
for {
|
||
|
_, ok = r.ptc_map[r.ptc_last_id]
|
||
|
if !ok {
|
||
|
break
|
||
|
}
|
||
|
r.ptc_last_id++
|
||
|
if r.ptc_last_id == start_id {
|
||
|
// unlikely to happen but it cycled through the whole range.
|
||
|
return nil, fmt.Errorf("failed to assign peer-to-table connection id")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ptc = NewClientPeerConn(r, c, r.ptc_last_id)
|
||
|
r.ptc_map[ptc.conn_id] = ptc
|
||
|
r.ptc_last_id++
|
||
|
|
||
|
return ptc, nil
|
||
|
}
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
func (c *Client) AddNewServerConn(addr *net.TCPAddr, psc Hodu_PacketStreamClient) (*ServerConn, error) {
|
||
|
var cts ServerConn
|
||
|
var ok bool
|
||
|
|
||
|
cts.cli = c
|
||
|
cts.routes = make(ClientRouteMap)
|
||
|
cts.saddr = addr
|
||
|
cts.psc = psc
|
||
|
|
||
|
cts.stop_req.Store(false)
|
||
|
cts.greeted = false
|
||
|
|
||
|
c.cts_mtx.Lock()
|
||
|
defer c.cts_mtx.Unlock()
|
||
|
|
||
|
_, ok = c.cts_map[addr]
|
||
|
if ok {
|
||
|
return nil, fmt.Errorf("existing server - %s", addr.String())
|
||
|
}
|
||
|
|
||
|
c.cts_map[addr] = &cts;
|
||
|
fmt.Printf ("ADD total servers %d\n", len(c.cts_map));
|
||
|
return &cts, nil
|
||
|
}
|
||
|
|
||
|
func (c *Client) RemoveServerConn(cts *ServerConn) {
|
||
|
c.cts_mtx.Lock()
|
||
|
delete(c.cts_map, cts.saddr)
|
||
|
fmt.Printf ("REMOVE total servers %d\n", len(c.cts_map));
|
||
|
c.cts_mtx.Unlock()
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
func NewClient(cfg *ClientConfig, tlscfg *tls.Config) (*Client, error) {
|
||
|
var c Client
|
||
|
var saddr *net.TCPAddr
|
||
|
var err error
|
||
|
|
||
|
if len(cfg.peer_addrs) < 0 || len(cfg.peer_addrs) > int(^uint16(0)) { // TODO: change this check... not really right...
|
||
|
return nil, fmt.Errorf("no peer addresses or too many peer addresses")
|
||
|
}
|
||
|
|
||
|
saddr, err = net.ResolveTCPAddr(NET_TYPE_TCP, cfg.server_addr)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("unable to resolve %s - %s", cfg.server_addr, err.Error())
|
||
|
}
|
||
|
|
||
|
c.cfg = cfg
|
||
|
c.tlscfg = tlscfg
|
||
|
c.saddr = saddr
|
||
|
c.cts_map = make(ServerConnMap) // TODO: make it configurable...
|
||
|
c.stop_req.Store(false)
|
||
|
|
||
|
return &c, nil
|
||
|
}
|
||
|
|
||
|
func (c *Client) RunTask(ctx context.Context) {
|
||
|
var conn *grpc.ClientConn
|
||
|
var cts *ServerConn
|
||
|
var err error
|
||
|
|
||
|
// TODO: HANDLE connection timeout..
|
||
|
// ctx, _/*cancel*/ := context.WithTimeout(context.Background(), time.Second)
|
||
|
conn, err = grpc.NewClient(c.saddr.String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||
|
if err != nil {
|
||
|
// TODO: logging
|
||
|
fmt.Printf("ERROR - unable to connect to %s - %s", c.cfg.server_addr, err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
c.sc = conn
|
||
|
c.sg = NewHoduClient(conn)
|
||
|
|
||
|
c.psc, err = c.sg.PacketStream(ctx) // TODO: accept external context and use it.L
|
||
|
if err != nil {
|
||
|
conn.Close()
|
||
|
fmt.Printf ("failed to get the packet stream - %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cts, err = c.AddNewServerConn(c.saddr, c.psc)
|
||
|
if err != nil {
|
||
|
conn.Close()
|
||
|
fmt.Printf ("failed to register connection to server - %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = cts.AddClientRoutes(c.cfg.peer_addrs)
|
||
|
if err != nil {
|
||
|
conn.Close()
|
||
|
fmt.Printf("unable to make client routes - %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for {
|
||
|
var pkt *Packet
|
||
|
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
fmt.Printf("context doine... error - %s\n", ctx.Err().Error())
|
||
|
default:
|
||
|
// no other case is ready.
|
||
|
// without the default case, the select construct would block
|
||
|
}
|
||
|
|
||
|
pkt, err = c.psc.Recv()
|
||
|
if err == io.EOF {
|
||
|
// return will close stream from server side
|
||
|
fmt.Printf("server disconnected\n")
|
||
|
break
|
||
|
}
|
||
|
if err != nil {
|
||
|
fmt.Printf("server receive error - %s\n", err.Error())
|
||
|
break
|
||
|
}
|
||
|
|
||
|
switch pkt.Kind {
|
||
|
case PACKET_KIND_ROUTE_STARTED:
|
||
|
var x *Packet_Route
|
||
|
var ok bool
|
||
|
x, ok = pkt.U.(*Packet_Route)
|
||
|
if ok {
|
||
|
fmt.Printf ("SERVER LISTENING ON %s\n", x.Route.AddrStr);
|
||
|
err = cts.ReportEvent(x.Route.RouteId, 0, pkt.Kind, nil)
|
||
|
if err != nil {
|
||
|
// TODO:
|
||
|
} else {
|
||
|
// TODO:
|
||
|
}
|
||
|
} else {
|
||
|
// TODO: send invalid request... or simply keep quiet?
|
||
|
}
|
||
|
|
||
|
case PACKET_KIND_ROUTE_STOPPED:
|
||
|
var x *Packet_Route
|
||
|
var ok bool
|
||
|
x, ok = pkt.U.(*Packet_Route)
|
||
|
if ok {
|
||
|
err = cts.ReportEvent(x.Route.RouteId, 0, pkt.Kind, nil)
|
||
|
if err != nil {
|
||
|
// TODO:
|
||
|
} else {
|
||
|
// TODO:
|
||
|
}
|
||
|
} else {
|
||
|
// TODO: send invalid request... or simply keep quiet?
|
||
|
}
|
||
|
|
||
|
case PACKET_KIND_PEER_STARTED:
|
||
|
// the connection from the client to a peer has been established
|
||
|
var x *Packet_Peer
|
||
|
var ok bool
|
||
|
x, ok = pkt.U.(*Packet_Peer)
|
||
|
if ok {
|
||
|
err = cts.ReportEvent(x.Peer.RouteId, x.Peer.PeerId, PACKET_KIND_PEER_STARTED, nil)
|
||
|
if err != nil {
|
||
|
// TODO:
|
||
|
} else {
|
||
|
// TODO:
|
||
|
}
|
||
|
} else {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
case PACKET_KIND_PEER_STOPPED:
|
||
|
// the connection from the client to a peer has been established
|
||
|
var x *Packet_Peer
|
||
|
var ok bool
|
||
|
x, ok = pkt.U.(*Packet_Peer)
|
||
|
if ok {
|
||
|
err = cts.ReportEvent(x.Peer.RouteId, x.Peer.PeerId, PACKET_KIND_PEER_STOPPED, nil)
|
||
|
if err != nil {
|
||
|
// TODO:
|
||
|
} else {
|
||
|
// TODO:
|
||
|
}
|
||
|
} else {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
case PACKET_KIND_PEER_DATA:
|
||
|
// the connection from the client to a peer has been established
|
||
|
var x *Packet_Data
|
||
|
var ok bool
|
||
|
x, ok = pkt.U.(*Packet_Data)
|
||
|
if ok {
|
||
|
err = cts.ReportEvent(x.Data.RouteId, x.Data.PeerId, PACKET_KIND_PEER_DATA, x.Data.Data)
|
||
|
if err != nil {
|
||
|
// TODO:
|
||
|
} else {
|
||
|
// TODO:
|
||
|
}
|
||
|
} else {
|
||
|
// TODO
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//done:
|
||
|
c.ReqStop() // just in case...
|
||
|
c.wg.Wait()
|
||
|
c.sc.Close()
|
||
|
}
|
||
|
|
||
|
func (c *Client) ReqStop() {
|
||
|
if c.stop_req.CompareAndSwap(false, true) {
|
||
|
// TODO: notify the server.. send term command???
|
||
|
c.sc.Close()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
const rootCert = `-----BEGIN CERTIFICATE-----
|
||
|
MIIB+TCCAZ+gAwIBAgIJAL05LKXo6PrrMAoGCCqGSM49BAMCMFkxCzAJBgNVBAYT
|
||
|
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
|
||
|
aXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEyMDgxNDAxMTNa
|
||
|
Fw0yNTEyMDUxNDAxMTNaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
|
||
|
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM
|
||
|
CWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHGaaHVod0hLOR4d
|
||
|
66xIrtS2TmEmjSFjt+DIEcb6sM9RTKS8TZcdBnEqq8YT7m2sKbV+TEq9Nn7d9pHz
|
||
|
pWG2heWjUDBOMB0GA1UdDgQWBBR0fqrecDJ44D/fiYJiOeBzfoqEijAfBgNVHSME
|
||
|
GDAWgBR0fqrecDJ44D/fiYJiOeBzfoqEijAMBgNVHRMEBTADAQH/MAoGCCqGSM49
|
||
|
BAMCA0gAMEUCIEKzVMF3JqjQjuM2rX7Rx8hancI5KJhwfeKu1xbyR7XaAiEA2UT7
|
||
|
1xOP035EcraRmWPe7tO0LpXgMxlh2VItpc2uc2w=
|
||
|
-----END CERTIFICATE-----
|
||
|
`
|
||
|
|
||
|
func client_main(server_addr string, peer_addrs []string) error {
|
||
|
var c *Client
|
||
|
var err error
|
||
|
var cert_pool *x509.CertPool
|
||
|
var tlscfg *tls.Config
|
||
|
var cc ClientConfig
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
cert_pool = x509.NewCertPool()
|
||
|
ok := cert_pool.AppendCertsFromPEM([]byte(rootCert))
|
||
|
if !ok {
|
||
|
log.Fatal("failed to parse root certificate")
|
||
|
}
|
||
|
tlscfg = &tls.Config{RootCAs: cert_pool, ServerName: "localhost", InsecureSkipVerify: true}
|
||
|
|
||
|
cc.server_addr = server_addr
|
||
|
cc.peer_addrs = peer_addrs
|
||
|
c, err = NewClient(&cc, tlscfg)
|
||
|
if err != nil {
|
||
|
fmt.Printf("failed create client - %s\n", err.Error())
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
wg.Add(1)
|
||
|
go c.RunTask(context.Background());
|
||
|
|
||
|
wg.Wait();
|
||
|
return nil
|
||
|
}
|