Draw the actual rest of the fucking owl.

Change-Id: Ia04fb49ebbe3a5afccc57e62f6335e35b45192fe
diff --git a/bgpwtf/cccampix/verifier/service.go b/bgpwtf/cccampix/verifier/service.go
new file mode 100644
index 0000000..e00b85f
--- /dev/null
+++ b/bgpwtf/cccampix/verifier/service.go
@@ -0,0 +1,362 @@
+package main
+
+import (
+	"context"
+	"encoding/hex"
+	"fmt"
+	"io"
+
+	pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+	"code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model"
+	"github.com/golang/glog"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+func (s *service) ProcessorStatus(ctx context.Context, req *pb.ProcessorStatusRequest) (*pb.ProcessorStatusResponse, error) {
+	s.processorsMu.RLock()
+	defer s.processorsMu.RUnlock()
+
+	res := &pb.ProcessorStatusResponse{
+		Processors: make([]*pb.ProcessorStatusResponse_Processor, len(s.processors)),
+	}
+
+	i := 0
+	for _, p := range s.processors {
+		res.Processors[i] = &pb.ProcessorStatusResponse_Processor{
+			Name:    p.name,
+			Status:  pb.ProcessorStatusResponse_Processor_STATUS_OK,
+			LastRun: 0,
+			NextRun: 0,
+		}
+
+		if p.lastRun != nil {
+			res.Processors[i].LastRun = p.lastRun.UnixNano()
+			res.Processors[i].NextRun = p.p.NextRun(*p.lastRun, p.lastErr != nil).UnixNano()
+		}
+
+		if p.lastErr != nil {
+			res.Processors[i].Status = pb.ProcessorStatusResponse_Processor_STATUS_ERROR
+		}
+
+		i += 1
+	}
+	return res, nil
+}
+
+func (s *service) PeerSummary(req *pb.PeerSummaryRequest, stream pb.Verifier_PeerSummaryServer) error {
+	peers, err := s.model.GetCheckablePeers(stream.Context())
+	if err != nil {
+		glog.Errorf("model.GetCheckablePeers: %v", err)
+		return status.Error(codes.Unavailable, "model error")
+	}
+
+	asns := make([]int64, len(peers))
+	asnToRes := make(map[int64]*pb.PeerSummaryResponse)
+
+	for i, peer := range peers {
+		routers := make([]*pb.PeeringDBMember_Router, len(peer.Routers))
+		for i, router := range peer.Routers {
+			routers[i] = &pb.PeeringDBMember_Router{}
+			if router.V4 != nil {
+				routers[i].Ipv4 = router.V4.String()
+			}
+			if router.V6 != nil {
+				routers[i].Ipv6 = router.V6.String()
+			}
+		}
+		p := &pb.PeeringDBMember{
+			Asn:     peer.ASN,
+			Name:    peer.Name,
+			Routers: routers,
+		}
+		res := &pb.PeerSummaryResponse{
+			PeeringdbInfo: p,
+			CheckStatus:   pb.PeerSummaryResponse_STATUS_OK,
+		}
+		asnToRes[peer.ASN] = res
+		asns[i] = peer.ASN
+	}
+
+	checkres, err := s.model.GetPeerCheckResults(stream.Context(), asns)
+	if err != nil {
+		glog.Errorf("GetPeerCheckResults(%v): %v", asns, err)
+		for _, res := range asnToRes {
+			res.CheckStatus = pb.PeerSummaryResponse_STATUS_UNKNOWN
+		}
+	} else {
+		passedChecks := make(map[int64]map[string]bool)
+		for _, c := range checkres {
+			if _, ok := passedChecks[c.PeerASN]; !ok {
+				passedChecks[c.PeerASN] = make(map[string]bool)
+			}
+			passedChecks[c.PeerASN][c.CheckName] = c.Status == model.PeerCheckStatus_Okay
+		}
+
+		for asn, checks := range passedChecks {
+			for _, required := range s.requiredChecks {
+				if !checks[required] {
+					asnToRes[asn].CheckStatus = pb.PeerSummaryResponse_STATUS_FAILED
+					break
+				}
+			}
+		}
+	}
+
+	for _, res := range asnToRes {
+		if err := stream.Send(res); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (s *service) PeerDetails(ctx context.Context, req *pb.PeerDetailsRequest) (*pb.PeerDetailsResponse, error) {
+	if req.Asn <= 0 {
+		return nil, status.Error(codes.InvalidArgument, "asn must be set")
+	}
+
+	res := &pb.PeerDetailsResponse{}
+
+	peeringdb, err := s.model.GetPeeringDBPeer(ctx, req.Asn)
+	if err != nil {
+		glog.Errorf("GetPeeringDBPeer(%v): %v", req.Asn, err)
+		return nil, status.Error(codes.Unavailable, "could not get allowed prefixes")
+	}
+
+	if peeringdb.Asn != req.Asn {
+		return nil, status.Error(codes.NotFound, "no such ASN")
+	}
+
+	res.PeeringdbInfo = peeringdb
+
+	checkres, err := s.model.GetPeerCheckResults(ctx, []int64{req.Asn})
+	if err != nil {
+		glog.Errorf("GetPeerCheckResults(%v): %v", req.Asn, err)
+		return nil, status.Error(codes.Unavailable, "could not get check results")
+	}
+
+	res.Checks = make([]*pb.PeerDetailsResponse_Check, len(checkres))
+	for i, check := range checkres {
+		status := pb.PeerDetailsResponse_Check_STATUS_INVALID
+		switch check.Status {
+		case model.PeerCheckStatus_Okay:
+			status = pb.PeerDetailsResponse_Check_STATUS_OK
+		case model.PeerCheckStatus_SoftFailed:
+			status = pb.PeerDetailsResponse_Check_STATUS_OK
+		case model.PeerCheckStatus_Failed:
+			status = pb.PeerDetailsResponse_Check_STATUS_FAILED
+		}
+		res.Checks[i] = &pb.PeerDetailsResponse_Check{
+			Name:   check.CheckName,
+			Status: status,
+			Time:   check.Time.UnixNano(),
+			Msg:    check.Message,
+		}
+	}
+
+	prefixes, err := s.model.GetAllowedPrefixes(ctx, req.Asn)
+	if err != nil {
+		glog.Errorf("GetAllowedPrefixes(%v): %v", req.Asn, err)
+		return nil, status.Error(codes.Unavailable, "could not get allowed prefixes")
+	}
+
+	res.AllowedPrefixes = make([]*pb.PeerDetailsResponse_AllowedPrefix, len(prefixes))
+	for i, prefix := range prefixes {
+		res.AllowedPrefixes[i] = &pb.PeerDetailsResponse_AllowedPrefix{
+			Prefix:    prefix.Prefix.String(),
+			MaxLength: prefix.MaxLength,
+			Ta:        prefix.TA,
+		}
+	}
+
+	return res, nil
+}
+
+func (s *service) RouterHeartbeat(ctx context.Context, req *pb.RouterHeartbeatRequest) (*pb.RouterHeartbeatResponse, error) {
+	if req.Name == "" {
+		return nil, status.Error(codes.InvalidArgument, "name must be set")
+	}
+
+	pcfgM := make(map[string]*model.PeerConfiguration)
+	pcfgs, err := s.model.GetPeerConfiguration(ctx)
+	if err != nil {
+		glog.Errorf("GetPeerConfiguration: %v", err)
+		return nil, status.Error(codes.Unavailable, "could not get peer configs")
+	}
+
+	for _, pcfg := range pcfgs {
+		ask := fmt.Sprintf("AS%d", pcfg.Peer.ASN)
+		pcfgM[ask] = pcfg
+	}
+
+	peers, err := s.model.GetCheckablePeers(ctx)
+	if err != nil {
+		glog.Errorf("GetChecablePeers: %v", err)
+		return nil, status.Error(codes.Unavailable, "could not get peers")
+	}
+
+	asconfs := make(map[string]*pb.RouterHeartbeatResponse_ASConfig)
+
+	for _, peer := range peers {
+		as := fmt.Sprintf("AS%d", peer.ASN)
+
+		pcfg, ok := pcfgM[as]
+		if !ok {
+			continue
+		}
+
+		asconfs[as] = &pb.RouterHeartbeatResponse_ASConfig{
+			Asn:      peer.ASN,
+			Routers:  make([]*pb.RouterHeartbeatResponse_ASConfig_Router, len(pcfg.Peer.Routers)),
+			Prefixes: []*pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{},
+		}
+
+		glog.Infof("%+v", pcfg.Peer.Routers)
+		for i, r := range pcfg.Peer.Routers {
+			ipv6 := ""
+			if r.V6 != nil {
+				ipv6 = r.V6.String()
+			}
+			ipv4 := ""
+			if r.V4 != nil {
+				ipv4 = r.V4.String()
+			}
+			asconfs[as].Routers[i] = &pb.RouterHeartbeatResponse_ASConfig_Router{
+				Ipv6:     ipv6,
+				Ipv4:     ipv4,
+				Password: r.Config.BGPSecret,
+			}
+		}
+
+		prefixes, err := s.model.GetAllowedPrefixes(ctx, peer.ASN)
+		if err != nil {
+			glog.Errorf("GetAllowedPrefixes(_, %d): %v", peer.ASN, err)
+			return nil, status.Error(codes.Unavailable, "could not get peer prefixes")
+		}
+
+		for _, prefix := range prefixes {
+			asconfs[as].Prefixes = append(asconfs[as].Prefixes, &pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{
+				Prefix:    prefix.Prefix.String(),
+				MaxLength: prefix.MaxLength,
+			})
+		}
+	}
+
+	res := &pb.RouterHeartbeatResponse{
+		AsConfigs: make([]*pb.RouterHeartbeatResponse_ASConfig, len(asconfs)),
+	}
+
+	i := 0
+	for _, asconf := range asconfs {
+		res.AsConfigs[i] = asconf
+		i += 1
+	}
+
+	return res, nil
+}
+
+func (s *service) PeerSecrets(ctx context.Context, req *pb.PeerSecretsRequest) (*pb.PeerSecretsResponse, error) {
+	if req.Asn <= 0 {
+		return nil, status.Error(codes.InvalidArgument, "asn must be set")
+	}
+	pcrs, err := s.model.GetPeerConfiguration(ctx)
+	if err != nil {
+		glog.Errorf("GetPeerConfiguration: %v", err)
+		return nil, status.Error(codes.Unavailable, "error when retrieving peer configs")
+	}
+
+	var pcr *model.PeerConfiguration
+	for _, p := range pcrs {
+		if p.Peer.ASN == req.Asn {
+			pcr = p
+			break
+		}
+	}
+
+	if pcr == nil {
+		return nil, status.Error(codes.NotFound, "no such ASN")
+	}
+
+	plain := fmt.Sprintf(`
+Hello AS %d!
+
+Here are your config settings:
+`, req.Asn)
+
+	for _, router := range pcr.Peer.Routers {
+		if router.V4 != nil {
+			plain += fmt.Sprintf(`
+our addresses: 185.236.243.5 (rs1), 185.236.243.6 (rs2)
+      our asn: 208521
+ your address: %s
+     your asn: %d
+   bgp secret: %s
+`, router.V4.String(), req.Asn, router.Config.BGPSecret)
+		}
+		if router.V6 != nil {
+			plain += fmt.Sprintf(`
+our addresses: 2a0d:eb02:4242:4242::5 (rs1), 2a0d:eb02:4242:4242::6 (rs2)
+      our asn: 208521
+ your address: %s
+     your asn: %d
+   bgp secret: %s
+`, router.V6.String(), req.Asn, router.Config.BGPSecret)
+		}
+	}
+
+	plain += `
+Happy exchanging!
+bgp.wtf (DECT: 4735)
+`
+
+	key, err := s.model.GetPeerPGPKey(ctx, req.Asn)
+	if err != nil {
+		glog.Errorf("GetPeerPGPKey: %v", err)
+		return nil, status.Error(codes.Unavailable, "could not get pgp key")
+	}
+
+	plainB := []byte(plain)
+
+	stream, err := s.pgp.Encrypt(ctx)
+	if err != nil {
+		glog.Errorf("Encrypt: %v", err)
+		return nil, status.Error(codes.Unavailable, "could not encrypt")
+	}
+
+	fingerprint, err := hex.DecodeString(key.Fingerprint)
+	if err != nil {
+		glog.Errorf("Invalid fingerprint %q: %v", key.Fingerprint, err)
+		return nil, status.Error(codes.Unavailable, "could not encrypt")
+	}
+
+	reqE := &pb.EncryptRequest{
+		Data:        plainB,
+		Info:        pb.EncryptRequest_CHUNK_LAST,
+		Fingerprint: fingerprint,
+	}
+
+	if err := stream.Send(reqE); err != nil {
+		glog.Errorf("Encrypt.Send: %v", err)
+		return nil, status.Error(codes.Unavailable, "could not encrypt")
+	}
+	stream.CloseSend()
+
+	cipher := []byte{}
+	for {
+		in, err := stream.Recv()
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			glog.Errorf("Encrypt.Recv: %v", err)
+			return nil, status.Error(codes.Unavailable, "could not encrypt")
+		}
+		cipher = append(cipher, in.Data...)
+	}
+
+	return &pb.PeerSecretsResponse{
+		PgpData: cipher,
+	}, nil
+}