github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/nat/upnp/discover.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package upnp 19 20 import ( 21 "fmt" 22 "net/http" 23 "strings" 24 25 "github.com/mysteriumnetwork/node/firewall" 26 "github.com/rs/zerolog/log" 27 28 "github.com/huin/goupnp" 29 "github.com/huin/goupnp/httpu" 30 "github.com/huin/goupnp/ssdp" 31 "github.com/pkg/errors" 32 ) 33 34 // GatewayDevice represents a scanned gateway device 35 type GatewayDevice struct { 36 root *goupnp.RootDevice 37 server string 38 } 39 40 // ToMap returns a map representation of the device 41 func (device GatewayDevice) ToMap() map[string]string { 42 return map[string]string{ 43 "server": device.server, 44 "deviceType": device.root.Device.DeviceType, 45 "manufacturer": device.root.Device.Manufacturer, 46 "friendlyName": device.root.Device.FriendlyName, 47 "modelName": device.root.Device.ModelName, 48 "modelNo": device.root.Device.ModelNumber, 49 } 50 } 51 52 // String returns human-readable string representation of gatewayDevice 53 func (device GatewayDevice) String() string { 54 return fmt.Sprintf("%v", device.ToMap()) 55 } 56 57 func printGatewayInfo(gateways []GatewayDevice) { 58 log.Info().Msgf("UPnP gateways detected: %d", len(gateways)) 59 for _, device := range gateways { 60 log.Info().Msgf("UPnP gateway detected %v", device) 61 } 62 } 63 64 func discoverGateways() ([]GatewayDevice, error) { 65 client, err := httpu.NewHTTPUClient() 66 if err != nil { 67 return nil, err 68 } 69 defer client.Close() 70 71 // this IP address used to discover gateways - allow it 72 if _, err := firewall.AllowIPAccess("239.255.255.250"); err != nil { 73 return nil, err 74 } 75 76 responses, err := ssdp.SSDPRawSearch(client, ssdp.UPNPRootDevice, 2, 3) 77 if err != nil { 78 return nil, err 79 } 80 81 var results []GatewayDevice 82 83 for _, response := range responses { 84 device, err := parseDiscoveryResponse(response) 85 if err != nil { 86 log.Warn().Err(err).Msg("Error parsing discovery response") 87 continue 88 } 89 if !isGateway(device) { 90 log.Debug().Msgf("not a gateway device: %v", device) 91 continue 92 } 93 94 results = append(results, *device) 95 } 96 97 return results, nil 98 } 99 100 func parseDiscoveryResponse(res *http.Response) (*GatewayDevice, error) { 101 device := &GatewayDevice{server: res.Header.Get("server")} 102 103 loc, err := res.Location() 104 if err != nil { 105 return nil, errors.Wrap(err, "unexpected bad location from search") 106 } 107 108 device.root, err = goupnp.DeviceByURL(loc) 109 if err != nil { 110 return nil, errors.Wrap(err, "error querying device by location") 111 } 112 113 return device, nil 114 } 115 116 func isGateway(device *GatewayDevice) bool { 117 return strings.Contains(device.root.Device.DeviceType, "InternetGatewayDevice") 118 }