Files
2025-05-21 13:51:41 -05:00

134 lines
4.6 KiB
Python

from http.server import BaseHTTPRequestHandler, HTTPServer
from dotenv import load_dotenv
import socket
import json
import threading
import logging
import win32serviceutil
import win32service
import win32event
import servicemanager
import os
import sys
# Load environment variables from .env file
load_dotenv()
HOST = os.getenv("HOST", "0.0.0.0")
PORT = int(os.getenv("PORT", 8999))
LOG_FILE = os.path.join(os.path.dirname(__file__), 'hostname_service.log')
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def get_hostname(ip):
try:
return socket.gethostbyaddr(ip)[0]
except socket.herror:
return None
class RequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('Content-Length', 0))
data = self.rfile.read(content_length)
try:
request = json.loads(data.decode())
logging.info(f"Received POST request: {request}")
ip = request.get("ip")
if not ip:
response = {"error": "Missing 'ip' field"}
else:
hostname = get_hostname(ip)
response = {"hostname": hostname} if hostname else {"error": f"Hostname not found for IP {ip}"}
except json.JSONDecodeError:
response = {"error": "Invalid JSON format"}
except Exception as e:
logging.exception("Unexpected error during request handling")
response = {"error": "Internal server error"}
response_data = json.dumps(response).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(response_data)))
self.end_headers()
self.wfile.write(response_data)
logging.info(f"Response sent: {response}")
def do_GET(self):
self.send_error(405, "Method Not Allowed")
logging.warning("Rejected GET request")
def do_PUT(self):
self.send_error(405, "Method Not Allowed")
logging.warning("Rejected PUT request")
def do_DELETE(self):
self.send_error(405, "Method Not Allowed")
logging.warning("Rejected DELETE request")
def log_message(self, format, *args):
return # Silence default stdout logging
class HostnameService(win32serviceutil.ServiceFramework):
_svc_name_ = "HostnameHTTPService"
_svc_display_name_ = "Hostname HTTP Resolver Service"
_svc_description_ = "HTTP service that resolves hostnames from IPs on port 8999 (POST only)."
def __init__(self, args):
super().__init__(args)
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
self.is_running = True
self.server_thread = None
self.httpd = None
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.is_running = False
logging.info("Service stopping...")
if self.httpd:
self.httpd.shutdown()
win32event.SetEvent(self.stop_event)
if self.server_thread and self.server_thread.is_alive():
self.server_thread.join()
logging.info("HTTP server stopped.")
def SvcDoRun(self):
logging.info("Service starting...")
self.server_thread = threading.Thread(target=self.run_server)
self.server_thread.start()
win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
logging.info("Service stopped.")
def run_server(self):
try:
self.httpd = HTTPServer((HOST, PORT), RequestHandler)
logging.info(f"HTTP server started on port {PORT}")
self.httpd.serve_forever()
except Exception as e:
logging.exception("Server failed: %s", str(e))
if __name__ == '__main__':
try:
if len(sys.argv) == 1:
win32serviceutil.HandleCommandLine(HostnameService)
else:
if sys.argv[1] == "run":
# Allow running standalone (not as service)
logging.info("Running in standalone HTTP mode.")
httpd = HTTPServer((HOST, PORT), RequestHandler)
print(f"Listening on port {PORT}...")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("Shutting down...")
logging.info("HTTP server shut down by user.")
httpd.shutdown()
else:
win32serviceutil.HandleCommandLine(HostnameService)
except Exception as e:
logging.exception("Service startup error: %s", str(e))
sys.exit(1)