Files
2026-07-13 12:31:02 +08:00

159 lines
4.3 KiB
Go

package operations
import (
"crypto/tls"
"fmt"
"net"
"strings"
"time"
"service-operation/types"
)
type SSLOperation struct {
timeout time.Duration
}
func NewSSLOperation(timeout time.Duration) *SSLOperation {
return &SSLOperation{
timeout: timeout,
}
}
func (op *SSLOperation) Execute(domain string) (*types.OperationResult, error) {
startTime := time.Now()
// Clean and normalize domain
domain = op.normalizeDomain(domain)
// Validate domain format
if domain == "" {
return op.createErrorResult(domain, startTime, "domain cannot be empty")
}
// Add port if not present
host := domain
if !strings.Contains(host, ":") {
host = host + ":443"
}
// Set up TLS connection with timeout
dialer := &net.Dialer{
Timeout: op.timeout,
}
// Create TLS config with proper verification
tlsConfig := &tls.Config{
ServerName: strings.Split(host, ":")[0],
InsecureSkipVerify: false,
MinVersion: tls.VersionTLS12,
}
// Attempt TLS connection
conn, err := tls.DialWithDialer(dialer, "tcp", host, tlsConfig)
endTime := time.Now()
responseTime := endTime.Sub(startTime)
if err != nil {
return &types.OperationResult{
Type: types.OperationSSL,
Host: strings.Split(host, ":")[0],
Success: false,
ResponseTime: responseTime,
Error: fmt.Sprintf("TLS connection failed: %v", err),
StartTime: startTime,
EndTime: endTime,
}, nil
}
defer conn.Close()
// Get certificate chain information
state := conn.ConnectionState()
if len(state.PeerCertificates) == 0 {
return &types.OperationResult{
Type: types.OperationSSL,
Host: strings.Split(host, ":")[0],
Success: false,
ResponseTime: responseTime,
Error: "No certificates found in chain",
StartTime: startTime,
EndTime: endTime,
}, nil
}
cert := state.PeerCertificates[0]
hostname := strings.Split(host, ":")[0]
// Perform comprehensive certificate validation
validationError := op.validateCertificate(cert, hostname)
// Calculate days left until expiration
daysLeft := int(time.Until(cert.NotAfter).Hours() / 24)
// Extract Subject Alternative Names
sans := op.extractSANs(cert)
// Get resolved IP address
resolvedIP := op.getResolvedIP(hostname)
// Extract certificate algorithm information
algorithm := op.getCertificateAlgorithm(cert)
// Extract issuer organization (O=) instead of full distinguished name
issuerOrganization := op.extractIssuerOrganization(cert.Issuer)
// Extract subject organization (O=) instead of full distinguished name
subjectOrganization := op.extractSubjectOrganization(cert.Subject)
// Check if certificate is valid (not expired and passes validation)
isValid := validationError == nil && time.Now().Before(cert.NotAfter) && time.Now().After(cert.NotBefore)
// Build detailed error message if validation failed
errorMsg := ""
if validationError != nil {
errorMsg = validationError.Error()
} else if time.Now().After(cert.NotAfter) {
errorMsg = "Certificate has expired"
} else if time.Now().Before(cert.NotBefore) {
errorMsg = "Certificate is not yet valid"
}
// Create comprehensive result
result := &types.OperationResult{
Type: types.OperationSSL,
Host: hostname,
Success: isValid,
ResponseTime: responseTime,
Error: errorMsg,
StartTime: startTime,
EndTime: endTime,
// SSL specific fields - using organization instead of full DN
SSLValidFrom: cert.NotBefore,
SSLValidTill: cert.NotAfter,
SSLDaysLeft: daysLeft,
SSLIssuer: issuerOrganization, // Now shows "Google Trust Services" instead of full DN
SSLSubject: subjectOrganization, // Now shows organization instead of full DN
SSLSerialNumber: cert.SerialNumber.String(),
SSLAlgorithm: algorithm,
SSLSANs: strings.Join(sans, ","),
SSLResolvedIP: resolvedIP,
}
return result, nil
}
// createErrorResult creates a standardized error result
func (op *SSLOperation) createErrorResult(domain string, startTime time.Time, errorMsg string) (*types.OperationResult, error) {
return &types.OperationResult{
Type: types.OperationSSL,
Host: domain,
Success: false,
ResponseTime: time.Since(startTime),
Error: errorMsg,
StartTime: startTime,
EndTime: time.Now(),
}, nil
}