@@ -24,6 +24,7 @@ import (
2424 "net"
2525 "net/http"
2626 "net/textproto"
27+ "os"
2728 "strconv"
2829 "strings"
2930 "sync"
@@ -39,6 +40,14 @@ import (
3940 "github.com/scionproto-contrib/http-proxy/forward/utils"
4041)
4142
43+ const (
44+ hostsFile = "/etc/hosts"
45+ hostsComment = " # Line added by the SCION HTTP Forward Proxy"
46+ hostName = "forward-proxy.scion"
47+ // TODO: make the address injectable via configuration
48+ hostsEntry = "127.0.0.1\t " + hostName
49+ )
50+
4251// ResolveHandler defines an interface for handling HTTP requests related to
4352// host resolution and redirection. Implementations of this interface should
4453// provide mechanisms to handle redirection back or errors, as well as host
@@ -89,11 +98,18 @@ func (cp *CoreProxy) Initialize() error {
8998 }
9099 cp .metricsHandler = panpolicy .NewMetricsHandler (cp .policyManager , cp .logger .With (zap .String ("component" , "metrics-handler" )))
91100 cp .resolver = resolver .NewPANResolver (cp .logger .With (zap .String ("component" , "resolver" )), cp .resolveTimeout )
101+
102+ if err := cp .addHostsEntry (); err != nil {
103+ cp .logger .Warn ("Failed to add entry to /etc/hosts file" , zap .Error (err ))
104+ }
92105 return nil
93106}
94107
95108// Cleanup cleans up the core proxy logic.
96109func (cp * CoreProxy ) Cleanup () error {
110+ if err := cp .removeHostsEntry (); err != nil {
111+ cp .logger .Warn ("Failed to remove entry from /etc/hosts file" , zap .Error (err ))
112+ }
97113 return cp .policyManager .Stop ()
98114}
99115
@@ -158,6 +174,58 @@ func (cp *CoreProxy) HandleTunnelRequest(w http.ResponseWriter, r *http.Request)
158174 return cp .forwardRequest (w , r , dialer )
159175}
160176
177+ func (cp * CoreProxy ) addHostsEntry () error {
178+ content , err := os .ReadFile (hostsFile )
179+ if err != nil {
180+ return fmt .Errorf ("failed to read hosts file: %w" , err )
181+ }
182+
183+ lines := strings .Split (string (content ), "\n " )
184+ for _ , line := range lines {
185+ if ! strings .HasPrefix (strings .TrimSpace (line ), "#" ) && strings .Contains (line , hostName ) {
186+ cp .logger .Debug ("Entry for host name already exists" , zap .String ("entry" , line ))
187+ return nil
188+ }
189+ }
190+
191+ file , err := os .OpenFile (hostsFile , os .O_APPEND | os .O_WRONLY , 0644 )
192+ if err != nil {
193+ return fmt .Errorf ("failed to open hosts file for writing: %w" , err )
194+ }
195+ defer file .Close ()
196+
197+ entry := hostsComment + "\n " + hostsEntry + "\n "
198+ if _ , err := file .WriteString (entry ); err != nil {
199+ return fmt .Errorf ("failed to write to hosts file: %w" , err )
200+ }
201+
202+ cp .logger .Info ("Added entry to hosts file" , zap .String ("entry" , hostsEntry ))
203+ return nil
204+ }
205+
206+ func (cp * CoreProxy ) removeHostsEntry () error {
207+ content , err := os .ReadFile (hostsFile )
208+ if err != nil {
209+ return fmt .Errorf ("failed to read hosts file: %w" , err )
210+ }
211+
212+ var newLines []string
213+ lines := strings .Split (string (content ), "\n " )
214+ for _ , line := range lines {
215+ if ! strings .Contains (line , hostsEntry ) && ! strings .Contains (line , hostsComment ) {
216+ newLines = append (newLines , line )
217+ }
218+ }
219+
220+ err = os .WriteFile (hostsFile , []byte (strings .Join (newLines , "\n " )), 0644 )
221+ if err != nil {
222+ return fmt .Errorf ("failed to write to hosts file: %w" , err )
223+ }
224+
225+ cp .logger .Info ("Removed entry from hosts file" , zap .String ("entry" , hostsEntry ))
226+ return nil
227+ }
228+
161229func (cp * CoreProxy ) parseCookieFromProxyAuth (w http.ResponseWriter , r * http.Request ) error {
162230 // the path policy cookie is passed in the proxy-authorization header as the cookie
163231 username , cookie , err := proxyBasicAuth (r )
0 commit comments