├── LICENSE ├── README.md ├── goupnp ├── LICENSE ├── dcps │ └── internetgateway1 │ │ └── internetgateway1.go ├── device.go ├── goupnp.go ├── httpu │ ├── httpu.go │ └── serve.go ├── scpd │ └── scpd.go ├── service_client.go ├── soap │ ├── soap.go │ └── types.go └── ssdp │ ├── registry.go │ └── ssdp.go ├── upnp.go └── upnp_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nebulous 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## upnp ## 2 | 3 | The go-upnp repository has moved to [GitLab](https://gitlab.com/NebulousLabs/go-upnp). 4 | -------------------------------------------------------------------------------- /goupnp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, John Beisley 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /goupnp/dcps/internetgateway1/internetgateway1.go: -------------------------------------------------------------------------------- 1 | // Client for UPnP Device Control Protocol Internet Gateway Device v1. 2 | // 3 | // This DCP is documented in detail at: http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf 4 | // 5 | // Typically, use one of the New* functions to create clients for services. 6 | package internetgateway1 7 | 8 | // Generated file - do not edit by hand. See README.md 9 | 10 | import ( 11 | "context" 12 | "net/url" 13 | "time" 14 | 15 | "gitlab.com/NebulousLabs/go-upnp/goupnp" 16 | "gitlab.com/NebulousLabs/go-upnp/goupnp/soap" 17 | ) 18 | 19 | // Hack to avoid Go complaining if time isn't used. 20 | var _ time.Time 21 | 22 | // Device URNs: 23 | const ( 24 | URN_LANDevice_1 = "urn:schemas-upnp-org:device:LANDevice:1" 25 | URN_WANConnectionDevice_1 = "urn:schemas-upnp-org:device:WANConnectionDevice:1" 26 | URN_WANDevice_1 = "urn:schemas-upnp-org:device:WANDevice:1" 27 | ) 28 | 29 | // Service URNs: 30 | const ( 31 | URN_LANHostConfigManagement_1 = "urn:schemas-upnp-org:service:LANHostConfigManagement:1" 32 | URN_Layer3Forwarding_1 = "urn:schemas-upnp-org:service:Layer3Forwarding:1" 33 | URN_WANCableLinkConfig_1 = "urn:schemas-upnp-org:service:WANCableLinkConfig:1" 34 | URN_WANCommonInterfaceConfig_1 = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" 35 | URN_WANDSLLinkConfig_1 = "urn:schemas-upnp-org:service:WANDSLLinkConfig:1" 36 | URN_WANEthernetLinkConfig_1 = "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1" 37 | URN_WANIPConnection_1 = "urn:schemas-upnp-org:service:WANIPConnection:1" 38 | URN_WANPOTSLinkConfig_1 = "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1" 39 | URN_WANPPPConnection_1 = "urn:schemas-upnp-org:service:WANPPPConnection:1" 40 | ) 41 | 42 | // LANHostConfigManagement1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:LANHostConfigManagement:1". See 43 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 44 | // are provided for informational value. 45 | type LANHostConfigManagement1 struct { 46 | goupnp.ServiceClient 47 | } 48 | 49 | // NewLANHostConfigManagement1Clients discovers instances of the service on the network, 50 | // and returns clients to any that are found. errors will contain an error for 51 | // any devices that replied but which could not be queried, and err will be set 52 | // if the discovery process failed outright. 53 | // 54 | // This is a typical entry calling point into this package. 55 | func NewLANHostConfigManagement1Clients() (clients []*LANHostConfigManagement1, errors []error, err error) { 56 | var genericClients []goupnp.ServiceClient 57 | if genericClients, errors, err = goupnp.NewServiceClients(URN_LANHostConfigManagement_1); err != nil { 58 | return 59 | } 60 | clients = newLANHostConfigManagement1ClientsFromGenericClients(genericClients) 61 | return 62 | } 63 | 64 | // NewLANHostConfigManagement1ClientsByURL discovers instances of the service at the given 65 | // URL, and returns clients to any that are found. An error is returned if 66 | // there was an error probing the service. 67 | // 68 | // This is a typical entry calling point into this package when reusing an 69 | // previously discovered service URL. 70 | func NewLANHostConfigManagement1ClientsByURL(loc *url.URL) ([]*LANHostConfigManagement1, error) { 71 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_LANHostConfigManagement_1) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil 76 | } 77 | 78 | // NewLANHostConfigManagement1ClientsFromRootDevice discovers instances of the service in 79 | // a given root device, and returns clients to any that are found. An error is 80 | // returned if there was not at least one instance of the service within the 81 | // device. The location parameter is simply assigned to the Location attribute 82 | // of the wrapped ServiceClient(s). 83 | // 84 | // This is a typical entry calling point into this package when reusing an 85 | // previously discovered root device. 86 | func NewLANHostConfigManagement1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*LANHostConfigManagement1, error) { 87 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_LANHostConfigManagement_1) 88 | if err != nil { 89 | return nil, err 90 | } 91 | return newLANHostConfigManagement1ClientsFromGenericClients(genericClients), nil 92 | } 93 | 94 | func newLANHostConfigManagement1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*LANHostConfigManagement1 { 95 | clients := make([]*LANHostConfigManagement1, len(genericClients)) 96 | for i := range genericClients { 97 | clients[i] = &LANHostConfigManagement1{genericClients[i]} 98 | } 99 | return clients 100 | } 101 | 102 | func (client *LANHostConfigManagement1) SetDHCPServerConfigurable(NewDHCPServerConfigurable bool) (err error) { 103 | // Request structure. 104 | request := &struct { 105 | NewDHCPServerConfigurable string 106 | }{} 107 | // BEGIN Marshal arguments into request. 108 | 109 | if request.NewDHCPServerConfigurable, err = soap.MarshalBoolean(NewDHCPServerConfigurable); err != nil { 110 | return 111 | } 112 | // END Marshal arguments into request. 113 | 114 | // Response structure. 115 | response := interface{}(nil) 116 | 117 | // Perform the SOAP call. 118 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPServerConfigurable", request, response); err != nil { 119 | return 120 | } 121 | 122 | // BEGIN Unmarshal arguments from response. 123 | 124 | // END Unmarshal arguments from response. 125 | return 126 | } 127 | 128 | func (client *LANHostConfigManagement1) GetDHCPServerConfigurable() (NewDHCPServerConfigurable bool, err error) { 129 | // Request structure. 130 | request := interface{}(nil) 131 | // BEGIN Marshal arguments into request. 132 | 133 | // END Marshal arguments into request. 134 | 135 | // Response structure. 136 | response := &struct { 137 | NewDHCPServerConfigurable string 138 | }{} 139 | 140 | // Perform the SOAP call. 141 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPServerConfigurable", request, response); err != nil { 142 | return 143 | } 144 | 145 | // BEGIN Unmarshal arguments from response. 146 | 147 | if NewDHCPServerConfigurable, err = soap.UnmarshalBoolean(response.NewDHCPServerConfigurable); err != nil { 148 | return 149 | } 150 | // END Unmarshal arguments from response. 151 | return 152 | } 153 | 154 | func (client *LANHostConfigManagement1) SetDHCPRelay(NewDHCPRelay bool) (err error) { 155 | // Request structure. 156 | request := &struct { 157 | NewDHCPRelay string 158 | }{} 159 | // BEGIN Marshal arguments into request. 160 | 161 | if request.NewDHCPRelay, err = soap.MarshalBoolean(NewDHCPRelay); err != nil { 162 | return 163 | } 164 | // END Marshal arguments into request. 165 | 166 | // Response structure. 167 | response := interface{}(nil) 168 | 169 | // Perform the SOAP call. 170 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDHCPRelay", request, response); err != nil { 171 | return 172 | } 173 | 174 | // BEGIN Unmarshal arguments from response. 175 | 176 | // END Unmarshal arguments from response. 177 | return 178 | } 179 | 180 | func (client *LANHostConfigManagement1) GetDHCPRelay() (NewDHCPRelay bool, err error) { 181 | // Request structure. 182 | request := interface{}(nil) 183 | // BEGIN Marshal arguments into request. 184 | 185 | // END Marshal arguments into request. 186 | 187 | // Response structure. 188 | response := &struct { 189 | NewDHCPRelay string 190 | }{} 191 | 192 | // Perform the SOAP call. 193 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDHCPRelay", request, response); err != nil { 194 | return 195 | } 196 | 197 | // BEGIN Unmarshal arguments from response. 198 | 199 | if NewDHCPRelay, err = soap.UnmarshalBoolean(response.NewDHCPRelay); err != nil { 200 | return 201 | } 202 | // END Unmarshal arguments from response. 203 | return 204 | } 205 | 206 | func (client *LANHostConfigManagement1) SetSubnetMask(NewSubnetMask string) (err error) { 207 | // Request structure. 208 | request := &struct { 209 | NewSubnetMask string 210 | }{} 211 | // BEGIN Marshal arguments into request. 212 | 213 | if request.NewSubnetMask, err = soap.MarshalString(NewSubnetMask); err != nil { 214 | return 215 | } 216 | // END Marshal arguments into request. 217 | 218 | // Response structure. 219 | response := interface{}(nil) 220 | 221 | // Perform the SOAP call. 222 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetSubnetMask", request, response); err != nil { 223 | return 224 | } 225 | 226 | // BEGIN Unmarshal arguments from response. 227 | 228 | // END Unmarshal arguments from response. 229 | return 230 | } 231 | 232 | func (client *LANHostConfigManagement1) GetSubnetMask() (NewSubnetMask string, err error) { 233 | // Request structure. 234 | request := interface{}(nil) 235 | // BEGIN Marshal arguments into request. 236 | 237 | // END Marshal arguments into request. 238 | 239 | // Response structure. 240 | response := &struct { 241 | NewSubnetMask string 242 | }{} 243 | 244 | // Perform the SOAP call. 245 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetSubnetMask", request, response); err != nil { 246 | return 247 | } 248 | 249 | // BEGIN Unmarshal arguments from response. 250 | 251 | if NewSubnetMask, err = soap.UnmarshalString(response.NewSubnetMask); err != nil { 252 | return 253 | } 254 | // END Unmarshal arguments from response. 255 | return 256 | } 257 | 258 | func (client *LANHostConfigManagement1) SetIPRouter(NewIPRouters string) (err error) { 259 | // Request structure. 260 | request := &struct { 261 | NewIPRouters string 262 | }{} 263 | // BEGIN Marshal arguments into request. 264 | 265 | if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { 266 | return 267 | } 268 | // END Marshal arguments into request. 269 | 270 | // Response structure. 271 | response := interface{}(nil) 272 | 273 | // Perform the SOAP call. 274 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetIPRouter", request, response); err != nil { 275 | return 276 | } 277 | 278 | // BEGIN Unmarshal arguments from response. 279 | 280 | // END Unmarshal arguments from response. 281 | return 282 | } 283 | 284 | func (client *LANHostConfigManagement1) DeleteIPRouter(NewIPRouters string) (err error) { 285 | // Request structure. 286 | request := &struct { 287 | NewIPRouters string 288 | }{} 289 | // BEGIN Marshal arguments into request. 290 | 291 | if request.NewIPRouters, err = soap.MarshalString(NewIPRouters); err != nil { 292 | return 293 | } 294 | // END Marshal arguments into request. 295 | 296 | // Response structure. 297 | response := interface{}(nil) 298 | 299 | // Perform the SOAP call. 300 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteIPRouter", request, response); err != nil { 301 | return 302 | } 303 | 304 | // BEGIN Unmarshal arguments from response. 305 | 306 | // END Unmarshal arguments from response. 307 | return 308 | } 309 | 310 | func (client *LANHostConfigManagement1) GetIPRoutersList() (NewIPRouters string, err error) { 311 | // Request structure. 312 | request := interface{}(nil) 313 | // BEGIN Marshal arguments into request. 314 | 315 | // END Marshal arguments into request. 316 | 317 | // Response structure. 318 | response := &struct { 319 | NewIPRouters string 320 | }{} 321 | 322 | // Perform the SOAP call. 323 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetIPRoutersList", request, response); err != nil { 324 | return 325 | } 326 | 327 | // BEGIN Unmarshal arguments from response. 328 | 329 | if NewIPRouters, err = soap.UnmarshalString(response.NewIPRouters); err != nil { 330 | return 331 | } 332 | // END Unmarshal arguments from response. 333 | return 334 | } 335 | 336 | func (client *LANHostConfigManagement1) SetDomainName(NewDomainName string) (err error) { 337 | // Request structure. 338 | request := &struct { 339 | NewDomainName string 340 | }{} 341 | // BEGIN Marshal arguments into request. 342 | 343 | if request.NewDomainName, err = soap.MarshalString(NewDomainName); err != nil { 344 | return 345 | } 346 | // END Marshal arguments into request. 347 | 348 | // Response structure. 349 | response := interface{}(nil) 350 | 351 | // Perform the SOAP call. 352 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDomainName", request, response); err != nil { 353 | return 354 | } 355 | 356 | // BEGIN Unmarshal arguments from response. 357 | 358 | // END Unmarshal arguments from response. 359 | return 360 | } 361 | 362 | func (client *LANHostConfigManagement1) GetDomainName() (NewDomainName string, err error) { 363 | // Request structure. 364 | request := interface{}(nil) 365 | // BEGIN Marshal arguments into request. 366 | 367 | // END Marshal arguments into request. 368 | 369 | // Response structure. 370 | response := &struct { 371 | NewDomainName string 372 | }{} 373 | 374 | // Perform the SOAP call. 375 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDomainName", request, response); err != nil { 376 | return 377 | } 378 | 379 | // BEGIN Unmarshal arguments from response. 380 | 381 | if NewDomainName, err = soap.UnmarshalString(response.NewDomainName); err != nil { 382 | return 383 | } 384 | // END Unmarshal arguments from response. 385 | return 386 | } 387 | 388 | func (client *LANHostConfigManagement1) SetAddressRange(NewMinAddress string, NewMaxAddress string) (err error) { 389 | // Request structure. 390 | request := &struct { 391 | NewMinAddress string 392 | 393 | NewMaxAddress string 394 | }{} 395 | // BEGIN Marshal arguments into request. 396 | 397 | if request.NewMinAddress, err = soap.MarshalString(NewMinAddress); err != nil { 398 | return 399 | } 400 | if request.NewMaxAddress, err = soap.MarshalString(NewMaxAddress); err != nil { 401 | return 402 | } 403 | // END Marshal arguments into request. 404 | 405 | // Response structure. 406 | response := interface{}(nil) 407 | 408 | // Perform the SOAP call. 409 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetAddressRange", request, response); err != nil { 410 | return 411 | } 412 | 413 | // BEGIN Unmarshal arguments from response. 414 | 415 | // END Unmarshal arguments from response. 416 | return 417 | } 418 | 419 | func (client *LANHostConfigManagement1) GetAddressRange() (NewMinAddress string, NewMaxAddress string, err error) { 420 | // Request structure. 421 | request := interface{}(nil) 422 | // BEGIN Marshal arguments into request. 423 | 424 | // END Marshal arguments into request. 425 | 426 | // Response structure. 427 | response := &struct { 428 | NewMinAddress string 429 | 430 | NewMaxAddress string 431 | }{} 432 | 433 | // Perform the SOAP call. 434 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetAddressRange", request, response); err != nil { 435 | return 436 | } 437 | 438 | // BEGIN Unmarshal arguments from response. 439 | 440 | if NewMinAddress, err = soap.UnmarshalString(response.NewMinAddress); err != nil { 441 | return 442 | } 443 | if NewMaxAddress, err = soap.UnmarshalString(response.NewMaxAddress); err != nil { 444 | return 445 | } 446 | // END Unmarshal arguments from response. 447 | return 448 | } 449 | 450 | func (client *LANHostConfigManagement1) SetReservedAddress(NewReservedAddresses string) (err error) { 451 | // Request structure. 452 | request := &struct { 453 | NewReservedAddresses string 454 | }{} 455 | // BEGIN Marshal arguments into request. 456 | 457 | if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { 458 | return 459 | } 460 | // END Marshal arguments into request. 461 | 462 | // Response structure. 463 | response := interface{}(nil) 464 | 465 | // Perform the SOAP call. 466 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetReservedAddress", request, response); err != nil { 467 | return 468 | } 469 | 470 | // BEGIN Unmarshal arguments from response. 471 | 472 | // END Unmarshal arguments from response. 473 | return 474 | } 475 | 476 | func (client *LANHostConfigManagement1) DeleteReservedAddress(NewReservedAddresses string) (err error) { 477 | // Request structure. 478 | request := &struct { 479 | NewReservedAddresses string 480 | }{} 481 | // BEGIN Marshal arguments into request. 482 | 483 | if request.NewReservedAddresses, err = soap.MarshalString(NewReservedAddresses); err != nil { 484 | return 485 | } 486 | // END Marshal arguments into request. 487 | 488 | // Response structure. 489 | response := interface{}(nil) 490 | 491 | // Perform the SOAP call. 492 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteReservedAddress", request, response); err != nil { 493 | return 494 | } 495 | 496 | // BEGIN Unmarshal arguments from response. 497 | 498 | // END Unmarshal arguments from response. 499 | return 500 | } 501 | 502 | func (client *LANHostConfigManagement1) GetReservedAddresses() (NewReservedAddresses string, err error) { 503 | // Request structure. 504 | request := interface{}(nil) 505 | // BEGIN Marshal arguments into request. 506 | 507 | // END Marshal arguments into request. 508 | 509 | // Response structure. 510 | response := &struct { 511 | NewReservedAddresses string 512 | }{} 513 | 514 | // Perform the SOAP call. 515 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetReservedAddresses", request, response); err != nil { 516 | return 517 | } 518 | 519 | // BEGIN Unmarshal arguments from response. 520 | 521 | if NewReservedAddresses, err = soap.UnmarshalString(response.NewReservedAddresses); err != nil { 522 | return 523 | } 524 | // END Unmarshal arguments from response. 525 | return 526 | } 527 | 528 | func (client *LANHostConfigManagement1) SetDNSServer(NewDNSServers string) (err error) { 529 | // Request structure. 530 | request := &struct { 531 | NewDNSServers string 532 | }{} 533 | // BEGIN Marshal arguments into request. 534 | 535 | if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { 536 | return 537 | } 538 | // END Marshal arguments into request. 539 | 540 | // Response structure. 541 | response := interface{}(nil) 542 | 543 | // Perform the SOAP call. 544 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "SetDNSServer", request, response); err != nil { 545 | return 546 | } 547 | 548 | // BEGIN Unmarshal arguments from response. 549 | 550 | // END Unmarshal arguments from response. 551 | return 552 | } 553 | 554 | func (client *LANHostConfigManagement1) DeleteDNSServer(NewDNSServers string) (err error) { 555 | // Request structure. 556 | request := &struct { 557 | NewDNSServers string 558 | }{} 559 | // BEGIN Marshal arguments into request. 560 | 561 | if request.NewDNSServers, err = soap.MarshalString(NewDNSServers); err != nil { 562 | return 563 | } 564 | // END Marshal arguments into request. 565 | 566 | // Response structure. 567 | response := interface{}(nil) 568 | 569 | // Perform the SOAP call. 570 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "DeleteDNSServer", request, response); err != nil { 571 | return 572 | } 573 | 574 | // BEGIN Unmarshal arguments from response. 575 | 576 | // END Unmarshal arguments from response. 577 | return 578 | } 579 | 580 | func (client *LANHostConfigManagement1) GetDNSServers() (NewDNSServers string, err error) { 581 | // Request structure. 582 | request := interface{}(nil) 583 | // BEGIN Marshal arguments into request. 584 | 585 | // END Marshal arguments into request. 586 | 587 | // Response structure. 588 | response := &struct { 589 | NewDNSServers string 590 | }{} 591 | 592 | // Perform the SOAP call. 593 | if err = client.SOAPClient.PerformAction(URN_LANHostConfigManagement_1, "GetDNSServers", request, response); err != nil { 594 | return 595 | } 596 | 597 | // BEGIN Unmarshal arguments from response. 598 | 599 | if NewDNSServers, err = soap.UnmarshalString(response.NewDNSServers); err != nil { 600 | return 601 | } 602 | // END Unmarshal arguments from response. 603 | return 604 | } 605 | 606 | // Layer3Forwarding1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:Layer3Forwarding:1". See 607 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 608 | // are provided for informational value. 609 | type Layer3Forwarding1 struct { 610 | goupnp.ServiceClient 611 | } 612 | 613 | // NewLayer3Forwarding1Clients discovers instances of the service on the network, 614 | // and returns clients to any that are found. errors will contain an error for 615 | // any devices that replied but which could not be queried, and err will be set 616 | // if the discovery process failed outright. 617 | // 618 | // This is a typical entry calling point into this package. 619 | func NewLayer3Forwarding1Clients() (clients []*Layer3Forwarding1, errors []error, err error) { 620 | var genericClients []goupnp.ServiceClient 621 | if genericClients, errors, err = goupnp.NewServiceClients(URN_Layer3Forwarding_1); err != nil { 622 | return 623 | } 624 | clients = newLayer3Forwarding1ClientsFromGenericClients(genericClients) 625 | return 626 | } 627 | 628 | // NewLayer3Forwarding1ClientsByURL discovers instances of the service at the given 629 | // URL, and returns clients to any that are found. An error is returned if 630 | // there was an error probing the service. 631 | // 632 | // This is a typical entry calling point into this package when reusing an 633 | // previously discovered service URL. 634 | func NewLayer3Forwarding1ClientsByURL(loc *url.URL) ([]*Layer3Forwarding1, error) { 635 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_Layer3Forwarding_1) 636 | if err != nil { 637 | return nil, err 638 | } 639 | return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil 640 | } 641 | 642 | // NewLayer3Forwarding1ClientsFromRootDevice discovers instances of the service in 643 | // a given root device, and returns clients to any that are found. An error is 644 | // returned if there was not at least one instance of the service within the 645 | // device. The location parameter is simply assigned to the Location attribute 646 | // of the wrapped ServiceClient(s). 647 | // 648 | // This is a typical entry calling point into this package when reusing an 649 | // previously discovered root device. 650 | func NewLayer3Forwarding1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*Layer3Forwarding1, error) { 651 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_Layer3Forwarding_1) 652 | if err != nil { 653 | return nil, err 654 | } 655 | return newLayer3Forwarding1ClientsFromGenericClients(genericClients), nil 656 | } 657 | 658 | func newLayer3Forwarding1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*Layer3Forwarding1 { 659 | clients := make([]*Layer3Forwarding1, len(genericClients)) 660 | for i := range genericClients { 661 | clients[i] = &Layer3Forwarding1{genericClients[i]} 662 | } 663 | return clients 664 | } 665 | 666 | func (client *Layer3Forwarding1) SetDefaultConnectionService(NewDefaultConnectionService string) (err error) { 667 | // Request structure. 668 | request := &struct { 669 | NewDefaultConnectionService string 670 | }{} 671 | // BEGIN Marshal arguments into request. 672 | 673 | if request.NewDefaultConnectionService, err = soap.MarshalString(NewDefaultConnectionService); err != nil { 674 | return 675 | } 676 | // END Marshal arguments into request. 677 | 678 | // Response structure. 679 | response := interface{}(nil) 680 | 681 | // Perform the SOAP call. 682 | if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "SetDefaultConnectionService", request, response); err != nil { 683 | return 684 | } 685 | 686 | // BEGIN Unmarshal arguments from response. 687 | 688 | // END Unmarshal arguments from response. 689 | return 690 | } 691 | 692 | func (client *Layer3Forwarding1) GetDefaultConnectionService() (NewDefaultConnectionService string, err error) { 693 | // Request structure. 694 | request := interface{}(nil) 695 | // BEGIN Marshal arguments into request. 696 | 697 | // END Marshal arguments into request. 698 | 699 | // Response structure. 700 | response := &struct { 701 | NewDefaultConnectionService string 702 | }{} 703 | 704 | // Perform the SOAP call. 705 | if err = client.SOAPClient.PerformAction(URN_Layer3Forwarding_1, "GetDefaultConnectionService", request, response); err != nil { 706 | return 707 | } 708 | 709 | // BEGIN Unmarshal arguments from response. 710 | 711 | if NewDefaultConnectionService, err = soap.UnmarshalString(response.NewDefaultConnectionService); err != nil { 712 | return 713 | } 714 | // END Unmarshal arguments from response. 715 | return 716 | } 717 | 718 | // WANCableLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCableLinkConfig:1". See 719 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 720 | // are provided for informational value. 721 | type WANCableLinkConfig1 struct { 722 | goupnp.ServiceClient 723 | } 724 | 725 | // NewWANCableLinkConfig1Clients discovers instances of the service on the network, 726 | // and returns clients to any that are found. errors will contain an error for 727 | // any devices that replied but which could not be queried, and err will be set 728 | // if the discovery process failed outright. 729 | // 730 | // This is a typical entry calling point into this package. 731 | func NewWANCableLinkConfig1Clients() (clients []*WANCableLinkConfig1, errors []error, err error) { 732 | var genericClients []goupnp.ServiceClient 733 | if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCableLinkConfig_1); err != nil { 734 | return 735 | } 736 | clients = newWANCableLinkConfig1ClientsFromGenericClients(genericClients) 737 | return 738 | } 739 | 740 | // NewWANCableLinkConfig1ClientsByURL discovers instances of the service at the given 741 | // URL, and returns clients to any that are found. An error is returned if 742 | // there was an error probing the service. 743 | // 744 | // This is a typical entry calling point into this package when reusing an 745 | // previously discovered service URL. 746 | func NewWANCableLinkConfig1ClientsByURL(loc *url.URL) ([]*WANCableLinkConfig1, error) { 747 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCableLinkConfig_1) 748 | if err != nil { 749 | return nil, err 750 | } 751 | return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil 752 | } 753 | 754 | // NewWANCableLinkConfig1ClientsFromRootDevice discovers instances of the service in 755 | // a given root device, and returns clients to any that are found. An error is 756 | // returned if there was not at least one instance of the service within the 757 | // device. The location parameter is simply assigned to the Location attribute 758 | // of the wrapped ServiceClient(s). 759 | // 760 | // This is a typical entry calling point into this package when reusing an 761 | // previously discovered root device. 762 | func NewWANCableLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCableLinkConfig1, error) { 763 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCableLinkConfig_1) 764 | if err != nil { 765 | return nil, err 766 | } 767 | return newWANCableLinkConfig1ClientsFromGenericClients(genericClients), nil 768 | } 769 | 770 | func newWANCableLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCableLinkConfig1 { 771 | clients := make([]*WANCableLinkConfig1, len(genericClients)) 772 | for i := range genericClients { 773 | clients[i] = &WANCableLinkConfig1{genericClients[i]} 774 | } 775 | return clients 776 | } 777 | 778 | // 779 | // Return values: 780 | // 781 | // * NewCableLinkConfigState: allowed values: notReady, dsSyncComplete, usParamAcquired, rangingComplete, ipComplete, todEstablished, paramTransferComplete, registrationComplete, operational, accessDenied 782 | // 783 | // * NewLinkType: allowed values: Ethernet 784 | func (client *WANCableLinkConfig1) GetCableLinkConfigInfo() (NewCableLinkConfigState string, NewLinkType string, err error) { 785 | // Request structure. 786 | request := interface{}(nil) 787 | // BEGIN Marshal arguments into request. 788 | 789 | // END Marshal arguments into request. 790 | 791 | // Response structure. 792 | response := &struct { 793 | NewCableLinkConfigState string 794 | 795 | NewLinkType string 796 | }{} 797 | 798 | // Perform the SOAP call. 799 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetCableLinkConfigInfo", request, response); err != nil { 800 | return 801 | } 802 | 803 | // BEGIN Unmarshal arguments from response. 804 | 805 | if NewCableLinkConfigState, err = soap.UnmarshalString(response.NewCableLinkConfigState); err != nil { 806 | return 807 | } 808 | if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { 809 | return 810 | } 811 | // END Unmarshal arguments from response. 812 | return 813 | } 814 | 815 | func (client *WANCableLinkConfig1) GetDownstreamFrequency() (NewDownstreamFrequency uint32, err error) { 816 | // Request structure. 817 | request := interface{}(nil) 818 | // BEGIN Marshal arguments into request. 819 | 820 | // END Marshal arguments into request. 821 | 822 | // Response structure. 823 | response := &struct { 824 | NewDownstreamFrequency string 825 | }{} 826 | 827 | // Perform the SOAP call. 828 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamFrequency", request, response); err != nil { 829 | return 830 | } 831 | 832 | // BEGIN Unmarshal arguments from response. 833 | 834 | if NewDownstreamFrequency, err = soap.UnmarshalUi4(response.NewDownstreamFrequency); err != nil { 835 | return 836 | } 837 | // END Unmarshal arguments from response. 838 | return 839 | } 840 | 841 | // 842 | // Return values: 843 | // 844 | // * NewDownstreamModulation: allowed values: 64QAM, 256QAM 845 | func (client *WANCableLinkConfig1) GetDownstreamModulation() (NewDownstreamModulation string, err error) { 846 | // Request structure. 847 | request := interface{}(nil) 848 | // BEGIN Marshal arguments into request. 849 | 850 | // END Marshal arguments into request. 851 | 852 | // Response structure. 853 | response := &struct { 854 | NewDownstreamModulation string 855 | }{} 856 | 857 | // Perform the SOAP call. 858 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetDownstreamModulation", request, response); err != nil { 859 | return 860 | } 861 | 862 | // BEGIN Unmarshal arguments from response. 863 | 864 | if NewDownstreamModulation, err = soap.UnmarshalString(response.NewDownstreamModulation); err != nil { 865 | return 866 | } 867 | // END Unmarshal arguments from response. 868 | return 869 | } 870 | 871 | func (client *WANCableLinkConfig1) GetUpstreamFrequency() (NewUpstreamFrequency uint32, err error) { 872 | // Request structure. 873 | request := interface{}(nil) 874 | // BEGIN Marshal arguments into request. 875 | 876 | // END Marshal arguments into request. 877 | 878 | // Response structure. 879 | response := &struct { 880 | NewUpstreamFrequency string 881 | }{} 882 | 883 | // Perform the SOAP call. 884 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamFrequency", request, response); err != nil { 885 | return 886 | } 887 | 888 | // BEGIN Unmarshal arguments from response. 889 | 890 | if NewUpstreamFrequency, err = soap.UnmarshalUi4(response.NewUpstreamFrequency); err != nil { 891 | return 892 | } 893 | // END Unmarshal arguments from response. 894 | return 895 | } 896 | 897 | // 898 | // Return values: 899 | // 900 | // * NewUpstreamModulation: allowed values: QPSK, 16QAM 901 | func (client *WANCableLinkConfig1) GetUpstreamModulation() (NewUpstreamModulation string, err error) { 902 | // Request structure. 903 | request := interface{}(nil) 904 | // BEGIN Marshal arguments into request. 905 | 906 | // END Marshal arguments into request. 907 | 908 | // Response structure. 909 | response := &struct { 910 | NewUpstreamModulation string 911 | }{} 912 | 913 | // Perform the SOAP call. 914 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamModulation", request, response); err != nil { 915 | return 916 | } 917 | 918 | // BEGIN Unmarshal arguments from response. 919 | 920 | if NewUpstreamModulation, err = soap.UnmarshalString(response.NewUpstreamModulation); err != nil { 921 | return 922 | } 923 | // END Unmarshal arguments from response. 924 | return 925 | } 926 | 927 | func (client *WANCableLinkConfig1) GetUpstreamChannelID() (NewUpstreamChannelID uint32, err error) { 928 | // Request structure. 929 | request := interface{}(nil) 930 | // BEGIN Marshal arguments into request. 931 | 932 | // END Marshal arguments into request. 933 | 934 | // Response structure. 935 | response := &struct { 936 | NewUpstreamChannelID string 937 | }{} 938 | 939 | // Perform the SOAP call. 940 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamChannelID", request, response); err != nil { 941 | return 942 | } 943 | 944 | // BEGIN Unmarshal arguments from response. 945 | 946 | if NewUpstreamChannelID, err = soap.UnmarshalUi4(response.NewUpstreamChannelID); err != nil { 947 | return 948 | } 949 | // END Unmarshal arguments from response. 950 | return 951 | } 952 | 953 | func (client *WANCableLinkConfig1) GetUpstreamPowerLevel() (NewUpstreamPowerLevel uint32, err error) { 954 | // Request structure. 955 | request := interface{}(nil) 956 | // BEGIN Marshal arguments into request. 957 | 958 | // END Marshal arguments into request. 959 | 960 | // Response structure. 961 | response := &struct { 962 | NewUpstreamPowerLevel string 963 | }{} 964 | 965 | // Perform the SOAP call. 966 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetUpstreamPowerLevel", request, response); err != nil { 967 | return 968 | } 969 | 970 | // BEGIN Unmarshal arguments from response. 971 | 972 | if NewUpstreamPowerLevel, err = soap.UnmarshalUi4(response.NewUpstreamPowerLevel); err != nil { 973 | return 974 | } 975 | // END Unmarshal arguments from response. 976 | return 977 | } 978 | 979 | func (client *WANCableLinkConfig1) GetBPIEncryptionEnabled() (NewBPIEncryptionEnabled bool, err error) { 980 | // Request structure. 981 | request := interface{}(nil) 982 | // BEGIN Marshal arguments into request. 983 | 984 | // END Marshal arguments into request. 985 | 986 | // Response structure. 987 | response := &struct { 988 | NewBPIEncryptionEnabled string 989 | }{} 990 | 991 | // Perform the SOAP call. 992 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetBPIEncryptionEnabled", request, response); err != nil { 993 | return 994 | } 995 | 996 | // BEGIN Unmarshal arguments from response. 997 | 998 | if NewBPIEncryptionEnabled, err = soap.UnmarshalBoolean(response.NewBPIEncryptionEnabled); err != nil { 999 | return 1000 | } 1001 | // END Unmarshal arguments from response. 1002 | return 1003 | } 1004 | 1005 | func (client *WANCableLinkConfig1) GetConfigFile() (NewConfigFile string, err error) { 1006 | // Request structure. 1007 | request := interface{}(nil) 1008 | // BEGIN Marshal arguments into request. 1009 | 1010 | // END Marshal arguments into request. 1011 | 1012 | // Response structure. 1013 | response := &struct { 1014 | NewConfigFile string 1015 | }{} 1016 | 1017 | // Perform the SOAP call. 1018 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetConfigFile", request, response); err != nil { 1019 | return 1020 | } 1021 | 1022 | // BEGIN Unmarshal arguments from response. 1023 | 1024 | if NewConfigFile, err = soap.UnmarshalString(response.NewConfigFile); err != nil { 1025 | return 1026 | } 1027 | // END Unmarshal arguments from response. 1028 | return 1029 | } 1030 | 1031 | func (client *WANCableLinkConfig1) GetTFTPServer() (NewTFTPServer string, err error) { 1032 | // Request structure. 1033 | request := interface{}(nil) 1034 | // BEGIN Marshal arguments into request. 1035 | 1036 | // END Marshal arguments into request. 1037 | 1038 | // Response structure. 1039 | response := &struct { 1040 | NewTFTPServer string 1041 | }{} 1042 | 1043 | // Perform the SOAP call. 1044 | if err = client.SOAPClient.PerformAction(URN_WANCableLinkConfig_1, "GetTFTPServer", request, response); err != nil { 1045 | return 1046 | } 1047 | 1048 | // BEGIN Unmarshal arguments from response. 1049 | 1050 | if NewTFTPServer, err = soap.UnmarshalString(response.NewTFTPServer); err != nil { 1051 | return 1052 | } 1053 | // END Unmarshal arguments from response. 1054 | return 1055 | } 1056 | 1057 | // WANCommonInterfaceConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1". See 1058 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 1059 | // are provided for informational value. 1060 | type WANCommonInterfaceConfig1 struct { 1061 | goupnp.ServiceClient 1062 | } 1063 | 1064 | // NewWANCommonInterfaceConfig1Clients discovers instances of the service on the network, 1065 | // and returns clients to any that are found. errors will contain an error for 1066 | // any devices that replied but which could not be queried, and err will be set 1067 | // if the discovery process failed outright. 1068 | // 1069 | // This is a typical entry calling point into this package. 1070 | func NewWANCommonInterfaceConfig1Clients() (clients []*WANCommonInterfaceConfig1, errors []error, err error) { 1071 | var genericClients []goupnp.ServiceClient 1072 | if genericClients, errors, err = goupnp.NewServiceClients(URN_WANCommonInterfaceConfig_1); err != nil { 1073 | return 1074 | } 1075 | clients = newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients) 1076 | return 1077 | } 1078 | 1079 | // NewWANCommonInterfaceConfig1ClientsByURL discovers instances of the service at the given 1080 | // URL, and returns clients to any that are found. An error is returned if 1081 | // there was an error probing the service. 1082 | // 1083 | // This is a typical entry calling point into this package when reusing an 1084 | // previously discovered service URL. 1085 | func NewWANCommonInterfaceConfig1ClientsByURL(loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { 1086 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANCommonInterfaceConfig_1) 1087 | if err != nil { 1088 | return nil, err 1089 | } 1090 | return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil 1091 | } 1092 | 1093 | // NewWANCommonInterfaceConfig1ClientsFromRootDevice discovers instances of the service in 1094 | // a given root device, and returns clients to any that are found. An error is 1095 | // returned if there was not at least one instance of the service within the 1096 | // device. The location parameter is simply assigned to the Location attribute 1097 | // of the wrapped ServiceClient(s). 1098 | // 1099 | // This is a typical entry calling point into this package when reusing an 1100 | // previously discovered root device. 1101 | func NewWANCommonInterfaceConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANCommonInterfaceConfig1, error) { 1102 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANCommonInterfaceConfig_1) 1103 | if err != nil { 1104 | return nil, err 1105 | } 1106 | return newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients), nil 1107 | } 1108 | 1109 | func newWANCommonInterfaceConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANCommonInterfaceConfig1 { 1110 | clients := make([]*WANCommonInterfaceConfig1, len(genericClients)) 1111 | for i := range genericClients { 1112 | clients[i] = &WANCommonInterfaceConfig1{genericClients[i]} 1113 | } 1114 | return clients 1115 | } 1116 | 1117 | func (client *WANCommonInterfaceConfig1) SetEnabledForInternet(NewEnabledForInternet bool) (err error) { 1118 | // Request structure. 1119 | request := &struct { 1120 | NewEnabledForInternet string 1121 | }{} 1122 | // BEGIN Marshal arguments into request. 1123 | 1124 | if request.NewEnabledForInternet, err = soap.MarshalBoolean(NewEnabledForInternet); err != nil { 1125 | return 1126 | } 1127 | // END Marshal arguments into request. 1128 | 1129 | // Response structure. 1130 | response := interface{}(nil) 1131 | 1132 | // Perform the SOAP call. 1133 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "SetEnabledForInternet", request, response); err != nil { 1134 | return 1135 | } 1136 | 1137 | // BEGIN Unmarshal arguments from response. 1138 | 1139 | // END Unmarshal arguments from response. 1140 | return 1141 | } 1142 | 1143 | func (client *WANCommonInterfaceConfig1) GetEnabledForInternet() (NewEnabledForInternet bool, err error) { 1144 | // Request structure. 1145 | request := interface{}(nil) 1146 | // BEGIN Marshal arguments into request. 1147 | 1148 | // END Marshal arguments into request. 1149 | 1150 | // Response structure. 1151 | response := &struct { 1152 | NewEnabledForInternet string 1153 | }{} 1154 | 1155 | // Perform the SOAP call. 1156 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetEnabledForInternet", request, response); err != nil { 1157 | return 1158 | } 1159 | 1160 | // BEGIN Unmarshal arguments from response. 1161 | 1162 | if NewEnabledForInternet, err = soap.UnmarshalBoolean(response.NewEnabledForInternet); err != nil { 1163 | return 1164 | } 1165 | // END Unmarshal arguments from response. 1166 | return 1167 | } 1168 | 1169 | // 1170 | // Return values: 1171 | // 1172 | // * NewWANAccessType: allowed values: DSL, POTS, Cable, Ethernet 1173 | // 1174 | // * NewPhysicalLinkStatus: allowed values: Up, Down 1175 | func (client *WANCommonInterfaceConfig1) GetCommonLinkProperties() (NewWANAccessType string, NewLayer1UpstreamMaxBitRate uint32, NewLayer1DownstreamMaxBitRate uint32, NewPhysicalLinkStatus string, err error) { 1176 | // Request structure. 1177 | request := interface{}(nil) 1178 | // BEGIN Marshal arguments into request. 1179 | 1180 | // END Marshal arguments into request. 1181 | 1182 | // Response structure. 1183 | response := &struct { 1184 | NewWANAccessType string 1185 | 1186 | NewLayer1UpstreamMaxBitRate string 1187 | 1188 | NewLayer1DownstreamMaxBitRate string 1189 | 1190 | NewPhysicalLinkStatus string 1191 | }{} 1192 | 1193 | // Perform the SOAP call. 1194 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetCommonLinkProperties", request, response); err != nil { 1195 | return 1196 | } 1197 | 1198 | // BEGIN Unmarshal arguments from response. 1199 | 1200 | if NewWANAccessType, err = soap.UnmarshalString(response.NewWANAccessType); err != nil { 1201 | return 1202 | } 1203 | if NewLayer1UpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1UpstreamMaxBitRate); err != nil { 1204 | return 1205 | } 1206 | if NewLayer1DownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewLayer1DownstreamMaxBitRate); err != nil { 1207 | return 1208 | } 1209 | if NewPhysicalLinkStatus, err = soap.UnmarshalString(response.NewPhysicalLinkStatus); err != nil { 1210 | return 1211 | } 1212 | // END Unmarshal arguments from response. 1213 | return 1214 | } 1215 | 1216 | func (client *WANCommonInterfaceConfig1) GetWANAccessProvider() (NewWANAccessProvider string, err error) { 1217 | // Request structure. 1218 | request := interface{}(nil) 1219 | // BEGIN Marshal arguments into request. 1220 | 1221 | // END Marshal arguments into request. 1222 | 1223 | // Response structure. 1224 | response := &struct { 1225 | NewWANAccessProvider string 1226 | }{} 1227 | 1228 | // Perform the SOAP call. 1229 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetWANAccessProvider", request, response); err != nil { 1230 | return 1231 | } 1232 | 1233 | // BEGIN Unmarshal arguments from response. 1234 | 1235 | if NewWANAccessProvider, err = soap.UnmarshalString(response.NewWANAccessProvider); err != nil { 1236 | return 1237 | } 1238 | // END Unmarshal arguments from response. 1239 | return 1240 | } 1241 | 1242 | // 1243 | // Return values: 1244 | // 1245 | // * NewMaximumActiveConnections: allowed value range: minimum=1, step=1 1246 | func (client *WANCommonInterfaceConfig1) GetMaximumActiveConnections() (NewMaximumActiveConnections uint16, err error) { 1247 | // Request structure. 1248 | request := interface{}(nil) 1249 | // BEGIN Marshal arguments into request. 1250 | 1251 | // END Marshal arguments into request. 1252 | 1253 | // Response structure. 1254 | response := &struct { 1255 | NewMaximumActiveConnections string 1256 | }{} 1257 | 1258 | // Perform the SOAP call. 1259 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetMaximumActiveConnections", request, response); err != nil { 1260 | return 1261 | } 1262 | 1263 | // BEGIN Unmarshal arguments from response. 1264 | 1265 | if NewMaximumActiveConnections, err = soap.UnmarshalUi2(response.NewMaximumActiveConnections); err != nil { 1266 | return 1267 | } 1268 | // END Unmarshal arguments from response. 1269 | return 1270 | } 1271 | 1272 | func (client *WANCommonInterfaceConfig1) GetTotalBytesSent() (NewTotalBytesSent uint32, err error) { 1273 | // Request structure. 1274 | request := interface{}(nil) 1275 | // BEGIN Marshal arguments into request. 1276 | 1277 | // END Marshal arguments into request. 1278 | 1279 | // Response structure. 1280 | response := &struct { 1281 | NewTotalBytesSent string 1282 | }{} 1283 | 1284 | // Perform the SOAP call. 1285 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesSent", request, response); err != nil { 1286 | return 1287 | } 1288 | 1289 | // BEGIN Unmarshal arguments from response. 1290 | 1291 | if NewTotalBytesSent, err = soap.UnmarshalUi4(response.NewTotalBytesSent); err != nil { 1292 | return 1293 | } 1294 | // END Unmarshal arguments from response. 1295 | return 1296 | } 1297 | 1298 | func (client *WANCommonInterfaceConfig1) GetTotalBytesReceived() (NewTotalBytesReceived uint32, err error) { 1299 | // Request structure. 1300 | request := interface{}(nil) 1301 | // BEGIN Marshal arguments into request. 1302 | 1303 | // END Marshal arguments into request. 1304 | 1305 | // Response structure. 1306 | response := &struct { 1307 | NewTotalBytesReceived string 1308 | }{} 1309 | 1310 | // Perform the SOAP call. 1311 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalBytesReceived", request, response); err != nil { 1312 | return 1313 | } 1314 | 1315 | // BEGIN Unmarshal arguments from response. 1316 | 1317 | if NewTotalBytesReceived, err = soap.UnmarshalUi4(response.NewTotalBytesReceived); err != nil { 1318 | return 1319 | } 1320 | // END Unmarshal arguments from response. 1321 | return 1322 | } 1323 | 1324 | func (client *WANCommonInterfaceConfig1) GetTotalPacketsSent() (NewTotalPacketsSent uint32, err error) { 1325 | // Request structure. 1326 | request := interface{}(nil) 1327 | // BEGIN Marshal arguments into request. 1328 | 1329 | // END Marshal arguments into request. 1330 | 1331 | // Response structure. 1332 | response := &struct { 1333 | NewTotalPacketsSent string 1334 | }{} 1335 | 1336 | // Perform the SOAP call. 1337 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsSent", request, response); err != nil { 1338 | return 1339 | } 1340 | 1341 | // BEGIN Unmarshal arguments from response. 1342 | 1343 | if NewTotalPacketsSent, err = soap.UnmarshalUi4(response.NewTotalPacketsSent); err != nil { 1344 | return 1345 | } 1346 | // END Unmarshal arguments from response. 1347 | return 1348 | } 1349 | 1350 | func (client *WANCommonInterfaceConfig1) GetTotalPacketsReceived() (NewTotalPacketsReceived uint32, err error) { 1351 | // Request structure. 1352 | request := interface{}(nil) 1353 | // BEGIN Marshal arguments into request. 1354 | 1355 | // END Marshal arguments into request. 1356 | 1357 | // Response structure. 1358 | response := &struct { 1359 | NewTotalPacketsReceived string 1360 | }{} 1361 | 1362 | // Perform the SOAP call. 1363 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetTotalPacketsReceived", request, response); err != nil { 1364 | return 1365 | } 1366 | 1367 | // BEGIN Unmarshal arguments from response. 1368 | 1369 | if NewTotalPacketsReceived, err = soap.UnmarshalUi4(response.NewTotalPacketsReceived); err != nil { 1370 | return 1371 | } 1372 | // END Unmarshal arguments from response. 1373 | return 1374 | } 1375 | 1376 | func (client *WANCommonInterfaceConfig1) GetActiveConnection(NewActiveConnectionIndex uint16) (NewActiveConnDeviceContainer string, NewActiveConnectionServiceID string, err error) { 1377 | // Request structure. 1378 | request := &struct { 1379 | NewActiveConnectionIndex string 1380 | }{} 1381 | // BEGIN Marshal arguments into request. 1382 | 1383 | if request.NewActiveConnectionIndex, err = soap.MarshalUi2(NewActiveConnectionIndex); err != nil { 1384 | return 1385 | } 1386 | // END Marshal arguments into request. 1387 | 1388 | // Response structure. 1389 | response := &struct { 1390 | NewActiveConnDeviceContainer string 1391 | 1392 | NewActiveConnectionServiceID string 1393 | }{} 1394 | 1395 | // Perform the SOAP call. 1396 | if err = client.SOAPClient.PerformAction(URN_WANCommonInterfaceConfig_1, "GetActiveConnection", request, response); err != nil { 1397 | return 1398 | } 1399 | 1400 | // BEGIN Unmarshal arguments from response. 1401 | 1402 | if NewActiveConnDeviceContainer, err = soap.UnmarshalString(response.NewActiveConnDeviceContainer); err != nil { 1403 | return 1404 | } 1405 | if NewActiveConnectionServiceID, err = soap.UnmarshalString(response.NewActiveConnectionServiceID); err != nil { 1406 | return 1407 | } 1408 | // END Unmarshal arguments from response. 1409 | return 1410 | } 1411 | 1412 | // WANDSLLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANDSLLinkConfig:1". See 1413 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 1414 | // are provided for informational value. 1415 | type WANDSLLinkConfig1 struct { 1416 | goupnp.ServiceClient 1417 | } 1418 | 1419 | // NewWANDSLLinkConfig1Clients discovers instances of the service on the network, 1420 | // and returns clients to any that are found. errors will contain an error for 1421 | // any devices that replied but which could not be queried, and err will be set 1422 | // if the discovery process failed outright. 1423 | // 1424 | // This is a typical entry calling point into this package. 1425 | func NewWANDSLLinkConfig1Clients() (clients []*WANDSLLinkConfig1, errors []error, err error) { 1426 | var genericClients []goupnp.ServiceClient 1427 | if genericClients, errors, err = goupnp.NewServiceClients(URN_WANDSLLinkConfig_1); err != nil { 1428 | return 1429 | } 1430 | clients = newWANDSLLinkConfig1ClientsFromGenericClients(genericClients) 1431 | return 1432 | } 1433 | 1434 | // NewWANDSLLinkConfig1ClientsByURL discovers instances of the service at the given 1435 | // URL, and returns clients to any that are found. An error is returned if 1436 | // there was an error probing the service. 1437 | // 1438 | // This is a typical entry calling point into this package when reusing an 1439 | // previously discovered service URL. 1440 | func NewWANDSLLinkConfig1ClientsByURL(loc *url.URL) ([]*WANDSLLinkConfig1, error) { 1441 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANDSLLinkConfig_1) 1442 | if err != nil { 1443 | return nil, err 1444 | } 1445 | return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil 1446 | } 1447 | 1448 | // NewWANDSLLinkConfig1ClientsFromRootDevice discovers instances of the service in 1449 | // a given root device, and returns clients to any that are found. An error is 1450 | // returned if there was not at least one instance of the service within the 1451 | // device. The location parameter is simply assigned to the Location attribute 1452 | // of the wrapped ServiceClient(s). 1453 | // 1454 | // This is a typical entry calling point into this package when reusing an 1455 | // previously discovered root device. 1456 | func NewWANDSLLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANDSLLinkConfig1, error) { 1457 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANDSLLinkConfig_1) 1458 | if err != nil { 1459 | return nil, err 1460 | } 1461 | return newWANDSLLinkConfig1ClientsFromGenericClients(genericClients), nil 1462 | } 1463 | 1464 | func newWANDSLLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANDSLLinkConfig1 { 1465 | clients := make([]*WANDSLLinkConfig1, len(genericClients)) 1466 | for i := range genericClients { 1467 | clients[i] = &WANDSLLinkConfig1{genericClients[i]} 1468 | } 1469 | return clients 1470 | } 1471 | 1472 | func (client *WANDSLLinkConfig1) SetDSLLinkType(NewLinkType string) (err error) { 1473 | // Request structure. 1474 | request := &struct { 1475 | NewLinkType string 1476 | }{} 1477 | // BEGIN Marshal arguments into request. 1478 | 1479 | if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { 1480 | return 1481 | } 1482 | // END Marshal arguments into request. 1483 | 1484 | // Response structure. 1485 | response := interface{}(nil) 1486 | 1487 | // Perform the SOAP call. 1488 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDSLLinkType", request, response); err != nil { 1489 | return 1490 | } 1491 | 1492 | // BEGIN Unmarshal arguments from response. 1493 | 1494 | // END Unmarshal arguments from response. 1495 | return 1496 | } 1497 | 1498 | // 1499 | // Return values: 1500 | // 1501 | // * NewLinkStatus: allowed values: Up, Down 1502 | func (client *WANDSLLinkConfig1) GetDSLLinkInfo() (NewLinkType string, NewLinkStatus string, err error) { 1503 | // Request structure. 1504 | request := interface{}(nil) 1505 | // BEGIN Marshal arguments into request. 1506 | 1507 | // END Marshal arguments into request. 1508 | 1509 | // Response structure. 1510 | response := &struct { 1511 | NewLinkType string 1512 | 1513 | NewLinkStatus string 1514 | }{} 1515 | 1516 | // Perform the SOAP call. 1517 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDSLLinkInfo", request, response); err != nil { 1518 | return 1519 | } 1520 | 1521 | // BEGIN Unmarshal arguments from response. 1522 | 1523 | if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { 1524 | return 1525 | } 1526 | if NewLinkStatus, err = soap.UnmarshalString(response.NewLinkStatus); err != nil { 1527 | return 1528 | } 1529 | // END Unmarshal arguments from response. 1530 | return 1531 | } 1532 | 1533 | func (client *WANDSLLinkConfig1) GetAutoConfig() (NewAutoConfig bool, err error) { 1534 | // Request structure. 1535 | request := interface{}(nil) 1536 | // BEGIN Marshal arguments into request. 1537 | 1538 | // END Marshal arguments into request. 1539 | 1540 | // Response structure. 1541 | response := &struct { 1542 | NewAutoConfig string 1543 | }{} 1544 | 1545 | // Perform the SOAP call. 1546 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetAutoConfig", request, response); err != nil { 1547 | return 1548 | } 1549 | 1550 | // BEGIN Unmarshal arguments from response. 1551 | 1552 | if NewAutoConfig, err = soap.UnmarshalBoolean(response.NewAutoConfig); err != nil { 1553 | return 1554 | } 1555 | // END Unmarshal arguments from response. 1556 | return 1557 | } 1558 | 1559 | func (client *WANDSLLinkConfig1) GetModulationType() (NewModulationType string, err error) { 1560 | // Request structure. 1561 | request := interface{}(nil) 1562 | // BEGIN Marshal arguments into request. 1563 | 1564 | // END Marshal arguments into request. 1565 | 1566 | // Response structure. 1567 | response := &struct { 1568 | NewModulationType string 1569 | }{} 1570 | 1571 | // Perform the SOAP call. 1572 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetModulationType", request, response); err != nil { 1573 | return 1574 | } 1575 | 1576 | // BEGIN Unmarshal arguments from response. 1577 | 1578 | if NewModulationType, err = soap.UnmarshalString(response.NewModulationType); err != nil { 1579 | return 1580 | } 1581 | // END Unmarshal arguments from response. 1582 | return 1583 | } 1584 | 1585 | func (client *WANDSLLinkConfig1) SetDestinationAddress(NewDestinationAddress string) (err error) { 1586 | // Request structure. 1587 | request := &struct { 1588 | NewDestinationAddress string 1589 | }{} 1590 | // BEGIN Marshal arguments into request. 1591 | 1592 | if request.NewDestinationAddress, err = soap.MarshalString(NewDestinationAddress); err != nil { 1593 | return 1594 | } 1595 | // END Marshal arguments into request. 1596 | 1597 | // Response structure. 1598 | response := interface{}(nil) 1599 | 1600 | // Perform the SOAP call. 1601 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetDestinationAddress", request, response); err != nil { 1602 | return 1603 | } 1604 | 1605 | // BEGIN Unmarshal arguments from response. 1606 | 1607 | // END Unmarshal arguments from response. 1608 | return 1609 | } 1610 | 1611 | func (client *WANDSLLinkConfig1) GetDestinationAddress() (NewDestinationAddress string, err error) { 1612 | // Request structure. 1613 | request := interface{}(nil) 1614 | // BEGIN Marshal arguments into request. 1615 | 1616 | // END Marshal arguments into request. 1617 | 1618 | // Response structure. 1619 | response := &struct { 1620 | NewDestinationAddress string 1621 | }{} 1622 | 1623 | // Perform the SOAP call. 1624 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetDestinationAddress", request, response); err != nil { 1625 | return 1626 | } 1627 | 1628 | // BEGIN Unmarshal arguments from response. 1629 | 1630 | if NewDestinationAddress, err = soap.UnmarshalString(response.NewDestinationAddress); err != nil { 1631 | return 1632 | } 1633 | // END Unmarshal arguments from response. 1634 | return 1635 | } 1636 | 1637 | func (client *WANDSLLinkConfig1) SetATMEncapsulation(NewATMEncapsulation string) (err error) { 1638 | // Request structure. 1639 | request := &struct { 1640 | NewATMEncapsulation string 1641 | }{} 1642 | // BEGIN Marshal arguments into request. 1643 | 1644 | if request.NewATMEncapsulation, err = soap.MarshalString(NewATMEncapsulation); err != nil { 1645 | return 1646 | } 1647 | // END Marshal arguments into request. 1648 | 1649 | // Response structure. 1650 | response := interface{}(nil) 1651 | 1652 | // Perform the SOAP call. 1653 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetATMEncapsulation", request, response); err != nil { 1654 | return 1655 | } 1656 | 1657 | // BEGIN Unmarshal arguments from response. 1658 | 1659 | // END Unmarshal arguments from response. 1660 | return 1661 | } 1662 | 1663 | func (client *WANDSLLinkConfig1) GetATMEncapsulation() (NewATMEncapsulation string, err error) { 1664 | // Request structure. 1665 | request := interface{}(nil) 1666 | // BEGIN Marshal arguments into request. 1667 | 1668 | // END Marshal arguments into request. 1669 | 1670 | // Response structure. 1671 | response := &struct { 1672 | NewATMEncapsulation string 1673 | }{} 1674 | 1675 | // Perform the SOAP call. 1676 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetATMEncapsulation", request, response); err != nil { 1677 | return 1678 | } 1679 | 1680 | // BEGIN Unmarshal arguments from response. 1681 | 1682 | if NewATMEncapsulation, err = soap.UnmarshalString(response.NewATMEncapsulation); err != nil { 1683 | return 1684 | } 1685 | // END Unmarshal arguments from response. 1686 | return 1687 | } 1688 | 1689 | func (client *WANDSLLinkConfig1) SetFCSPreserved(NewFCSPreserved bool) (err error) { 1690 | // Request structure. 1691 | request := &struct { 1692 | NewFCSPreserved string 1693 | }{} 1694 | // BEGIN Marshal arguments into request. 1695 | 1696 | if request.NewFCSPreserved, err = soap.MarshalBoolean(NewFCSPreserved); err != nil { 1697 | return 1698 | } 1699 | // END Marshal arguments into request. 1700 | 1701 | // Response structure. 1702 | response := interface{}(nil) 1703 | 1704 | // Perform the SOAP call. 1705 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "SetFCSPreserved", request, response); err != nil { 1706 | return 1707 | } 1708 | 1709 | // BEGIN Unmarshal arguments from response. 1710 | 1711 | // END Unmarshal arguments from response. 1712 | return 1713 | } 1714 | 1715 | func (client *WANDSLLinkConfig1) GetFCSPreserved() (NewFCSPreserved bool, err error) { 1716 | // Request structure. 1717 | request := interface{}(nil) 1718 | // BEGIN Marshal arguments into request. 1719 | 1720 | // END Marshal arguments into request. 1721 | 1722 | // Response structure. 1723 | response := &struct { 1724 | NewFCSPreserved string 1725 | }{} 1726 | 1727 | // Perform the SOAP call. 1728 | if err = client.SOAPClient.PerformAction(URN_WANDSLLinkConfig_1, "GetFCSPreserved", request, response); err != nil { 1729 | return 1730 | } 1731 | 1732 | // BEGIN Unmarshal arguments from response. 1733 | 1734 | if NewFCSPreserved, err = soap.UnmarshalBoolean(response.NewFCSPreserved); err != nil { 1735 | return 1736 | } 1737 | // END Unmarshal arguments from response. 1738 | return 1739 | } 1740 | 1741 | // WANEthernetLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANEthernetLinkConfig:1". See 1742 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 1743 | // are provided for informational value. 1744 | type WANEthernetLinkConfig1 struct { 1745 | goupnp.ServiceClient 1746 | } 1747 | 1748 | // NewWANEthernetLinkConfig1Clients discovers instances of the service on the network, 1749 | // and returns clients to any that are found. errors will contain an error for 1750 | // any devices that replied but which could not be queried, and err will be set 1751 | // if the discovery process failed outright. 1752 | // 1753 | // This is a typical entry calling point into this package. 1754 | func NewWANEthernetLinkConfig1Clients() (clients []*WANEthernetLinkConfig1, errors []error, err error) { 1755 | var genericClients []goupnp.ServiceClient 1756 | if genericClients, errors, err = goupnp.NewServiceClients(URN_WANEthernetLinkConfig_1); err != nil { 1757 | return 1758 | } 1759 | clients = newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients) 1760 | return 1761 | } 1762 | 1763 | // NewWANEthernetLinkConfig1ClientsByURL discovers instances of the service at the given 1764 | // URL, and returns clients to any that are found. An error is returned if 1765 | // there was an error probing the service. 1766 | // 1767 | // This is a typical entry calling point into this package when reusing an 1768 | // previously discovered service URL. 1769 | func NewWANEthernetLinkConfig1ClientsByURL(loc *url.URL) ([]*WANEthernetLinkConfig1, error) { 1770 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANEthernetLinkConfig_1) 1771 | if err != nil { 1772 | return nil, err 1773 | } 1774 | return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil 1775 | } 1776 | 1777 | // NewWANEthernetLinkConfig1ClientsFromRootDevice discovers instances of the service in 1778 | // a given root device, and returns clients to any that are found. An error is 1779 | // returned if there was not at least one instance of the service within the 1780 | // device. The location parameter is simply assigned to the Location attribute 1781 | // of the wrapped ServiceClient(s). 1782 | // 1783 | // This is a typical entry calling point into this package when reusing an 1784 | // previously discovered root device. 1785 | func NewWANEthernetLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANEthernetLinkConfig1, error) { 1786 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANEthernetLinkConfig_1) 1787 | if err != nil { 1788 | return nil, err 1789 | } 1790 | return newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients), nil 1791 | } 1792 | 1793 | func newWANEthernetLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANEthernetLinkConfig1 { 1794 | clients := make([]*WANEthernetLinkConfig1, len(genericClients)) 1795 | for i := range genericClients { 1796 | clients[i] = &WANEthernetLinkConfig1{genericClients[i]} 1797 | } 1798 | return clients 1799 | } 1800 | 1801 | // 1802 | // Return values: 1803 | // 1804 | // * NewEthernetLinkStatus: allowed values: Up, Down 1805 | func (client *WANEthernetLinkConfig1) GetEthernetLinkStatus() (NewEthernetLinkStatus string, err error) { 1806 | // Request structure. 1807 | request := interface{}(nil) 1808 | // BEGIN Marshal arguments into request. 1809 | 1810 | // END Marshal arguments into request. 1811 | 1812 | // Response structure. 1813 | response := &struct { 1814 | NewEthernetLinkStatus string 1815 | }{} 1816 | 1817 | // Perform the SOAP call. 1818 | if err = client.SOAPClient.PerformAction(URN_WANEthernetLinkConfig_1, "GetEthernetLinkStatus", request, response); err != nil { 1819 | return 1820 | } 1821 | 1822 | // BEGIN Unmarshal arguments from response. 1823 | 1824 | if NewEthernetLinkStatus, err = soap.UnmarshalString(response.NewEthernetLinkStatus); err != nil { 1825 | return 1826 | } 1827 | // END Unmarshal arguments from response. 1828 | return 1829 | } 1830 | 1831 | // WANIPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANIPConnection:1". See 1832 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 1833 | // are provided for informational value. 1834 | type WANIPConnection1 struct { 1835 | goupnp.ServiceClient 1836 | } 1837 | 1838 | // NewWANIPConnection1Clients discovers instances of the service on the network, 1839 | // and returns clients to any that are found. errors will contain an error for 1840 | // any devices that replied but which could not be queried, and err will be set 1841 | // if the discovery process failed outright. 1842 | // 1843 | // This is a typical entry calling point into this package. 1844 | func NewWANIPConnection1Clients(ctx context.Context) (clients []*WANIPConnection1, errors []error, err error) { 1845 | var genericClients []goupnp.ServiceClient 1846 | if genericClients, errors, err = goupnp.NewServiceClientsCtx(ctx, URN_WANIPConnection_1); err != nil { 1847 | return 1848 | } 1849 | clients = newWANIPConnection1ClientsFromGenericClients(genericClients) 1850 | return 1851 | } 1852 | 1853 | // NewWANIPConnection1ClientsByURL discovers instances of the service at the given 1854 | // URL, and returns clients to any that are found. An error is returned if 1855 | // there was an error probing the service. 1856 | // 1857 | // This is a typical entry calling point into this package when reusing an 1858 | // previously discovered service URL. 1859 | func NewWANIPConnection1ClientsByURL(loc *url.URL) ([]*WANIPConnection1, error) { 1860 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANIPConnection_1) 1861 | if err != nil { 1862 | return nil, err 1863 | } 1864 | return newWANIPConnection1ClientsFromGenericClients(genericClients), nil 1865 | } 1866 | 1867 | // NewWANIPConnection1ClientsFromRootDevice discovers instances of the service in 1868 | // a given root device, and returns clients to any that are found. An error is 1869 | // returned if there was not at least one instance of the service within the 1870 | // device. The location parameter is simply assigned to the Location attribute 1871 | // of the wrapped ServiceClient(s). 1872 | // 1873 | // This is a typical entry calling point into this package when reusing an 1874 | // previously discovered root device. 1875 | func NewWANIPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANIPConnection1, error) { 1876 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANIPConnection_1) 1877 | if err != nil { 1878 | return nil, err 1879 | } 1880 | return newWANIPConnection1ClientsFromGenericClients(genericClients), nil 1881 | } 1882 | 1883 | func newWANIPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANIPConnection1 { 1884 | clients := make([]*WANIPConnection1, len(genericClients)) 1885 | for i := range genericClients { 1886 | clients[i] = &WANIPConnection1{genericClients[i]} 1887 | } 1888 | return clients 1889 | } 1890 | 1891 | func (client *WANIPConnection1) SetConnectionType(NewConnectionType string) (err error) { 1892 | // Request structure. 1893 | request := &struct { 1894 | NewConnectionType string 1895 | }{} 1896 | // BEGIN Marshal arguments into request. 1897 | 1898 | if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { 1899 | return 1900 | } 1901 | // END Marshal arguments into request. 1902 | 1903 | // Response structure. 1904 | response := interface{}(nil) 1905 | 1906 | // Perform the SOAP call. 1907 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetConnectionType", request, response); err != nil { 1908 | return 1909 | } 1910 | 1911 | // BEGIN Unmarshal arguments from response. 1912 | 1913 | // END Unmarshal arguments from response. 1914 | return 1915 | } 1916 | 1917 | // 1918 | // Return values: 1919 | // 1920 | // * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, IP_Bridged 1921 | func (client *WANIPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { 1922 | // Request structure. 1923 | request := interface{}(nil) 1924 | // BEGIN Marshal arguments into request. 1925 | 1926 | // END Marshal arguments into request. 1927 | 1928 | // Response structure. 1929 | response := &struct { 1930 | NewConnectionType string 1931 | 1932 | NewPossibleConnectionTypes string 1933 | }{} 1934 | 1935 | // Perform the SOAP call. 1936 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { 1937 | return 1938 | } 1939 | 1940 | // BEGIN Unmarshal arguments from response. 1941 | 1942 | if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { 1943 | return 1944 | } 1945 | if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { 1946 | return 1947 | } 1948 | // END Unmarshal arguments from response. 1949 | return 1950 | } 1951 | 1952 | func (client *WANIPConnection1) RequestConnection() (err error) { 1953 | // Request structure. 1954 | request := interface{}(nil) 1955 | // BEGIN Marshal arguments into request. 1956 | 1957 | // END Marshal arguments into request. 1958 | 1959 | // Response structure. 1960 | response := interface{}(nil) 1961 | 1962 | // Perform the SOAP call. 1963 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestConnection", request, response); err != nil { 1964 | return 1965 | } 1966 | 1967 | // BEGIN Unmarshal arguments from response. 1968 | 1969 | // END Unmarshal arguments from response. 1970 | return 1971 | } 1972 | 1973 | func (client *WANIPConnection1) RequestTermination() (err error) { 1974 | // Request structure. 1975 | request := interface{}(nil) 1976 | // BEGIN Marshal arguments into request. 1977 | 1978 | // END Marshal arguments into request. 1979 | 1980 | // Response structure. 1981 | response := interface{}(nil) 1982 | 1983 | // Perform the SOAP call. 1984 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "RequestTermination", request, response); err != nil { 1985 | return 1986 | } 1987 | 1988 | // BEGIN Unmarshal arguments from response. 1989 | 1990 | // END Unmarshal arguments from response. 1991 | return 1992 | } 1993 | 1994 | func (client *WANIPConnection1) ForceTermination() (err error) { 1995 | // Request structure. 1996 | request := interface{}(nil) 1997 | // BEGIN Marshal arguments into request. 1998 | 1999 | // END Marshal arguments into request. 2000 | 2001 | // Response structure. 2002 | response := interface{}(nil) 2003 | 2004 | // Perform the SOAP call. 2005 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "ForceTermination", request, response); err != nil { 2006 | return 2007 | } 2008 | 2009 | // BEGIN Unmarshal arguments from response. 2010 | 2011 | // END Unmarshal arguments from response. 2012 | return 2013 | } 2014 | 2015 | func (client *WANIPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { 2016 | // Request structure. 2017 | request := &struct { 2018 | NewAutoDisconnectTime string 2019 | }{} 2020 | // BEGIN Marshal arguments into request. 2021 | 2022 | if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { 2023 | return 2024 | } 2025 | // END Marshal arguments into request. 2026 | 2027 | // Response structure. 2028 | response := interface{}(nil) 2029 | 2030 | // Perform the SOAP call. 2031 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { 2032 | return 2033 | } 2034 | 2035 | // BEGIN Unmarshal arguments from response. 2036 | 2037 | // END Unmarshal arguments from response. 2038 | return 2039 | } 2040 | 2041 | func (client *WANIPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { 2042 | // Request structure. 2043 | request := &struct { 2044 | NewIdleDisconnectTime string 2045 | }{} 2046 | // BEGIN Marshal arguments into request. 2047 | 2048 | if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { 2049 | return 2050 | } 2051 | // END Marshal arguments into request. 2052 | 2053 | // Response structure. 2054 | response := interface{}(nil) 2055 | 2056 | // Perform the SOAP call. 2057 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { 2058 | return 2059 | } 2060 | 2061 | // BEGIN Unmarshal arguments from response. 2062 | 2063 | // END Unmarshal arguments from response. 2064 | return 2065 | } 2066 | 2067 | func (client *WANIPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { 2068 | // Request structure. 2069 | request := &struct { 2070 | NewWarnDisconnectDelay string 2071 | }{} 2072 | // BEGIN Marshal arguments into request. 2073 | 2074 | if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { 2075 | return 2076 | } 2077 | // END Marshal arguments into request. 2078 | 2079 | // Response structure. 2080 | response := interface{}(nil) 2081 | 2082 | // Perform the SOAP call. 2083 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { 2084 | return 2085 | } 2086 | 2087 | // BEGIN Unmarshal arguments from response. 2088 | 2089 | // END Unmarshal arguments from response. 2090 | return 2091 | } 2092 | 2093 | // 2094 | // Return values: 2095 | // 2096 | // * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected 2097 | // 2098 | // * NewLastConnectionError: allowed values: ERROR_NONE 2099 | func (client *WANIPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { 2100 | // Request structure. 2101 | request := interface{}(nil) 2102 | // BEGIN Marshal arguments into request. 2103 | 2104 | // END Marshal arguments into request. 2105 | 2106 | // Response structure. 2107 | response := &struct { 2108 | NewConnectionStatus string 2109 | 2110 | NewLastConnectionError string 2111 | 2112 | NewUptime string 2113 | }{} 2114 | 2115 | // Perform the SOAP call. 2116 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetStatusInfo", request, response); err != nil { 2117 | return 2118 | } 2119 | 2120 | // BEGIN Unmarshal arguments from response. 2121 | 2122 | if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { 2123 | return 2124 | } 2125 | if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { 2126 | return 2127 | } 2128 | if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { 2129 | return 2130 | } 2131 | // END Unmarshal arguments from response. 2132 | return 2133 | } 2134 | 2135 | func (client *WANIPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { 2136 | // Request structure. 2137 | request := interface{}(nil) 2138 | // BEGIN Marshal arguments into request. 2139 | 2140 | // END Marshal arguments into request. 2141 | 2142 | // Response structure. 2143 | response := &struct { 2144 | NewAutoDisconnectTime string 2145 | }{} 2146 | 2147 | // Perform the SOAP call. 2148 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { 2149 | return 2150 | } 2151 | 2152 | // BEGIN Unmarshal arguments from response. 2153 | 2154 | if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { 2155 | return 2156 | } 2157 | // END Unmarshal arguments from response. 2158 | return 2159 | } 2160 | 2161 | func (client *WANIPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { 2162 | // Request structure. 2163 | request := interface{}(nil) 2164 | // BEGIN Marshal arguments into request. 2165 | 2166 | // END Marshal arguments into request. 2167 | 2168 | // Response structure. 2169 | response := &struct { 2170 | NewIdleDisconnectTime string 2171 | }{} 2172 | 2173 | // Perform the SOAP call. 2174 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { 2175 | return 2176 | } 2177 | 2178 | // BEGIN Unmarshal arguments from response. 2179 | 2180 | if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { 2181 | return 2182 | } 2183 | // END Unmarshal arguments from response. 2184 | return 2185 | } 2186 | 2187 | func (client *WANIPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { 2188 | // Request structure. 2189 | request := interface{}(nil) 2190 | // BEGIN Marshal arguments into request. 2191 | 2192 | // END Marshal arguments into request. 2193 | 2194 | // Response structure. 2195 | response := &struct { 2196 | NewWarnDisconnectDelay string 2197 | }{} 2198 | 2199 | // Perform the SOAP call. 2200 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { 2201 | return 2202 | } 2203 | 2204 | // BEGIN Unmarshal arguments from response. 2205 | 2206 | if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { 2207 | return 2208 | } 2209 | // END Unmarshal arguments from response. 2210 | return 2211 | } 2212 | 2213 | func (client *WANIPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { 2214 | // Request structure. 2215 | request := interface{}(nil) 2216 | // BEGIN Marshal arguments into request. 2217 | 2218 | // END Marshal arguments into request. 2219 | 2220 | // Response structure. 2221 | response := &struct { 2222 | NewRSIPAvailable string 2223 | 2224 | NewNATEnabled string 2225 | }{} 2226 | 2227 | // Perform the SOAP call. 2228 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetNATRSIPStatus", request, response); err != nil { 2229 | return 2230 | } 2231 | 2232 | // BEGIN Unmarshal arguments from response. 2233 | 2234 | if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { 2235 | return 2236 | } 2237 | if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { 2238 | return 2239 | } 2240 | // END Unmarshal arguments from response. 2241 | return 2242 | } 2243 | 2244 | // 2245 | // Return values: 2246 | // 2247 | // * NewProtocol: allowed values: TCP, UDP 2248 | func (client *WANIPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { 2249 | // Request structure. 2250 | request := &struct { 2251 | NewPortMappingIndex string 2252 | }{} 2253 | // BEGIN Marshal arguments into request. 2254 | 2255 | if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { 2256 | return 2257 | } 2258 | // END Marshal arguments into request. 2259 | 2260 | // Response structure. 2261 | response := &struct { 2262 | NewRemoteHost string 2263 | 2264 | NewExternalPort string 2265 | 2266 | NewProtocol string 2267 | 2268 | NewInternalPort string 2269 | 2270 | NewInternalClient string 2271 | 2272 | NewEnabled string 2273 | 2274 | NewPortMappingDescription string 2275 | 2276 | NewLeaseDuration string 2277 | }{} 2278 | 2279 | // Perform the SOAP call. 2280 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { 2281 | return 2282 | } 2283 | 2284 | // BEGIN Unmarshal arguments from response. 2285 | 2286 | if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { 2287 | return 2288 | } 2289 | if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { 2290 | return 2291 | } 2292 | if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { 2293 | return 2294 | } 2295 | if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { 2296 | return 2297 | } 2298 | if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { 2299 | return 2300 | } 2301 | if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { 2302 | return 2303 | } 2304 | if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { 2305 | return 2306 | } 2307 | if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { 2308 | return 2309 | } 2310 | // END Unmarshal arguments from response. 2311 | return 2312 | } 2313 | 2314 | // 2315 | // Arguments: 2316 | // 2317 | // * NewProtocol: allowed values: TCP, UDP 2318 | 2319 | func (client *WANIPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { 2320 | // Request structure. 2321 | request := &struct { 2322 | NewRemoteHost string 2323 | 2324 | NewExternalPort string 2325 | 2326 | NewProtocol string 2327 | }{} 2328 | // BEGIN Marshal arguments into request. 2329 | 2330 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 2331 | return 2332 | } 2333 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 2334 | return 2335 | } 2336 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 2337 | return 2338 | } 2339 | // END Marshal arguments into request. 2340 | 2341 | // Response structure. 2342 | response := &struct { 2343 | NewInternalPort string 2344 | 2345 | NewInternalClient string 2346 | 2347 | NewEnabled string 2348 | 2349 | NewPortMappingDescription string 2350 | 2351 | NewLeaseDuration string 2352 | }{} 2353 | 2354 | // Perform the SOAP call. 2355 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { 2356 | return 2357 | } 2358 | 2359 | // BEGIN Unmarshal arguments from response. 2360 | 2361 | if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { 2362 | return 2363 | } 2364 | if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { 2365 | return 2366 | } 2367 | if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { 2368 | return 2369 | } 2370 | if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { 2371 | return 2372 | } 2373 | if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { 2374 | return 2375 | } 2376 | // END Unmarshal arguments from response. 2377 | return 2378 | } 2379 | 2380 | // 2381 | // Arguments: 2382 | // 2383 | // * NewProtocol: allowed values: TCP, UDP 2384 | 2385 | func (client *WANIPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { 2386 | // Request structure. 2387 | request := &struct { 2388 | NewRemoteHost string 2389 | 2390 | NewExternalPort string 2391 | 2392 | NewProtocol string 2393 | 2394 | NewInternalPort string 2395 | 2396 | NewInternalClient string 2397 | 2398 | NewEnabled string 2399 | 2400 | NewPortMappingDescription string 2401 | 2402 | NewLeaseDuration string 2403 | }{} 2404 | // BEGIN Marshal arguments into request. 2405 | 2406 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 2407 | return 2408 | } 2409 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 2410 | return 2411 | } 2412 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 2413 | return 2414 | } 2415 | if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { 2416 | return 2417 | } 2418 | if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { 2419 | return 2420 | } 2421 | if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { 2422 | return 2423 | } 2424 | if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { 2425 | return 2426 | } 2427 | if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { 2428 | return 2429 | } 2430 | // END Marshal arguments into request. 2431 | 2432 | // Response structure. 2433 | response := interface{}(nil) 2434 | 2435 | // Perform the SOAP call. 2436 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "AddPortMapping", request, response); err != nil { 2437 | return 2438 | } 2439 | 2440 | // BEGIN Unmarshal arguments from response. 2441 | 2442 | // END Unmarshal arguments from response. 2443 | return 2444 | } 2445 | 2446 | // 2447 | // Arguments: 2448 | // 2449 | // * NewProtocol: allowed values: TCP, UDP 2450 | 2451 | func (client *WANIPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { 2452 | // Request structure. 2453 | request := &struct { 2454 | NewRemoteHost string 2455 | 2456 | NewExternalPort string 2457 | 2458 | NewProtocol string 2459 | }{} 2460 | // BEGIN Marshal arguments into request. 2461 | 2462 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 2463 | return 2464 | } 2465 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 2466 | return 2467 | } 2468 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 2469 | return 2470 | } 2471 | // END Marshal arguments into request. 2472 | 2473 | // Response structure. 2474 | response := interface{}(nil) 2475 | 2476 | // Perform the SOAP call. 2477 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "DeletePortMapping", request, response); err != nil { 2478 | return 2479 | } 2480 | 2481 | // BEGIN Unmarshal arguments from response. 2482 | 2483 | // END Unmarshal arguments from response. 2484 | return 2485 | } 2486 | 2487 | func (client *WANIPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { 2488 | // Request structure. 2489 | request := interface{}(nil) 2490 | // BEGIN Marshal arguments into request. 2491 | 2492 | // END Marshal arguments into request. 2493 | 2494 | // Response structure. 2495 | response := &struct { 2496 | NewExternalIPAddress string 2497 | }{} 2498 | 2499 | // Perform the SOAP call. 2500 | if err = client.SOAPClient.PerformAction(URN_WANIPConnection_1, "GetExternalIPAddress", request, response); err != nil { 2501 | return 2502 | } 2503 | 2504 | // BEGIN Unmarshal arguments from response. 2505 | 2506 | if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { 2507 | return 2508 | } 2509 | // END Unmarshal arguments from response. 2510 | return 2511 | } 2512 | 2513 | // WANPOTSLinkConfig1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPOTSLinkConfig:1". See 2514 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 2515 | // are provided for informational value. 2516 | type WANPOTSLinkConfig1 struct { 2517 | goupnp.ServiceClient 2518 | } 2519 | 2520 | // NewWANPOTSLinkConfig1Clients discovers instances of the service on the network, 2521 | // and returns clients to any that are found. errors will contain an error for 2522 | // any devices that replied but which could not be queried, and err will be set 2523 | // if the discovery process failed outright. 2524 | // 2525 | // This is a typical entry calling point into this package. 2526 | func NewWANPOTSLinkConfig1Clients() (clients []*WANPOTSLinkConfig1, errors []error, err error) { 2527 | var genericClients []goupnp.ServiceClient 2528 | if genericClients, errors, err = goupnp.NewServiceClients(URN_WANPOTSLinkConfig_1); err != nil { 2529 | return 2530 | } 2531 | clients = newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients) 2532 | return 2533 | } 2534 | 2535 | // NewWANPOTSLinkConfig1ClientsByURL discovers instances of the service at the given 2536 | // URL, and returns clients to any that are found. An error is returned if 2537 | // there was an error probing the service. 2538 | // 2539 | // This is a typical entry calling point into this package when reusing an 2540 | // previously discovered service URL. 2541 | func NewWANPOTSLinkConfig1ClientsByURL(loc *url.URL) ([]*WANPOTSLinkConfig1, error) { 2542 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPOTSLinkConfig_1) 2543 | if err != nil { 2544 | return nil, err 2545 | } 2546 | return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil 2547 | } 2548 | 2549 | // NewWANPOTSLinkConfig1ClientsFromRootDevice discovers instances of the service in 2550 | // a given root device, and returns clients to any that are found. An error is 2551 | // returned if there was not at least one instance of the service within the 2552 | // device. The location parameter is simply assigned to the Location attribute 2553 | // of the wrapped ServiceClient(s). 2554 | // 2555 | // This is a typical entry calling point into this package when reusing an 2556 | // previously discovered root device. 2557 | func NewWANPOTSLinkConfig1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPOTSLinkConfig1, error) { 2558 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPOTSLinkConfig_1) 2559 | if err != nil { 2560 | return nil, err 2561 | } 2562 | return newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients), nil 2563 | } 2564 | 2565 | func newWANPOTSLinkConfig1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPOTSLinkConfig1 { 2566 | clients := make([]*WANPOTSLinkConfig1, len(genericClients)) 2567 | for i := range genericClients { 2568 | clients[i] = &WANPOTSLinkConfig1{genericClients[i]} 2569 | } 2570 | return clients 2571 | } 2572 | 2573 | // 2574 | // Arguments: 2575 | // 2576 | // * NewLinkType: allowed values: PPP_Dialup 2577 | 2578 | func (client *WANPOTSLinkConfig1) SetISPInfo(NewISPPhoneNumber string, NewISPInfo string, NewLinkType string) (err error) { 2579 | // Request structure. 2580 | request := &struct { 2581 | NewISPPhoneNumber string 2582 | 2583 | NewISPInfo string 2584 | 2585 | NewLinkType string 2586 | }{} 2587 | // BEGIN Marshal arguments into request. 2588 | 2589 | if request.NewISPPhoneNumber, err = soap.MarshalString(NewISPPhoneNumber); err != nil { 2590 | return 2591 | } 2592 | if request.NewISPInfo, err = soap.MarshalString(NewISPInfo); err != nil { 2593 | return 2594 | } 2595 | if request.NewLinkType, err = soap.MarshalString(NewLinkType); err != nil { 2596 | return 2597 | } 2598 | // END Marshal arguments into request. 2599 | 2600 | // Response structure. 2601 | response := interface{}(nil) 2602 | 2603 | // Perform the SOAP call. 2604 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetISPInfo", request, response); err != nil { 2605 | return 2606 | } 2607 | 2608 | // BEGIN Unmarshal arguments from response. 2609 | 2610 | // END Unmarshal arguments from response. 2611 | return 2612 | } 2613 | 2614 | func (client *WANPOTSLinkConfig1) SetCallRetryInfo(NewNumberOfRetries uint32, NewDelayBetweenRetries uint32) (err error) { 2615 | // Request structure. 2616 | request := &struct { 2617 | NewNumberOfRetries string 2618 | 2619 | NewDelayBetweenRetries string 2620 | }{} 2621 | // BEGIN Marshal arguments into request. 2622 | 2623 | if request.NewNumberOfRetries, err = soap.MarshalUi4(NewNumberOfRetries); err != nil { 2624 | return 2625 | } 2626 | if request.NewDelayBetweenRetries, err = soap.MarshalUi4(NewDelayBetweenRetries); err != nil { 2627 | return 2628 | } 2629 | // END Marshal arguments into request. 2630 | 2631 | // Response structure. 2632 | response := interface{}(nil) 2633 | 2634 | // Perform the SOAP call. 2635 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "SetCallRetryInfo", request, response); err != nil { 2636 | return 2637 | } 2638 | 2639 | // BEGIN Unmarshal arguments from response. 2640 | 2641 | // END Unmarshal arguments from response. 2642 | return 2643 | } 2644 | 2645 | // 2646 | // Return values: 2647 | // 2648 | // * NewLinkType: allowed values: PPP_Dialup 2649 | func (client *WANPOTSLinkConfig1) GetISPInfo() (NewISPPhoneNumber string, NewISPInfo string, NewLinkType string, err error) { 2650 | // Request structure. 2651 | request := interface{}(nil) 2652 | // BEGIN Marshal arguments into request. 2653 | 2654 | // END Marshal arguments into request. 2655 | 2656 | // Response structure. 2657 | response := &struct { 2658 | NewISPPhoneNumber string 2659 | 2660 | NewISPInfo string 2661 | 2662 | NewLinkType string 2663 | }{} 2664 | 2665 | // Perform the SOAP call. 2666 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetISPInfo", request, response); err != nil { 2667 | return 2668 | } 2669 | 2670 | // BEGIN Unmarshal arguments from response. 2671 | 2672 | if NewISPPhoneNumber, err = soap.UnmarshalString(response.NewISPPhoneNumber); err != nil { 2673 | return 2674 | } 2675 | if NewISPInfo, err = soap.UnmarshalString(response.NewISPInfo); err != nil { 2676 | return 2677 | } 2678 | if NewLinkType, err = soap.UnmarshalString(response.NewLinkType); err != nil { 2679 | return 2680 | } 2681 | // END Unmarshal arguments from response. 2682 | return 2683 | } 2684 | 2685 | func (client *WANPOTSLinkConfig1) GetCallRetryInfo() (NewNumberOfRetries uint32, NewDelayBetweenRetries uint32, err error) { 2686 | // Request structure. 2687 | request := interface{}(nil) 2688 | // BEGIN Marshal arguments into request. 2689 | 2690 | // END Marshal arguments into request. 2691 | 2692 | // Response structure. 2693 | response := &struct { 2694 | NewNumberOfRetries string 2695 | 2696 | NewDelayBetweenRetries string 2697 | }{} 2698 | 2699 | // Perform the SOAP call. 2700 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetCallRetryInfo", request, response); err != nil { 2701 | return 2702 | } 2703 | 2704 | // BEGIN Unmarshal arguments from response. 2705 | 2706 | if NewNumberOfRetries, err = soap.UnmarshalUi4(response.NewNumberOfRetries); err != nil { 2707 | return 2708 | } 2709 | if NewDelayBetweenRetries, err = soap.UnmarshalUi4(response.NewDelayBetweenRetries); err != nil { 2710 | return 2711 | } 2712 | // END Unmarshal arguments from response. 2713 | return 2714 | } 2715 | 2716 | func (client *WANPOTSLinkConfig1) GetFclass() (NewFclass string, err error) { 2717 | // Request structure. 2718 | request := interface{}(nil) 2719 | // BEGIN Marshal arguments into request. 2720 | 2721 | // END Marshal arguments into request. 2722 | 2723 | // Response structure. 2724 | response := &struct { 2725 | NewFclass string 2726 | }{} 2727 | 2728 | // Perform the SOAP call. 2729 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetFclass", request, response); err != nil { 2730 | return 2731 | } 2732 | 2733 | // BEGIN Unmarshal arguments from response. 2734 | 2735 | if NewFclass, err = soap.UnmarshalString(response.NewFclass); err != nil { 2736 | return 2737 | } 2738 | // END Unmarshal arguments from response. 2739 | return 2740 | } 2741 | 2742 | func (client *WANPOTSLinkConfig1) GetDataModulationSupported() (NewDataModulationSupported string, err error) { 2743 | // Request structure. 2744 | request := interface{}(nil) 2745 | // BEGIN Marshal arguments into request. 2746 | 2747 | // END Marshal arguments into request. 2748 | 2749 | // Response structure. 2750 | response := &struct { 2751 | NewDataModulationSupported string 2752 | }{} 2753 | 2754 | // Perform the SOAP call. 2755 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataModulationSupported", request, response); err != nil { 2756 | return 2757 | } 2758 | 2759 | // BEGIN Unmarshal arguments from response. 2760 | 2761 | if NewDataModulationSupported, err = soap.UnmarshalString(response.NewDataModulationSupported); err != nil { 2762 | return 2763 | } 2764 | // END Unmarshal arguments from response. 2765 | return 2766 | } 2767 | 2768 | func (client *WANPOTSLinkConfig1) GetDataProtocol() (NewDataProtocol string, err error) { 2769 | // Request structure. 2770 | request := interface{}(nil) 2771 | // BEGIN Marshal arguments into request. 2772 | 2773 | // END Marshal arguments into request. 2774 | 2775 | // Response structure. 2776 | response := &struct { 2777 | NewDataProtocol string 2778 | }{} 2779 | 2780 | // Perform the SOAP call. 2781 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataProtocol", request, response); err != nil { 2782 | return 2783 | } 2784 | 2785 | // BEGIN Unmarshal arguments from response. 2786 | 2787 | if NewDataProtocol, err = soap.UnmarshalString(response.NewDataProtocol); err != nil { 2788 | return 2789 | } 2790 | // END Unmarshal arguments from response. 2791 | return 2792 | } 2793 | 2794 | func (client *WANPOTSLinkConfig1) GetDataCompression() (NewDataCompression string, err error) { 2795 | // Request structure. 2796 | request := interface{}(nil) 2797 | // BEGIN Marshal arguments into request. 2798 | 2799 | // END Marshal arguments into request. 2800 | 2801 | // Response structure. 2802 | response := &struct { 2803 | NewDataCompression string 2804 | }{} 2805 | 2806 | // Perform the SOAP call. 2807 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetDataCompression", request, response); err != nil { 2808 | return 2809 | } 2810 | 2811 | // BEGIN Unmarshal arguments from response. 2812 | 2813 | if NewDataCompression, err = soap.UnmarshalString(response.NewDataCompression); err != nil { 2814 | return 2815 | } 2816 | // END Unmarshal arguments from response. 2817 | return 2818 | } 2819 | 2820 | func (client *WANPOTSLinkConfig1) GetPlusVTRCommandSupported() (NewPlusVTRCommandSupported bool, err error) { 2821 | // Request structure. 2822 | request := interface{}(nil) 2823 | // BEGIN Marshal arguments into request. 2824 | 2825 | // END Marshal arguments into request. 2826 | 2827 | // Response structure. 2828 | response := &struct { 2829 | NewPlusVTRCommandSupported string 2830 | }{} 2831 | 2832 | // Perform the SOAP call. 2833 | if err = client.SOAPClient.PerformAction(URN_WANPOTSLinkConfig_1, "GetPlusVTRCommandSupported", request, response); err != nil { 2834 | return 2835 | } 2836 | 2837 | // BEGIN Unmarshal arguments from response. 2838 | 2839 | if NewPlusVTRCommandSupported, err = soap.UnmarshalBoolean(response.NewPlusVTRCommandSupported); err != nil { 2840 | return 2841 | } 2842 | // END Unmarshal arguments from response. 2843 | return 2844 | } 2845 | 2846 | // WANPPPConnection1 is a client for UPnP SOAP service with URN "urn:schemas-upnp-org:service:WANPPPConnection:1". See 2847 | // goupnp.ServiceClient, which contains RootDevice and Service attributes which 2848 | // are provided for informational value. 2849 | type WANPPPConnection1 struct { 2850 | goupnp.ServiceClient 2851 | } 2852 | 2853 | // NewWANPPPConnection1Clients discovers instances of the service on the network, 2854 | // and returns clients to any that are found. errors will contain an error for 2855 | // any devices that replied but which could not be queried, and err will be set 2856 | // if the discovery process failed outright. 2857 | // 2858 | // This is a typical entry calling point into this package. 2859 | func NewWANPPPConnection1Clients(ctx context.Context) (clients []*WANPPPConnection1, errors []error, err error) { 2860 | var genericClients []goupnp.ServiceClient 2861 | if genericClients, errors, err = goupnp.NewServiceClientsCtx(ctx, URN_WANPPPConnection_1); err != nil { 2862 | return 2863 | } 2864 | clients = newWANPPPConnection1ClientsFromGenericClients(genericClients) 2865 | return 2866 | } 2867 | 2868 | // NewWANPPPConnection1ClientsByURL discovers instances of the service at the given 2869 | // URL, and returns clients to any that are found. An error is returned if 2870 | // there was an error probing the service. 2871 | // 2872 | // This is a typical entry calling point into this package when reusing an 2873 | // previously discovered service URL. 2874 | func NewWANPPPConnection1ClientsByURL(loc *url.URL) ([]*WANPPPConnection1, error) { 2875 | genericClients, err := goupnp.NewServiceClientsByURL(loc, URN_WANPPPConnection_1) 2876 | if err != nil { 2877 | return nil, err 2878 | } 2879 | return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil 2880 | } 2881 | 2882 | // NewWANPPPConnection1ClientsFromRootDevice discovers instances of the service in 2883 | // a given root device, and returns clients to any that are found. An error is 2884 | // returned if there was not at least one instance of the service within the 2885 | // device. The location parameter is simply assigned to the Location attribute 2886 | // of the wrapped ServiceClient(s). 2887 | // 2888 | // This is a typical entry calling point into this package when reusing an 2889 | // previously discovered root device. 2890 | func NewWANPPPConnection1ClientsFromRootDevice(rootDevice *goupnp.RootDevice, loc *url.URL) ([]*WANPPPConnection1, error) { 2891 | genericClients, err := goupnp.NewServiceClientsFromRootDevice(rootDevice, loc, URN_WANPPPConnection_1) 2892 | if err != nil { 2893 | return nil, err 2894 | } 2895 | return newWANPPPConnection1ClientsFromGenericClients(genericClients), nil 2896 | } 2897 | 2898 | func newWANPPPConnection1ClientsFromGenericClients(genericClients []goupnp.ServiceClient) []*WANPPPConnection1 { 2899 | clients := make([]*WANPPPConnection1, len(genericClients)) 2900 | for i := range genericClients { 2901 | clients[i] = &WANPPPConnection1{genericClients[i]} 2902 | } 2903 | return clients 2904 | } 2905 | 2906 | func (client *WANPPPConnection1) SetConnectionType(NewConnectionType string) (err error) { 2907 | // Request structure. 2908 | request := &struct { 2909 | NewConnectionType string 2910 | }{} 2911 | // BEGIN Marshal arguments into request. 2912 | 2913 | if request.NewConnectionType, err = soap.MarshalString(NewConnectionType); err != nil { 2914 | return 2915 | } 2916 | // END Marshal arguments into request. 2917 | 2918 | // Response structure. 2919 | response := interface{}(nil) 2920 | 2921 | // Perform the SOAP call. 2922 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetConnectionType", request, response); err != nil { 2923 | return 2924 | } 2925 | 2926 | // BEGIN Unmarshal arguments from response. 2927 | 2928 | // END Unmarshal arguments from response. 2929 | return 2930 | } 2931 | 2932 | // 2933 | // Return values: 2934 | // 2935 | // * NewPossibleConnectionTypes: allowed values: Unconfigured, IP_Routed, DHCP_Spoofed, PPPoE_Bridged, PPTP_Relay, L2TP_Relay, PPPoE_Relay 2936 | func (client *WANPPPConnection1) GetConnectionTypeInfo() (NewConnectionType string, NewPossibleConnectionTypes string, err error) { 2937 | // Request structure. 2938 | request := interface{}(nil) 2939 | // BEGIN Marshal arguments into request. 2940 | 2941 | // END Marshal arguments into request. 2942 | 2943 | // Response structure. 2944 | response := &struct { 2945 | NewConnectionType string 2946 | 2947 | NewPossibleConnectionTypes string 2948 | }{} 2949 | 2950 | // Perform the SOAP call. 2951 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetConnectionTypeInfo", request, response); err != nil { 2952 | return 2953 | } 2954 | 2955 | // BEGIN Unmarshal arguments from response. 2956 | 2957 | if NewConnectionType, err = soap.UnmarshalString(response.NewConnectionType); err != nil { 2958 | return 2959 | } 2960 | if NewPossibleConnectionTypes, err = soap.UnmarshalString(response.NewPossibleConnectionTypes); err != nil { 2961 | return 2962 | } 2963 | // END Unmarshal arguments from response. 2964 | return 2965 | } 2966 | 2967 | func (client *WANPPPConnection1) ConfigureConnection(NewUserName string, NewPassword string) (err error) { 2968 | // Request structure. 2969 | request := &struct { 2970 | NewUserName string 2971 | 2972 | NewPassword string 2973 | }{} 2974 | // BEGIN Marshal arguments into request. 2975 | 2976 | if request.NewUserName, err = soap.MarshalString(NewUserName); err != nil { 2977 | return 2978 | } 2979 | if request.NewPassword, err = soap.MarshalString(NewPassword); err != nil { 2980 | return 2981 | } 2982 | // END Marshal arguments into request. 2983 | 2984 | // Response structure. 2985 | response := interface{}(nil) 2986 | 2987 | // Perform the SOAP call. 2988 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ConfigureConnection", request, response); err != nil { 2989 | return 2990 | } 2991 | 2992 | // BEGIN Unmarshal arguments from response. 2993 | 2994 | // END Unmarshal arguments from response. 2995 | return 2996 | } 2997 | 2998 | func (client *WANPPPConnection1) RequestConnection() (err error) { 2999 | // Request structure. 3000 | request := interface{}(nil) 3001 | // BEGIN Marshal arguments into request. 3002 | 3003 | // END Marshal arguments into request. 3004 | 3005 | // Response structure. 3006 | response := interface{}(nil) 3007 | 3008 | // Perform the SOAP call. 3009 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestConnection", request, response); err != nil { 3010 | return 3011 | } 3012 | 3013 | // BEGIN Unmarshal arguments from response. 3014 | 3015 | // END Unmarshal arguments from response. 3016 | return 3017 | } 3018 | 3019 | func (client *WANPPPConnection1) RequestTermination() (err error) { 3020 | // Request structure. 3021 | request := interface{}(nil) 3022 | // BEGIN Marshal arguments into request. 3023 | 3024 | // END Marshal arguments into request. 3025 | 3026 | // Response structure. 3027 | response := interface{}(nil) 3028 | 3029 | // Perform the SOAP call. 3030 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "RequestTermination", request, response); err != nil { 3031 | return 3032 | } 3033 | 3034 | // BEGIN Unmarshal arguments from response. 3035 | 3036 | // END Unmarshal arguments from response. 3037 | return 3038 | } 3039 | 3040 | func (client *WANPPPConnection1) ForceTermination() (err error) { 3041 | // Request structure. 3042 | request := interface{}(nil) 3043 | // BEGIN Marshal arguments into request. 3044 | 3045 | // END Marshal arguments into request. 3046 | 3047 | // Response structure. 3048 | response := interface{}(nil) 3049 | 3050 | // Perform the SOAP call. 3051 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "ForceTermination", request, response); err != nil { 3052 | return 3053 | } 3054 | 3055 | // BEGIN Unmarshal arguments from response. 3056 | 3057 | // END Unmarshal arguments from response. 3058 | return 3059 | } 3060 | 3061 | func (client *WANPPPConnection1) SetAutoDisconnectTime(NewAutoDisconnectTime uint32) (err error) { 3062 | // Request structure. 3063 | request := &struct { 3064 | NewAutoDisconnectTime string 3065 | }{} 3066 | // BEGIN Marshal arguments into request. 3067 | 3068 | if request.NewAutoDisconnectTime, err = soap.MarshalUi4(NewAutoDisconnectTime); err != nil { 3069 | return 3070 | } 3071 | // END Marshal arguments into request. 3072 | 3073 | // Response structure. 3074 | response := interface{}(nil) 3075 | 3076 | // Perform the SOAP call. 3077 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetAutoDisconnectTime", request, response); err != nil { 3078 | return 3079 | } 3080 | 3081 | // BEGIN Unmarshal arguments from response. 3082 | 3083 | // END Unmarshal arguments from response. 3084 | return 3085 | } 3086 | 3087 | func (client *WANPPPConnection1) SetIdleDisconnectTime(NewIdleDisconnectTime uint32) (err error) { 3088 | // Request structure. 3089 | request := &struct { 3090 | NewIdleDisconnectTime string 3091 | }{} 3092 | // BEGIN Marshal arguments into request. 3093 | 3094 | if request.NewIdleDisconnectTime, err = soap.MarshalUi4(NewIdleDisconnectTime); err != nil { 3095 | return 3096 | } 3097 | // END Marshal arguments into request. 3098 | 3099 | // Response structure. 3100 | response := interface{}(nil) 3101 | 3102 | // Perform the SOAP call. 3103 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetIdleDisconnectTime", request, response); err != nil { 3104 | return 3105 | } 3106 | 3107 | // BEGIN Unmarshal arguments from response. 3108 | 3109 | // END Unmarshal arguments from response. 3110 | return 3111 | } 3112 | 3113 | func (client *WANPPPConnection1) SetWarnDisconnectDelay(NewWarnDisconnectDelay uint32) (err error) { 3114 | // Request structure. 3115 | request := &struct { 3116 | NewWarnDisconnectDelay string 3117 | }{} 3118 | // BEGIN Marshal arguments into request. 3119 | 3120 | if request.NewWarnDisconnectDelay, err = soap.MarshalUi4(NewWarnDisconnectDelay); err != nil { 3121 | return 3122 | } 3123 | // END Marshal arguments into request. 3124 | 3125 | // Response structure. 3126 | response := interface{}(nil) 3127 | 3128 | // Perform the SOAP call. 3129 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "SetWarnDisconnectDelay", request, response); err != nil { 3130 | return 3131 | } 3132 | 3133 | // BEGIN Unmarshal arguments from response. 3134 | 3135 | // END Unmarshal arguments from response. 3136 | return 3137 | } 3138 | 3139 | // 3140 | // Return values: 3141 | // 3142 | // * NewConnectionStatus: allowed values: Unconfigured, Connected, Disconnected 3143 | // 3144 | // * NewLastConnectionError: allowed values: ERROR_NONE 3145 | func (client *WANPPPConnection1) GetStatusInfo() (NewConnectionStatus string, NewLastConnectionError string, NewUptime uint32, err error) { 3146 | // Request structure. 3147 | request := interface{}(nil) 3148 | // BEGIN Marshal arguments into request. 3149 | 3150 | // END Marshal arguments into request. 3151 | 3152 | // Response structure. 3153 | response := &struct { 3154 | NewConnectionStatus string 3155 | 3156 | NewLastConnectionError string 3157 | 3158 | NewUptime string 3159 | }{} 3160 | 3161 | // Perform the SOAP call. 3162 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetStatusInfo", request, response); err != nil { 3163 | return 3164 | } 3165 | 3166 | // BEGIN Unmarshal arguments from response. 3167 | 3168 | if NewConnectionStatus, err = soap.UnmarshalString(response.NewConnectionStatus); err != nil { 3169 | return 3170 | } 3171 | if NewLastConnectionError, err = soap.UnmarshalString(response.NewLastConnectionError); err != nil { 3172 | return 3173 | } 3174 | if NewUptime, err = soap.UnmarshalUi4(response.NewUptime); err != nil { 3175 | return 3176 | } 3177 | // END Unmarshal arguments from response. 3178 | return 3179 | } 3180 | 3181 | func (client *WANPPPConnection1) GetLinkLayerMaxBitRates() (NewUpstreamMaxBitRate uint32, NewDownstreamMaxBitRate uint32, err error) { 3182 | // Request structure. 3183 | request := interface{}(nil) 3184 | // BEGIN Marshal arguments into request. 3185 | 3186 | // END Marshal arguments into request. 3187 | 3188 | // Response structure. 3189 | response := &struct { 3190 | NewUpstreamMaxBitRate string 3191 | 3192 | NewDownstreamMaxBitRate string 3193 | }{} 3194 | 3195 | // Perform the SOAP call. 3196 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetLinkLayerMaxBitRates", request, response); err != nil { 3197 | return 3198 | } 3199 | 3200 | // BEGIN Unmarshal arguments from response. 3201 | 3202 | if NewUpstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewUpstreamMaxBitRate); err != nil { 3203 | return 3204 | } 3205 | if NewDownstreamMaxBitRate, err = soap.UnmarshalUi4(response.NewDownstreamMaxBitRate); err != nil { 3206 | return 3207 | } 3208 | // END Unmarshal arguments from response. 3209 | return 3210 | } 3211 | 3212 | func (client *WANPPPConnection1) GetPPPEncryptionProtocol() (NewPPPEncryptionProtocol string, err error) { 3213 | // Request structure. 3214 | request := interface{}(nil) 3215 | // BEGIN Marshal arguments into request. 3216 | 3217 | // END Marshal arguments into request. 3218 | 3219 | // Response structure. 3220 | response := &struct { 3221 | NewPPPEncryptionProtocol string 3222 | }{} 3223 | 3224 | // Perform the SOAP call. 3225 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPEncryptionProtocol", request, response); err != nil { 3226 | return 3227 | } 3228 | 3229 | // BEGIN Unmarshal arguments from response. 3230 | 3231 | if NewPPPEncryptionProtocol, err = soap.UnmarshalString(response.NewPPPEncryptionProtocol); err != nil { 3232 | return 3233 | } 3234 | // END Unmarshal arguments from response. 3235 | return 3236 | } 3237 | 3238 | func (client *WANPPPConnection1) GetPPPCompressionProtocol() (NewPPPCompressionProtocol string, err error) { 3239 | // Request structure. 3240 | request := interface{}(nil) 3241 | // BEGIN Marshal arguments into request. 3242 | 3243 | // END Marshal arguments into request. 3244 | 3245 | // Response structure. 3246 | response := &struct { 3247 | NewPPPCompressionProtocol string 3248 | }{} 3249 | 3250 | // Perform the SOAP call. 3251 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPCompressionProtocol", request, response); err != nil { 3252 | return 3253 | } 3254 | 3255 | // BEGIN Unmarshal arguments from response. 3256 | 3257 | if NewPPPCompressionProtocol, err = soap.UnmarshalString(response.NewPPPCompressionProtocol); err != nil { 3258 | return 3259 | } 3260 | // END Unmarshal arguments from response. 3261 | return 3262 | } 3263 | 3264 | func (client *WANPPPConnection1) GetPPPAuthenticationProtocol() (NewPPPAuthenticationProtocol string, err error) { 3265 | // Request structure. 3266 | request := interface{}(nil) 3267 | // BEGIN Marshal arguments into request. 3268 | 3269 | // END Marshal arguments into request. 3270 | 3271 | // Response structure. 3272 | response := &struct { 3273 | NewPPPAuthenticationProtocol string 3274 | }{} 3275 | 3276 | // Perform the SOAP call. 3277 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPPPAuthenticationProtocol", request, response); err != nil { 3278 | return 3279 | } 3280 | 3281 | // BEGIN Unmarshal arguments from response. 3282 | 3283 | if NewPPPAuthenticationProtocol, err = soap.UnmarshalString(response.NewPPPAuthenticationProtocol); err != nil { 3284 | return 3285 | } 3286 | // END Unmarshal arguments from response. 3287 | return 3288 | } 3289 | 3290 | func (client *WANPPPConnection1) GetUserName() (NewUserName string, err error) { 3291 | // Request structure. 3292 | request := interface{}(nil) 3293 | // BEGIN Marshal arguments into request. 3294 | 3295 | // END Marshal arguments into request. 3296 | 3297 | // Response structure. 3298 | response := &struct { 3299 | NewUserName string 3300 | }{} 3301 | 3302 | // Perform the SOAP call. 3303 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetUserName", request, response); err != nil { 3304 | return 3305 | } 3306 | 3307 | // BEGIN Unmarshal arguments from response. 3308 | 3309 | if NewUserName, err = soap.UnmarshalString(response.NewUserName); err != nil { 3310 | return 3311 | } 3312 | // END Unmarshal arguments from response. 3313 | return 3314 | } 3315 | 3316 | func (client *WANPPPConnection1) GetPassword() (NewPassword string, err error) { 3317 | // Request structure. 3318 | request := interface{}(nil) 3319 | // BEGIN Marshal arguments into request. 3320 | 3321 | // END Marshal arguments into request. 3322 | 3323 | // Response structure. 3324 | response := &struct { 3325 | NewPassword string 3326 | }{} 3327 | 3328 | // Perform the SOAP call. 3329 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetPassword", request, response); err != nil { 3330 | return 3331 | } 3332 | 3333 | // BEGIN Unmarshal arguments from response. 3334 | 3335 | if NewPassword, err = soap.UnmarshalString(response.NewPassword); err != nil { 3336 | return 3337 | } 3338 | // END Unmarshal arguments from response. 3339 | return 3340 | } 3341 | 3342 | func (client *WANPPPConnection1) GetAutoDisconnectTime() (NewAutoDisconnectTime uint32, err error) { 3343 | // Request structure. 3344 | request := interface{}(nil) 3345 | // BEGIN Marshal arguments into request. 3346 | 3347 | // END Marshal arguments into request. 3348 | 3349 | // Response structure. 3350 | response := &struct { 3351 | NewAutoDisconnectTime string 3352 | }{} 3353 | 3354 | // Perform the SOAP call. 3355 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetAutoDisconnectTime", request, response); err != nil { 3356 | return 3357 | } 3358 | 3359 | // BEGIN Unmarshal arguments from response. 3360 | 3361 | if NewAutoDisconnectTime, err = soap.UnmarshalUi4(response.NewAutoDisconnectTime); err != nil { 3362 | return 3363 | } 3364 | // END Unmarshal arguments from response. 3365 | return 3366 | } 3367 | 3368 | func (client *WANPPPConnection1) GetIdleDisconnectTime() (NewIdleDisconnectTime uint32, err error) { 3369 | // Request structure. 3370 | request := interface{}(nil) 3371 | // BEGIN Marshal arguments into request. 3372 | 3373 | // END Marshal arguments into request. 3374 | 3375 | // Response structure. 3376 | response := &struct { 3377 | NewIdleDisconnectTime string 3378 | }{} 3379 | 3380 | // Perform the SOAP call. 3381 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetIdleDisconnectTime", request, response); err != nil { 3382 | return 3383 | } 3384 | 3385 | // BEGIN Unmarshal arguments from response. 3386 | 3387 | if NewIdleDisconnectTime, err = soap.UnmarshalUi4(response.NewIdleDisconnectTime); err != nil { 3388 | return 3389 | } 3390 | // END Unmarshal arguments from response. 3391 | return 3392 | } 3393 | 3394 | func (client *WANPPPConnection1) GetWarnDisconnectDelay() (NewWarnDisconnectDelay uint32, err error) { 3395 | // Request structure. 3396 | request := interface{}(nil) 3397 | // BEGIN Marshal arguments into request. 3398 | 3399 | // END Marshal arguments into request. 3400 | 3401 | // Response structure. 3402 | response := &struct { 3403 | NewWarnDisconnectDelay string 3404 | }{} 3405 | 3406 | // Perform the SOAP call. 3407 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetWarnDisconnectDelay", request, response); err != nil { 3408 | return 3409 | } 3410 | 3411 | // BEGIN Unmarshal arguments from response. 3412 | 3413 | if NewWarnDisconnectDelay, err = soap.UnmarshalUi4(response.NewWarnDisconnectDelay); err != nil { 3414 | return 3415 | } 3416 | // END Unmarshal arguments from response. 3417 | return 3418 | } 3419 | 3420 | func (client *WANPPPConnection1) GetNATRSIPStatus() (NewRSIPAvailable bool, NewNATEnabled bool, err error) { 3421 | // Request structure. 3422 | request := interface{}(nil) 3423 | // BEGIN Marshal arguments into request. 3424 | 3425 | // END Marshal arguments into request. 3426 | 3427 | // Response structure. 3428 | response := &struct { 3429 | NewRSIPAvailable string 3430 | 3431 | NewNATEnabled string 3432 | }{} 3433 | 3434 | // Perform the SOAP call. 3435 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetNATRSIPStatus", request, response); err != nil { 3436 | return 3437 | } 3438 | 3439 | // BEGIN Unmarshal arguments from response. 3440 | 3441 | if NewRSIPAvailable, err = soap.UnmarshalBoolean(response.NewRSIPAvailable); err != nil { 3442 | return 3443 | } 3444 | if NewNATEnabled, err = soap.UnmarshalBoolean(response.NewNATEnabled); err != nil { 3445 | return 3446 | } 3447 | // END Unmarshal arguments from response. 3448 | return 3449 | } 3450 | 3451 | // 3452 | // Return values: 3453 | // 3454 | // * NewProtocol: allowed values: TCP, UDP 3455 | func (client *WANPPPConnection1) GetGenericPortMappingEntry(NewPortMappingIndex uint16) (NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { 3456 | // Request structure. 3457 | request := &struct { 3458 | NewPortMappingIndex string 3459 | }{} 3460 | // BEGIN Marshal arguments into request. 3461 | 3462 | if request.NewPortMappingIndex, err = soap.MarshalUi2(NewPortMappingIndex); err != nil { 3463 | return 3464 | } 3465 | // END Marshal arguments into request. 3466 | 3467 | // Response structure. 3468 | response := &struct { 3469 | NewRemoteHost string 3470 | 3471 | NewExternalPort string 3472 | 3473 | NewProtocol string 3474 | 3475 | NewInternalPort string 3476 | 3477 | NewInternalClient string 3478 | 3479 | NewEnabled string 3480 | 3481 | NewPortMappingDescription string 3482 | 3483 | NewLeaseDuration string 3484 | }{} 3485 | 3486 | // Perform the SOAP call. 3487 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetGenericPortMappingEntry", request, response); err != nil { 3488 | return 3489 | } 3490 | 3491 | // BEGIN Unmarshal arguments from response. 3492 | 3493 | if NewRemoteHost, err = soap.UnmarshalString(response.NewRemoteHost); err != nil { 3494 | return 3495 | } 3496 | if NewExternalPort, err = soap.UnmarshalUi2(response.NewExternalPort); err != nil { 3497 | return 3498 | } 3499 | if NewProtocol, err = soap.UnmarshalString(response.NewProtocol); err != nil { 3500 | return 3501 | } 3502 | if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { 3503 | return 3504 | } 3505 | if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { 3506 | return 3507 | } 3508 | if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { 3509 | return 3510 | } 3511 | if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { 3512 | return 3513 | } 3514 | if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { 3515 | return 3516 | } 3517 | // END Unmarshal arguments from response. 3518 | return 3519 | } 3520 | 3521 | // 3522 | // Arguments: 3523 | // 3524 | // * NewProtocol: allowed values: TCP, UDP 3525 | 3526 | func (client *WANPPPConnection1) GetSpecificPortMappingEntry(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32, err error) { 3527 | // Request structure. 3528 | request := &struct { 3529 | NewRemoteHost string 3530 | 3531 | NewExternalPort string 3532 | 3533 | NewProtocol string 3534 | }{} 3535 | // BEGIN Marshal arguments into request. 3536 | 3537 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 3538 | return 3539 | } 3540 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 3541 | return 3542 | } 3543 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 3544 | return 3545 | } 3546 | // END Marshal arguments into request. 3547 | 3548 | // Response structure. 3549 | response := &struct { 3550 | NewInternalPort string 3551 | 3552 | NewInternalClient string 3553 | 3554 | NewEnabled string 3555 | 3556 | NewPortMappingDescription string 3557 | 3558 | NewLeaseDuration string 3559 | }{} 3560 | 3561 | // Perform the SOAP call. 3562 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetSpecificPortMappingEntry", request, response); err != nil { 3563 | return 3564 | } 3565 | 3566 | // BEGIN Unmarshal arguments from response. 3567 | 3568 | if NewInternalPort, err = soap.UnmarshalUi2(response.NewInternalPort); err != nil { 3569 | return 3570 | } 3571 | if NewInternalClient, err = soap.UnmarshalString(response.NewInternalClient); err != nil { 3572 | return 3573 | } 3574 | if NewEnabled, err = soap.UnmarshalBoolean(response.NewEnabled); err != nil { 3575 | return 3576 | } 3577 | if NewPortMappingDescription, err = soap.UnmarshalString(response.NewPortMappingDescription); err != nil { 3578 | return 3579 | } 3580 | if NewLeaseDuration, err = soap.UnmarshalUi4(response.NewLeaseDuration); err != nil { 3581 | return 3582 | } 3583 | // END Unmarshal arguments from response. 3584 | return 3585 | } 3586 | 3587 | // 3588 | // Arguments: 3589 | // 3590 | // * NewProtocol: allowed values: TCP, UDP 3591 | 3592 | func (client *WANPPPConnection1) AddPortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string, NewInternalPort uint16, NewInternalClient string, NewEnabled bool, NewPortMappingDescription string, NewLeaseDuration uint32) (err error) { 3593 | // Request structure. 3594 | request := &struct { 3595 | NewRemoteHost string 3596 | 3597 | NewExternalPort string 3598 | 3599 | NewProtocol string 3600 | 3601 | NewInternalPort string 3602 | 3603 | NewInternalClient string 3604 | 3605 | NewEnabled string 3606 | 3607 | NewPortMappingDescription string 3608 | 3609 | NewLeaseDuration string 3610 | }{} 3611 | // BEGIN Marshal arguments into request. 3612 | 3613 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 3614 | return 3615 | } 3616 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 3617 | return 3618 | } 3619 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 3620 | return 3621 | } 3622 | if request.NewInternalPort, err = soap.MarshalUi2(NewInternalPort); err != nil { 3623 | return 3624 | } 3625 | if request.NewInternalClient, err = soap.MarshalString(NewInternalClient); err != nil { 3626 | return 3627 | } 3628 | if request.NewEnabled, err = soap.MarshalBoolean(NewEnabled); err != nil { 3629 | return 3630 | } 3631 | if request.NewPortMappingDescription, err = soap.MarshalString(NewPortMappingDescription); err != nil { 3632 | return 3633 | } 3634 | if request.NewLeaseDuration, err = soap.MarshalUi4(NewLeaseDuration); err != nil { 3635 | return 3636 | } 3637 | // END Marshal arguments into request. 3638 | 3639 | // Response structure. 3640 | response := interface{}(nil) 3641 | 3642 | // Perform the SOAP call. 3643 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "AddPortMapping", request, response); err != nil { 3644 | return 3645 | } 3646 | 3647 | // BEGIN Unmarshal arguments from response. 3648 | 3649 | // END Unmarshal arguments from response. 3650 | return 3651 | } 3652 | 3653 | // 3654 | // Arguments: 3655 | // 3656 | // * NewProtocol: allowed values: TCP, UDP 3657 | 3658 | func (client *WANPPPConnection1) DeletePortMapping(NewRemoteHost string, NewExternalPort uint16, NewProtocol string) (err error) { 3659 | // Request structure. 3660 | request := &struct { 3661 | NewRemoteHost string 3662 | 3663 | NewExternalPort string 3664 | 3665 | NewProtocol string 3666 | }{} 3667 | // BEGIN Marshal arguments into request. 3668 | 3669 | if request.NewRemoteHost, err = soap.MarshalString(NewRemoteHost); err != nil { 3670 | return 3671 | } 3672 | if request.NewExternalPort, err = soap.MarshalUi2(NewExternalPort); err != nil { 3673 | return 3674 | } 3675 | if request.NewProtocol, err = soap.MarshalString(NewProtocol); err != nil { 3676 | return 3677 | } 3678 | // END Marshal arguments into request. 3679 | 3680 | // Response structure. 3681 | response := interface{}(nil) 3682 | 3683 | // Perform the SOAP call. 3684 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "DeletePortMapping", request, response); err != nil { 3685 | return 3686 | } 3687 | 3688 | // BEGIN Unmarshal arguments from response. 3689 | 3690 | // END Unmarshal arguments from response. 3691 | return 3692 | } 3693 | 3694 | func (client *WANPPPConnection1) GetExternalIPAddress() (NewExternalIPAddress string, err error) { 3695 | // Request structure. 3696 | request := interface{}(nil) 3697 | // BEGIN Marshal arguments into request. 3698 | 3699 | // END Marshal arguments into request. 3700 | 3701 | // Response structure. 3702 | response := &struct { 3703 | NewExternalIPAddress string 3704 | }{} 3705 | 3706 | // Perform the SOAP call. 3707 | if err = client.SOAPClient.PerformAction(URN_WANPPPConnection_1, "GetExternalIPAddress", request, response); err != nil { 3708 | return 3709 | } 3710 | 3711 | // BEGIN Unmarshal arguments from response. 3712 | 3713 | if NewExternalIPAddress, err = soap.UnmarshalString(response.NewExternalIPAddress); err != nil { 3714 | return 3715 | } 3716 | // END Unmarshal arguments from response. 3717 | return 3718 | } 3719 | -------------------------------------------------------------------------------- /goupnp/device.go: -------------------------------------------------------------------------------- 1 | // This file contains XML structures for communicating with UPnP devices. 2 | 3 | package goupnp 4 | 5 | import ( 6 | "encoding/xml" 7 | "errors" 8 | "fmt" 9 | "net/url" 10 | 11 | "gitlab.com/NebulousLabs/go-upnp/goupnp/scpd" 12 | "gitlab.com/NebulousLabs/go-upnp/goupnp/soap" 13 | ) 14 | 15 | const ( 16 | DeviceXMLNamespace = "urn:schemas-upnp-org:device-1-0" 17 | ) 18 | 19 | // RootDevice is the device description as described by section 2.3 "Device 20 | // description" in 21 | // http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf 22 | type RootDevice struct { 23 | XMLName xml.Name `xml:"root"` 24 | SpecVersion SpecVersion `xml:"specVersion"` 25 | URLBase url.URL `xml:"-"` 26 | URLBaseStr string `xml:"URLBase"` 27 | Device Device `xml:"device"` 28 | } 29 | 30 | // SetURLBase sets the URLBase for the RootDevice and its underlying components. 31 | func (root *RootDevice) SetURLBase(urlBase *url.URL) { 32 | root.URLBase = *urlBase 33 | root.URLBaseStr = urlBase.String() 34 | root.Device.SetURLBase(urlBase) 35 | } 36 | 37 | // SpecVersion is part of a RootDevice, describes the version of the 38 | // specification that the data adheres to. 39 | type SpecVersion struct { 40 | Major int32 `xml:"major"` 41 | Minor int32 `xml:"minor"` 42 | } 43 | 44 | // Device is a UPnP device. It can have child devices. 45 | type Device struct { 46 | DeviceType string `xml:"deviceType"` 47 | FriendlyName string `xml:"friendlyName"` 48 | Manufacturer string `xml:"manufacturer"` 49 | ManufacturerURL URLField `xml:"manufacturerURL"` 50 | ModelDescription string `xml:"modelDescription"` 51 | ModelName string `xml:"modelName"` 52 | ModelNumber string `xml:"modelNumber"` 53 | ModelURL URLField `xml:"modelURL"` 54 | SerialNumber string `xml:"serialNumber"` 55 | UDN string `xml:"UDN"` 56 | UPC string `xml:"UPC,omitempty"` 57 | Icons []Icon `xml:"iconList>icon,omitempty"` 58 | Services []Service `xml:"serviceList>service,omitempty"` 59 | Devices []Device `xml:"deviceList>device,omitempty"` 60 | 61 | // Extra observed elements: 62 | PresentationURL URLField `xml:"presentationURL"` 63 | } 64 | 65 | // VisitDevices calls visitor for the device, and all its descendent devices. 66 | func (device *Device) VisitDevices(visitor func(*Device)) { 67 | visitor(device) 68 | for i := range device.Devices { 69 | device.Devices[i].VisitDevices(visitor) 70 | } 71 | } 72 | 73 | // VisitServices calls visitor for all Services under the device and all its 74 | // descendent devices. 75 | func (device *Device) VisitServices(visitor func(*Service)) { 76 | device.VisitDevices(func(d *Device) { 77 | for i := range d.Services { 78 | visitor(&d.Services[i]) 79 | } 80 | }) 81 | } 82 | 83 | // FindService finds all (if any) Services under the device and its descendents 84 | // that have the given ServiceType. 85 | func (device *Device) FindService(serviceType string) []*Service { 86 | var services []*Service 87 | device.VisitServices(func(s *Service) { 88 | if s.ServiceType == serviceType { 89 | services = append(services, s) 90 | } 91 | }) 92 | return services 93 | } 94 | 95 | // SetURLBase sets the URLBase for the Device and its underlying components. 96 | func (device *Device) SetURLBase(urlBase *url.URL) { 97 | device.ManufacturerURL.SetURLBase(urlBase) 98 | device.ModelURL.SetURLBase(urlBase) 99 | device.PresentationURL.SetURLBase(urlBase) 100 | for i := range device.Icons { 101 | device.Icons[i].SetURLBase(urlBase) 102 | } 103 | for i := range device.Services { 104 | device.Services[i].SetURLBase(urlBase) 105 | } 106 | for i := range device.Devices { 107 | device.Devices[i].SetURLBase(urlBase) 108 | } 109 | } 110 | 111 | func (device *Device) String() string { 112 | return fmt.Sprintf("Device ID %s : %s (%s)", device.UDN, device.DeviceType, device.FriendlyName) 113 | } 114 | 115 | // Icon is a representative image that a device might include in its 116 | // description. 117 | type Icon struct { 118 | Mimetype string `xml:"mimetype"` 119 | Width int32 `xml:"width"` 120 | Height int32 `xml:"height"` 121 | Depth int32 `xml:"depth"` 122 | URL URLField `xml:"url"` 123 | } 124 | 125 | // SetURLBase sets the URLBase for the Icon. 126 | func (icon *Icon) SetURLBase(url *url.URL) { 127 | icon.URL.SetURLBase(url) 128 | } 129 | 130 | // Service is a service provided by a UPnP Device. 131 | type Service struct { 132 | ServiceType string `xml:"serviceType"` 133 | ServiceId string `xml:"serviceId"` 134 | SCPDURL URLField `xml:"SCPDURL"` 135 | ControlURL URLField `xml:"controlURL"` 136 | EventSubURL URLField `xml:"eventSubURL"` 137 | } 138 | 139 | // SetURLBase sets the URLBase for the Service. 140 | func (srv *Service) SetURLBase(urlBase *url.URL) { 141 | srv.SCPDURL.SetURLBase(urlBase) 142 | srv.ControlURL.SetURLBase(urlBase) 143 | srv.EventSubURL.SetURLBase(urlBase) 144 | } 145 | 146 | func (srv *Service) String() string { 147 | return fmt.Sprintf("Service ID %s : %s", srv.ServiceId, srv.ServiceType) 148 | } 149 | 150 | // RequestSCDP requests the SCPD (soap actions and state variables description) 151 | // for the service. 152 | func (srv *Service) RequestSCDP() (*scpd.SCPD, error) { 153 | if !srv.SCPDURL.Ok { 154 | return nil, errors.New("bad/missing SCPD URL, or no URLBase has been set") 155 | } 156 | s := new(scpd.SCPD) 157 | if err := requestXml(srv.SCPDURL.URL.String(), scpd.SCPDXMLNamespace, s); err != nil { 158 | return nil, err 159 | } 160 | return s, nil 161 | } 162 | 163 | func (srv *Service) NewSOAPClient() *soap.SOAPClient { 164 | return soap.NewSOAPClient(srv.ControlURL.URL) 165 | } 166 | 167 | // URLField is a URL that is part of a device description. 168 | type URLField struct { 169 | URL url.URL `xml:"-"` 170 | Ok bool `xml:"-"` 171 | Str string `xml:",chardata"` 172 | } 173 | 174 | func (uf *URLField) SetURLBase(urlBase *url.URL) { 175 | refUrl, err := url.Parse(uf.Str) 176 | if err != nil { 177 | uf.URL = url.URL{} 178 | uf.Ok = false 179 | return 180 | } 181 | 182 | uf.URL = *urlBase.ResolveReference(refUrl) 183 | uf.Ok = true 184 | } 185 | -------------------------------------------------------------------------------- /goupnp/goupnp.go: -------------------------------------------------------------------------------- 1 | // goupnp is an implementation of a client for various UPnP services. 2 | // 3 | // For most uses, it is recommended to use the code-generated packages under 4 | // github.com/huin/goupnp/dcps. Example use is shown at 5 | // http://godoc.org/github.com/huin/goupnp/example 6 | // 7 | // A commonly used client is internetgateway1.WANPPPConnection1: 8 | // http://godoc.org/github.com/huin/goupnp/dcps/internetgateway1#WANPPPConnection1 9 | // 10 | // Currently only a couple of schemas have code generated for them from the 11 | // UPnP example XML specifications. Not all methods will work on these clients, 12 | // because the generated stubs contain the full set of specified methods from 13 | // the XML specifications, and the discovered services will likely support a 14 | // subset of those methods. 15 | package goupnp 16 | 17 | import ( 18 | "context" 19 | "encoding/xml" 20 | "fmt" 21 | "net/http" 22 | "net/url" 23 | "time" 24 | 25 | "gitlab.com/NebulousLabs/go-upnp/goupnp/httpu" 26 | "gitlab.com/NebulousLabs/go-upnp/goupnp/ssdp" 27 | 28 | "golang.org/x/net/html/charset" 29 | ) 30 | 31 | // ContextError is an error that wraps an error with some context information. 32 | type ContextError struct { 33 | Context string 34 | Err error 35 | } 36 | 37 | func (err ContextError) Error() string { 38 | return fmt.Sprintf("%s: %v", err.Context, err.Err) 39 | } 40 | 41 | // MaybeRootDevice contains either a RootDevice or an error. 42 | type MaybeRootDevice struct { 43 | // Set iff Err == nil. 44 | Root *RootDevice 45 | 46 | // The location the device was discovered at. This can be used with 47 | // DeviceByURL, assuming the device is still present. A location represents 48 | // the discovery of a device, regardless of if there was an error probing it. 49 | Location *url.URL 50 | 51 | // Any error encountered probing a discovered device. 52 | Err error 53 | } 54 | 55 | // DiscoverDevices is deprecated. Use DiscoverDevicesCtx instead. 56 | func DiscoverDevices(searchTarget string) ([]MaybeRootDevice, error) { 57 | return DiscoverDevicesCtx(context.Background(), searchTarget) 58 | } 59 | 60 | // DiscoverDevicesCtx attempts to find targets of the given type. This is 61 | // typically the entry-point for this package. searchTarget is typically a URN 62 | // in the form "urn:schemas-upnp-org:device:..." or 63 | // "urn:schemas-upnp-org:service:...". A single error is returned for errors 64 | // while attempting to send the query. An error or RootDevice is returned for 65 | // each discovered RootDevice. 66 | func DiscoverDevicesCtx(ctx context.Context, searchTarget string) ([]MaybeRootDevice, error) { 67 | httpu, err := httpu.NewHTTPUClient() 68 | if err != nil { 69 | return nil, err 70 | } 71 | defer httpu.Close() 72 | responses, err := ssdp.SSDPRawSearchCtx(ctx, httpu, string(searchTarget), 2, 3) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | results := make([]MaybeRootDevice, len(responses)) 78 | for i, response := range responses { 79 | maybe := &results[i] 80 | loc, err := response.Location() 81 | if err != nil { 82 | maybe.Err = ContextError{"unexpected bad location from search", err} 83 | continue 84 | } 85 | maybe.Location = loc 86 | if root, err := DeviceByURL(loc); err != nil { 87 | maybe.Err = err 88 | } else { 89 | maybe.Root = root 90 | } 91 | } 92 | 93 | return results, nil 94 | } 95 | 96 | func DeviceByURL(loc *url.URL) (*RootDevice, error) { 97 | locStr := loc.String() 98 | root := new(RootDevice) 99 | if err := requestXml(locStr, DeviceXMLNamespace, root); err != nil { 100 | return nil, ContextError{fmt.Sprintf("error requesting root device details from %q", locStr), err} 101 | } 102 | var urlBaseStr string 103 | if root.URLBaseStr != "" { 104 | urlBaseStr = root.URLBaseStr 105 | } else { 106 | urlBaseStr = locStr 107 | } 108 | urlBase, err := url.Parse(urlBaseStr) 109 | if err != nil { 110 | return nil, ContextError{fmt.Sprintf("error parsing location URL %q", locStr), err} 111 | } 112 | root.SetURLBase(urlBase) 113 | return root, nil 114 | } 115 | 116 | func requestXml(url string, defaultSpace string, doc interface{}) error { 117 | timeout := time.Duration(3 * time.Second) 118 | client := http.Client{ 119 | Timeout: timeout, 120 | } 121 | resp, err := client.Get(url) 122 | if err != nil { 123 | return err 124 | } 125 | defer resp.Body.Close() 126 | 127 | if resp.StatusCode != 200 { 128 | return fmt.Errorf("goupnp: got response status %s from %q", 129 | resp.Status, url) 130 | } 131 | 132 | decoder := xml.NewDecoder(resp.Body) 133 | decoder.DefaultSpace = defaultSpace 134 | decoder.CharsetReader = charset.NewReaderLabel 135 | 136 | return decoder.Decode(doc) 137 | } 138 | -------------------------------------------------------------------------------- /goupnp/httpu/httpu.go: -------------------------------------------------------------------------------- 1 | package httpu 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | // HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical 14 | // function is for HTTPMU, and particularly SSDP. 15 | type HTTPUClient struct { 16 | connLock sync.Mutex // Protects use of conn. 17 | conn net.PacketConn 18 | } 19 | 20 | // NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the 21 | // purpose. 22 | func NewHTTPUClient() (*HTTPUClient, error) { 23 | conn, err := net.ListenPacket("udp", ":0") 24 | if err != nil { 25 | return nil, err 26 | } 27 | return &HTTPUClient{conn: conn}, nil 28 | } 29 | 30 | // Close shuts down the client. The client will no longer be useful following 31 | // this. 32 | func (httpu *HTTPUClient) Close() error { 33 | httpu.connLock.Lock() 34 | defer httpu.connLock.Unlock() 35 | return httpu.conn.Close() 36 | } 37 | 38 | // Do performs a request. The timeout is how long to wait for before returning 39 | // the responses that were received. An error is only returned for failing to 40 | // send the request. Failures in receipt simply do not add to the resulting 41 | // responses. 42 | // 43 | // Note that at present only one concurrent connection will happen per 44 | // HTTPUClient. 45 | func (httpu *HTTPUClient) Do(req *http.Request, timeout time.Duration, numSends int) ([]*http.Response, error) { 46 | httpu.connLock.Lock() 47 | defer httpu.connLock.Unlock() 48 | 49 | // Create the request. This is a subset of what http.Request.Write does 50 | // deliberately to avoid creating extra fields which may confuse some 51 | // devices. 52 | var requestBuf bytes.Buffer 53 | method := req.Method 54 | if method == "" { 55 | method = "GET" 56 | } 57 | if _, err := fmt.Fprintf(&requestBuf, "%s %s HTTP/1.1\r\n", method, req.URL.RequestURI()); err != nil { 58 | return nil, err 59 | } 60 | if err := req.Header.Write(&requestBuf); err != nil { 61 | return nil, err 62 | } 63 | if _, err := requestBuf.Write([]byte{'\r', '\n'}); err != nil { 64 | return nil, err 65 | } 66 | 67 | destAddr, err := net.ResolveUDPAddr("udp", req.Host) 68 | if err != nil { 69 | return nil, err 70 | } 71 | if err = httpu.conn.SetDeadline(time.Now().Add(timeout)); err != nil { 72 | return nil, err 73 | } 74 | 75 | // Spawn cancellation goroutine 76 | done := make(chan struct{}) 77 | defer close(done) 78 | go func() { 79 | select { 80 | case <-done: 81 | case <-req.Context().Done(): 82 | httpu.conn.SetDeadline(time.Unix(1, 0)) 83 | } 84 | }() 85 | 86 | // Send request. 87 | for i := 0; i < numSends; i++ { 88 | if n, err := httpu.conn.WriteTo(requestBuf.Bytes(), destAddr); err != nil { 89 | return nil, err 90 | } else if n < len(requestBuf.Bytes()) { 91 | return nil, fmt.Errorf("httpu: wrote %d bytes rather than full %d in request", 92 | n, len(requestBuf.Bytes())) 93 | } 94 | time.Sleep(5 * time.Millisecond) 95 | } 96 | 97 | // Await responses until timeout. 98 | var responses []*http.Response 99 | responseBytes := make([]byte, 2048) 100 | for { 101 | // 2048 bytes should be sufficient for most networks. 102 | n, _, err := httpu.conn.ReadFrom(responseBytes) 103 | if err != nil { 104 | if err, ok := err.(net.Error); ok { 105 | if err.Timeout() { 106 | break 107 | } 108 | if err.Temporary() { 109 | // Sleep in case this is a persistent error to avoid pegging CPU until deadline. 110 | time.Sleep(10 * time.Millisecond) 111 | continue 112 | } 113 | } 114 | return nil, err 115 | } 116 | 117 | // Parse response. 118 | response, err := http.ReadResponse(bufio.NewReader(bytes.NewBuffer(responseBytes[:n])), req) 119 | if err != nil { 120 | continue 121 | } 122 | 123 | responses = append(responses, response) 124 | } 125 | return responses, err 126 | } 127 | -------------------------------------------------------------------------------- /goupnp/httpu/serve.go: -------------------------------------------------------------------------------- 1 | package httpu 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "net" 7 | "net/http" 8 | "regexp" 9 | ) 10 | 11 | const ( 12 | DefaultMaxMessageBytes = 2048 13 | ) 14 | 15 | var ( 16 | trailingWhitespaceRx = regexp.MustCompile(" +\r\n") 17 | crlf = []byte("\r\n") 18 | ) 19 | 20 | // Handler is the interface by which received HTTPU messages are passed to 21 | // handling code. 22 | type Handler interface { 23 | // ServeMessage is called for each HTTPU message received. peerAddr contains 24 | // the address that the message was received from. 25 | ServeMessage(r *http.Request) 26 | } 27 | 28 | // HandlerFunc is a function-to-Handler adapter. 29 | type HandlerFunc func(r *http.Request) 30 | 31 | func (f HandlerFunc) ServeMessage(r *http.Request) { 32 | f(r) 33 | } 34 | 35 | // A Server defines parameters for running an HTTPU server. 36 | type Server struct { 37 | Addr string // UDP address to listen on 38 | Multicast bool // Should listen for multicast? 39 | Interface *net.Interface // Network interface to listen on for multicast, nil for default multicast interface 40 | Handler Handler // handler to invoke 41 | MaxMessageBytes int // maximum number of bytes to read from a packet, DefaultMaxMessageBytes if 0 42 | } 43 | 44 | // ListenAndServe listens on the UDP network address srv.Addr. If srv.Multicast 45 | // is true, then a multicast UDP listener will be used on srv.Interface (or 46 | // default interface if nil). 47 | func (srv *Server) ListenAndServe() error { 48 | var err error 49 | 50 | var addr *net.UDPAddr 51 | if addr, err = net.ResolveUDPAddr("udp", srv.Addr); err != nil { 52 | return err 53 | } 54 | 55 | var conn net.PacketConn 56 | if srv.Multicast { 57 | if conn, err = net.ListenMulticastUDP("udp", srv.Interface, addr); err != nil { 58 | return err 59 | } 60 | } else { 61 | if conn, err = net.ListenUDP("udp", addr); err != nil { 62 | return err 63 | } 64 | } 65 | 66 | return srv.Serve(conn) 67 | } 68 | 69 | // Serve messages received on the given packet listener to the srv.Handler. 70 | func (srv *Server) Serve(l net.PacketConn) error { 71 | maxMessageBytes := DefaultMaxMessageBytes 72 | if srv.MaxMessageBytes != 0 { 73 | maxMessageBytes = srv.MaxMessageBytes 74 | } 75 | for { 76 | buf := make([]byte, maxMessageBytes) 77 | n, peerAddr, err := l.ReadFrom(buf) 78 | if err != nil { 79 | return err 80 | } 81 | buf = buf[:n] 82 | 83 | go func(buf []byte, peerAddr net.Addr) { 84 | // At least one router's UPnP implementation has added a trailing space 85 | // after "HTTP/1.1" - trim it. 86 | buf = trailingWhitespaceRx.ReplaceAllLiteral(buf, crlf) 87 | 88 | req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf))) 89 | if err != nil { 90 | return 91 | } 92 | req.RemoteAddr = peerAddr.String() 93 | srv.Handler.ServeMessage(req) 94 | // No need to call req.Body.Close - underlying reader is bytes.Buffer. 95 | }(buf, peerAddr) 96 | } 97 | } 98 | 99 | // Serve messages received on the given packet listener to the given handler. 100 | func Serve(l net.PacketConn, handler Handler) error { 101 | srv := Server{ 102 | Handler: handler, 103 | MaxMessageBytes: DefaultMaxMessageBytes, 104 | } 105 | return srv.Serve(l) 106 | } 107 | -------------------------------------------------------------------------------- /goupnp/scpd/scpd.go: -------------------------------------------------------------------------------- 1 | package scpd 2 | 3 | import ( 4 | "encoding/xml" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | SCPDXMLNamespace = "urn:schemas-upnp-org:service-1-0" 10 | ) 11 | 12 | func cleanWhitespace(s *string) { 13 | *s = strings.TrimSpace(*s) 14 | } 15 | 16 | // SCPD is the service description as described by section 2.5 "Service 17 | // description" in 18 | // http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf 19 | type SCPD struct { 20 | XMLName xml.Name `xml:"scpd"` 21 | ConfigId string `xml:"configId,attr"` 22 | SpecVersion SpecVersion `xml:"specVersion"` 23 | Actions []Action `xml:"actionList>action"` 24 | StateVariables []StateVariable `xml:"serviceStateTable>stateVariable"` 25 | } 26 | 27 | // Clean attempts to remove stray whitespace etc. in the structure. It seems 28 | // unfortunately common for stray whitespace to be present in SCPD documents, 29 | // this method attempts to make it easy to clean them out. 30 | func (scpd *SCPD) Clean() { 31 | cleanWhitespace(&scpd.ConfigId) 32 | for i := range scpd.Actions { 33 | scpd.Actions[i].clean() 34 | } 35 | for i := range scpd.StateVariables { 36 | scpd.StateVariables[i].clean() 37 | } 38 | } 39 | 40 | func (scpd *SCPD) GetStateVariable(variable string) *StateVariable { 41 | for i := range scpd.StateVariables { 42 | v := &scpd.StateVariables[i] 43 | if v.Name == variable { 44 | return v 45 | } 46 | } 47 | return nil 48 | } 49 | 50 | func (scpd *SCPD) GetAction(action string) *Action { 51 | for i := range scpd.Actions { 52 | a := &scpd.Actions[i] 53 | if a.Name == action { 54 | return a 55 | } 56 | } 57 | return nil 58 | } 59 | 60 | // SpecVersion is part of a SCPD document, describes the version of the 61 | // specification that the data adheres to. 62 | type SpecVersion struct { 63 | Major int32 `xml:"major"` 64 | Minor int32 `xml:"minor"` 65 | } 66 | 67 | type Action struct { 68 | Name string `xml:"name"` 69 | Arguments []Argument `xml:"argumentList>argument"` 70 | } 71 | 72 | func (action *Action) clean() { 73 | cleanWhitespace(&action.Name) 74 | for i := range action.Arguments { 75 | action.Arguments[i].clean() 76 | } 77 | } 78 | 79 | func (action *Action) InputArguments() []*Argument { 80 | var result []*Argument 81 | for i := range action.Arguments { 82 | arg := &action.Arguments[i] 83 | if arg.IsInput() { 84 | result = append(result, arg) 85 | } 86 | } 87 | return result 88 | } 89 | 90 | func (action *Action) OutputArguments() []*Argument { 91 | var result []*Argument 92 | for i := range action.Arguments { 93 | arg := &action.Arguments[i] 94 | if arg.IsOutput() { 95 | result = append(result, arg) 96 | } 97 | } 98 | return result 99 | } 100 | 101 | type Argument struct { 102 | Name string `xml:"name"` 103 | Direction string `xml:"direction"` // in|out 104 | RelatedStateVariable string `xml:"relatedStateVariable"` // ? 105 | Retval string `xml:"retval"` // ? 106 | } 107 | 108 | func (arg *Argument) clean() { 109 | cleanWhitespace(&arg.Name) 110 | cleanWhitespace(&arg.Direction) 111 | cleanWhitespace(&arg.RelatedStateVariable) 112 | cleanWhitespace(&arg.Retval) 113 | } 114 | 115 | func (arg *Argument) IsInput() bool { 116 | return arg.Direction == "in" 117 | } 118 | 119 | func (arg *Argument) IsOutput() bool { 120 | return arg.Direction == "out" 121 | } 122 | 123 | type StateVariable struct { 124 | Name string `xml:"name"` 125 | SendEvents string `xml:"sendEvents,attr"` // yes|no 126 | Multicast string `xml:"multicast,attr"` // yes|no 127 | DataType DataType `xml:"dataType"` 128 | DefaultValue string `xml:"defaultValue"` 129 | AllowedValueRange *AllowedValueRange `xml:"allowedValueRange"` 130 | AllowedValues []string `xml:"allowedValueList>allowedValue"` 131 | } 132 | 133 | func (v *StateVariable) clean() { 134 | cleanWhitespace(&v.Name) 135 | cleanWhitespace(&v.SendEvents) 136 | cleanWhitespace(&v.Multicast) 137 | v.DataType.clean() 138 | cleanWhitespace(&v.DefaultValue) 139 | if v.AllowedValueRange != nil { 140 | v.AllowedValueRange.clean() 141 | } 142 | for i := range v.AllowedValues { 143 | cleanWhitespace(&v.AllowedValues[i]) 144 | } 145 | } 146 | 147 | type AllowedValueRange struct { 148 | Minimum string `xml:"minimum"` 149 | Maximum string `xml:"maximum"` 150 | Step string `xml:"step"` 151 | } 152 | 153 | func (r *AllowedValueRange) clean() { 154 | cleanWhitespace(&r.Minimum) 155 | cleanWhitespace(&r.Maximum) 156 | cleanWhitespace(&r.Step) 157 | } 158 | 159 | type DataType struct { 160 | Name string `xml:",chardata"` 161 | Type string `xml:"type,attr"` 162 | } 163 | 164 | func (dt *DataType) clean() { 165 | cleanWhitespace(&dt.Name) 166 | cleanWhitespace(&dt.Type) 167 | } 168 | -------------------------------------------------------------------------------- /goupnp/service_client.go: -------------------------------------------------------------------------------- 1 | package goupnp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/url" 7 | 8 | "gitlab.com/NebulousLabs/go-upnp/goupnp/soap" 9 | ) 10 | 11 | // ServiceClient is a SOAP client, root device and the service for the SOAP 12 | // client rolled into one value. The root device, location, and service are 13 | // intended to be informational. Location can be used to later recreate a 14 | // ServiceClient with NewServiceClientByURL if the service is still present; 15 | // bypassing the discovery process. 16 | type ServiceClient struct { 17 | SOAPClient *soap.SOAPClient 18 | RootDevice *RootDevice 19 | Location *url.URL 20 | Service *Service 21 | } 22 | 23 | // NewServiceClients is deprecated. Use NewServiceClientsCtx instead. 24 | func NewServiceClients(searchTarget string) (clients []ServiceClient, errors []error, err error) { 25 | return NewServiceClientsCtx(context.Background(), searchTarget) 26 | } 27 | 28 | // NewServiceClientsCtx discovers services, and returns clients for them. err will 29 | // report any error with the discovery process (blocking any device/service 30 | // discovery), errors reports errors on a per-root-device basis. 31 | func NewServiceClientsCtx(ctx context.Context, searchTarget string) (clients []ServiceClient, errors []error, err error) { 32 | var maybeRootDevices []MaybeRootDevice 33 | if maybeRootDevices, err = DiscoverDevicesCtx(ctx, searchTarget); err != nil { 34 | return 35 | } 36 | 37 | clients = make([]ServiceClient, 0, len(maybeRootDevices)) 38 | 39 | for _, maybeRootDevice := range maybeRootDevices { 40 | if maybeRootDevice.Err != nil { 41 | errors = append(errors, maybeRootDevice.Err) 42 | continue 43 | } 44 | 45 | deviceClients, err := NewServiceClientsFromRootDevice(maybeRootDevice.Root, maybeRootDevice.Location, searchTarget) 46 | if err != nil { 47 | errors = append(errors, err) 48 | continue 49 | } 50 | clients = append(clients, deviceClients...) 51 | } 52 | 53 | return 54 | } 55 | 56 | // NewServiceClientsByURL creates client(s) for the given service URN, for a 57 | // root device at the given URL. 58 | func NewServiceClientsByURL(loc *url.URL, searchTarget string) ([]ServiceClient, error) { 59 | rootDevice, err := DeviceByURL(loc) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return NewServiceClientsFromRootDevice(rootDevice, loc, searchTarget) 64 | } 65 | 66 | // NewServiceClientsFromDevice creates client(s) for the given service URN, in 67 | // a given root device. The loc parameter is simply assigned to the 68 | // Location attribute of the returned ServiceClient(s). 69 | func NewServiceClientsFromRootDevice(rootDevice *RootDevice, loc *url.URL, searchTarget string) ([]ServiceClient, error) { 70 | device := &rootDevice.Device 71 | srvs := device.FindService(searchTarget) 72 | if len(srvs) == 0 { 73 | return nil, fmt.Errorf("goupnp: service %q not found within device %q (UDN=%q)", 74 | searchTarget, device.FriendlyName, device.UDN) 75 | } 76 | 77 | clients := make([]ServiceClient, 0, len(srvs)) 78 | for _, srv := range srvs { 79 | clients = append(clients, ServiceClient{ 80 | SOAPClient: srv.NewSOAPClient(), 81 | RootDevice: rootDevice, 82 | Location: loc, 83 | Service: srv, 84 | }) 85 | } 86 | return clients, nil 87 | } 88 | 89 | // GetServiceClient returns the ServiceClient itself. This is provided so that the 90 | // service client attributes can be accessed via an interface method on a 91 | // wrapping type. 92 | func (client *ServiceClient) GetServiceClient() *ServiceClient { 93 | return client 94 | } 95 | -------------------------------------------------------------------------------- /goupnp/soap/soap.go: -------------------------------------------------------------------------------- 1 | // Definition for the SOAP structure required for UPnP's SOAP usage. 2 | 3 | package soap 4 | 5 | import ( 6 | "bytes" 7 | "encoding/xml" 8 | "fmt" 9 | "io/ioutil" 10 | "net/http" 11 | "net/url" 12 | "reflect" 13 | ) 14 | 15 | const ( 16 | soapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" 17 | soapPrefix = xml.Header + `` 18 | soapSuffix = `` 19 | ) 20 | 21 | type SOAPClient struct { 22 | EndpointURL url.URL 23 | HTTPClient http.Client 24 | } 25 | 26 | func NewSOAPClient(endpointURL url.URL) *SOAPClient { 27 | return &SOAPClient{ 28 | EndpointURL: endpointURL, 29 | } 30 | } 31 | 32 | // PerformSOAPAction makes a SOAP request, with the given action. 33 | // inAction and outAction must both be pointers to structs with string fields 34 | // only. 35 | func (client *SOAPClient) PerformAction(actionNamespace, actionName string, inAction interface{}, outAction interface{}) error { 36 | requestBytes, err := encodeRequestAction(actionNamespace, actionName, inAction) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | response, err := client.HTTPClient.Do(&http.Request{ 42 | Method: "POST", 43 | URL: &client.EndpointURL, 44 | Header: http.Header{ 45 | "SOAPACTION": []string{`"` + actionNamespace + "#" + actionName + `"`}, 46 | "CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""}, 47 | }, 48 | Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)), 49 | // Set ContentLength to avoid chunked encoding - some servers might not support it. 50 | ContentLength: int64(len(requestBytes)), 51 | }) 52 | if err != nil { 53 | return fmt.Errorf("goupnp: error performing SOAP HTTP request: %v", err) 54 | } 55 | defer response.Body.Close() 56 | if response.StatusCode != 200 { 57 | resp, _ := ioutil.ReadAll(response.Body) 58 | return fmt.Errorf("goupnp: SOAP request got HTTP %s: %s", response.Status, resp) 59 | } 60 | 61 | responseEnv := newSOAPEnvelope() 62 | decoder := xml.NewDecoder(response.Body) 63 | if err := decoder.Decode(responseEnv); err != nil { 64 | return fmt.Errorf("goupnp: error decoding response body: %v", err) 65 | } 66 | 67 | if responseEnv.Body.Fault != nil { 68 | return responseEnv.Body.Fault 69 | } 70 | 71 | if outAction != nil { 72 | if err := xml.Unmarshal(responseEnv.Body.RawAction, outAction); err != nil { 73 | return fmt.Errorf("goupnp: error unmarshalling out action: %v, %v", err, responseEnv.Body.RawAction) 74 | } 75 | } 76 | 77 | return nil 78 | } 79 | 80 | // newSOAPAction creates a soapEnvelope with the given action and arguments. 81 | func newSOAPEnvelope() *soapEnvelope { 82 | return &soapEnvelope{ 83 | EncodingStyle: soapEncodingStyle, 84 | } 85 | } 86 | 87 | // encodeRequestAction is a hacky way to create an encoded SOAP envelope 88 | // containing the given action. Experiments with one router have shown that it 89 | // 500s for requests where the outer default xmlns is set to the SOAP 90 | // namespace, and then reassigning the default namespace within that to the 91 | // service namespace. Hand-coding the outer XML to work-around this. 92 | func encodeRequestAction(actionNamespace, actionName string, inAction interface{}) ([]byte, error) { 93 | requestBuf := new(bytes.Buffer) 94 | requestBuf.WriteString(soapPrefix) 95 | requestBuf.WriteString(``) 100 | if inAction != nil { 101 | if err := encodeRequestArgs(requestBuf, inAction); err != nil { 102 | return nil, err 103 | } 104 | } 105 | requestBuf.WriteString(``) 108 | requestBuf.WriteString(soapSuffix) 109 | return requestBuf.Bytes(), nil 110 | } 111 | 112 | func encodeRequestArgs(w *bytes.Buffer, inAction interface{}) error { 113 | in := reflect.Indirect(reflect.ValueOf(inAction)) 114 | if in.Kind() != reflect.Struct { 115 | return fmt.Errorf("goupnp: SOAP inAction is not a struct but of type %v", in.Type()) 116 | } 117 | enc := xml.NewEncoder(w) 118 | nFields := in.NumField() 119 | inType := in.Type() 120 | for i := 0; i < nFields; i++ { 121 | field := inType.Field(i) 122 | argName := field.Name 123 | if nameOverride := field.Tag.Get("soap"); nameOverride != "" { 124 | argName = nameOverride 125 | } 126 | value := in.Field(i) 127 | if value.Kind() != reflect.String { 128 | return fmt.Errorf("goupnp: SOAP arg %q is not of type string, but of type %v", argName, value.Type()) 129 | } 130 | if err := enc.EncodeElement(value.Interface(), xml.StartElement{xml.Name{"", argName}, nil}); err != nil { 131 | return fmt.Errorf("goupnp: error encoding SOAP arg %q: %v", argName, err) 132 | } 133 | } 134 | enc.Flush() 135 | return nil 136 | } 137 | 138 | type soapEnvelope struct { 139 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` 140 | EncodingStyle string `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"` 141 | Body soapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` 142 | } 143 | 144 | type soapBody struct { 145 | Fault *SOAPFaultError `xml:"Fault"` 146 | RawAction []byte `xml:",innerxml"` 147 | } 148 | 149 | // SOAPFaultError implements error, and contains SOAP fault information. 150 | type SOAPFaultError struct { 151 | FaultCode string `xml:"faultcode"` 152 | FaultString string `xml:"faultstring"` 153 | Detail string `xml:"detail"` 154 | } 155 | 156 | func (err *SOAPFaultError) Error() string { 157 | return fmt.Sprintf("SOAP fault: %s", err.FaultString) 158 | } 159 | -------------------------------------------------------------------------------- /goupnp/soap/types.go: -------------------------------------------------------------------------------- 1 | package soap 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "net/url" 9 | "regexp" 10 | "strconv" 11 | "strings" 12 | "time" 13 | "unicode/utf8" 14 | ) 15 | 16 | var ( 17 | // localLoc acts like time.Local for this package, but is faked out by the 18 | // unit tests to ensure that things stay constant (especially when running 19 | // this test in a place where local time is UTC which might mask bugs). 20 | localLoc = time.Local 21 | ) 22 | 23 | func MarshalUi1(v uint8) (string, error) { 24 | return strconv.FormatUint(uint64(v), 10), nil 25 | } 26 | 27 | func UnmarshalUi1(s string) (uint8, error) { 28 | v, err := strconv.ParseUint(s, 10, 8) 29 | return uint8(v), err 30 | } 31 | 32 | func MarshalUi2(v uint16) (string, error) { 33 | return strconv.FormatUint(uint64(v), 10), nil 34 | } 35 | 36 | func UnmarshalUi2(s string) (uint16, error) { 37 | v, err := strconv.ParseUint(s, 10, 16) 38 | return uint16(v), err 39 | } 40 | 41 | func MarshalUi4(v uint32) (string, error) { 42 | return strconv.FormatUint(uint64(v), 10), nil 43 | } 44 | 45 | func UnmarshalUi4(s string) (uint32, error) { 46 | v, err := strconv.ParseUint(s, 10, 32) 47 | return uint32(v), err 48 | } 49 | 50 | func MarshalI1(v int8) (string, error) { 51 | return strconv.FormatInt(int64(v), 10), nil 52 | } 53 | 54 | func UnmarshalI1(s string) (int8, error) { 55 | v, err := strconv.ParseInt(s, 10, 8) 56 | return int8(v), err 57 | } 58 | 59 | func MarshalI2(v int16) (string, error) { 60 | return strconv.FormatInt(int64(v), 10), nil 61 | } 62 | 63 | func UnmarshalI2(s string) (int16, error) { 64 | v, err := strconv.ParseInt(s, 10, 16) 65 | return int16(v), err 66 | } 67 | 68 | func MarshalI4(v int32) (string, error) { 69 | return strconv.FormatInt(int64(v), 10), nil 70 | } 71 | 72 | func UnmarshalI4(s string) (int32, error) { 73 | v, err := strconv.ParseInt(s, 10, 32) 74 | return int32(v), err 75 | } 76 | 77 | func MarshalInt(v int64) (string, error) { 78 | return strconv.FormatInt(v, 10), nil 79 | } 80 | 81 | func UnmarshalInt(s string) (int64, error) { 82 | return strconv.ParseInt(s, 10, 64) 83 | } 84 | 85 | func MarshalR4(v float32) (string, error) { 86 | return strconv.FormatFloat(float64(v), 'G', -1, 32), nil 87 | } 88 | 89 | func UnmarshalR4(s string) (float32, error) { 90 | v, err := strconv.ParseFloat(s, 32) 91 | return float32(v), err 92 | } 93 | 94 | func MarshalR8(v float64) (string, error) { 95 | return strconv.FormatFloat(v, 'G', -1, 64), nil 96 | } 97 | 98 | func UnmarshalR8(s string) (float64, error) { 99 | v, err := strconv.ParseFloat(s, 64) 100 | return float64(v), err 101 | } 102 | 103 | // MarshalFixed14_4 marshals float64 to SOAP "fixed.14.4" type. 104 | func MarshalFixed14_4(v float64) (string, error) { 105 | if v >= 1e14 || v <= -1e14 { 106 | return "", fmt.Errorf("soap fixed14.4: value %v out of bounds", v) 107 | } 108 | return strconv.FormatFloat(v, 'f', 4, 64), nil 109 | } 110 | 111 | // UnmarshalFixed14_4 unmarshals float64 from SOAP "fixed.14.4" type. 112 | func UnmarshalFixed14_4(s string) (float64, error) { 113 | v, err := strconv.ParseFloat(s, 64) 114 | if err != nil { 115 | return 0, err 116 | } 117 | if v >= 1e14 || v <= -1e14 { 118 | return 0, fmt.Errorf("soap fixed14.4: value %q out of bounds", s) 119 | } 120 | return v, nil 121 | } 122 | 123 | // MarshalChar marshals rune to SOAP "char" type. 124 | func MarshalChar(v rune) (string, error) { 125 | if v == 0 { 126 | return "", errors.New("soap char: rune 0 is not allowed") 127 | } 128 | return string(v), nil 129 | } 130 | 131 | // UnmarshalChar unmarshals rune from SOAP "char" type. 132 | func UnmarshalChar(s string) (rune, error) { 133 | if len(s) == 0 { 134 | return 0, errors.New("soap char: got empty string") 135 | } 136 | r, n := utf8.DecodeRune([]byte(s)) 137 | if n != len(s) { 138 | return 0, fmt.Errorf("soap char: value %q is not a single rune", s) 139 | } 140 | return r, nil 141 | } 142 | 143 | func MarshalString(v string) (string, error) { 144 | return v, nil 145 | } 146 | 147 | func UnmarshalString(v string) (string, error) { 148 | return v, nil 149 | } 150 | 151 | func parseInt(s string, err *error) int { 152 | v, parseErr := strconv.ParseInt(s, 10, 64) 153 | if parseErr != nil { 154 | *err = parseErr 155 | } 156 | return int(v) 157 | } 158 | 159 | var dateRegexps = []*regexp.Regexp{ 160 | // yyyy[-mm[-dd]] 161 | regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`), 162 | // yyyy[mm[dd]] 163 | regexp.MustCompile(`^(\d{4})(?:(\d{2})(?:(\d{2}))?)?$`), 164 | } 165 | 166 | func parseDateParts(s string) (year, month, day int, err error) { 167 | var parts []string 168 | for _, re := range dateRegexps { 169 | parts = re.FindStringSubmatch(s) 170 | if parts != nil { 171 | break 172 | } 173 | } 174 | if parts == nil { 175 | err = fmt.Errorf("soap date: value %q is not in a recognized ISO8601 date format", s) 176 | return 177 | } 178 | 179 | year = parseInt(parts[1], &err) 180 | month = 1 181 | day = 1 182 | if len(parts[2]) != 0 { 183 | month = parseInt(parts[2], &err) 184 | if len(parts[3]) != 0 { 185 | day = parseInt(parts[3], &err) 186 | } 187 | } 188 | 189 | if err != nil { 190 | err = fmt.Errorf("soap date: %q: %v", s, err) 191 | } 192 | 193 | return 194 | } 195 | 196 | var timeRegexps = []*regexp.Regexp{ 197 | // hh[:mm[:ss]] 198 | regexp.MustCompile(`^(\d{2})(?::(\d{2})(?::(\d{2}))?)?$`), 199 | // hh[mm[ss]] 200 | regexp.MustCompile(`^(\d{2})(?:(\d{2})(?:(\d{2}))?)?$`), 201 | } 202 | 203 | func parseTimeParts(s string) (hour, minute, second int, err error) { 204 | var parts []string 205 | for _, re := range timeRegexps { 206 | parts = re.FindStringSubmatch(s) 207 | if parts != nil { 208 | break 209 | } 210 | } 211 | if parts == nil { 212 | err = fmt.Errorf("soap time: value %q is not in ISO8601 time format", s) 213 | return 214 | } 215 | 216 | hour = parseInt(parts[1], &err) 217 | if len(parts[2]) != 0 { 218 | minute = parseInt(parts[2], &err) 219 | if len(parts[3]) != 0 { 220 | second = parseInt(parts[3], &err) 221 | } 222 | } 223 | 224 | if err != nil { 225 | err = fmt.Errorf("soap time: %q: %v", s, err) 226 | } 227 | 228 | return 229 | } 230 | 231 | // (+|-)hh[[:]mm] 232 | var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2})(?::?(\d{2}))?$`) 233 | 234 | func parseTimezone(s string) (offset int, err error) { 235 | if s == "Z" { 236 | return 0, nil 237 | } 238 | parts := timezoneRegexp.FindStringSubmatch(s) 239 | if parts == nil { 240 | err = fmt.Errorf("soap timezone: value %q is not in ISO8601 timezone format", s) 241 | return 242 | } 243 | 244 | offset = parseInt(parts[2], &err) * 3600 245 | if len(parts[3]) != 0 { 246 | offset += parseInt(parts[3], &err) * 60 247 | } 248 | if parts[1] == "-" { 249 | offset = -offset 250 | } 251 | 252 | if err != nil { 253 | err = fmt.Errorf("soap timezone: %q: %v", s, err) 254 | } 255 | 256 | return 257 | } 258 | 259 | var completeDateTimeZoneRegexp = regexp.MustCompile(`^([^T]+)(?:T([^-+Z]+)(.+)?)?$`) 260 | 261 | // splitCompleteDateTimeZone splits date, time and timezone apart from an 262 | // ISO8601 string. It does not ensure that the contents of each part are 263 | // correct, it merely splits on certain delimiters. 264 | // e.g "2010-09-08T12:15:10+0700" => "2010-09-08", "12:15:10", "+0700". 265 | // Timezone can only be present if time is also present. 266 | func splitCompleteDateTimeZone(s string) (dateStr, timeStr, zoneStr string, err error) { 267 | parts := completeDateTimeZoneRegexp.FindStringSubmatch(s) 268 | if parts == nil { 269 | err = fmt.Errorf("soap date/time/zone: value %q is not in ISO8601 datetime format", s) 270 | return 271 | } 272 | dateStr = parts[1] 273 | timeStr = parts[2] 274 | zoneStr = parts[3] 275 | return 276 | } 277 | 278 | // MarshalDate marshals time.Time to SOAP "date" type. Note that this converts 279 | // to local time, and discards the time-of-day components. 280 | func MarshalDate(v time.Time) (string, error) { 281 | return v.In(localLoc).Format("2006-01-02"), nil 282 | } 283 | 284 | var dateFmts = []string{"2006-01-02", "20060102"} 285 | 286 | // UnmarshalDate unmarshals time.Time from SOAP "date" type. This outputs the 287 | // date as midnight in the local time zone. 288 | func UnmarshalDate(s string) (time.Time, error) { 289 | year, month, day, err := parseDateParts(s) 290 | if err != nil { 291 | return time.Time{}, err 292 | } 293 | return time.Date(year, time.Month(month), day, 0, 0, 0, 0, localLoc), nil 294 | } 295 | 296 | // TimeOfDay is used in cases where SOAP "time" or "time.tz" is used. 297 | type TimeOfDay struct { 298 | // Duration of time since midnight. 299 | FromMidnight time.Duration 300 | 301 | // Set to true if Offset is specified. If false, then the timezone is 302 | // unspecified (and by ISO8601 - implies some "local" time). 303 | HasOffset bool 304 | 305 | // Offset is non-zero only if time.tz is used. It is otherwise ignored. If 306 | // non-zero, then it is regarded as a UTC offset in seconds. Note that the 307 | // sub-minutes is ignored by the marshal function. 308 | Offset int 309 | } 310 | 311 | // MarshalTimeOfDay marshals TimeOfDay to the "time" type. 312 | func MarshalTimeOfDay(v TimeOfDay) (string, error) { 313 | d := int64(v.FromMidnight / time.Second) 314 | hour := d / 3600 315 | d = d % 3600 316 | minute := d / 60 317 | second := d % 60 318 | 319 | return fmt.Sprintf("%02d:%02d:%02d", hour, minute, second), nil 320 | } 321 | 322 | // UnmarshalTimeOfDay unmarshals TimeOfDay from the "time" type. 323 | func UnmarshalTimeOfDay(s string) (TimeOfDay, error) { 324 | t, err := UnmarshalTimeOfDayTz(s) 325 | if err != nil { 326 | return TimeOfDay{}, err 327 | } else if t.HasOffset { 328 | return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone") 329 | } 330 | return t, nil 331 | } 332 | 333 | // MarshalTimeOfDayTz marshals TimeOfDay to the "time.tz" type. 334 | func MarshalTimeOfDayTz(v TimeOfDay) (string, error) { 335 | d := int64(v.FromMidnight / time.Second) 336 | hour := d / 3600 337 | d = d % 3600 338 | minute := d / 60 339 | second := d % 60 340 | 341 | tz := "" 342 | if v.HasOffset { 343 | if v.Offset == 0 { 344 | tz = "Z" 345 | } else { 346 | offsetMins := v.Offset / 60 347 | sign := '+' 348 | if offsetMins < 1 { 349 | offsetMins = -offsetMins 350 | sign = '-' 351 | } 352 | tz = fmt.Sprintf("%c%02d:%02d", sign, offsetMins/60, offsetMins%60) 353 | } 354 | } 355 | 356 | return fmt.Sprintf("%02d:%02d:%02d%s", hour, minute, second, tz), nil 357 | } 358 | 359 | // UnmarshalTimeOfDayTz unmarshals TimeOfDay from the "time.tz" type. 360 | func UnmarshalTimeOfDayTz(s string) (tod TimeOfDay, err error) { 361 | zoneIndex := strings.IndexAny(s, "Z+-") 362 | var timePart string 363 | var hasOffset bool 364 | var offset int 365 | if zoneIndex == -1 { 366 | hasOffset = false 367 | timePart = s 368 | } else { 369 | hasOffset = true 370 | timePart = s[:zoneIndex] 371 | if offset, err = parseTimezone(s[zoneIndex:]); err != nil { 372 | return 373 | } 374 | } 375 | 376 | hour, minute, second, err := parseTimeParts(timePart) 377 | if err != nil { 378 | return 379 | } 380 | 381 | fromMidnight := time.Duration(hour*3600+minute*60+second) * time.Second 382 | 383 | // ISO8601 special case - values up to 24:00:00 are allowed, so using 384 | // strictly greater-than for the maximum value. 385 | if fromMidnight > 24*time.Hour || minute >= 60 || second >= 60 { 386 | return TimeOfDay{}, fmt.Errorf("soap time.tz: value %q has value(s) out of range", s) 387 | } 388 | 389 | return TimeOfDay{ 390 | FromMidnight: time.Duration(hour*3600+minute*60+second) * time.Second, 391 | HasOffset: hasOffset, 392 | Offset: offset, 393 | }, nil 394 | } 395 | 396 | // MarshalDateTime marshals time.Time to SOAP "dateTime" type. Note that this 397 | // converts to local time. 398 | func MarshalDateTime(v time.Time) (string, error) { 399 | return v.In(localLoc).Format("2006-01-02T15:04:05"), nil 400 | } 401 | 402 | // UnmarshalDateTime unmarshals time.Time from the SOAP "dateTime" type. This 403 | // returns a value in the local timezone. 404 | func UnmarshalDateTime(s string) (result time.Time, err error) { 405 | dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) 406 | if err != nil { 407 | return 408 | } 409 | 410 | if len(zoneStr) != 0 { 411 | err = fmt.Errorf("soap datetime: unexpected timezone in %q", s) 412 | return 413 | } 414 | 415 | year, month, day, err := parseDateParts(dateStr) 416 | if err != nil { 417 | return 418 | } 419 | 420 | var hour, minute, second int 421 | if len(timeStr) != 0 { 422 | hour, minute, second, err = parseTimeParts(timeStr) 423 | if err != nil { 424 | return 425 | } 426 | } 427 | 428 | result = time.Date(year, time.Month(month), day, hour, minute, second, 0, localLoc) 429 | return 430 | } 431 | 432 | // MarshalDateTimeTz marshals time.Time to SOAP "dateTime.tz" type. 433 | func MarshalDateTimeTz(v time.Time) (string, error) { 434 | return v.Format("2006-01-02T15:04:05-07:00"), nil 435 | } 436 | 437 | // UnmarshalDateTimeTz unmarshals time.Time from the SOAP "dateTime.tz" type. 438 | // This returns a value in the local timezone when the timezone is unspecified. 439 | func UnmarshalDateTimeTz(s string) (result time.Time, err error) { 440 | dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s) 441 | if err != nil { 442 | return 443 | } 444 | 445 | year, month, day, err := parseDateParts(dateStr) 446 | if err != nil { 447 | return 448 | } 449 | 450 | var hour, minute, second int 451 | var location *time.Location = localLoc 452 | if len(timeStr) != 0 { 453 | hour, minute, second, err = parseTimeParts(timeStr) 454 | if err != nil { 455 | return 456 | } 457 | if len(zoneStr) != 0 { 458 | var offset int 459 | offset, err = parseTimezone(zoneStr) 460 | if offset == 0 { 461 | location = time.UTC 462 | } else { 463 | location = time.FixedZone("", offset) 464 | } 465 | } 466 | } 467 | 468 | result = time.Date(year, time.Month(month), day, hour, minute, second, 0, location) 469 | return 470 | } 471 | 472 | // MarshalBoolean marshals bool to SOAP "boolean" type. 473 | func MarshalBoolean(v bool) (string, error) { 474 | if v { 475 | return "1", nil 476 | } 477 | return "0", nil 478 | } 479 | 480 | // UnmarshalBoolean unmarshals bool from the SOAP "boolean" type. 481 | func UnmarshalBoolean(s string) (bool, error) { 482 | switch s { 483 | case "0", "false", "no": 484 | return false, nil 485 | case "1", "true", "yes": 486 | return true, nil 487 | } 488 | return false, fmt.Errorf("soap boolean: %q is not a valid boolean value", s) 489 | } 490 | 491 | // MarshalBinBase64 marshals []byte to SOAP "bin.base64" type. 492 | func MarshalBinBase64(v []byte) (string, error) { 493 | return base64.StdEncoding.EncodeToString(v), nil 494 | } 495 | 496 | // UnmarshalBinBase64 unmarshals []byte from the SOAP "bin.base64" type. 497 | func UnmarshalBinBase64(s string) ([]byte, error) { 498 | return base64.StdEncoding.DecodeString(s) 499 | } 500 | 501 | // MarshalBinHex marshals []byte to SOAP "bin.hex" type. 502 | func MarshalBinHex(v []byte) (string, error) { 503 | return hex.EncodeToString(v), nil 504 | } 505 | 506 | // UnmarshalBinHex unmarshals []byte from the SOAP "bin.hex" type. 507 | func UnmarshalBinHex(s string) ([]byte, error) { 508 | return hex.DecodeString(s) 509 | } 510 | 511 | // MarshalURI marshals *url.URL to SOAP "uri" type. 512 | func MarshalURI(v *url.URL) (string, error) { 513 | return v.String(), nil 514 | } 515 | 516 | // UnmarshalURI unmarshals *url.URL from the SOAP "uri" type. 517 | func UnmarshalURI(s string) (*url.URL, error) { 518 | return url.Parse(s) 519 | } 520 | -------------------------------------------------------------------------------- /goupnp/ssdp/registry.go: -------------------------------------------------------------------------------- 1 | package ssdp 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | "regexp" 8 | "strconv" 9 | "sync" 10 | "time" 11 | 12 | "gitlab.com/NebulousLabs/go-upnp/goupnp/httpu" 13 | ) 14 | 15 | const ( 16 | maxExpiryTimeSeconds = 24 * 60 * 60 17 | ) 18 | 19 | var ( 20 | maxAgeRx = regexp.MustCompile("max-age=([0-9]+)") 21 | ) 22 | 23 | const ( 24 | EventAlive = EventType(iota) 25 | EventUpdate 26 | EventByeBye 27 | ) 28 | 29 | type EventType int8 30 | 31 | func (et EventType) String() string { 32 | switch et { 33 | case EventAlive: 34 | return "EventAlive" 35 | case EventUpdate: 36 | return "EventUpdate" 37 | case EventByeBye: 38 | return "EventByeBye" 39 | default: 40 | return fmt.Sprintf("EventUnknown(%d)", int8(et)) 41 | } 42 | } 43 | 44 | type Update struct { 45 | // The USN of the service. 46 | USN string 47 | // What happened. 48 | EventType EventType 49 | // The entry, which is nil if the service was not known and 50 | // EventType==EventByeBye. The contents of this must not be modified as it is 51 | // shared with the registry and other listeners. Once created, the Registry 52 | // does not modify the Entry value - any updates are replaced with a new 53 | // Entry value. 54 | Entry *Entry 55 | } 56 | 57 | type Entry struct { 58 | // The address that the entry data was actually received from. 59 | RemoteAddr string 60 | // Unique Service Name. Identifies a unique instance of a device or service. 61 | USN string 62 | // Notfication Type. The type of device or service being announced. 63 | NT string 64 | // Server's self-identifying string. 65 | Server string 66 | Host string 67 | // Location of the UPnP root device description. 68 | Location url.URL 69 | 70 | // Despite BOOTID,CONFIGID being required fields, apparently they are not 71 | // always set by devices. Set to -1 if not present. 72 | 73 | BootID int32 74 | ConfigID int32 75 | 76 | SearchPort uint16 77 | 78 | // When the last update was received for this entry identified by this USN. 79 | LastUpdate time.Time 80 | // When the last update's cached values are advised to expire. 81 | CacheExpiry time.Time 82 | } 83 | 84 | func newEntryFromRequest(r *http.Request) (*Entry, error) { 85 | now := time.Now() 86 | expiryDuration, err := parseCacheControlMaxAge(r.Header.Get("CACHE-CONTROL")) 87 | if err != nil { 88 | return nil, fmt.Errorf("ssdp: error parsing CACHE-CONTROL max age: %v", err) 89 | } 90 | 91 | loc, err := url.Parse(r.Header.Get("LOCATION")) 92 | if err != nil { 93 | return nil, fmt.Errorf("ssdp: error parsing entry Location URL: %v", err) 94 | } 95 | 96 | bootID, err := parseUpnpIntHeader(r.Header, "BOOTID.UPNP.ORG", -1) 97 | if err != nil { 98 | return nil, err 99 | } 100 | configID, err := parseUpnpIntHeader(r.Header, "CONFIGID.UPNP.ORG", -1) 101 | if err != nil { 102 | return nil, err 103 | } 104 | searchPort, err := parseUpnpIntHeader(r.Header, "SEARCHPORT.UPNP.ORG", ssdpSearchPort) 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | if searchPort < 1 || searchPort > 65535 { 110 | return nil, fmt.Errorf("ssdp: search port %d is out of range", searchPort) 111 | } 112 | 113 | return &Entry{ 114 | RemoteAddr: r.RemoteAddr, 115 | USN: r.Header.Get("USN"), 116 | NT: r.Header.Get("NT"), 117 | Server: r.Header.Get("SERVER"), 118 | Host: r.Header.Get("HOST"), 119 | Location: *loc, 120 | BootID: bootID, 121 | ConfigID: configID, 122 | SearchPort: uint16(searchPort), 123 | LastUpdate: now, 124 | CacheExpiry: now.Add(expiryDuration), 125 | }, nil 126 | } 127 | 128 | func parseCacheControlMaxAge(cc string) (time.Duration, error) { 129 | matches := maxAgeRx.FindStringSubmatch(cc) 130 | if len(matches) != 2 { 131 | return 0, fmt.Errorf("did not find exactly one max-age in cache control header: %q", cc) 132 | } 133 | expirySeconds, err := strconv.ParseInt(matches[1], 10, 16) 134 | if err != nil { 135 | return 0, err 136 | } 137 | if expirySeconds < 1 || expirySeconds > maxExpiryTimeSeconds { 138 | return 0, fmt.Errorf("rejecting bad expiry time of %d seconds", expirySeconds) 139 | } 140 | return time.Duration(expirySeconds) * time.Second, nil 141 | } 142 | 143 | // parseUpnpIntHeader is intended to parse the 144 | // {BOOT,CONFIGID,SEARCHPORT}.UPNP.ORG header fields. It returns the def if 145 | // the head is empty or missing. 146 | func parseUpnpIntHeader(headers http.Header, headerName string, def int32) (int32, error) { 147 | s := headers.Get(headerName) 148 | if s == "" { 149 | return def, nil 150 | } 151 | v, err := strconv.ParseInt(s, 10, 32) 152 | if err != nil { 153 | return 0, fmt.Errorf("ssdp: could not parse header %s: %v", headerName, err) 154 | } 155 | return int32(v), nil 156 | } 157 | 158 | var _ httpu.Handler = new(Registry) 159 | 160 | // Registry maintains knowledge of discovered devices and services. 161 | // 162 | // NOTE: the interface for this is experimental and may change, or go away 163 | // entirely. 164 | type Registry struct { 165 | lock sync.Mutex 166 | byUSN map[string]*Entry 167 | 168 | listenersLock sync.RWMutex 169 | listeners map[chan<- Update]struct{} 170 | } 171 | 172 | func NewRegistry() *Registry { 173 | return &Registry{ 174 | byUSN: make(map[string]*Entry), 175 | listeners: make(map[chan<- Update]struct{}), 176 | } 177 | } 178 | 179 | // NewServerAndRegistry is a convenience function to create a registry, and an 180 | // httpu server to pass it messages. Call ListenAndServe on the server for 181 | // messages to be processed. 182 | func NewServerAndRegistry() (*httpu.Server, *Registry) { 183 | reg := NewRegistry() 184 | srv := &httpu.Server{ 185 | Addr: ssdpUDP4Addr, 186 | Multicast: true, 187 | Handler: reg, 188 | } 189 | return srv, reg 190 | } 191 | 192 | func (reg *Registry) AddListener(c chan<- Update) { 193 | reg.listenersLock.Lock() 194 | defer reg.listenersLock.Unlock() 195 | reg.listeners[c] = struct{}{} 196 | } 197 | 198 | func (reg *Registry) RemoveListener(c chan<- Update) { 199 | reg.listenersLock.Lock() 200 | defer reg.listenersLock.Unlock() 201 | delete(reg.listeners, c) 202 | } 203 | 204 | func (reg *Registry) sendUpdate(u Update) { 205 | reg.listenersLock.RLock() 206 | defer reg.listenersLock.RUnlock() 207 | for c := range reg.listeners { 208 | c <- u 209 | } 210 | } 211 | 212 | // GetService returns known service (or device) entries for the given service 213 | // URN. 214 | func (reg *Registry) GetService(serviceURN string) []*Entry { 215 | // Currently assumes that the map is small, so we do a linear search rather 216 | // than indexed to avoid maintaining two maps. 217 | var results []*Entry 218 | reg.lock.Lock() 219 | defer reg.lock.Unlock() 220 | for _, entry := range reg.byUSN { 221 | if entry.NT == serviceURN { 222 | results = append(results, entry) 223 | } 224 | } 225 | return results 226 | } 227 | 228 | // ServeMessage implements httpu.Handler, and uses SSDP NOTIFY requests to 229 | // maintain the registry of devices and services. 230 | func (reg *Registry) ServeMessage(r *http.Request) { 231 | if r.Method != methodNotify { 232 | return 233 | } 234 | 235 | nts := r.Header.Get("nts") 236 | 237 | switch nts { 238 | case ntsAlive: 239 | reg.handleNTSAlive(r) 240 | case ntsUpdate: 241 | reg.handleNTSUpdate(r) 242 | case ntsByebye: 243 | reg.handleNTSByebye(r) 244 | } 245 | } 246 | 247 | func (reg *Registry) handleNTSAlive(r *http.Request) error { 248 | entry, err := newEntryFromRequest(r) 249 | if err != nil { 250 | return err 251 | } 252 | 253 | reg.lock.Lock() 254 | reg.byUSN[entry.USN] = entry 255 | reg.lock.Unlock() 256 | 257 | reg.sendUpdate(Update{ 258 | USN: entry.USN, 259 | EventType: EventAlive, 260 | Entry: entry, 261 | }) 262 | 263 | return nil 264 | } 265 | 266 | func (reg *Registry) handleNTSUpdate(r *http.Request) error { 267 | entry, err := newEntryFromRequest(r) 268 | if err != nil { 269 | return err 270 | } 271 | nextBootID, err := parseUpnpIntHeader(r.Header, "NEXTBOOTID.UPNP.ORG", -1) 272 | if err != nil { 273 | return err 274 | } 275 | entry.BootID = nextBootID 276 | 277 | reg.lock.Lock() 278 | reg.byUSN[entry.USN] = entry 279 | reg.lock.Unlock() 280 | 281 | reg.sendUpdate(Update{ 282 | USN: entry.USN, 283 | EventType: EventUpdate, 284 | Entry: entry, 285 | }) 286 | 287 | return nil 288 | } 289 | 290 | func (reg *Registry) handleNTSByebye(r *http.Request) error { 291 | usn := r.Header.Get("USN") 292 | 293 | reg.lock.Lock() 294 | entry := reg.byUSN[usn] 295 | delete(reg.byUSN, usn) 296 | reg.lock.Unlock() 297 | 298 | reg.sendUpdate(Update{ 299 | USN: usn, 300 | EventType: EventByeBye, 301 | Entry: entry, 302 | }) 303 | 304 | return nil 305 | } 306 | -------------------------------------------------------------------------------- /goupnp/ssdp/ssdp.go: -------------------------------------------------------------------------------- 1 | package ssdp 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | "time" 10 | 11 | "gitlab.com/NebulousLabs/go-upnp/goupnp/httpu" 12 | ) 13 | 14 | const ( 15 | ssdpDiscover = `"ssdp:discover"` 16 | ntsAlive = `ssdp:alive` 17 | ntsByebye = `ssdp:byebye` 18 | ntsUpdate = `ssdp:update` 19 | ssdpUDP4Addr = "239.255.255.250:1900" 20 | ssdpSearchPort = 1900 21 | methodSearch = "M-SEARCH" 22 | methodNotify = "NOTIFY" 23 | ) 24 | 25 | // SSDPRawSearch is deprecated; use SSDPRawSearchCtx instead 26 | func SSDPRawSearch(httpu *httpu.HTTPUClient, searchTarget string, maxWaitSeconds int, numSends int) ([]*http.Response, error) { 27 | return SSDPRawSearchCtx(context.Background(), httpu, searchTarget, maxWaitSeconds, numSends) 28 | } 29 | 30 | // SSDPRawSearch performs a fairly raw SSDP search request, and returns the 31 | // unique response(s) that it receives. Each response has the requested 32 | // searchTarget, a USN, and a valid location. maxWaitSeconds states how long to 33 | // wait for responses in seconds, and must be a minimum of 1 (the 34 | // implementation waits an additional 100ms for responses to arrive), 2 is a 35 | // reasonable value for this. numSends is the number of requests to send - 3 is 36 | // a reasonable value for this. 37 | func SSDPRawSearchCtx(ctx context.Context, httpu *httpu.HTTPUClient, searchTarget string, maxWaitSeconds int, numSends int) ([]*http.Response, error) { 38 | if maxWaitSeconds < 1 { 39 | return nil, errors.New("ssdp: maxWaitSeconds must be >= 1") 40 | } 41 | 42 | seenUsns := make(map[string]bool) 43 | var responses []*http.Response 44 | req := (&http.Request{ 45 | Method: methodSearch, 46 | // TODO: Support both IPv4 and IPv6. 47 | Host: ssdpUDP4Addr, 48 | URL: &url.URL{Opaque: "*"}, 49 | Header: http.Header{ 50 | // Putting headers in here avoids them being title-cased. 51 | // (The UPnP discovery protocol uses case-sensitive headers) 52 | "HOST": []string{ssdpUDP4Addr}, 53 | "MX": []string{strconv.FormatInt(int64(maxWaitSeconds), 10)}, 54 | "MAN": []string{ssdpDiscover}, 55 | "ST": []string{searchTarget}, 56 | }, 57 | }).WithContext(ctx) 58 | allResponses, err := httpu.Do(req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends) 59 | if err != nil { 60 | return nil, err 61 | } 62 | for _, response := range allResponses { 63 | if response.StatusCode != 200 { 64 | continue 65 | } 66 | if st := response.Header.Get("ST"); st != searchTarget { 67 | continue 68 | } 69 | location, err := response.Location() 70 | if err != nil { 71 | continue 72 | } 73 | usn := response.Header.Get("USN") 74 | if usn == "" { 75 | usn = location.String() 76 | } 77 | if _, alreadySeen := seenUsns[usn]; !alreadySeen { 78 | seenUsns[usn] = true 79 | responses = append(responses, response) 80 | } 81 | } 82 | 83 | return responses, nil 84 | } 85 | -------------------------------------------------------------------------------- /upnp.go: -------------------------------------------------------------------------------- 1 | // Package upnp provides a simple and opinionated interface to UPnP-enabled 2 | // routers, allowing users to forward ports and discover their external IP 3 | // address. Specific quirks: 4 | // 5 | // - When attempting to discover UPnP-enabled routers on the network, only the 6 | // first such router is returned. If you have multiple routers, this may cause 7 | // some trouble. But why would you do that? 8 | // 9 | // - Forwarded ports are always symmetric, e.g. the router's port 9980 will be 10 | // mapped to the client's port 9980. This will be unacceptable for some 11 | // purposes, but too bad. Symmetric mappings are the desired behavior 99% of 12 | // the time, and they save a function argument. 13 | // 14 | // - TCP and UDP protocols are forwarded together. 15 | // 16 | // - Ports are forwarded permanently. Some other implementations lease a port 17 | // mapping for a set duration, and then renew it periodically. This is nice, 18 | // because it means mappings won't stick around after they've served their 19 | // purpose. Unfortunately, some routers only support permanent mappings, so this 20 | // package has chosen to support the lowest common denominator. To un-forward a 21 | // port, you must use the Clear function (or do it manually). 22 | // 23 | // Once you've discovered your router, you can retrieve its address by calling 24 | // its Location method. This address can be supplied to Load to connect to the 25 | // router directly, which is much faster than calling Discover. 26 | package upnp 27 | 28 | import ( 29 | "context" 30 | "errors" 31 | "net" 32 | "net/url" 33 | "strings" 34 | "time" 35 | 36 | "gitlab.com/NebulousLabs/fastrand" 37 | "gitlab.com/NebulousLabs/go-upnp/goupnp" 38 | "gitlab.com/NebulousLabs/go-upnp/goupnp/dcps/internetgateway1" 39 | ) 40 | 41 | // An IGD provides an interface to the most commonly used functions of an 42 | // Internet Gateway Device: discovering the external IP, and forwarding ports. 43 | type IGD struct { 44 | // This interface is satisfied by the internetgateway1.WANIPConnection1 45 | // and internetgateway1.WANPPPConnection1 types. 46 | client interface { 47 | GetExternalIPAddress() (string, error) 48 | AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error 49 | GetSpecificPortMappingEntry(string, uint16, string) (uint16, string, bool, string, uint32, error) 50 | DeletePortMapping(string, uint16, string) error 51 | GetServiceClient() *goupnp.ServiceClient 52 | } 53 | } 54 | 55 | // ExternalIP returns the router's external IP. 56 | func (d *IGD) ExternalIP() (string, error) { 57 | return d.client.GetExternalIPAddress() 58 | } 59 | 60 | // IsForwardedTCP checks whether a specific TCP port is forwarded to this host 61 | func (d *IGD) IsForwardedTCP(port uint16) (bool, error) { 62 | return d.checkForward(port, "TCP") 63 | } 64 | 65 | // IsForwardedUDP checks whether a specific UDP port is forwarded to this host 66 | func (d *IGD) IsForwardedUDP(port uint16) (bool, error) { 67 | return d.checkForward(port, "UDP") 68 | } 69 | 70 | // checkForward checks whether a specific TCP or UDP port is forwarded to this host 71 | func (d *IGD) checkForward(port uint16, proto string) (bool, error) { 72 | time.Sleep(time.Millisecond) 73 | _, _, enabled, _, _, err := d.client.GetSpecificPortMappingEntry("", port, proto) 74 | 75 | if err != nil { 76 | // 714 "NoSuchEntryInArray" means that there is no such forwarding 77 | if strings.Contains(err.Error(), "714") { 78 | return false, nil 79 | } 80 | return false, err 81 | } 82 | 83 | return enabled, nil 84 | } 85 | 86 | // Forward forwards the specified port, and adds its description to the 87 | // router's port mapping table. 88 | func (d *IGD) Forward(port uint16, desc string) error { 89 | ip, err := d.getInternalIP() 90 | if err != nil { 91 | return err 92 | } 93 | 94 | time.Sleep(time.Millisecond) 95 | err = d.client.AddPortMapping("", port, "TCP", port, ip, true, desc, 0) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | time.Sleep(time.Millisecond) 101 | return d.client.AddPortMapping("", port, "UDP", port, ip, true, desc, 0) 102 | } 103 | 104 | // Clear un-forwards a port, removing it from the router's port mapping table. 105 | func (d *IGD) Clear(port uint16) error { 106 | time.Sleep(time.Millisecond) 107 | tcpErr := d.client.DeletePortMapping("", port, "TCP") 108 | time.Sleep(time.Millisecond) 109 | udpErr := d.client.DeletePortMapping("", port, "UDP") 110 | 111 | // only return an error if both deletions failed 112 | if tcpErr != nil && udpErr != nil { 113 | return tcpErr 114 | } 115 | return nil 116 | } 117 | 118 | // Location returns the URL of the router, for future lookups (see Load). 119 | func (d *IGD) Location() string { 120 | return d.client.GetServiceClient().Location.String() 121 | } 122 | 123 | // getInternalIP returns the user's local IP. 124 | func (d *IGD) getInternalIP() (string, error) { 125 | host, _, _ := net.SplitHostPort(d.client.GetServiceClient().RootDevice.URLBase.Host) 126 | devIP := net.ParseIP(host) 127 | if devIP == nil { 128 | return "", errors.New("could not determine router's internal IP") 129 | } 130 | 131 | ifaces, err := net.Interfaces() 132 | if err != nil { 133 | return "", err 134 | } 135 | 136 | for _, iface := range ifaces { 137 | addrs, err := iface.Addrs() 138 | if err != nil { 139 | return "", err 140 | } 141 | 142 | for _, addr := range addrs { 143 | if x, ok := addr.(*net.IPNet); ok && x.Contains(devIP) { 144 | return x.IP.String(), nil 145 | } 146 | } 147 | } 148 | 149 | return "", errors.New("could not determine internal IP") 150 | } 151 | 152 | // Discover is deprecated; use DiscoverCtx instead. 153 | func Discover() (*IGD, error) { 154 | return DiscoverCtx(context.Background()) 155 | } 156 | 157 | // DiscoverCtx scans the local network for routers and returns the first 158 | // UPnP-enabled router it encounters. It will try up to 3 times to find a 159 | // router, sleeping a random duration between each attempt. This is to 160 | // mitigate a race condition with many callers attempting to discover 161 | // simultaneously. 162 | func DiscoverCtx(ctx context.Context) (*IGD, error) { 163 | // TODO: if more than one client is found, only return those on the same 164 | // subnet as the user? 165 | maxTries := 3 166 | sleepTime := time.Millisecond * time.Duration(fastrand.Intn(5000)) 167 | for try := 0; try < maxTries; try++ { 168 | pppclients, _, _ := internetgateway1.NewWANPPPConnection1Clients(ctx) 169 | if len(pppclients) > 0 { 170 | return &IGD{pppclients[0]}, nil 171 | } 172 | ipclients, _, _ := internetgateway1.NewWANIPConnection1Clients(ctx) 173 | if len(ipclients) > 0 { 174 | return &IGD{ipclients[0]}, nil 175 | } 176 | select { 177 | case <-ctx.Done(): 178 | return nil, context.Canceled 179 | case <-time.After(sleepTime): 180 | } 181 | sleepTime *= 2 182 | } 183 | return nil, errors.New("no UPnP-enabled gateway found") 184 | } 185 | 186 | // Load connects to the router service specified by rawurl. This is much 187 | // faster than Discover. Generally, Load should only be called with values 188 | // returned by the IGD's Location method. 189 | func Load(rawurl string) (*IGD, error) { 190 | loc, err := url.Parse(rawurl) 191 | if err != nil { 192 | return nil, err 193 | } 194 | pppclients, _ := internetgateway1.NewWANPPPConnection1ClientsByURL(loc) 195 | if len(pppclients) > 0 { 196 | return &IGD{pppclients[0]}, nil 197 | } 198 | ipclients, _ := internetgateway1.NewWANIPConnection1ClientsByURL(loc) 199 | if len(ipclients) > 0 { 200 | return &IGD{ipclients[0]}, nil 201 | } 202 | return nil, errors.New("no UPnP-enabled gateway found at URL " + rawurl) 203 | } 204 | -------------------------------------------------------------------------------- /upnp_test.go: -------------------------------------------------------------------------------- 1 | package upnp 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | // TestConcurrentUPNP tests that several threads calling Discover() concurrently 11 | // succeed. 12 | func TestConcurrentUPNP(t *testing.T) { 13 | if testing.Short() { 14 | t.SkipNow() 15 | } 16 | // verify that a router exists 17 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 18 | defer cancel() 19 | _, err := DiscoverCtx(ctx) 20 | if err != nil { 21 | t.Skip(err) 22 | } 23 | 24 | // now try to concurrently Discover() using 20 threads 25 | var wg sync.WaitGroup 26 | for i := 0; i < 20; i++ { 27 | wg.Add(1) 28 | go func() { 29 | defer wg.Done() 30 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 31 | defer cancel() 32 | _, err := DiscoverCtx(ctx) 33 | if err != nil { 34 | t.Error(err) 35 | } 36 | }() 37 | } 38 | wg.Wait() 39 | } 40 | 41 | func TestIGD(t *testing.T) { 42 | // connect to router 43 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 44 | defer cancel() 45 | d, err := DiscoverCtx(ctx) 46 | if err != nil { 47 | t.Skip(err) 48 | } 49 | 50 | // discover external IP 51 | ip, err := d.ExternalIP() 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | t.Log("Your external IP is:", ip) 56 | 57 | // forward a port 58 | err = d.Forward(9001, "upnp test") 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | 63 | // check that port 9001 is now forwarded 64 | forwarded, err := d.IsForwardedTCP(9001) 65 | if err != nil { 66 | t.Fatal(err) 67 | } else if !forwarded { 68 | t.Fatal("port 9001 was not reported as forwarded") 69 | } 70 | 71 | // un-forward a port 72 | err = d.Clear(9001) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | // check that port 9001 is no longer forwarded 78 | forwarded, err = d.IsForwardedTCP(9001) 79 | if err != nil { 80 | t.Fatal(err) 81 | } else if forwarded { 82 | t.Fatal("port 9001 should no longer be forwarded") 83 | } 84 | 85 | // record router's location 86 | loc := d.Location() 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | t.Log("Loc:", loc) 91 | 92 | // connect to router directly 93 | d, err = Load(loc) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | } 98 | --------------------------------------------------------------------------------