├── README.md ├── go.mod ├── _examples └── nat-tester.go ├── nat.go ├── natpmp.go ├── upnp.go └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # go-nat 2 | 3 | [![GoDoc](https://godoc.org/github.com/fd/go-nat?status.svg)](https://godoc.org/github.com/fd/go-nat) [![status](https://sourcegraph.com/api/repos/github.com/fd/go-nat/.badges/status.png)](https://sourcegraph.com/github.com/fd/go-nat) 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fd/go-nat 2 | 3 | require ( 4 | github.com/huin/goupnp v0.0.0-20180415215157-1395d1447324 5 | github.com/jackpal/gateway v1.0.5 6 | github.com/jackpal/go-nat-pmp v1.0.1 7 | golang.org/x/net v0.0.0-20180524181706-dfa909b99c79 8 | golang.org/x/text v0.3.2 9 | ) 10 | -------------------------------------------------------------------------------- /_examples/nat-tester.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/fd/go-nat" 10 | ) 11 | 12 | func main() { 13 | nat, err := nat.DiscoverGateway() 14 | if err != nil { 15 | log.Fatalf("error: %s", err) 16 | } 17 | log.Printf("nat type: %s", nat.Type()) 18 | 19 | daddr, err := nat.GetDeviceAddress() 20 | if err != nil { 21 | log.Fatalf("error: %s", err) 22 | } 23 | log.Printf("device address: %s", daddr) 24 | 25 | iaddr, err := nat.GetInternalAddress() 26 | if err != nil { 27 | log.Fatalf("error: %s", err) 28 | } 29 | log.Printf("internal address: %s", iaddr) 30 | 31 | eaddr, err := nat.GetExternalAddress() 32 | if err != nil { 33 | log.Fatalf("error: %s", err) 34 | } 35 | log.Printf("external address: %s", eaddr) 36 | 37 | eport, err := nat.AddPortMapping("tcp", 3080, "http", 60) 38 | if err != nil { 39 | log.Fatalf("error: %s", err) 40 | } 41 | 42 | log.Printf("test-page: http://%s:%d/", eaddr, eport) 43 | 44 | go func() { 45 | for { 46 | time.Sleep(30 * time.Second) 47 | 48 | _, err = nat.AddPortMapping("tcp", 3080, "http", 60) 49 | if err != nil { 50 | log.Fatalf("error: %s", err) 51 | } 52 | } 53 | }() 54 | 55 | defer nat.DeletePortMapping("txp", 3080) 56 | 57 | http.ListenAndServe(":3080", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 58 | rw.Header().Set("Content-Type", "text/plain") 59 | rw.WriteHeader(200) 60 | fmt.Fprintf(rw, "Hello there!\n") 61 | fmt.Fprintf(rw, "nat type: %s\n", nat.Type()) 62 | fmt.Fprintf(rw, "device address: %s\n", daddr) 63 | fmt.Fprintf(rw, "internal address: %s\n", iaddr) 64 | fmt.Fprintf(rw, "external address: %s\n", eaddr) 65 | fmt.Fprintf(rw, "test-page: http://%s:%d/\n", eaddr, eport) 66 | })) 67 | } 68 | -------------------------------------------------------------------------------- /nat.go: -------------------------------------------------------------------------------- 1 | // Package nat implements NAT handling facilities 2 | package nat 3 | 4 | import ( 5 | "errors" 6 | "math" 7 | "math/rand" 8 | "net" 9 | "time" 10 | ) 11 | 12 | var ErrNoExternalAddress = errors.New("no external address") 13 | var ErrNoInternalAddress = errors.New("no internal address") 14 | var ErrNoNATFound = errors.New("no NAT found") 15 | 16 | // protocol is either "udp" or "tcp" 17 | type NAT interface { 18 | // Type returns the kind of NAT port mapping service that is used 19 | Type() string 20 | 21 | // GetDeviceAddress returns the internal address of the gateway device. 22 | GetDeviceAddress() (addr net.IP, err error) 23 | 24 | // GetExternalAddress returns the external address of the gateway device. 25 | GetExternalAddress() (addr net.IP, err error) 26 | 27 | // GetInternalAddress returns the address of the local host. 28 | GetInternalAddress() (addr net.IP, err error) 29 | 30 | // AddPortMapping maps a port on the local host to an external port. 31 | AddPortMapping(protocol string, internalPort int, description string, timeout time.Duration) (mappedExternalPort int, err error) 32 | 33 | // DeletePortMapping removes a port mapping. 34 | DeletePortMapping(protocol string, internalPort int) (err error) 35 | } 36 | 37 | // DiscoverGateway attempts to find a gateway device. 38 | func DiscoverGateway() (NAT, error) { 39 | select { 40 | case nat := <-discoverUPNP_IG1(): 41 | return nat, nil 42 | case nat := <-discoverUPNP_IG2(): 43 | return nat, nil 44 | case nat := <-discoverUPNP_GenIGDev(): 45 | return nat, nil 46 | case nat := <-discoverNATPMP(): 47 | return nat, nil 48 | case <-time.After(10 * time.Second): 49 | return nil, ErrNoNATFound 50 | } 51 | } 52 | 53 | func randomPort() int { 54 | rand.Seed(time.Now().UnixNano()) 55 | return rand.Intn(math.MaxUint16-10000) + 10000 56 | } 57 | -------------------------------------------------------------------------------- /natpmp.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | "github.com/jackpal/gateway" 8 | "github.com/jackpal/go-nat-pmp" 9 | ) 10 | 11 | var ( 12 | _ NAT = (*natpmpNAT)(nil) 13 | ) 14 | 15 | func discoverNATPMP() <-chan NAT { 16 | res := make(chan NAT, 1) 17 | 18 | ip, err := gateway.DiscoverGateway() 19 | if err == nil { 20 | go discoverNATPMPWithAddr(res, ip) 21 | } 22 | 23 | return res 24 | } 25 | 26 | func discoverNATPMPWithAddr(c chan NAT, ip net.IP) { 27 | client := natpmp.NewClient(ip) 28 | _, err := client.GetExternalAddress() 29 | if err != nil { 30 | return 31 | } 32 | 33 | c <- &natpmpNAT{client, ip, make(map[int]int)} 34 | } 35 | 36 | type natpmpNAT struct { 37 | c *natpmp.Client 38 | gateway net.IP 39 | ports map[int]int 40 | } 41 | 42 | func (n *natpmpNAT) GetDeviceAddress() (addr net.IP, err error) { 43 | return n.gateway, nil 44 | } 45 | 46 | func (n *natpmpNAT) GetInternalAddress() (addr net.IP, err error) { 47 | ifaces, err := net.Interfaces() 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | for _, iface := range ifaces { 53 | addrs, err := iface.Addrs() 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | for _, addr := range addrs { 59 | switch x := addr.(type) { 60 | case *net.IPNet: 61 | if x.Contains(n.gateway) { 62 | return x.IP, nil 63 | } 64 | } 65 | } 66 | } 67 | 68 | return nil, ErrNoInternalAddress 69 | } 70 | 71 | func (n *natpmpNAT) GetExternalAddress() (addr net.IP, err error) { 72 | res, err := n.c.GetExternalAddress() 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | d := res.ExternalIPAddress 78 | return net.IPv4(d[0], d[1], d[2], d[3]), nil 79 | } 80 | 81 | func (n *natpmpNAT) AddPortMapping(protocol string, internalPort int, description string, timeout time.Duration) (int, error) { 82 | var ( 83 | err error 84 | ) 85 | 86 | timeoutInSeconds := int(timeout / time.Second) 87 | 88 | if externalPort := n.ports[internalPort]; externalPort > 0 { 89 | _, err = n.c.AddPortMapping(protocol, internalPort, externalPort, timeoutInSeconds) 90 | if err == nil { 91 | n.ports[internalPort] = externalPort 92 | return externalPort, nil 93 | } 94 | } 95 | 96 | for i := 0; i < 3; i++ { 97 | externalPort := randomPort() 98 | _, err = n.c.AddPortMapping(protocol, internalPort, externalPort, timeoutInSeconds) 99 | if err == nil { 100 | n.ports[internalPort] = externalPort 101 | return externalPort, nil 102 | } 103 | } 104 | 105 | return 0, err 106 | } 107 | 108 | func (n *natpmpNAT) DeletePortMapping(protocol string, internalPort int) (err error) { 109 | delete(n.ports, internalPort) 110 | return nil 111 | } 112 | 113 | func (n *natpmpNAT) Type() string { 114 | return "NAT-PMP" 115 | } 116 | -------------------------------------------------------------------------------- /upnp.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | "strings" 7 | "time" 8 | 9 | "github.com/huin/goupnp" 10 | "github.com/huin/goupnp/dcps/internetgateway1" 11 | "github.com/huin/goupnp/dcps/internetgateway2" 12 | 13 | "github.com/koron/go-ssdp" 14 | ) 15 | 16 | var ( 17 | _ NAT = (*upnp_NAT)(nil) 18 | ) 19 | 20 | func discoverUPNP_IG1() <-chan NAT { 21 | res := make(chan NAT, 1) 22 | go func() { 23 | 24 | // find devices 25 | devs, err := goupnp.DiscoverDevices(internetgateway1.URN_WANConnectionDevice_1) 26 | if err != nil { 27 | return 28 | } 29 | 30 | for _, dev := range devs { 31 | if dev.Root == nil { 32 | continue 33 | } 34 | 35 | dev.Root.Device.VisitServices(func(srv *goupnp.Service) { 36 | switch srv.ServiceType { 37 | case internetgateway1.URN_WANIPConnection_1: 38 | client := &internetgateway1.WANIPConnection1{ServiceClient: goupnp.ServiceClient{ 39 | SOAPClient: srv.NewSOAPClient(), 40 | RootDevice: dev.Root, 41 | Service: srv, 42 | }} 43 | _, isNat, err := client.GetNATRSIPStatus() 44 | if err == nil && isNat { 45 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG1-IP1)", dev.Root} 46 | return 47 | } 48 | 49 | case internetgateway1.URN_WANPPPConnection_1: 50 | client := &internetgateway1.WANPPPConnection1{ServiceClient: goupnp.ServiceClient{ 51 | SOAPClient: srv.NewSOAPClient(), 52 | RootDevice: dev.Root, 53 | Service: srv, 54 | }} 55 | _, isNat, err := client.GetNATRSIPStatus() 56 | if err == nil && isNat { 57 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG1-PPP1)", dev.Root} 58 | return 59 | } 60 | 61 | } 62 | }) 63 | } 64 | 65 | }() 66 | return res 67 | } 68 | 69 | func discoverUPNP_IG2() <-chan NAT { 70 | res := make(chan NAT, 1) 71 | go func() { 72 | 73 | // find devices 74 | devs, err := goupnp.DiscoverDevices(internetgateway2.URN_WANConnectionDevice_2) 75 | if err != nil { 76 | return 77 | } 78 | 79 | for _, dev := range devs { 80 | if dev.Root == nil { 81 | continue 82 | } 83 | 84 | dev.Root.Device.VisitServices(func(srv *goupnp.Service) { 85 | switch srv.ServiceType { 86 | case internetgateway2.URN_WANIPConnection_1: 87 | client := &internetgateway2.WANIPConnection1{ServiceClient: goupnp.ServiceClient{ 88 | SOAPClient: srv.NewSOAPClient(), 89 | RootDevice: dev.Root, 90 | Service: srv, 91 | }} 92 | _, isNat, err := client.GetNATRSIPStatus() 93 | if err == nil && isNat { 94 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG2-IP1)", dev.Root} 95 | return 96 | } 97 | 98 | case internetgateway2.URN_WANIPConnection_2: 99 | client := &internetgateway2.WANIPConnection2{ServiceClient: goupnp.ServiceClient{ 100 | SOAPClient: srv.NewSOAPClient(), 101 | RootDevice: dev.Root, 102 | Service: srv, 103 | }} 104 | _, isNat, err := client.GetNATRSIPStatus() 105 | if err == nil && isNat { 106 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG2-IP2)", dev.Root} 107 | return 108 | } 109 | 110 | case internetgateway2.URN_WANPPPConnection_1: 111 | client := &internetgateway2.WANPPPConnection1{ServiceClient: goupnp.ServiceClient{ 112 | SOAPClient: srv.NewSOAPClient(), 113 | RootDevice: dev.Root, 114 | Service: srv, 115 | }} 116 | _, isNat, err := client.GetNATRSIPStatus() 117 | if err == nil && isNat { 118 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG2-PPP1)", dev.Root} 119 | return 120 | } 121 | 122 | } 123 | }) 124 | } 125 | 126 | }() 127 | return res 128 | } 129 | 130 | func discoverUPNP_GenIGDev() <-chan NAT { 131 | res := make(chan NAT, 1) 132 | go func() { 133 | DeviceList, err := ssdp.Search(ssdp.All, 5, "") 134 | if err != nil { 135 | return 136 | } 137 | var gw ssdp.Service 138 | for _, Service := range DeviceList { 139 | if strings.Contains(Service.Type, "InternetGatewayDevice") { 140 | gw = Service 141 | break 142 | } 143 | } 144 | 145 | DeviceURL, err := url.Parse(gw.Location) 146 | if err != nil { 147 | return 148 | } 149 | RootDevice, err := goupnp.DeviceByURL(DeviceURL) 150 | if err != nil { 151 | return 152 | } 153 | 154 | RootDevice.Device.VisitServices(func(srv *goupnp.Service) { 155 | switch srv.ServiceType { 156 | case internetgateway1.URN_WANIPConnection_1: 157 | client := &internetgateway1.WANIPConnection1{ServiceClient: goupnp.ServiceClient{ 158 | SOAPClient: srv.NewSOAPClient(), 159 | RootDevice: RootDevice, 160 | Service: srv, 161 | }} 162 | _, isNat, err := client.GetNATRSIPStatus() 163 | if err == nil && isNat { 164 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG1-IP1)", RootDevice} 165 | return 166 | } 167 | 168 | case internetgateway1.URN_WANPPPConnection_1: 169 | client := &internetgateway1.WANPPPConnection1{ServiceClient: goupnp.ServiceClient{ 170 | SOAPClient: srv.NewSOAPClient(), 171 | RootDevice: RootDevice, 172 | Service: srv, 173 | }} 174 | _, isNat, err := client.GetNATRSIPStatus() 175 | if err == nil && isNat { 176 | res <- &upnp_NAT{client, make(map[int]int), "UPNP (IG1-PPP1)", RootDevice} 177 | return 178 | } 179 | 180 | } 181 | }) 182 | }() 183 | return res 184 | } 185 | 186 | type upnp_NAT_Client interface { 187 | GetExternalIPAddress() (string, error) 188 | AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error 189 | DeletePortMapping(string, uint16, string) error 190 | } 191 | 192 | type upnp_NAT struct { 193 | c upnp_NAT_Client 194 | ports map[int]int 195 | typ string 196 | rootDevice *goupnp.RootDevice 197 | } 198 | 199 | func (u *upnp_NAT) GetExternalAddress() (addr net.IP, err error) { 200 | ipString, err := u.c.GetExternalIPAddress() 201 | if err != nil { 202 | return nil, err 203 | } 204 | 205 | ip := net.ParseIP(ipString) 206 | if ip == nil { 207 | return nil, ErrNoExternalAddress 208 | } 209 | 210 | return ip, nil 211 | } 212 | 213 | func mapProtocol(s string) string { 214 | switch s { 215 | case "udp": 216 | return "UDP" 217 | case "tcp": 218 | return "TCP" 219 | default: 220 | panic("invalid protocol: " + s) 221 | } 222 | } 223 | 224 | func (u *upnp_NAT) AddPortMapping(protocol string, internalPort int, description string, timeout time.Duration) (int, error) { 225 | ip, err := u.GetInternalAddress() 226 | if err != nil { 227 | return 0, nil 228 | } 229 | 230 | timeoutInSeconds := uint32(timeout / time.Second) 231 | 232 | if externalPort := u.ports[internalPort]; externalPort > 0 { 233 | err = u.c.AddPortMapping("", uint16(externalPort), mapProtocol(protocol), uint16(internalPort), ip.String(), true, description, timeoutInSeconds) 234 | if err == nil { 235 | return externalPort, nil 236 | } 237 | } 238 | 239 | for i := 0; i < 3; i++ { 240 | externalPort := randomPort() 241 | err = u.c.AddPortMapping("", uint16(externalPort), mapProtocol(protocol), uint16(internalPort), ip.String(), true, description, timeoutInSeconds) 242 | if err == nil { 243 | u.ports[internalPort] = externalPort 244 | return externalPort, nil 245 | } 246 | } 247 | 248 | return 0, err 249 | } 250 | 251 | func (u *upnp_NAT) DeletePortMapping(protocol string, internalPort int) error { 252 | if externalPort := u.ports[internalPort]; externalPort > 0 { 253 | delete(u.ports, internalPort) 254 | return u.c.DeletePortMapping("", uint16(externalPort), mapProtocol(protocol)) 255 | } 256 | 257 | return nil 258 | } 259 | 260 | func (u *upnp_NAT) GetDeviceAddress() (net.IP, error) { 261 | addr, err := net.ResolveUDPAddr("udp4", u.rootDevice.URLBase.Host) 262 | if err != nil { 263 | return nil, err 264 | } 265 | 266 | return addr.IP, nil 267 | } 268 | 269 | func (u *upnp_NAT) GetInternalAddress() (net.IP, error) { 270 | devAddr, err := u.GetDeviceAddress() 271 | if err != nil { 272 | return nil, err 273 | } 274 | 275 | ifaces, err := net.Interfaces() 276 | if err != nil { 277 | return nil, err 278 | } 279 | 280 | for _, iface := range ifaces { 281 | addrs, err := iface.Addrs() 282 | if err != nil { 283 | return nil, err 284 | } 285 | 286 | for _, addr := range addrs { 287 | switch x := addr.(type) { 288 | case *net.IPNet: 289 | if x.Contains(devAddr) { 290 | return x.IP, nil 291 | } 292 | } 293 | } 294 | } 295 | 296 | return nil, ErrNoInternalAddress 297 | } 298 | 299 | func (n *upnp_NAT) Type() string { return n.typ } 300 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | --------------------------------------------------------------------------------