package matcher import ( "crypto/tls" "net" "strings" "time" omd "git.loafle.net/overflow/model/discovery" omm "git.loafle.net/overflow/model/meta" osm "git.loafle.net/overflow/service_matcher-go" ouej "git.loafle.net/overflow/util-go/encoding/json" ounp "git.loafle.net/overflow/util-go/net/ping" "git.loafle.net/overflow_scanner/probe/internal/matcher" ) func Ping(service *omd.Service, pingOption ounp.Option) (ounp.Result, error) { portNumber, err := ouej.NumberToInt(service.Port.PortNumber) if nil != err { return nil, err } matchCtx := osm.NewMatchCtx(service.Port.Host.Address, portNumber) _matcher := matcher.GetMatcherByKey(service.Key) if _matcher.IsPrePacket() { return processPrepacket(service, pingOption, matchCtx, _matcher), nil } return processPostpacket(service, pingOption, matchCtx, _matcher), nil } func processPrepacket(service *omd.Service, pingOption ounp.Option, matchCtx *osm.MatchCtx, _matcher osm.Matcher) ounp.Result { pingResult := &ounp.PingResult{ Responses: make(map[int]ounp.Response, 0), Summary: &ounp.PingSummary{}, } buf := make([]byte, 1024) LOOP: for indexR := 0; indexR < pingOption.GetCount(); indexR++ { startTime := time.Now() matchCtx.InitAttribute() conn, err := getConnection(service, pingOption) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } continue LOOP } if err := conn.SetReadDeadline(time.Now().Add(time.Duration(pingOption.GetDeadline()) * time.Second)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } conn.Close() continue LOOP } n, err := conn.Read(buf) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } conn.Close() continue LOOP } if err := _matcher.Match(matchCtx, 0, osm.NewPacket(buf, n)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } conn.Close() continue LOOP } packetCount := _matcher.PacketCount(matchCtx) if 0 == packetCount { pingResult.Responses[indexR] = &ounp.PingResponse{ Time: 0, } conn.Close() continue LOOP } isMatched := false INNER_LOOP: for indexM := 0; indexM < packetCount; indexM++ { _packet := _matcher.Packet(matchCtx, indexM) if err := conn.SetWriteDeadline(time.Now().Add(time.Duration(pingOption.GetDeadline()) * time.Second)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } _, err := conn.Write(_packet.Buffer) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } if err := conn.SetReadDeadline(time.Now().Add(time.Duration(pingOption.GetDeadline()) * time.Second)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } n, err := conn.Read(buf) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } if err := _matcher.Match(matchCtx, indexM+1, osm.NewPacket(buf, n)); nil == err { isMatched = true } else { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: "Protocol not match", } break INNER_LOOP } } conn.Close() if isMatched { elapsed := time.Since(startTime) pingResult.Responses[indexR] = &ounp.PingResponse{ Time: float32(elapsed), } } } return pingResult } func processPostpacket(service *omd.Service, pingOption ounp.Option, matchCtx *osm.MatchCtx, _matcher osm.Matcher) ounp.Result { pingResult := &ounp.PingResult{ Responses: make(map[int]ounp.Response, 0), Summary: &ounp.PingSummary{}, } buf := make([]byte, 1024) LOOP: for indexR := 0; indexR < pingOption.GetCount(); indexR++ { startTime := time.Now() matchCtx.InitAttribute() packetCount := _matcher.PacketCount(matchCtx) if 0 == packetCount { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: "Protocol not match", } continue LOOP } conn, err := getConnection(service, pingOption) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } continue LOOP } isMatched := false INNER_LOOP: for indexM := 0; indexM < packetCount; indexM++ { _packet := _matcher.Packet(matchCtx, indexM) if err := conn.SetWriteDeadline(time.Now().Add(time.Duration(pingOption.GetDeadline()) * time.Second)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } _, err := conn.Write(_packet.Buffer) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } if err := conn.SetReadDeadline(time.Now().Add(time.Duration(pingOption.GetDeadline()) * time.Second)); nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } n, err := conn.Read(buf) if nil != err { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: err.Error(), } break INNER_LOOP } if err := _matcher.Match(matchCtx, indexM+1, osm.NewPacket(buf, n)); nil == err { if packetCount-1 == indexM { isMatched = true break INNER_LOOP } } else { pingResult.Responses[indexR] = &ounp.PingResponse{ Error: "Protocol not match", } break INNER_LOOP } } conn.Close() if isMatched { elapsed := time.Since(startTime) pingResult.Responses[indexR] = &ounp.PingResponse{ Time: float32(elapsed), } } } return pingResult } func getConnection(service *omd.Service, pingOption ounp.Option) (net.Conn, error) { addr := net.JoinHostPort(service.Port.Host.Address, service.Port.PortNumber.String()) portType := strings.ToLower(service.Port.MetaPortType.Key) switch omm.ToMetaCryptoTypeEnum(service.MetaCryptoType) { case omm.MetaCryptoTypeEnumTLS: dialer := &net.Dialer{ Timeout: time.Duration(pingOption.GetDeadline()) * time.Second, } return tls.DialWithDialer( dialer, portType, addr, &tls.Config{ InsecureSkipVerify: true, ServerName: service.Port.Host.Address, }, ) default: return net.DialTimeout(portType, addr, time.Duration(pingOption.GetDeadline())*time.Second) } }