package main import ( "encoding/json" "io" "log" "net" "net/http" "os" "github.com/kardianos/service" ) const ( port = "8999" logFile = "hostname_service.log" logFlags = log.Ldate | log.Ltime | log.Lshortfile ) var logger *log.Logger // Request structure type Request struct { IP string `json:"ip"` } // Response structure type Response struct { Hostname string `json:"hostname,omitempty"` Error string `json:"error,omitempty"` } type program struct { server *http.Server } func (p *program) Start(s service.Service) error { // Start should not block go p.run() return nil } func (p *program) run() { mux := http.NewServeMux() mux.HandleFunc("/", handleRequest) p.server = &http.Server{ Addr: ":" + port, Handler: mux, } logger.Println("Starting server on port", port) err := p.server.ListenAndServe() if err != nil && err != http.ErrServerClosed { logger.Fatalf("Server failed: %v", err) } } func (p *program) Stop(s service.Service) error { logger.Println("Stopping server...") if p.server != nil { return p.server.Close() } return nil } func handleRequest(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { logger.Printf("Rejected %s request from %s", r.Method, r.RemoteAddr) http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } body, err := io.ReadAll(r.Body) if err != nil { logger.Printf("Failed to read request: %v", err) http.Error(w, "Invalid request", http.StatusBadRequest) return } defer r.Body.Close() var req Request err = json.Unmarshal(body, &req) if err != nil || req.IP == "" { logger.Printf("Invalid JSON or missing IP: %s", string(body)) respond(w, Response{Error: "Invalid JSON or missing 'ip'"}) return } logger.Printf("Received IP lookup request: %s", req.IP) names, err := net.LookupAddr(req.IP) if err != nil || len(names) == 0 { logger.Printf("Hostname not found for IP: %s", req.IP) respond(w, Response{Error: "Hostname not found"}) return } hostname := names[0] logger.Printf("Resolved %s to %s", req.IP, hostname) respond(w, Response{Hostname: hostname}) } func respond(w http.ResponseWriter, resp Response) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } func setupLogging() { file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatalf("Unable to open log file: %v", err) } logger = log.New(file, "", logFlags) } func main() { setupLogging() svcConfig := &service.Config{ Name: "HostnameGoHTTPService", DisplayName: "Hostname Go HTTP Resolver Service", Description: "Resolves hostnames from IPs via POST JSON requests on port 8999.", } prg := &program{} s, err := service.New(prg, svcConfig) if err != nil { logger.Fatalf("Failed to create service: %v", err) } // Logging via service (or fallback) errs := make(chan error, 5) loggerAdapter, err := s.Logger(errs) if err == nil { logger.SetOutput(serviceLogWriter{loggerAdapter}) } if len(os.Args) > 1 && os.Args[1] == "run" { // Standalone mode for testing prg.run() } else { err = s.Run() if err != nil { logger.Fatalf("Service failed: %v", err) } } } // Adapter to redirect standard logger to service log type serviceLogWriter struct { logger service.Logger } func (w serviceLogWriter) Write(p []byte) (n int, err error) { w.logger.Info(string(p)) return len(p), nil }