Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions _examples/resvpnproxy/fastly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file describes a Fastly Compute package. To learn more visit:
# https://developer.fastly.com/reference/fastly-toml/

authors = ["oss@fastly.com"]
description = "Test ResVPNProxy hostcalls"
language = "go"
manifest_version = 2
name = "resvpnproxy-test"
61 changes: 61 additions & 0 deletions _examples/resvpnproxy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"

"github.com/fastly/compute-sdk-go/fsthttp"
)

// Response represents the complete response structure
type Response struct {
ClientIP string `json:"client_ip"`
*fsthttp.ResVPNProxyResult `json:",inline"`
}

// ErrorResponse represents an error response structure
type ErrorResponse struct {
Error string `json:"error"`
ClientIP string `json:"client_ip"`
}

func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
w.Header().Set("Content-Type", "application/json")

// Test the ResVPNProxy data using the request method
vpnData, err := r.ResVPNProxyData()
if err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
errorResp := ErrorResponse{
Error: fmt.Sprintf("Error getting ResVPNProxy data: %s", err),
ClientIP: r.RemoteAddr,
}
if jsonData, jsonErr := json.Marshal(errorResp); jsonErr != nil {
fmt.Fprintf(w, `{"error": "JSON marshaling failed"}`)
} else {
w.Write(jsonData)
}
return
}

// Success case - return the ResVPNProxy data
w.WriteHeader(fsthttp.StatusOK)
response := Response{
ClientIP: r.RemoteAddr,
ResVPNProxyResult: vpnData,
}

if jsonData, err := json.Marshal(response); err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
fmt.Fprintf(w, `{"error": "JSON marshaling failed"}`)
} else {
w.Write(jsonData)
}

// Log to console for debugging
log.Printf("ResVPNProxy analysis complete for client %s", r.RemoteAddr)
})
}
1 change: 0 additions & 1 deletion fsthttp/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,6 @@ func (inspect *InspectResponse) IsRedirect() bool {
// {"waf_response":200,"redirect_url":"","tags":[],"verdict":"allow","decision_ms":0}

func (r *Request) Inspect(opts *InspectOptions) (*InspectResponse, error) {

type inspectJSON struct {
WafResponse int `json:"waf_response"`
RedirectURL string `json:"redirect_url"`
Expand Down
97 changes: 97 additions & 0 deletions fsthttp/resvpnproxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package fsthttp

import (
"fmt"
)

// ResVPNProxyResult represents additional IP Proxy and VPN Intelligence data for a request.
type ResVPNProxyResult struct {
IsAnonymous bool `json:"is_anonymous"` // True if the IP address is present in one or more categories of anonymous flags.
IsAnonymousVPN bool `json:"is_anonymous_vpn"` // True if the IP address was identified as being from a Virtual Private Network (VPN) exit node.
IsHostingProvider bool `json:"is_hosting_provider"` // True if the IP address was identified as being from a hosting provider or data center.
IsProxyOverVPN bool `json:"is_proxy_over_vpn"` // True if the IP address was detected with the Proxy over VPN technique from premium VPN providers like ExpressVPN.
IsPublicProxy bool `json:"is_public_proxy"` // True if the IP address was identified as being from a proxy exit node.
IsRelayProxy bool `json:"is_relay_proxy"` // True if the IP address was identified as being from a relay proxy.
IsResidentialProxy bool `json:"is_residential_proxy"` // True if the IP address was identified as being from a proxy associated with a residential ISP.
IsSmartDNSProxy bool `json:"is_smart_dns_proxy"` // True if the IP address was identified as being from a SmartDNS exit node.
IsTorExitNode bool `json:"is_tor_exit_node"` // True if the IP address was identified as being from a Tor exit node.
IsVPNDatacenter bool `json:"is_vpn_datacenter"` // True if the IP address was identified as being part of a known VPN data center or IP address range.
VPNServiceName string `json:"vpn_service_name"` // Displays the name of the VPN associated with the network of the IP address.
}

// ResVPNProxyData analyzes the current downstream request's IP address and returns VPN and proxy intelligence data.
//
// Returns an error if the ResVPNProxy feature is not enabled for your service or intelligence data is not available.
//
// Example usage:
//
// vpnData, err := r.ResVPNProxyData()
// if err != nil {
// // Feature not enabled or other error
// return err
// }
func (r *Request) ResVPNProxyData() (*ResVPNProxyResult, error) {
if r.downstream.req == nil {
return nil, fmt.Errorf("downstream request not available")
}

result := &ResVPNProxyResult{}
var err error

result.IsAnonymous, err = r.downstream.req.DownstreamResVPNProxyIsAnonymous()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_anonymous: %w", err)
}

result.IsAnonymousVPN, err = r.downstream.req.DownstreamResVPNProxyIsAnonymousVPN()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_anonymous_vpn: %w", err)
}

result.IsHostingProvider, err = r.downstream.req.DownstreamResVPNProxyIsHostingProvider()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_hosting_provider: %w", err)
}

result.IsProxyOverVPN, err = r.downstream.req.DownstreamResVPNProxyIsProxyOverVPN()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_proxy_over_vpn: %w", err)
}

result.IsPublicProxy, err = r.downstream.req.DownstreamResVPNProxyIsPublicProxy()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_public_proxy: %w", err)
}

result.IsRelayProxy, err = r.downstream.req.DownstreamResVPNProxyIsRelayProxy()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_relay_proxy: %w", err)
}

result.IsResidentialProxy, err = r.downstream.req.DownstreamResVPNProxyIsResidentialProxy()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_residential_proxy: %w", err)
}

result.IsSmartDNSProxy, err = r.downstream.req.DownstreamResVPNProxyIsSmartDNSProxy()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_smart_dns_proxy: %w", err)
}

result.IsTorExitNode, err = r.downstream.req.DownstreamResVPNProxyIsTorExitNode()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_tor_exit_node: %w", err)
}

result.IsVPNDatacenter, err = r.downstream.req.DownstreamResVPNProxyIsVPNDatacenter()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("is_vpn_datacenter: %w", err)
}

result.VPNServiceName, err = r.downstream.req.DownstreamResVPNProxyVPNServiceName()
if err = ignoreNoneError(err); err != nil {
return nil, fmt.Errorf("vpn_service_name: %w", err)
}

return result, nil
}
45 changes: 45 additions & 0 deletions internal/abi/fastly/hostcalls_noguest.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,50 @@ func (r *HTTPRequest) DownstreamBotVerified() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsAnonymous() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsAnonymousVPN() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsHostingProvider() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsProxyOverVPN() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsPublicProxy() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsRelayProxy() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsResidentialProxy() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsSmartDNSProxy() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsTorExitNode() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyIsVPNDatacenter() (bool, error) {
return false, fmt.Errorf("not implemented")
}

func (r *HTTPRequest) DownstreamResVPNProxyVPNServiceName() (string, error) {
return "", fmt.Errorf("not implemented")
}

func NewHTTPRequest() (*HTTPRequest, error) {
return nil, fmt.Errorf("not implemented")
}
Expand Down Expand Up @@ -473,6 +517,7 @@ func GeoLookup(ip net.IP) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}


type KVStore struct{}

func OpenKVStore(name string) (*KVStore, error) {
Expand Down
Loading
Loading