package main import ( "context" "fmt" "net" "os" "strings" "time" "github.com/Microsoft/go-winio" pb "github.com/grmrgecko/virtual-vxlan/vxlan" log "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) // Allows go generate to compile the protobuf to golang. //go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./vxlan/vxlan.proto // GRPC server structure. type GRPCServer struct { pb.UnimplementedVxlanServer RPCPath string server *grpc.Server } // Start serving GRPC requests. func (s *GRPCServer) Serve(li net.Listener) { err := s.server.Serve(li) if err != nil { log.Errorf("Error serving grpc: %v", err) } } // Stop GRPC server. func (s *GRPCServer) Close() { s.server.Stop() } func pipeDialer(ctx context.Context, addr string) (net.Conn, error) { // The addr argument passed by gRPC will be the string we pass to grpc.DialContext (e.g., namedPipePath). // winio.DialPipe handles connecting to the named pipe and returns a net.Conn. // You may need to use winio.DialPipeContext for a cancellable context, but DialPipe // is simpler for a basic example and relies on the deadline set by the gRPC call. return winio.DialPipe(addr, nil) } func RPCCleanPath(rpcPath string) { if !strings.HasPrefix(rpcPath, `\\.\`) { // Check if the RPC socket already exists. _, err := os.Stat(rpcPath) if err == nil { // If the socket exists, see if its listening. _, err = net.Dial("unix", rpcPath) // If its not listening, remove it to allow us to start. if err != nil { os.Remove(rpcPath) } } } } // Start GRPC server. func NewGRPCServer(rpcPath string) (s *GRPCServer, err error) { // Verify another server doesn't exist. if app.grpcServer != nil { return nil, fmt.Errorf("grpc server is already running") } // Connect to RPC path. var li net.Listener if strings.HasPrefix(rpcPath, `\\.\`) { li, err = winio.ListenPipe(rpcPath, nil) } else { li, err = net.Listen("unix", rpcPath) } if err != nil { return nil, fmt.Errorf("failed to listen on socket: %v", err) } // Setup server. s = new(GRPCServer) s.server = grpc.NewServer() // Register the vxlan service to this server. pb.RegisterVxlanServer(s.server, s) // Update the global app gRPC server. app.grpcServer = s // Start serving requests. go s.Serve(li) return s, nil } // Start a connection to the gRPC Server. func NewGRPCClient() (c pb.VxlanClient, conn *grpc.ClientConn, err error) { // Read the minimal config. config := ReadMinimalConfig() // Start an gRPC client connection to the unix socket. if strings.HasPrefix(config.RPCPath, `\\.\`) { dialOption := grpc.WithContextDialer(pipeDialer) conn, err = grpc.DialContext( context.Background(), config.RPCPath, dialOption, grpc.WithTransportCredentials(insecure.NewCredentials()), // Add a timeout for the initial connection grpc.WithBlock(), grpc.WithTimeout(5*time.Second), ) } else { conn, err = grpc.NewClient(fmt.Sprintf("unix:%s", config.RPCPath), grpc.WithTransportCredentials(insecure.NewCredentials())) } // If connection is successful, provide client to the vxlan service. if err == nil { c = pb.NewVxlanClient(conn) } return }