parseSquidAccessLog parses a squid access log file and extracts domain information
(logPath string, verbose bool)
| 49 | |
| 50 | // parseSquidAccessLog parses a squid access log file and extracts domain information |
| 51 | func parseSquidAccessLog(logPath string, verbose bool) (*DomainAnalysis, error) { |
| 52 | accessLogLog.Printf("Parsing squid access log: %s", logPath) |
| 53 | |
| 54 | file, err := os.Open(logPath) |
| 55 | if err != nil { |
| 56 | accessLogLog.Printf("Failed to open access log %s: %v", logPath, err) |
| 57 | return nil, fmt.Errorf("failed to open access log: %w", err) |
| 58 | } |
| 59 | defer file.Close() |
| 60 | |
| 61 | analysis := &DomainAnalysis{} |
| 62 | |
| 63 | allowedDomainsSet := make(map[string]struct { |
| 64 | }) |
| 65 | blockedDomainsSet := make(map[string]struct { |
| 66 | }) |
| 67 | |
| 68 | scanner := bufio.NewScanner(file) |
| 69 | for scanner.Scan() { |
| 70 | line := strings.TrimSpace(scanner.Text()) |
| 71 | if line == "" || strings.HasPrefix(line, "#") { |
| 72 | continue |
| 73 | } |
| 74 | |
| 75 | entry, err := parseSquidLogLine(line) |
| 76 | if err != nil { |
| 77 | if verbose { |
| 78 | fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to parse log line: %v", err))) |
| 79 | } |
| 80 | continue |
| 81 | } |
| 82 | |
| 83 | analysis.TotalRequests++ |
| 84 | |
| 85 | // Extract domain from URL |
| 86 | domain := stringutil.ExtractDomainFromURL(entry.URL) |
| 87 | if domain == "" { |
| 88 | continue |
| 89 | } |
| 90 | |
| 91 | // Determine if request was allowed or blocked based on status code |
| 92 | // Squid typically returns: |
| 93 | // - 200, 206, 304: Allowed/successful |
| 94 | // - 403: Forbidden (blocked by ACL) |
| 95 | // - 407: Proxy authentication required |
| 96 | // - 502, 503: Connection/upstream errors |
| 97 | statusCode := entry.Status |
| 98 | isAllowed := statusCode == "TCP_HIT/200" || statusCode == "TCP_MISS/200" || |
| 99 | statusCode == "TCP_REFRESH_MODIFIED/200" || statusCode == "TCP_IMS_HIT/304" || |
| 100 | strings.Contains(statusCode, "/200") || strings.Contains(statusCode, "/206") || |
| 101 | strings.Contains(statusCode, "/304") |
| 102 | |
| 103 | if isAllowed { |
| 104 | analysis.AllowedCount++ |
| 105 | if !setutil.Contains(allowedDomainsSet, domain) { |
| 106 | allowedDomainsSet[domain] = struct { |
| 107 | }{} |
| 108 | analysis.AllowedDomains = append(analysis.AllowedDomains, domain) |