huin / goupnp Goto Github PK
View Code? Open in Web Editor NEWUPnP client library for Go (#golang)
License: BSD 2-Clause "Simplified" License
UPnP client library for Go (#golang)
License: BSD 2-Clause "Simplified" License
I am wondering if this is beyond the scope of this project.
I want to show devices connected to my UPnP router in the subnet.
I have been reading the godocs but I can't seem to figure this out
It seems to take about 2.2 seconds to go through the discovery process, here. Could I store a Location after a successful discovery, and just open it directly on subsequent runs? I don't quite see how that'd be done with the apis, DiscoverDevices seems to do some xml fetching and parsing that isn't exposed separately.
Env: Router(ipv4+ipv6) , Client(ipv4)
With the same USN, ipv4Response was replaced by ipv6Response, which is not effected for ipv4only client.
This will be fixed by change seenUSNs[usn]
with seenUSNs[location.String()+"/"+usn]
.
Code:
usn := response.Header.Get("USN")
location, err := response.Location()
if err != nil {
continue
}
locusn := location.String() + "/" + usn
if _, alreadySeen := seenUSNs[locusn]; !alreadySeen {
seenUSNs[locusn] = true
responses = append(responses, response)
}
Because the ST on the request and response don't match, a search target such as "ssdp:all" can't be use to query all devices.
I was wondering whether it would be possible to add contexts to any client calls, to make it more idiomatic. It would be easy to implement as a breaking change, as you could just stick a context into the codegen template, but that's definitely painful to old users of the package. I think it also may be a lot of bloat to add a new set of methods for context, but I was wondering what you thought of it.
Hi !
I need to test upnp connectivity over virtual "any addresses" IE 0.0.0.0
.
The current API auto detects and auto selects the IPs based on the output of net.Interface
which does not include those.
For consistency this line might be an issue
Line 147 in 8c07ff7
Indeed, when listening upon v4AnyAddr, here at least, it returns AnyAddrV6, ie [::]
.... I am not looking forward for ipv6 support.
Is it a subnet issue?
I can find this device using a third party app.
Finally i can load it by url
<root xmlns="urn:schemas-upnp-org:device-1-0"> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <device> <deviceType> urn:schemas-upnp-org:device:InternetGatewayDevice:1 </deviceType> <friendlyName>OpenWRT router</friendlyName> <manufacturer>OpenWRT</manufacturer> <manufacturerURL>http://www.openwrt.org/</manufacturerURL> <modelDescription>OpenWRT router</modelDescription> <modelName>OpenWRT router</modelName> <modelNumber>1</modelNumber> <modelURL>http://www.openwrt.org/</modelURL> <serialNumber>00000000</serialNumber> <UDN>uuid:8d483abf-ca0e-45cc-9b....</UDN> <serviceList> <service> <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType> <serviceId>urn:upnp-org:serviceId:Layer3Forwarding1</serviceId> <controlURL>/ctl/L3F</controlURL> <eventSubURL>/evt/L3F</eventSubURL> <SCPDURL>/L3F.xml</SCPDURL> </service> </serviceList> <deviceList> <device> <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType> <friendlyName>WANDevice</friendlyName> <manufacturer>MiniUPnP</manufacturer> <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL> <modelDescription>WAN Device</modelDescription> <modelName>WAN Device</modelName> <modelNumber>20160127</modelNumber> <modelURL>http://miniupnp.free.fr/</modelURL> <serialNumber>00000000</serialNumber> <UDN>uuid:8d483abf-ca0e-4f</UDN> <UPC>000000000000</UPC> <serviceList> <service> <serviceType> urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 </serviceType> <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId> <controlURL>/ctl/CmnIfCfg</controlURL> <eventSubURL>/evt/CmnIfCfg</eventSubURL> <SCPDURL>/WANCfg.xml</SCPDURL> </service> </serviceList> <deviceList> <device> <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType> <friendlyName>WANConnectionDevice</friendlyName> <manufacturer>MiniUPnP</manufacturer> <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL> <modelDescription>MiniUPnP daemon</modelDescription> <modelName>MiniUPnPd</modelName> <modelNumber>20160127</modelNumber> <modelURL>http://miniupnp.free.fr/</modelURL> <serialNumber>00000000</serialNumber> <UDN>uuid:8d483abf-ca0e-45cc-9bf0</UDN> <UPC>000000000000</UPC> <serviceList> <service> <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType> <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId> <controlURL>/ctl/IPConn</controlURL> <eventSubURL>/evt/IPConn</eventSubURL> <SCPDURL>/WANIPCn.xml</SCPDURL> </service> </serviceList> </device> </deviceList> </device> </deviceList> <presentationURL>http://192....1/</presentationURL> </device> </root>
Hi,
In order to perform a PortMapping, the computer IP to route the traffic to, seems to be required (the remote does not appear capable of figuring that out by itself upon a query), otherwise a soap fault is encountered.
In this code,
func GetIPAndForwardPort(ctx context.Context, lPort, rPort int, proto string) error {
client, err := PickRouterClient(ctx)
if err != nil {
return err
}
...
return client.AddPortMapping(
...
// Internal address on the LAN we want to forward to.
"", // <- how to fill this value
...
)
}
I was hoping that the ssdp discovery result would contain the IP used to perform the successful query, but I was not able to locate it.
https://github.com/huin/goupnp/blob/master/network.go#L21
My setup is like that
$ ifconfig
enp58s0f1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 80:fa:5b:43:59:24 txqueuelen 1000 (Ethernet)
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Boucle locale)
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1331
inet 198.18.80.9 netmask 255.255.248.0 destination 198.18.80.9
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 100 (UNSPEC)
wlp59s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.78 netmask 255.255.255.0 broadcast 192.168.1.255
ether 00:28:f8:4b:d0:b1 txqueuelen 1000 (Ethernet)
I find it hard to guess but maybe i am missing something obvious ? ><
I see that there are tons of different functions and I really don't know where to start implementing a simple "hello world" connections between 2 peers
Thanks
In this image, it shows that device(ipTIME N104R3) is detected but http post request was fail.
I have a long time to find why my router doesn't response on post request. so I have took a specification about my router but there are no statement related to soap... so I have no idea whether my router cannot response or other problem is there...
Definition of my function
func AddPortMapping_ver1(PrivateIP string, ExternalPort uint16, InternalPort uint16, Protocol string) {
clients, errors, err := internetgateway1.NewWANIPConnection1Clients()
if errors != nil {
return
}
if err != nil {
return
}
for _, client := range clients {
if err := client.AddPortMapping(PrivateIP, ExternalPort, Protocol, InternalPort, PrivateIP, true, "test", 0); err != nil {
fmt.Println(err.Error())
}
}
}
And call AddPortMapping_ver1() in main function
AddPortMapping_ver1("192.168.0.2", 7871, 7871, "TCP") // my computer address is 192.168.0.2 and 192.168.0.1 is address of router
Do you have any idea about this problem?
Sorry for my English.
Using a single PacketConn to send multicast UDP requests for SSDP search fails, if there are multiple interfaces in the system.
The WriteTo() will pick any interface and use that alone, not sending the SSDP search to any other network interface.
I think that for SSDP search to work we need a PacketConn per network interface.
I apologize if this is more of a support question than an actual Issue, although I would expect this to work properly on OSX and Windows out of the box.
I've created a very bare-bones simple SSDP test to verify SSDP discovery of roku devices
package main
import (
"fmt"
"github.com/huin/goupnp/httpu"
"github.com/huin/goupnp/ssdp"
)
func main() {
fmt.Println("Searching")
httpu_client, _ := httpu.NewHTTPUClient()
responses, _ := ssdp.SSDPRawSearch(httpu_client, "roku:ecp", 2, 3)
for _, response := range responses {
fmt.Println(response)
}
}
When I compile and run this on my linux box (Ubuntu 17.04) it works no problem and I get one discovered roku device response
Searching...
&{200 OK 200 HTTP/1.1 1 1 map[Server:[Roku UPnP/1.0 Roku/7.7.0] Location:[http://192.168.1.10:8060/] Wakeup:[MAC=08:05:81:25:89:51;Timeout=10] Cache-Control:[max-age=3600] St:[roku:ecp] Usn:[uuid:roku:ecp:YY005F185283] Ext:[]] 0xc42005e200 -1 [] true false map[] 0xc420104000 <nil>}
But when I compile and run the same thing on my macbook and two other windows machines on my network, I get no response back.
My guess as to what's happening is that there are already services listening on port 1900, but looking at the HTTPU code, it looks like it's just binding to any available ephemeral port, not 1900, so it should work just fine.
Any ideas? I want to be able to do device discovery from any of those OSes without requiring disabling the network discovery service (or whatever may be bound to port 1900 already)
Thanks!
at https://github.com/huin/goupnp/blob/main/GUIDE.md
we can change the example to use the LocalAddr() method :D
type RouterClient interface {
AddPortMapping(
NewRemoteHost string,
NewExternalPort uint16,
NewProtocol string,
NewInternalPort uint16,
NewInternalClient string,
NewEnabled bool,
NewPortMappingDescription string,
NewLeaseDuration uint32,
) (err error)
LocalAddr() net.IP
GetExternalIPAddress() (
NewExternalIPAddress string,
err error,
)
}
//....
return client.AddPortMapping(
"",
// External port number to expose to Internet:
1234,
// Forward TCP (this could be "UDP" if we wanted that instead).
"TCP",
// Internal port number on the LAN to forward to.
// Some routers might not support this being different to the external
// port number.
1234,
// Internal address on the LAN we want to forward to.
client.LocalAddr().String(),
// Enabled:
true,
// Informational description for the client requesting the port forwarding.
"MyProgramName",
// How long should the port forward last for in seconds.
// If you want to keep it open for longer and potentially across router
// resets, you might want to periodically request before this elapses.
3600,
)
Hi
I try know to use search criteria with my upnp server.
with curl I've got the expected results :
curl -v -H 'SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Search"' -H 'content-type: text/xml ;charset="utf-8"' "http://10.188.2.125:8200/ctl/ContentDir" -d "@search.xml"
with search file content :
<?xml version="1.0" encoding="utf-8"?> <s:Envelope xmlns:ns0="urn:schemas-upnp-org:service:ContentDirectory:1" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <ns0:Search> <Filter>*</Filter> <ContainerID>*</ContainerID> <SearchCriteria>(dc:title contains "star wars") and (upnp:class derivedfrom "object.item.videoItem")</SearchCriteria> <StartingIndex>0</StartingIndex> <RequestedCount>0</RequestedCount> <SortCriteria/> </ns0:Search> </s:Body> </s:Envelope>
When I use the goupnp api like this :
result, returnedNumber, totalMatches, _, err := client.Search("*", "(dc:title contains \"star wars\") and (upnp:class derivedfrom \"object.item.videoItem\")", "*", 0, 0, "")
the quotes are interpreted as html in my upnp server, here the log of the query :
[2017/11/03 12:27:43] sql.c:88: error: prepare failed: near "&": syntax error SELECT (select count(distinct DETAIL_ID) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID) where (OBJECT_ID glob '*$*') and ((d.TITLE like "star wars") and (o.CLASS like "object.item.videoItem") )) + (select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID) where (OBJECT_ID = '*') and ((d.TITLE like "star wars") and (o.CLASS like "object.item.videoItem") )) [2017/11/03 12:27:43] upnpsoap.c:123: warn: Returning UPnPError 708: Unsupported or invalid search criteria
the quotes are replaced by the html code.
I tried to find a workaround with runes, but I am still the same issue.
I have you got any advises ?
Best regards
Jerome
Problem:
err
returned in the following code
Lines 130 to 132 in 167b976
points to ResolveUDPAddr
err
object.
Lines 83 to 84 in 167b976
Scope of ReadFrom
err
is only in the for
loop. Due to which if ReadFrom
throws timeout error, for loop breaks but the error returned is not the one generated by ReadFrom
instead it is the one generated by ResolveUDPAddr
hence nil
.
Lines 105 to 112 in 167b976
Hey, thanks for the work!
I was looking to use this client in a memory constrained environment, and was wondering whether it'd be possible to make the charset dependency optional by assuming a utf-8 encoding. If so, I can do the work to make a PR for it.
I am trying a very simple test against a Fritz!Box device:
package main
import (
"fmt"
"log"
"github.com/huin/goupnp/dcps/internetgateway1"
)
func main() {
clients, _, err := internetgateway1.NewWANIPConnection1Clients ()
if err != nil {
log.Fatal(err)
}
for _, client := range clients {
status, _, _, err := client.GetStatusInfo()
if err != nil {
log.Fatal(err)
}
fmt.Println(status)
}
}
but this always fails:
2014/06/05 11:30:25 goupnp: SOAP request got HTTP 500 Internal Server Error
First, thanks for this lib, it's quite amazing how much goes into something like this.
I'm planning to use it for my project for (surprise) port-mapping, and ran some quick experiments to see how it'd fit. I'll be taking different actions depending on status, and in some cases I'll use short lease time.
Issue: The lack of context (or a Close()
call) makes it hard to quickly interrupt and shut down ongoing client operations. My project uses a lot of goroutines and I'm really meticulous about teardown logic. Would it be possible to add context to all remaining client calls, such as httpu?
Issue: Even without context, my understanding is that the search functions cannot return until the timeout was reached, even if available sooner. Perhaps this is only true for certain calls like ssdp.SSDPRawSearch
? Note: I ran the experiments with the wrapper lib libp2p/go-nat
. I'm planning to solicit quick responses and use a fallback otherwise, so any delay will also delay the fallback. Would it be possible to return search results as they come available instead of all-at-once?
Question: The 2s default timeout on SSDP seems like a lot when dealing with local-only connections. How was this determined? Do you have any real-life data points? I may want to set a different default timeout, and I'm curious on how to set it best for my purposes.
I'm happy to contribute if needed, and thanks again.
Would it be possible to create a dlna device implementation with this library? So a device that actually listens to play, stop, previous, next, requests and can receive direct URI commands? I believe it is called a media renderer? If so, please provide a little example with the play stop previous next as stubs. I would like to use this as a starting point for creating the device that can be controlled via dlna. Thanks.
Hi
I would like to create a simple app to display all items stored on a media server.
To discover media server, I just wrote this simple code :
`package main
import (
"flag"
"log"
"github.com/huin/goupnp"
)
var servers = flag.Bool("servers",true,"list all the dlna servers availabled.")
var list = flag.String("list","","get content from server uri")
func main() {
flag.Parse()
if *servers == true {
devices, err := goupnp.DiscoverDevices("urn:schemas-upnp-org:device:MediaServer:1")
if err != nil {
log.Printf("error %v",err.Error())
} else {
for _,d := range devices {
log.Printf("Device %v,%v",d.Root,d.Location)
}
}
} else {
flag.Usage()
}
}`
Now to display the items content from a media server I call those requests :
curl -v -H 'SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"' -H 'content-type: text/xml ;charset="utf-8"' "http://10.188.2.125:8200/ctl/ContentDir" -d "@browse.xml"
and the browse.xml content is :
<?xml version="1.0" encoding="utf-8"?> <s:Envelope xmlns:ns0="urn:schemas-upnp-org:service:ContentDirectory:1" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <ns0:Browse> <ObjectID>0</ObjectID> <BrowseFlag>BrowseDirectChildren</BrowseFlag> <Filter>*</Filter> <StartingIndex>0</StartingIndex> <RequestedCount>0</RequestedCount> <SortCriteria/> </ns0:Browse> </s:Body> </s:Envelope>
My question is how can I do by code the same feature ?
Best regards
Jerome
when running the following:
go get -u github.com/huin/goupnp
I am getting an error on mac yosemite latest version
/usr/local/bin/src/github.com/huin/goupnp/goupnp.go:109: undefined: charset.NewReaderByNam
Most google fiber routers come with their UPnP turned off by default. I am still a newbie and trying to figure out if it is possible to know that my router indeed has UPnP but currently turned off. As opposed to just not finding it, and not knowing that my router has such capabilities.
I was wondering if this library supports port mapping? not too familiar with upnp but I'm basically just looking for the ability to setup an open port through the router. I tried searching for urn:schemas-upnp-org:service:WANIPConnection:1
and couldn't find any references but not sure if I'm ever searching for the right thing =)
What is the ideal response format of httpu.conn.ReadFrom? Can someone give me a example?
I am trying to connect to my Yeelight(https://www.yeelight.com/download/Yeelight_Inter-Operation_Spec.pdf) that respond in this way (I copied this from ResponseBytes.string() of VSCode debugger):
HTTP/1.1 200 OK\r\nCache-Control: max-age=3584\r\nDate: \r\nExt: \r\nLocation: yeelight://192.168.1.2:123456\r\nServer: POSIX UPnP/1.0 YGLC/1\r\nid: 0x0000000007fe523f\r\nmodel: color\r\nfw_ver: 26\r\nsupport: get_prop set_default set_power toggle set_bright start_cf stop_cf set_scene cron_add cron_get cron_del set_ct_abx set_rgb set_hsv set_adjust adjust_bright adjust_ct adjust_color set_music set\r\npower: off\r\nbright: 100\r\ncolor_mode: 2\r\nct: 2899\r\nrgb: 16711680\r\nhue: 359\r\nsat: 100\r\nname: \r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
But every time I fall into "httpu: error while parsing response: unexpected EOF".
I tried to make a fake response only to test, but I keep falling in "httpu: error while parsing response: unexpected EOF". Here is the change that I made to test with the fake response:
buf := []byte("HTTP/1.1 200 OK\r\n")
response, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(buf)), req)
if err != nil {
log.Printf("httpu: error while parsing response: %v", err)
continue
}
Which make it make it possible to write a DMR
Sent:
POST /upnp/control/WANCommonIFC1 HTTP/1.1
Host: 192.168.1.1:4562
User-Agent: Go-http-client/1.1
Content-Length: 302
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1#GetTotalBytesSent"
Accept-Encoding: gzip
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetTotalBytesSent xmlns:u="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"></u:GetTotalBytesSent></s:Body></s:Envelope>
Received:
HTTP/1.1 200 OK
CONTENT-LENGTH:334
CONTENT-TYPE:text/xml
DATE: Fri, 21 Jun 2019 21:00:19 GMT
EXT:
SERVER:Linux/2.6.36 UPnP/1.0 myigd/1.0
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
<u:GetTotalBytesSentResponse xmlns:u="urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1">
<NewTotalBytesSent>-1363540943</NewTotalBytesSent>
</u:GetTotalBytesSentResponse>
</s:Body></s:Envelope>.
AFAIK this device is not following standards. However, miniupnpc client is not failing to parse this response.
I just started using this library and initially was very happy that clients, errors, err := av1.NewAVTransport1Clients()
found my Samsung TV and Teufel loudspeakers, but soon after that, it stopped working and clients
was empty. After like 20 runs, it worked again, but eventually it's no longer finding anything. My system is Windows 10. Before using your library, I played around with Developer Tools for UPnP Technologies (http://www.meshcommander.com/upnptools) and their "Device Spy" that works all the time, so it doesn't appear to be a limitation of my OS or network / firewall. Any idea what might be wrong? Should I use Wireshark to inspect the traffic or do you recommend something else?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.