├── go.mod ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── README.md ├── examples_test.go ├── doc.go ├── contact.go ├── campus.go ├── organization.go ├── facility.go ├── api.go ├── api_test.go ├── carrier.go ├── net.go ├── ix.go └── LICENSE /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gmazoyer/peeringdb 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | go-version: 11 | - '1.22' 12 | - '1.23' 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Setup Go ${{ matrix.go-version }} 16 | uses: actions/setup-go@v3 17 | with: 18 | go-version: ${{ matrix.go-version }} 19 | - name: Display Go version 20 | run: go version 21 | - name: Run tests 22 | run: go test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Go ### 2 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 3 | *.o 4 | *.a 5 | *.so 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | 27 | 28 | ### Vim ### 29 | # swap 30 | [._]*.s[a-w][a-z] 31 | [._]s[a-w][a-z] 32 | # session 33 | Session.vim 34 | # temporary 35 | .netrwhist 36 | *~ 37 | # auto-generated tag files 38 | tags 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PeeringDB API - Go package 2 | 3 | [![GoDoc](https://godoc.org/github.com/gmazoyer/peeringdb?status.svg)](https://godoc.org/github.com/gmazoyer/peeringdb) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/gmazoyer/peeringdb)](https://goreportcard.com/report/github.com/gmazoyer/peeringdb) 5 | 6 | This is a Go package that allows developer to interact with the 7 | [PeeringDB API](https://peeringdb.com/apidocs/) in the easiest way possible. 8 | There are no binaries provided with this package. It can only be used as a 9 | library. 10 | 11 | ## Installation 12 | 13 | Install the library package with `go get github.com/gmazoyer/peeringdb`. 14 | 15 | ## Example 16 | 17 | There are small examples in the 18 | [package documentation](https://godoc.org/github.com/gmazoyer/peeringdb). 19 | 20 | You can also found a real life example with the 21 | [PeeringDB synchronization tool](https://github.com/gmazoyer/peeringdb-sync). 22 | -------------------------------------------------------------------------------- /examples_test.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import "fmt" 4 | 5 | func Example() { 6 | api := NewAPI() 7 | 8 | // Look for the organization given a name 9 | search := make(map[string]interface{}) 10 | search["name"] = "Guillaume Mazoyer" 11 | 12 | // Get the organization, pointer to slice returned 13 | organizations, err := api.GetOrganization(search) 14 | 15 | // If an error as occurred, print it 16 | if err != nil { 17 | fmt.Println(err) 18 | return 19 | } 20 | 21 | // No organization found 22 | if len(*organizations) < 1 { 23 | fmt.Printf("No organization found with name '%s'\n", search["name"]) 24 | return 25 | } 26 | 27 | // Several organizations found 28 | if len(*organizations) > 1 { 29 | fmt.Printf("More than one organizations found with name '%s'\n", 30 | search["name"]) 31 | return 32 | } 33 | 34 | // Get the first found organization 35 | org := (*organizations)[0] 36 | 37 | // Find if there are networks linked to the organization 38 | if len(org.NetworkSet) > 0 { 39 | // For each network 40 | for _, networkID := range org.NetworkSet { 41 | // Get the details and print it 42 | network, err := api.GetNetworkByID(networkID) 43 | if err != nil { 44 | fmt.Println(err) 45 | } else { 46 | fmt.Print(network.Name) 47 | } 48 | } 49 | } 50 | // Output: Guillaume Mazoyer 51 | } 52 | 53 | func ExampleAPI_GetASN() { 54 | api := NewAPI() 55 | as201281, err := api.GetASN(201281) 56 | 57 | if err != nil { 58 | fmt.Println(err.Error()) 59 | return 60 | } 61 | 62 | fmt.Printf("Name: %s\n", as201281.Name) 63 | fmt.Printf("ASN: %d\n", as201281.ASN) 64 | // Output: 65 | // Name: Guillaume Mazoyer 66 | // ASN: 201281 67 | } 68 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package peeringdb provides structures and functions to interact with the 3 | PeeringDB API. The API documentation is available here: 4 | https://www.peeringdb.com/apidocs/ 5 | 6 | The PeeringDB API is based on REST principles and returns data formatted in 7 | JSON. This package queries the API with the correct URL and parameters, parses 8 | the JSON response, and converts it into Go structures. Currently, this package 9 | only supports GET requests and cannot be used to modify any PeeringDB records. 10 | 11 | There are two levels of structures in this package. The first level represents 12 | the top level of the JSON returned by the API. These structures are named 13 | *Resource. They all have a Meta field containing metadata returned by the API, 14 | and a Data field which is an array of the second level structures. 15 | 16 | All calls to the PeeringDB API use the "depth=1" parameter. This means that 17 | sets are expanded as integer slices instead of slices of structures, which 18 | speeds up the API processing time. To get the structures for a given set, you 19 | just need to iterate over the set and call the appropriate function to retrieve 20 | structures from IDs. 21 | 22 | For example, when requesting one or more objects from the PeeringDB API, the 23 | response is always formatted in the same way: first comes the metadata, then 24 | the data. The data is always in an array since it might contain more than one 25 | object. When asking the API for a network object (called Net and represented by 26 | the struct of the same name), this package parses the first level as a 27 | NetResource structure. This structure contains metadata in its Meta field (if 28 | there is any) and Net structures in the Data field (as an array). 29 | */ 30 | package peeringdb 31 | -------------------------------------------------------------------------------- /contact.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // networkContactResource is the top-level structure when parsing the JSON 9 | // output from the API. This structure is not used if the NetworkContact JSON 10 | // object is included as a field in another JSON object. This structure is used 11 | // only if the proper namespace is queried. 12 | type networkContactResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []NetworkContact `json:"data"` 17 | } 18 | 19 | // NetworkContact represents a contact for a network. 20 | type NetworkContact struct { 21 | ID int `json:"id"` 22 | NetworkID int `json:"net_id"` 23 | Network Network `json:"net"` 24 | Role string `json:"role"` 25 | Visible string `json:"visible"` 26 | Name string `json:"name"` 27 | Phone string `json:"phone"` 28 | Email string `json:"email"` 29 | URL string `json:"url"` 30 | Created time.Time `json:"created"` 31 | Updated time.Time `json:"updated"` 32 | Status string `json:"status"` 33 | } 34 | 35 | // getNetworkContactResource returns a pointer to an networkContactResource 36 | // structure corresponding to the API JSON response. An error can be returned 37 | // if something went wrong. 38 | func (api *API) getNetworkContactResource(search map[string]interface{}) (*networkContactResource, error) { 39 | // Get the NetworkContactResource from the API 40 | response, err := api.lookup(networkContactNamespace, search) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | // Ask for cleanup once we are done 46 | defer response.Body.Close() 47 | 48 | // Decode what the API has given to us 49 | resource := &networkContactResource{} 50 | err = json.NewDecoder(response.Body).Decode(&resource) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | return resource, nil 56 | } 57 | 58 | // GetNetworkContact returns a pointer to a slice of NetworkContact structures 59 | // that the PeeringDB API can provide matching the given search parameters map. 60 | // If an error occurs, the returned error will be non-nil. The returned value 61 | // can be nil if no object could be found. 62 | func (api *API) GetNetworkContact(search map[string]interface{}) (*[]NetworkContact, error) { 63 | // Ask for the all NetworkContact objects 64 | networkContactResource, err := api.getNetworkContactResource(search) 65 | 66 | // Error as occurred while querying the API 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | // Return all NetworkContact objects, will be nil if slice is empty 72 | return &networkContactResource.Data, nil 73 | } 74 | 75 | // GetAllNetworkContacts returns a pointer to a slice of NetworkContact 76 | // structures that the PeeringDB API can provide. If an error occurs, the 77 | // returned error will be non-nil. The can be nil if no object could be found. 78 | func (api *API) GetAllNetworkContacts() (*[]NetworkContact, error) { 79 | // Return all NetworkContact objects 80 | return api.GetNetworkContact(nil) 81 | } 82 | 83 | // GetNetworkContactByID returns a pointer to a NetworkContact structure that 84 | // matches the given ID. If the ID is lesser than 0, it will return nil. The 85 | // returned error will be non-nil if an issue as occurred while trying to query 86 | // the API. If for some reasons the API returns more than one object for the 87 | // given ID (but it must not) only the first will be used for the returned 88 | // value. 89 | func (api *API) GetNetworkContactByID(id int) (*NetworkContact, error) { 90 | // No point of looking for the network contact with an ID < 0 91 | if id < 0 { 92 | return nil, nil 93 | } 94 | 95 | // Ask for the NetworkContact given it ID 96 | search := make(map[string]interface{}) 97 | search["id"] = id 98 | 99 | // Actually ask for it 100 | networkContacts, err := api.GetNetworkContact(search) 101 | 102 | // Error as occurred while querying the API 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | // No NetworkContact matching the ID 108 | if len(*networkContacts) < 1 { 109 | return nil, nil 110 | } 111 | 112 | // Only return the first match, they must be only one match (ID being 113 | // unique) 114 | return &(*networkContacts)[0], nil 115 | } 116 | -------------------------------------------------------------------------------- /campus.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // campusResource is the top-level structure when parsing the JSON output 9 | // from the API. This structure is not used if the Campus JSON object is 10 | // included as a field in another JSON object. This structure is used only if 11 | // the proper namespace is queried. 12 | type campusResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []Campus `json:"data"` 17 | } 18 | 19 | // Campus is the representation of a site where facilities are. 20 | type Campus struct { 21 | ID int `json:"id"` 22 | OrganizationID int `json:"org_id"` 23 | OrganizationName string `json:"org_name"` 24 | Organization Organization `json:"organization,omitempty"` 25 | Name string `json:"name"` 26 | NameLong string `json:"name_long"` 27 | AKA string `json:"aka"` 28 | Website string `json:"website"` 29 | Notes string `json:"notes"` 30 | Created time.Time `json:"created"` 31 | Updated time.Time `json:"updated"` 32 | Status string `json:"status"` 33 | City string `json:"city"` 34 | Country string `json:"country"` 35 | State string `json:"state"` 36 | Zipcode string `json:"zipcode"` 37 | FacilitySet []int `json:"fac_set"` 38 | SocialMedia []struct { 39 | Service string `json:"service"` 40 | Identifier string `json:"identifier"` 41 | } `json:"social_media"` 42 | } 43 | 44 | // getCampusResource returns a pointer to a campusResource structure 45 | // corresponding to the API JSON response. An error can be returned if 46 | // something went wrong. 47 | func (api *API) getCampusResource(search map[string]interface{}) (*campusResource, error) { 48 | // Get the CampusResource from the API 49 | response, err := api.lookup(campusNamespace, search) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | // Ask for cleanup once we are done 55 | defer response.Body.Close() 56 | 57 | // Decode what the API has given to us 58 | resource := &campusResource{} 59 | err = json.NewDecoder(response.Body).Decode(&resource) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return resource, nil 65 | } 66 | 67 | // GetCampus returns a pointer to a slice of Campus structures that the 68 | // PeeringDB API can provide matching the given search parameters map. If an 69 | // error occurs, the returned error will be non-nil. The returned value can be 70 | // nil if no object could be found. 71 | func (api *API) GetCampus(search map[string]interface{}) (*[]Campus, error) { 72 | // Ask for the all Campus objects 73 | campusResource, err := api.getCampusResource(search) 74 | 75 | // Error as occurred while querying the API 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | // Return all Campus objects, will be nil if slice is empty 81 | return &campusResource.Data, nil 82 | } 83 | 84 | // GetAllCampuses returns a pointer to a slice of Campus structures that the 85 | // PeeringDB API can provide. If an error occurs, the returned error will be 86 | // non-nil. The can be nil if no object could be found. 87 | func (api *API) GetAllCampuses() (*[]Campus, error) { 88 | // Return all Campus objects 89 | return api.GetCampus(nil) 90 | } 91 | 92 | // GetCampusByID returns a pointer to a Campus structure that matches the 93 | // given ID. If the ID is lesser than 0, it will return nil. The returned 94 | // error will be non-nil if an issue as occurred while trying to query the 95 | // API. If for some reasons the API returns more than one object for the 96 | // given ID (but it must not) only the first will be used for the returned 97 | // value. 98 | func (api *API) GetCampusByID(id int) (*Campus, error) { 99 | // No point of looking for the campus with an ID < 0 100 | if id < 0 { 101 | return nil, nil 102 | } 103 | 104 | // Ask for the Campus given it ID 105 | search := make(map[string]interface{}) 106 | search["id"] = id 107 | 108 | // Actually ask for it 109 | campuses, err := api.GetCampus(search) 110 | 111 | // Error as occurred while querying the API 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | // No Campus matching the ID 117 | if len(*campuses) < 1 { 118 | return nil, nil 119 | } 120 | 121 | // Only return the first match, they must be only one match (ID being 122 | // unique) 123 | return &(*campuses)[0], nil 124 | } 125 | -------------------------------------------------------------------------------- /organization.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // organizationResource is the top-level structure when parsing the JSON output 9 | // from the API. This structure is not used if the Organization JSON object is 10 | // included as a field in another JSON object. This structure is used only if 11 | // the proper namespace is queried. 12 | type organizationResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []Organization `json:"data"` 17 | } 18 | 19 | // Organization is a structure representing an Organization. An organization 20 | // can be seen as an enterprise linked to networks, facilities and internet 21 | // exchange points. 22 | type Organization struct { 23 | ID int `json:"id"` 24 | Name string `json:"name"` 25 | AKA string `json:"aka"` 26 | NameLong string `json:"name_long"` 27 | Website string `json:"website"` 28 | Notes string `json:"notes"` 29 | Require2FA bool `json:"require_2fa"` 30 | NetworkSet []int `json:"net_set"` 31 | FacilitySet []int `json:"fac_set"` 32 | InternetExchangeSet []int `json:"ix_set"` 33 | CarrierSet []int `json:"carrier_set"` 34 | CampusSet []int `json:"campus_set"` 35 | Address1 string `json:"address1"` 36 | Address2 string `json:"address2"` 37 | City string `json:"city"` 38 | Country string `json:"country"` 39 | State string `json:"state"` 40 | Zipcode string `json:"zipcode"` 41 | Floor string `json:"floor"` 42 | Suite string `json:"suite"` 43 | Latitude float64 `json:"latitude"` 44 | Longitude float64 `json:"longitude"` 45 | Created time.Time `json:"created"` 46 | Updated time.Time `json:"updated"` 47 | Status string `json:"status"` 48 | SocialMedia []struct { 49 | Service string `json:"service"` 50 | Identifier string `json:"identifier"` 51 | } `json:"social_media"` 52 | } 53 | 54 | // getOrganizationResource returns a pointer to an organizationResource 55 | // structure corresponding to the API JSON response. An error can be returned 56 | // if something went wrong. 57 | func (api *API) getOrganizationResource(search map[string]interface{}) (*organizationResource, error) { 58 | // Get the OrganizationResource from the API 59 | response, err := api.lookup(organizationNamespace, search) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | // Ask for cleanup once we are done 65 | defer response.Body.Close() 66 | 67 | // Decode what the API has given to us 68 | resource := &organizationResource{} 69 | err = json.NewDecoder(response.Body).Decode(&resource) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return resource, nil 75 | } 76 | 77 | // GetOrganization returns a pointer to a slice of Organization structures that 78 | // the PeeringDB API can provide matching the given search parameters map. If 79 | // an error occurs, the returned error will be non-nil. The returned value can 80 | // be nil if no object could be found. 81 | func (api *API) GetOrganization(search map[string]interface{}) (*[]Organization, error) { 82 | // Ask for the all Organization objects 83 | organizationResource, err := api.getOrganizationResource(search) 84 | 85 | // Error as occurred while querying the API 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | // Return all Organization objects, will be nil if slice is empty 91 | return &organizationResource.Data, nil 92 | } 93 | 94 | // GetAllOrganizations returns a pointer to a slice of Organization structures 95 | // that the PeeringDB API can provide. If an error occurs, the returned error 96 | // will be non-nil. The can be nil if no object could be found. 97 | func (api *API) GetAllOrganizations() (*[]Organization, error) { 98 | // Return all Organization objects 99 | return api.GetOrganization(nil) 100 | } 101 | 102 | // GetOrganizationByID returns a pointer to a Organization structure that 103 | // matches the given ID. If the ID is lesser than 0, it will return nil. The 104 | // returned error will be non-nil if an issue as occurred while trying to query 105 | // the API. If for some reasons the API returns more than one object for the 106 | // given ID (but it must not) only the first will be used for the returned 107 | // value. 108 | func (api *API) GetOrganizationByID(id int) (*Organization, error) { 109 | // No point of looking for the organization with an ID < 0 110 | if id < 0 { 111 | return nil, nil 112 | } 113 | 114 | // Ask for the Organization given it ID 115 | search := make(map[string]interface{}) 116 | search["id"] = id 117 | 118 | // Actually ask for it 119 | organizations, err := api.GetOrganization(search) 120 | 121 | // Error as occurred while querying the API 122 | if err != nil { 123 | return nil, err 124 | } 125 | 126 | // No Organization matching the ID 127 | if len(*organizations) < 1 { 128 | return nil, nil 129 | } 130 | 131 | // Only return the first match, they must be only one match (ID being 132 | // unique) 133 | return &(*organizations)[0], nil 134 | } 135 | -------------------------------------------------------------------------------- /facility.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // facilityResource is the top-level structure when parsing the JSON output 9 | // from the API. This structure is not used if the Facility JSON object is 10 | // included as a field in another JSON object. This structure is used only if 11 | // the proper namespace is queried. 12 | type facilityResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []Facility `json:"data"` 17 | } 18 | 19 | // Facility is the representation of a location where network operators and 20 | // Internet exchange points are located. Most of the time you know a facility 21 | // as a datacenter. 22 | type Facility struct { 23 | ID int `json:"id"` 24 | OrganizationID int `json:"org_id"` 25 | OrganizationName string `json:"org_name"` 26 | Organization Organization `json:"organization,omitempty"` 27 | CampusID int `json:"campus_id"` 28 | Campus Campus `json:"campus,omitempty"` 29 | Name string `json:"name"` 30 | AKA string `json:"aka"` 31 | NameLong string `json:"name_long"` 32 | Website string `json:"website"` 33 | CLLI string `json:"clli"` 34 | Rencode string `json:"rencode"` 35 | Npanxx string `json:"npanxx"` 36 | Notes string `json:"notes"` 37 | NetCount int `json:"net_count"` 38 | IXCount int `json:"ix_count"` 39 | SalesEmail string `json:"sales_email"` 40 | SalesPhone string `json:"sales_phone"` 41 | TechEmail string `json:"tech_email"` 42 | TechPhone string `json:"tech_phone"` 43 | AvailableVoltageServices []string `json:"available_voltage_services"` 44 | DiverseServingSubstations bool `json:"diverse_serving_substations"` 45 | Property string `json:"property"` 46 | RegionContinent string `json:"region_continent"` 47 | StatusDashboard string `json:"status_dashboard"` 48 | Created time.Time `json:"created"` 49 | Updated time.Time `json:"updated"` 50 | Status string `json:"status"` 51 | Address1 string `json:"address1"` 52 | Address2 string `json:"address2"` 53 | City string `json:"city"` 54 | Country string `json:"country"` 55 | State string `json:"state"` 56 | Zipcode string `json:"zipcode"` 57 | Floor string `json:"floor"` 58 | Suite string `json:"suite"` 59 | Latitude float64 `json:"latitude"` 60 | Longitude float64 `json:"longitude"` 61 | SocialMedia []struct { 62 | Service string `json:"service"` 63 | Identifier string `json:"identifier"` 64 | } `json:"social_media"` 65 | } 66 | 67 | // getFacilityResource returns a pointer to a facilityResource structure 68 | // corresponding to the API JSON response. An error can be returned if 69 | // something went wrong. 70 | func (api *API) getFacilityResource(search map[string]interface{}) (*facilityResource, error) { 71 | // Get the FacilityResource from the API 72 | response, err := api.lookup(facilityNamespace, search) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | // Ask for cleanup once we are done 78 | defer response.Body.Close() 79 | 80 | // Decode what the API has given to us 81 | resource := &facilityResource{} 82 | err = json.NewDecoder(response.Body).Decode(&resource) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | return resource, nil 88 | } 89 | 90 | // GetFacility returns a pointer to a slice of Facility structures that the 91 | // PeeringDB API can provide matching the given search parameters map. If an 92 | // error occurs, the returned error will be non-nil. The returned value can be 93 | // nil if no object could be found. 94 | func (api *API) GetFacility(search map[string]interface{}) (*[]Facility, error) { 95 | // Ask for the all Facility objects 96 | facilyResource, err := api.getFacilityResource(search) 97 | 98 | // Error as occurred while querying the API 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | // Return all Facility objects, will be nil if slice is empty 104 | return &facilyResource.Data, nil 105 | } 106 | 107 | // GetAllFacilities returns a pointer to a slice of Facility structures that 108 | // the PeeringDB API can provide. If an error occurs, the returned error will 109 | // be non-nil. The can be nil if no object could be found. 110 | func (api *API) GetAllFacilities() (*[]Facility, error) { 111 | // Return all Facility objects 112 | return api.GetFacility(nil) 113 | } 114 | 115 | // GetFacilityByID returns a pointer to a Facility structure that matches the 116 | // given ID. If the ID is lesser than 0, it will return nil. The returned error 117 | // will be non-nil if an issue as occurred while trying to query the API. If for 118 | // some reasons the API returns more than one object for the given ID (but it 119 | // must not) only the first will be used for the returned value. 120 | func (api *API) GetFacilityByID(id int) (*Facility, error) { 121 | // No point of looking for the facility with an ID < 0 122 | if id < 0 { 123 | return nil, nil 124 | } 125 | 126 | // Ask for the Facility given it ID 127 | search := make(map[string]interface{}) 128 | search["id"] = id 129 | 130 | // Actually ask for it 131 | facilities, err := api.GetFacility(search) 132 | 133 | // Error as occurred while querying the API 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | // No Facility matching the ID 139 | if len(*facilities) < 1 { 140 | return nil, nil 141 | } 142 | 143 | // Only return the first match, they must be only one match (ID being 144 | // unique) 145 | return &(*facilities)[0], nil 146 | } 147 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "sort" 10 | ) 11 | 12 | const ( 13 | baseAPI = "https://www.peeringdb.com/api/" 14 | facilityNamespace = "fac" 15 | carrierNamespace = "carrier" 16 | carrierFacilityNamespace = "carrierfac" 17 | campusNamespace = "campus" 18 | internetExchangeNamespace = "ix" 19 | internetExchangeFacilityNamespace = "ixfac" 20 | internetExchangeLANNamespace = "ixlan" 21 | internetExchangePrefixNamespace = "ixpfx" 22 | networkNamespace = "net" 23 | networkFacilityNamespace = "netfac" 24 | networkInternetExchangeLANNamepsace = "netixlan" 25 | organizationNamespace = "org" 26 | networkContactNamespace = "poc" 27 | ) 28 | 29 | var ( 30 | // ErrBuildingURL is the error that will be returned if the URL to call the 31 | // API cannot be built as expected. 32 | ErrBuildingURL = errors.New("error while building the URL to call the peeringdb api") 33 | // ErrBuildingRequest is the error that will be returned if the HTTP 34 | // request to call the API cannot be built as expected. 35 | ErrBuildingRequest = errors.New("error while building the request to send to the peeringdb api") 36 | // ErrQueryingAPI is the error that will be returned if there is an issue 37 | // while making the request to the API. 38 | ErrQueryingAPI = errors.New("error while querying peeringdb api") 39 | // ErrRateLimitExceeded is the error that will be returned if the API rate 40 | // limit is exceeded. 41 | ErrRateLimitExceeded = errors.New("rate limit exceeded") 42 | ) 43 | 44 | // API is the structure used to interact with the PeeringDB API. This is the 45 | // main structure of this package. All functions to make API calls are 46 | // associated to this structure. 47 | type API struct { 48 | url string 49 | apiKey string 50 | } 51 | 52 | // NewAPI returns a pointer to a new API structure. It uses the publicly known 53 | // PeeringDB API endpoint. 54 | func NewAPI() *API { 55 | return &API{url: baseAPI} 56 | } 57 | 58 | // NewAPIWithAuth returns a pointer to a new API structure. The API will point 59 | // to the publicly known PeeringDB API endpoint and will use the provided API 60 | // key for authentication while making API calls. 61 | func NewAPIWithAPIKey(apiKey string) *API { 62 | return &API{ 63 | url: baseAPI, 64 | apiKey: apiKey, 65 | } 66 | } 67 | 68 | // NewAPIFromURL returns a pointer to a new API structure from a given URL. If 69 | // the given URL is empty it will use the default PeeringDB API URL. 70 | func NewAPIFromURL(url string) *API { 71 | if url == "" { 72 | return NewAPI() 73 | } 74 | 75 | return &API{url: url} 76 | } 77 | 78 | // NewAPIFromURLWithAPIKey returns a pointer to a new API structure from a given 79 | // URL. If the given URL is empty it will use the default PeeringDB API URL. It 80 | // will use the provided API key for authentication while making API calls. 81 | func NewAPIFromURLWithAPIKey(url, apiKey string) *API { 82 | if url == "" { 83 | return NewAPIWithAPIKey(apiKey) 84 | } 85 | 86 | return &API{ 87 | url: url, 88 | apiKey: apiKey, 89 | } 90 | } 91 | 92 | // formatSearchParameters is used to format parameters for a request. When 93 | // building the search string the keys will be used in the alphabetic order. 94 | func formatSearchParameters(parameters map[string]interface{}) string { 95 | // Nothing in slice, just return empty string 96 | if parameters == nil { 97 | return "" 98 | } 99 | 100 | var search string 101 | var keys []string 102 | 103 | // Get all map keys 104 | for i := range parameters { 105 | keys = append(keys, i) 106 | } 107 | 108 | // Sort the keys slice 109 | sort.Strings(keys) 110 | 111 | // For each element, append it to the request separated by a & symbol. 112 | for _, key := range keys { 113 | search = search + "&" + key + "=" + url.QueryEscape(fmt.Sprintf("%v", parameters[key])) 114 | } 115 | 116 | return search 117 | } 118 | 119 | // formatURL is used to format a URL to make a request on PeeringDB API. 120 | func formatURL(base, namespace string, search map[string]interface{}) string { 121 | return fmt.Sprintf("%s%s?depth=1%s", base, namespace, 122 | formatSearchParameters(search)) 123 | } 124 | 125 | // lookup is used to query the PeeringDB API given a namespace to use and data 126 | // to format the request. It returns an HTTP response that the caller must 127 | // decode with a JSON decoder. 128 | func (api *API) lookup(namespace string, search map[string]interface{}) (*http.Response, error) { 129 | url := formatURL(api.url, namespace, search) 130 | if url == "" { 131 | return nil, ErrBuildingURL 132 | } 133 | 134 | // Prepare the GET request to the API, no need to set a body since 135 | // everything is in the URL 136 | request, err := http.NewRequest("GET", url, nil) 137 | if err != nil { 138 | return nil, ErrBuildingRequest 139 | } 140 | 141 | if api.apiKey != "" { 142 | request.Header.Add("Authorization", fmt.Sprintf("Api-Key %s", api.apiKey)) 143 | } 144 | 145 | // Send the request to the API using a simple HTTP client 146 | client := &http.Client{} 147 | response, err := client.Do(request) 148 | if err != nil { 149 | return nil, ErrQueryingAPI 150 | } 151 | 152 | // Special handling for PeeringDB rate limit 153 | if response.StatusCode == http.StatusTooManyRequests { 154 | return nil, ErrRateLimitExceeded 155 | } 156 | // Generic handling for non-OK responses 157 | if response.StatusCode != http.StatusOK { 158 | body, _ := io.ReadAll(response.Body) 159 | return nil, fmt.Errorf("%s: %s", response.Status, body) 160 | } 161 | 162 | return response, nil 163 | } 164 | 165 | // GetASN is a simplified function to get PeeringDB details about a given AS 166 | // number. It basically gets the Net object matching the AS number. If the AS 167 | // number cannot be found, nil is returned. 168 | func (api *API) GetASN(asn int) (*Network, error) { 169 | search := make(map[string]interface{}) 170 | search["asn"] = asn 171 | 172 | // Actually fetch the Network from PeeringDB 173 | network, err := api.GetNetwork(search) 174 | 175 | // Error, so nil pointer returned 176 | if err != nil { 177 | return nil, err 178 | } 179 | 180 | if len(*network) == 0 { 181 | return nil, fmt.Errorf("no network found for ASN %d", asn) 182 | } 183 | return &(*network)[0], nil 184 | } 185 | -------------------------------------------------------------------------------- /api_test.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import "testing" 4 | 5 | func TestFormatSearchParameters(t *testing.T) { 6 | var searchMap map[string]interface{} 7 | var expected string 8 | var searchParameters string 9 | 10 | // Test for nil map 11 | expected = "" 12 | searchParameters = formatSearchParameters(nil) 13 | if searchParameters != expected { 14 | t.Errorf("formatSearchParameters, want '%s' got '%s'", expected, 15 | searchParameters) 16 | } 17 | 18 | // Test for empty map 19 | searchMap = make(map[string]interface{}) 20 | expected = "" 21 | searchParameters = formatSearchParameters(searchMap) 22 | if searchParameters != expected { 23 | t.Errorf("formatSearchParameters, want '%s' got '%s'", expected, 24 | searchParameters) 25 | } 26 | 27 | // Test one value 28 | searchMap = make(map[string]interface{}) 29 | searchMap["id"] = 10 30 | expected = "&id=10" 31 | searchParameters = formatSearchParameters(searchMap) 32 | if searchParameters != expected { 33 | t.Errorf("formatSearchParameters, want '%s' got '%s'", expected, 34 | searchParameters) 35 | } 36 | 37 | // Test two values 38 | searchMap = make(map[string]interface{}) 39 | searchMap["id"] = 10 40 | searchMap["asn"] = 65536 41 | expected = "&asn=65536&id=10" 42 | searchParameters = formatSearchParameters(searchMap) 43 | if searchParameters != expected { 44 | t.Errorf("formatSearchParameters, want '%s' got '%s'", expected, 45 | searchParameters) 46 | } 47 | } 48 | 49 | func TestFormatURL(t *testing.T) { 50 | var expected string 51 | var url string 52 | 53 | base := "https://www.peeringdb.com/api/" 54 | searchMap := make(map[string]interface{}) 55 | searchMap["id"] = 10 56 | 57 | // Test fac namespace with search parameter 58 | expected = "https://www.peeringdb.com/api/fac?depth=1&id=10" 59 | url = formatURL(base, facilityNamespace, searchMap) 60 | if url != expected { 61 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 62 | } 63 | 64 | // Test ix namespace with search parameter 65 | expected = "https://www.peeringdb.com/api/ix?depth=1&id=10" 66 | url = formatURL(base, internetExchangeNamespace, searchMap) 67 | if url != expected { 68 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 69 | } 70 | 71 | // Test ixfac namespace with search parameter 72 | expected = "https://www.peeringdb.com/api/ixfac?depth=1&id=10" 73 | url = formatURL(base, internetExchangeFacilityNamespace, searchMap) 74 | if url != expected { 75 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 76 | } 77 | 78 | // Test ixlan namespace with search parameter 79 | expected = "https://www.peeringdb.com/api/ixlan?depth=1&id=10" 80 | url = formatURL(base, internetExchangeLANNamespace, searchMap) 81 | if url != expected { 82 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 83 | } 84 | 85 | // Test ixpfx namespace with search parameter 86 | expected = "https://www.peeringdb.com/api/ixpfx?depth=1&id=10" 87 | url = formatURL(base, internetExchangePrefixNamespace, searchMap) 88 | if url != expected { 89 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 90 | } 91 | 92 | // Test net namespace with search parameter 93 | expected = "https://www.peeringdb.com/api/net?depth=1&id=10" 94 | url = formatURL(base, networkNamespace, searchMap) 95 | if url != expected { 96 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 97 | } 98 | 99 | // Test netfac namespace with search parameter 100 | expected = "https://www.peeringdb.com/api/netfac?depth=1&id=10" 101 | url = formatURL(base, networkFacilityNamespace, searchMap) 102 | if url != expected { 103 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 104 | } 105 | 106 | // Test netixlan namespace with search parameter 107 | expected = "https://www.peeringdb.com/api/netixlan?depth=1&id=10" 108 | url = formatURL(base, networkInternetExchangeLANNamepsace, searchMap) 109 | if url != expected { 110 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 111 | } 112 | 113 | // Test org namespace with search parameter 114 | expected = "https://www.peeringdb.com/api/org?depth=1&id=10" 115 | url = formatURL(base, organizationNamespace, searchMap) 116 | if url != expected { 117 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 118 | } 119 | 120 | // Test poc namespace with search parameter 121 | expected = "https://www.peeringdb.com/api/poc?depth=1&id=10" 122 | url = formatURL(base, networkContactNamespace, searchMap) 123 | if url != expected { 124 | t.Errorf("formatURL, want '%s' got '%s'", expected, url) 125 | } 126 | } 127 | 128 | func TestNewAPI(t *testing.T) { 129 | var expectedURL string 130 | 131 | // Test to use the public PeeringDB API 132 | api := NewAPI() 133 | expectedURL = "https://www.peeringdb.com/api/" 134 | if api.url != expectedURL { 135 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 136 | } 137 | } 138 | 139 | func TestNewAPIWithAPIKey(t *testing.T) { 140 | var expectedURL, expectedApiKey string 141 | 142 | // Test to use the public PeeringDB API with authentication 143 | api := NewAPIWithAPIKey("test123") 144 | expectedURL = "https://www.peeringdb.com/api/" 145 | expectedApiKey = "test123" 146 | if api.url != expectedURL { 147 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 148 | } 149 | if api.apiKey != expectedApiKey { 150 | t.Errorf("formatURL, want '%s' got '%s'", expectedApiKey, api.apiKey) 151 | } 152 | } 153 | 154 | func TestNewAPIFromURL(t *testing.T) { 155 | var expectedURL string 156 | var api *API 157 | 158 | // Test to see if an empty string parameter will force to use the public 159 | // PeeringDB API. 160 | api = NewAPIFromURL("") 161 | expectedURL = "https://www.peeringdb.com/api/" 162 | if api.url != expectedURL { 163 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 164 | } 165 | 166 | // Test with 167 | api = NewAPIFromURL("http://localhost/api/") 168 | expectedURL = "http://localhost/api/" 169 | if api.url != expectedURL { 170 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 171 | } 172 | } 173 | 174 | func TestNewAPIFromURLWithAPIKey(t *testing.T) { 175 | var expectedURL, expectedApiKey string 176 | var api *API 177 | 178 | // Test to see if an empty string parameter will force to use the public 179 | // PeeringDB API. 180 | api = NewAPIFromURLWithAPIKey("", "test123") 181 | expectedURL = "https://www.peeringdb.com/api/" 182 | expectedApiKey = "test123" 183 | if api.url != expectedURL { 184 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 185 | } 186 | if api.apiKey != expectedApiKey { 187 | t.Errorf("formatURL, want '%s' got '%s'", expectedApiKey, api.apiKey) 188 | } 189 | 190 | // Test with 191 | api = NewAPIFromURLWithAPIKey("http://localhost/api/", "test123") 192 | expectedURL = "http://localhost/api/" 193 | expectedApiKey = "test123" 194 | if api.url != expectedURL { 195 | t.Errorf("formatURL, want '%s' got '%s'", expectedURL, api.url) 196 | } 197 | if api.apiKey != expectedApiKey { 198 | t.Errorf("formatURL, want '%s' got '%s'", expectedApiKey, api.apiKey) 199 | } 200 | } 201 | 202 | func TestGetASN(t *testing.T) { 203 | api := NewAPI() 204 | expectedASN := 201281 205 | net, err := api.GetASN(expectedASN) 206 | 207 | if err != nil { 208 | t.Fail() 209 | return 210 | } 211 | 212 | if net.ASN != expectedASN { 213 | t.Errorf("GetASN, want ASN '%d' got '%d'", expectedASN, net.ASN) 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /carrier.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // carrierResource is the top-level structure when parsing the JSON output 9 | // from the API. This structure is not used if the Carrier JSON object is 10 | // included as a field in another JSON object. This structure is used only if 11 | // the proper namespace is queried. 12 | type carrierResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []Carrier `json:"data"` 17 | } 18 | 19 | // Carrier is the representation of a network able to provider transport from 20 | // one facility to another. 21 | type Carrier struct { 22 | ID int `json:"id"` 23 | OrganizationID int `json:"org_id"` 24 | OrganizationName string `json:"org_name"` 25 | Organization Organization `json:"organization,omitempty"` 26 | Name string `json:"name"` 27 | AKA string `json:"aka"` 28 | NameLong string `json:"name_long"` 29 | Website string `json:"website"` 30 | Notes string `json:"notes"` 31 | Created time.Time `json:"created"` 32 | Updated time.Time `json:"updated"` 33 | Status string `json:"status"` 34 | SocialMedia []struct { 35 | Service string `json:"service"` 36 | Identifier string `json:"identifier"` 37 | } `json:"social_media"` 38 | } 39 | 40 | // getCarrierResource returns a pointer to a carrierResource structure 41 | // corresponding to the API JSON response. An error can be returned if 42 | // something went wrong. 43 | func (api *API) getCarrierResource(search map[string]interface{}) (*carrierResource, error) { 44 | // Get the CarrierResource from the API 45 | response, err := api.lookup(carrierNamespace, search) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | // Ask for cleanup once we are done 51 | defer response.Body.Close() 52 | 53 | // Decode what the API has given to us 54 | resource := &carrierResource{} 55 | err = json.NewDecoder(response.Body).Decode(&resource) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return resource, nil 61 | } 62 | 63 | // GetCarrier returns a pointer to a slice of Carrier structures that the 64 | // PeeringDB API can provide matching the given search parameters map. If an 65 | // error occurs, the returned error will be non-nil. The returned value can be 66 | // nil if no object could be found. 67 | func (api *API) GetCarrier(search map[string]interface{}) (*[]Carrier, error) { 68 | // Ask for the all Carrier objects 69 | carrierResource, err := api.getCarrierResource(search) 70 | 71 | // Error as occurred while querying the API 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | // Return all Carrier objects, will be nil if slice is empty 77 | return &carrierResource.Data, nil 78 | } 79 | 80 | // GetAllCarriers returns a pointer to a slice of Carrier structures that the 81 | // PeeringDB API can provide. If an error occurs, the returned error will be 82 | // non-nil. The can be nil if no object could be found. 83 | func (api *API) GetAllCarriers() (*[]Carrier, error) { 84 | // Return all Carrier objects 85 | return api.GetCarrier(nil) 86 | } 87 | 88 | // GetCarrierByID returns a pointer to a Carrier structure that matches the 89 | // given ID. If the ID is lesser than 0, it will return nil. The returned 90 | // error will be non-nil if an issue as occurred while trying to query the 91 | // API. If for some reasons the API returns more than one object for the 92 | // given ID (but it must not) only the first will be used for the returned 93 | // value. 94 | func (api *API) GetCarrierByID(id int) (*Carrier, error) { 95 | // No point of looking for the carrier with an ID < 0 96 | if id < 0 { 97 | return nil, nil 98 | } 99 | 100 | // Ask for the Carrier given it ID 101 | search := make(map[string]interface{}) 102 | search["id"] = id 103 | 104 | // Actually ask for it 105 | carriers, err := api.GetCarrier(search) 106 | 107 | // Error as occurred while querying the API 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | // No Carrier matching the ID 113 | if len(*carriers) < 1 { 114 | return nil, nil 115 | } 116 | 117 | // Only return the first match, they must be only one match (ID being 118 | // unique) 119 | return &(*carriers)[0], nil 120 | } 121 | 122 | // carrierFacilityResource is the top-level structure when parsing the JSON 123 | // output from the API. This structure is not used if the CarrierFacility JSON 124 | // object is included as a field in another JSON object. This structure is 125 | // used only if the proper namespace is queried. 126 | type carrierFacilityResource struct { 127 | Meta struct { 128 | Generated float64 `json:"generated,omitempty"` 129 | } `json:"meta"` 130 | Data []CarrierFacility `json:"data"` 131 | } 132 | 133 | // CarrierFacility is a structure used to link an Carrier structure with a 134 | // Facility structure. It helps to know in which facilities a carrier 135 | // operates. 136 | type CarrierFacility struct { 137 | ID int `json:"id"` 138 | Name string `json:"name"` 139 | CarrierID int `json:"carrier_id"` 140 | Carrier Carrier `json:"carrier,omitempty"` 141 | FacilityID int `json:"fac_id"` 142 | Facility Facility `json:"fac,omitempty"` 143 | Created time.Time `json:"created"` 144 | Updated time.Time `json:"updated"` 145 | Status string `json:"status"` 146 | } 147 | 148 | // getCarrierFacilityResource returns a pointer to an carrierFacilityResource 149 | // structure corresponding to the API JSON response. An error can be returned 150 | // if something went wrong. 151 | func (api *API) getCarrierFacilityResource(search map[string]interface{}) (*carrierFacilityResource, error) { 152 | // Get the CarrierFacilityResource from the API 153 | response, err := api.lookup(carrierFacilityNamespace, search) 154 | if err != nil { 155 | return nil, err 156 | } 157 | 158 | // Ask for cleanup once we are done 159 | defer response.Body.Close() 160 | 161 | // Decode what the API has given to us 162 | resource := &carrierFacilityResource{} 163 | err = json.NewDecoder(response.Body).Decode(&resource) 164 | if err != nil { 165 | return nil, err 166 | } 167 | 168 | return resource, nil 169 | } 170 | 171 | // GetCarrierFacility returns a pointer to a slice of CarrierFacility 172 | // structures that the PeeringDB API can provide matching the given search 173 | // parameters map. If an error occurs, the returned error will be non-nil. The 174 | // returned value can be nil if no object could be found. 175 | func (api *API) GetCarrierFacility(search map[string]interface{}) (*[]CarrierFacility, error) { 176 | // Ask for the all CarrierFacility objects 177 | carrierFacilityResource, err := api.getCarrierFacilityResource(search) 178 | 179 | // Error as occurred while querying the API 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | // Return all InternetExchangeFacility objects, will be nil if slice is 185 | // empty 186 | return &carrierFacilityResource.Data, nil 187 | } 188 | 189 | // GetAllCarrierFacilities returns a pointer to a slice of CarrierFacility 190 | // structures that the PeeringDB API can provide. If an error occurs, the 191 | // returned error will be non-nil. The can be nil if no object could be found. 192 | func (api *API) GetAllCarrierFacilities() (*[]CarrierFacility, error) { 193 | // Return all CarrierFacility objects 194 | return api.GetCarrierFacility(nil) 195 | } 196 | 197 | // GetCarrierFacilityByID returns a pointer to a CarrierFacility structure 198 | // that matches the given ID. If the ID is lesser than 0, it will return nil. 199 | // The returned error will be non-nil if an issue as occurred while trying to 200 | // query the API. If for some reasons the API returns more than one object for 201 | // the given ID (but it must not) only the first will be used for the returned 202 | // value. 203 | func (api *API) GetCarrierFacilityByID(id int) (*CarrierFacility, error) { 204 | // No point of looking for the carrier facility with an ID < 0 205 | if id < 0 { 206 | return nil, nil 207 | } 208 | 209 | // Ask for the CarrierFacility given it ID 210 | search := make(map[string]interface{}) 211 | search["id"] = id 212 | 213 | // Actually ask for it 214 | carrierFacilities, err := api.GetCarrierFacility(search) 215 | 216 | // Error as occurred while querying the API 217 | if err != nil { 218 | return nil, err 219 | } 220 | 221 | // No CarrierFacility matching the ID 222 | if len(*carrierFacilities) < 1 { 223 | return nil, nil 224 | } 225 | 226 | // Only return the first match, they must be only one match (ID being 227 | // unique) 228 | return &(*carrierFacilities)[0], nil 229 | } 230 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // networkResource is the top-level structure when parsing the JSON output from 9 | // the API. This structure is not used if the Network JSON object is included 10 | // as a field in another JSON object. This structure is used only if the proper 11 | // namespace is queried. 12 | type networkResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []Network `json:"data"` 17 | } 18 | 19 | // Network is a structure representing a network. Basically, a network is an 20 | // Autonomous System identified by an AS number and other details. It belongs 21 | // to an Organization, contains one or more NetworkContact, and is part of 22 | // several Facility and InternetExchangeLAN. 23 | type Network struct { 24 | ID int `json:"id"` 25 | OrganizationID int `json:"org_id"` 26 | Organization Organization `json:"org,omitempty"` 27 | Name string `json:"name"` 28 | AKA string `json:"aka"` 29 | NameLong string `json:"name_long"` 30 | Website string `json:"website"` 31 | ASN int `json:"asn"` 32 | LookingGlass string `json:"looking_glass"` 33 | RouteServer string `json:"route_server"` 34 | IRRASSet string `json:"irr_as_set"` 35 | InfoType string `json:"info_type"` 36 | InfoTypes []string `json:"info_types"` 37 | InfoPrefixes4 int `json:"info_prefixes4"` 38 | InfoPrefixes6 int `json:"info_prefixes6"` 39 | InfoTraffic string `json:"info_traffic"` 40 | InfoRatio string `json:"info_ratio"` 41 | InfoScope string `json:"info_scope"` 42 | InfoUnicast bool `json:"info_unicast"` 43 | InfoMulticast bool `json:"info_multicast"` 44 | InfoIPv6 bool `json:"info_ipv6"` 45 | InfoNeverViaRouteServers bool `json:"info_never_via_route_servers"` 46 | InternetExchangeCount int `json:"ix_count"` 47 | FacilityCount int `json:"fac_count"` 48 | Notes string `json:"notes"` 49 | NetworkInternetExchangeLANUpdated time.Time `json:"netixlan_updated"` 50 | NetworkFacilityUpdated time.Time `json:"netfac_updated"` 51 | NetworkContactUpdated time.Time `json:"poc_updated"` 52 | PolicyURL string `json:"policy_url"` 53 | PolicyGeneral string `json:"policy_general"` 54 | PolicyLocations string `json:"policy_locations"` 55 | PolicyRatio bool `json:"policy_ratio"` 56 | PolicyContracts string `json:"policy_contracts"` 57 | NetworkFacilitySet []int `json:"netfac_set"` 58 | NetworkInternetExchangeLANSet []int `json:"netixlan_set"` 59 | NetworkContactSet []int `json:"poc_set"` 60 | AllowIXPUpdate bool `json:"allow_ixp_update"` 61 | StatusDashboard string `json:"status_dashboard"` 62 | RIRStatus string `json:"rir_status"` 63 | RIRStatusUpdated time.Time `json:"rir_status_updated"` 64 | Created time.Time `json:"created"` 65 | Updated time.Time `json:"updated"` 66 | Status string `json:"status"` 67 | SocialMedia []struct { 68 | Service string `json:"service"` 69 | Identifier string `json:"identifier"` 70 | } `json:"social_media"` 71 | } 72 | 73 | // getNetworkResource returns a pointer to an networkResource structure 74 | // corresponding to the API JSON response. An error can be returned if 75 | // something went wrong. 76 | func (api *API) getNetworkResource(search map[string]interface{}) (*networkResource, error) { 77 | // Get the NetworkResource from the API 78 | response, err := api.lookup(networkNamespace, search) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | // Ask for cleanup once we are done 84 | defer response.Body.Close() 85 | 86 | // Decode what the API has given to us 87 | resource := &networkResource{} 88 | err = json.NewDecoder(response.Body).Decode(&resource) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | return resource, nil 94 | } 95 | 96 | // GetNetwork returns a pointer to a slice of Network structures that the 97 | // PeeringDB API can provide matching the given search parameters map. If an 98 | // error occurs, the returned error will be non-nil. The returned value can be 99 | // nil if no object could be found. 100 | func (api *API) GetNetwork(search map[string]interface{}) (*[]Network, error) { 101 | // Ask for the all Network objects 102 | networkResource, err := api.getNetworkResource(search) 103 | 104 | // Error as occurred while querying the API 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | // Return all Network objects, will be nil if slice is empty 110 | return &networkResource.Data, nil 111 | } 112 | 113 | // GetAllNetworks returns a pointer to a slice of Network structures that the 114 | // PeeringDB API can provide. If an error occurs, the returned error will be 115 | // non-nil. The can be nil if no object could be found. 116 | func (api *API) GetAllNetworks() (*[]Network, error) { 117 | // Return all Network objects 118 | return api.GetNetwork(nil) 119 | } 120 | 121 | // GetNetworkByID returns a pointer to a Network structure that matches the 122 | // given ID. If the ID is lesser than 0, it will return nil. The returned error 123 | // will be non-nil if an issue as occurred while trying to query the API. If for 124 | // some reasons the API returns more than one object for the given ID (but it 125 | // must not) only the first will be used for the returned value. 126 | func (api *API) GetNetworkByID(id int) (*Network, error) { 127 | // No point of looking for the network with an ID < 0 128 | if id < 0 { 129 | return nil, nil 130 | } 131 | 132 | // Ask for the Network given it ID 133 | search := make(map[string]interface{}) 134 | search["id"] = id 135 | 136 | // Actually ask for it 137 | networks, err := api.GetNetwork(search) 138 | 139 | // Error as occurred while querying the API 140 | if err != nil { 141 | return nil, err 142 | } 143 | 144 | // No Network matching the ID 145 | if len(*networks) < 1 { 146 | return nil, nil 147 | } 148 | 149 | // Only return the first match, they must be only one match (ID being 150 | // unique) 151 | return &(*networks)[0], nil 152 | } 153 | 154 | // networkFacilityResource is the top-level structure when parsing the JSON 155 | // output from the API. This structure is not used if the NetFacility JSON 156 | // object is included as a field in another JSON object. This structure is used 157 | // only if the proper namespace is queried. 158 | type networkFacilityResource struct { 159 | Meta struct { 160 | Generated float64 `json:"generated,omitempty"` 161 | } `json:"meta"` 162 | Data []NetworkFacility `json:"data"` 163 | } 164 | 165 | // NetworkFacility is a structure used to link a Network with a Facility. It 166 | // helps to know where a network is located (it can be in several facilities). 167 | // For example, it can be used to search common facilities between several 168 | // networks to know where they can interconnect themselves directly. 169 | type NetworkFacility struct { 170 | ID int `json:"id"` 171 | Name string `json:"name"` 172 | City string `json:"city"` 173 | Country string `json:"country"` 174 | NetworkID int `json:"net_id"` 175 | Network Network `json:"net,omitempty"` 176 | FacilityID int `json:"fac_id"` 177 | Facility Facility `json:"fac,omitempty"` 178 | LocalASN int `json:"local_asn"` 179 | Created time.Time `json:"created"` 180 | Updated time.Time `json:"updated"` 181 | Status string `json:"status"` 182 | } 183 | 184 | // getNetworkFacilityResource returns a pointer to an networkFacilityResource 185 | // structure corresponding to the API JSON response. An error can be returned 186 | // if something went wrong. 187 | func (api *API) getNetworkFacilityResource(search map[string]interface{}) (*networkFacilityResource, error) { 188 | // Get the NetworkFacilityResource from the API 189 | response, err := api.lookup(networkFacilityNamespace, search) 190 | if err != nil { 191 | return nil, err 192 | } 193 | 194 | // Ask for cleanup once we are done 195 | defer response.Body.Close() 196 | 197 | // Decode what the API has given to us 198 | resource := &networkFacilityResource{} 199 | err = json.NewDecoder(response.Body).Decode(&resource) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | return resource, nil 205 | } 206 | 207 | // GetNetworkFacility returns a pointer to a slice of NetworkFacility 208 | // structures that the PeeringDB API can provide matching the given search 209 | // parameters map. If an error occurs, the returned error will be non-nil. The 210 | // returned value can be nil if no object could be found. 211 | func (api *API) GetNetworkFacility(search map[string]interface{}) (*[]NetworkFacility, error) { 212 | // Ask for the all NetworkFacility objects 213 | networkFacilityResource, err := api.getNetworkFacilityResource(search) 214 | 215 | // Error as occurred while querying the API 216 | if err != nil { 217 | return nil, err 218 | } 219 | 220 | // Return all NetworkFacility objects, will be nil if slice is empty 221 | return &networkFacilityResource.Data, nil 222 | } 223 | 224 | // GetAllNetworkFacilities returns a pointer to a slice of NetworkFacility 225 | // structures that the PeeringDB API can provide. If an error occurs, the 226 | // returned error will be non-nil. The can be nil if no object could be found. 227 | func (api *API) GetAllNetworkFacilities() (*[]NetworkFacility, error) { 228 | // Return all NetFacility objects 229 | return api.GetNetworkFacility(nil) 230 | } 231 | 232 | // GetNetworkFacilityByID returns a pointer to a NetworkFacility structure that 233 | // matches the given ID. If the ID is lesser than 0, it will return nil. The 234 | // returned error will be non-nil if an issue as occurred while trying to query 235 | // the API. If for some reasons the API returns more than one object for the 236 | // given ID (but it must not) only the first will be used for the returned 237 | // value. 238 | func (api *API) GetNetworkFacilityByID(id int) (*NetworkFacility, error) { 239 | // No point of looking for the network facility with an ID < 0 240 | if id < 0 { 241 | return nil, nil 242 | } 243 | 244 | // Ask for the NetworkFacility given it ID 245 | search := make(map[string]interface{}) 246 | search["id"] = id 247 | 248 | // Actually ask for it 249 | networkFacilities, err := api.GetNetworkFacility(search) 250 | 251 | // Error as occurred while querying the API 252 | if err != nil { 253 | return nil, err 254 | } 255 | 256 | // No NetworkFacility matching the ID 257 | if len(*networkFacilities) < 1 { 258 | return nil, nil 259 | } 260 | 261 | // Only return the first match, they must be only one match (ID being 262 | // unique) 263 | return &(*networkFacilities)[0], nil 264 | } 265 | 266 | // networkInternetExchangeLANResource is the top-level structure when parsing 267 | // the JSON output from the API. This structure is not used if the 268 | // NetworkInternetExchangeLAN JSON object is included as a field in another 269 | // JSON object. This structure is used only if the proper namespace is queried. 270 | type networkInternetExchangeLANResource struct { 271 | Meta struct { 272 | Generated float64 `json:"generated,omitempty"` 273 | } `json:"meta"` 274 | Data []NetworkInternetExchangeLAN `json:"data"` 275 | } 276 | 277 | // NetworkInternetExchangeLAN is a structure allowing to know to which 278 | // InternetExchangeLAN a network is connected. It can be used, for example, to 279 | // know what are the common Internet exchange LANs between several networks. 280 | type NetworkInternetExchangeLAN struct { 281 | ID int `json:"id"` 282 | NetworkID int `json:"net_id"` 283 | Network Network `json:"net,omitempty"` 284 | InternetExchangeID int `json:"ix_id"` 285 | InternetExchange InternetExchange `json:"ix,omitempty"` 286 | Name string `json:"name"` 287 | InternetExchangeLANID int `json:"ixlan_id"` 288 | InternetExchangeLAN InternetExchangeLAN `json:"ixlan,omitempty"` 289 | Notes string `json:"notes"` 290 | Speed int `json:"speed"` 291 | ASN int `json:"asn"` 292 | IPAddr4 string `json:"ipaddr4"` 293 | IPAddr6 string `json:"ipaddr6"` 294 | IsRSPeer bool `json:"is_rs_peer"` 295 | BFDSupport bool `json:"bfd_support"` 296 | Operational bool `json:"operational"` 297 | NetworkSideID int `json:"net_side_id"` 298 | InternetExchangeSideID int `json:"ix_side_id"` 299 | Created time.Time `json:"created"` 300 | Updated time.Time `json:"updated"` 301 | Status string `json:"status"` 302 | } 303 | 304 | // getNetworkInternetExchangeLANResource returns a pointer to an 305 | // networkInternetExchangeLANResource structure corresponding to the API JSON 306 | // response. An error can be returned if something went wrong. 307 | func (api *API) getNetworkInternetExchangeLANResource(search map[string]interface{}) (*networkInternetExchangeLANResource, error) { 308 | // Get the NetworkInternetExchangeLANResource from the API 309 | response, err := api.lookup(networkInternetExchangeLANNamepsace, search) 310 | if err != nil { 311 | return nil, err 312 | } 313 | 314 | // Ask for cleanup once we are done 315 | defer response.Body.Close() 316 | 317 | // Decode what the API has given to us 318 | resource := &networkInternetExchangeLANResource{} 319 | err = json.NewDecoder(response.Body).Decode(&resource) 320 | if err != nil { 321 | return nil, err 322 | } 323 | 324 | return resource, nil 325 | } 326 | 327 | // GetNetworkInternetExchangeLAN returns a pointer to a slice of 328 | // NetworkInternetExchangeLAN structures that the PeeringDB API can provide 329 | // matching the given search parameters map. If an error occurs, the returned 330 | // error will be non-nil. The returned value can be nil if no object could be 331 | // found. 332 | func (api *API) GetNetworkInternetExchangeLAN(search map[string]interface{}) (*[]NetworkInternetExchangeLAN, error) { 333 | // Ask for the all NetInternetExchangeLAN objects 334 | networkInternetExchangeLANResource, err := api.getNetworkInternetExchangeLANResource(search) 335 | 336 | // Error as occurred while querying the API 337 | if err != nil { 338 | return nil, err 339 | } 340 | 341 | // Return all NetInternetExchangeLAN objects, will be nil if slice is empty 342 | return &networkInternetExchangeLANResource.Data, nil 343 | } 344 | 345 | // GetAllNetworkInternetExchangeLANs returns a pointer to a slice of 346 | // NetworkInternetExchangeLAN structures that the PeeringDB API can provide. If 347 | // an error occurs, the returned error will be non-nil. The can be nil if no 348 | // object could be found. 349 | func (api *API) GetAllNetworkInternetExchangeLANs() (*[]NetworkInternetExchangeLAN, error) { 350 | // Return all NetworkInternetExchangeLAN objects 351 | return api.GetNetworkInternetExchangeLAN(nil) 352 | } 353 | 354 | // GetNetworkInternetExchangeLANByID returns a pointer to a 355 | // NetworkInternetExchangeLAN structure that matches the given ID. If the ID is 356 | // lesser than 0, it will return nil. The returned error will be non-nil if an 357 | // issue as occurred while trying to query the API. If for some reasons the API 358 | // returns more than one object for the given ID (but it must not) only the 359 | // first will be used for the returned value. 360 | func (api *API) GetNetworkInternetExchangeLANByID(id int) (*NetworkInternetExchangeLAN, error) { 361 | // No point of looking for the Internet exchange LAN with an ID < 0 362 | if id < 0 { 363 | return nil, nil 364 | } 365 | 366 | // Ask for the NetworkInternetExchangeLAN given it ID 367 | search := make(map[string]interface{}) 368 | search["id"] = id 369 | 370 | // Actually ask for it 371 | networkInternetExchangeLANs, err := api.GetNetworkInternetExchangeLAN(search) 372 | 373 | // Error as occurred while querying the API 374 | if err != nil { 375 | return nil, err 376 | } 377 | 378 | // No NetworkInternetExchangeLAN matching the ID 379 | if len(*networkInternetExchangeLANs) < 1 { 380 | return nil, nil 381 | } 382 | 383 | // Only return the first match, they must be only one match (ID being 384 | // unique) 385 | return &(*networkInternetExchangeLANs)[0], nil 386 | } 387 | -------------------------------------------------------------------------------- /ix.go: -------------------------------------------------------------------------------- 1 | package peeringdb 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // internetExchangeResource is the top-level structure when parsing the JSON 9 | // output from the API. This structure is not used if the InternetExchange JSON 10 | // object is included as a field in another JSON object. This structure is used 11 | // only if the proper namespace is queried. 12 | type internetExchangeResource struct { 13 | Meta struct { 14 | Generated float64 `json:"generated,omitempty"` 15 | } `json:"meta"` 16 | Data []InternetExchange `json:"data"` 17 | } 18 | 19 | // InternetExchange is a structure representing an Internet exchange point. It 20 | // is directly linked to the Organization that manage the IX. 21 | type InternetExchange struct { 22 | ID int `json:"id"` 23 | OrganizationID int `json:"org_id"` 24 | Organization Organization `json:"org,omitempty"` 25 | Name string `json:"name"` 26 | AKA string `json:"aka"` 27 | NameLong string `json:"name_long"` 28 | City string `json:"city"` 29 | Country string `json:"country"` 30 | RegionContinent string `json:"region_continent"` 31 | Media string `json:"media"` 32 | Notes string `json:"notes"` 33 | ProtoUnicast bool `json:"proto_unicast"` 34 | ProtoMulticast bool `json:"proto_multicast"` 35 | ProtoIPv6 bool `json:"proto_ipv6"` 36 | Website string `json:"website"` 37 | URLStats string `json:"url_stats"` 38 | TechEmail string `json:"tech_email"` 39 | TechPhone string `json:"tech_phone"` 40 | PolicyEmail string `json:"policy_email"` 41 | PolicyPhone string `json:"policy_phone"` 42 | SalesPhone string `json:"sales_phone"` 43 | SalesEmail string `json:"sales_email"` 44 | FacilitySet []int `json:"fac_set"` 45 | InternetExchangeLANSet []int `json:"ixlan_set"` 46 | NetworkCount int `json:"net_count"` 47 | FacilityCount int `json:"fac_count"` 48 | IxfNetCount int `json:"ixf_net_count"` 49 | IxfLastImport time.Time `json:"ixf_last_import"` 50 | IxfImportRequest time.Time `json:"ixf_import_request"` 51 | IxfImportRequestStatus string `json:"ixf_import_request_status"` 52 | ServiceLevel string `json:"service_level"` 53 | Terms string `json:"terms"` 54 | StatusDashboard string `json:"status_dashboard"` 55 | Created time.Time `json:"created"` 56 | Updated time.Time `json:"updated"` 57 | Status string `json:"status"` 58 | SocialMedia []struct { 59 | Service string `json:"service"` 60 | Identifier string `json:"identifier"` 61 | } `json:"social_media"` 62 | } 63 | 64 | // getInternetExchangeResource returns a pointer to an internetExchangeResource 65 | // structure corresponding to the API JSON response. An error can be returned 66 | // if something went wrong. 67 | func (api *API) getInternetExchangeResource(search map[string]interface{}) (*internetExchangeResource, error) { 68 | // Get the InternetExchangeResource from the API 69 | response, err := api.lookup(internetExchangeNamespace, search) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | // Ask for cleanup once we are done 75 | defer response.Body.Close() 76 | 77 | // Decode what the API has given to us 78 | resource := &internetExchangeResource{} 79 | err = json.NewDecoder(response.Body).Decode(&resource) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | return resource, nil 85 | } 86 | 87 | // GetInternetExchange returns a pointer to a slice of InternetExchange 88 | // structures that the PeeringDB API can provide matching the given search 89 | // parameters map. If an error occurs, the returned error will be non-nil. The 90 | // returned value can be nil if no object could be found. 91 | func (api *API) GetInternetExchange(search map[string]interface{}) (*[]InternetExchange, error) { 92 | // Ask for the all InternetExchange objects 93 | internetExchangeResource, err := api.getInternetExchangeResource(search) 94 | 95 | // Error as occurred while querying the API 96 | if err != nil { 97 | return nil, err 98 | } 99 | 100 | // Return all InternetExchange objects, will be nil if slice is empty 101 | return &internetExchangeResource.Data, nil 102 | } 103 | 104 | // GetAllInternetExchanges returns a pointer to a slice of InternetExchange 105 | // structures that the PeeringDB API can provide. If an error occurs, the 106 | // returned error will be non-nil. The can be nil if no object could be found. 107 | func (api *API) GetAllInternetExchanges() (*[]InternetExchange, error) { 108 | // Return all InternetExchange objects 109 | return api.GetInternetExchange(nil) 110 | } 111 | 112 | // GetInternetExchangeByID returns a pointer to a InternetExchange structure 113 | // that matches the given ID. If the ID is lesser than 0, it will return nil. 114 | // The returned error will be non-nil if an issue as occurred while trying to 115 | // query the API. If for some reasons the API returns more than one object for 116 | // the given ID (but it must not) only the first will be used for the returned 117 | // value. 118 | func (api *API) GetInternetExchangeByID(id int) (*InternetExchange, error) { 119 | // No point of looking for the Internet exchange with an ID < 0 120 | if id < 0 { 121 | return nil, nil 122 | } 123 | 124 | // Ask for the InternetExchange given it ID 125 | search := make(map[string]interface{}) 126 | search["id"] = id 127 | 128 | // Actually ask for it 129 | internetExchanges, err := api.GetInternetExchange(search) 130 | 131 | // Error as occurred while querying the API 132 | if err != nil { 133 | return nil, err 134 | } 135 | 136 | // No InternetExchange matching the ID 137 | if len(*internetExchanges) < 1 { 138 | return nil, nil 139 | } 140 | 141 | // Only return the first match, they must be only one match (ID being 142 | // unique) 143 | return &(*internetExchanges)[0], nil 144 | } 145 | 146 | // internetExchangeLANResource is the top-level structure when parsing the JSON 147 | // output from the API. This structure is not used if the InternetExchangeLAN 148 | // JSON object is included as a field in another JSON object. This structure is 149 | // used only if the proper namespace is queried. 150 | type internetExchangeLANResource struct { 151 | Meta struct { 152 | Generated float64 `json:"generated,omitempty"` 153 | } `json:"meta"` 154 | Data []InternetExchangeLAN `json:"data"` 155 | } 156 | 157 | // InternetExchangeLAN is a structure representing the one of the network (LAN) 158 | // of an Internet exchange points. It contains details about the LAN like the 159 | // MTU, VLAN support, etc. 160 | type InternetExchangeLAN struct { 161 | ID int `json:"id"` 162 | InternetExchangeID int `json:"ix_id"` 163 | InternetExchange InternetExchange `json:"ix,omitempty"` 164 | Name string `json:"name"` 165 | Description string `json:"descr"` 166 | MTU int `json:"mtu"` 167 | Dot1QSupport bool `json:"dot1q_support"` 168 | RouteServerASN int `json:"rs_asn"` 169 | ARPSponge string `json:"arp_sponge"` 170 | NetworkSet []int `json:"net_set"` 171 | InternetExchangePrefixSet []int `json:"ixpfx_set"` 172 | IXFIXPMemberListURL string `json:"ixf_ixp_member_list_url"` 173 | IXFIXPMemberListURLVisible string `json:"ixf_ixp_member_list_url_visible"` 174 | IXFIXPImportEnabled bool `json:"ixf_ixp_import_enabled"` 175 | Created time.Time `json:"created"` 176 | Updated time.Time `json:"updated"` 177 | Status string `json:"status"` 178 | } 179 | 180 | // getInternetExchangeLANResource returns a pointer to an 181 | // internetExchangeLANResource structure corresponding to the API JSON 182 | // response. An error can be returned if something went wrong. 183 | func (api *API) getInternetExchangeLANResource(search map[string]interface{}) (*internetExchangeLANResource, error) { 184 | // Get the InternetExchangeLANResource from the API 185 | response, err := api.lookup(internetExchangeLANNamespace, search) 186 | if err != nil { 187 | return nil, err 188 | } 189 | 190 | // Ask for cleanup once we are done 191 | defer response.Body.Close() 192 | 193 | // Decode what the API has given to us 194 | resource := &internetExchangeLANResource{} 195 | err = json.NewDecoder(response.Body).Decode(&resource) 196 | if err != nil { 197 | return nil, err 198 | } 199 | 200 | return resource, nil 201 | } 202 | 203 | // GetInternetExchangeLAN returns a pointer to a slice of InternetExchangeLAN 204 | // structures that the PeeringDB API can provide matching the given search 205 | // parameters map. If an error occurs, the returned error will be non-nil. The 206 | // returned value can be nil if no object could be found. 207 | func (api *API) GetInternetExchangeLAN(search map[string]interface{}) (*[]InternetExchangeLAN, error) { 208 | // Ask for the all InternetExchangeLAN objects 209 | internetExchangeLANResource, err := api.getInternetExchangeLANResource(search) 210 | 211 | // Error as occurred while querying the API 212 | if err != nil { 213 | return nil, err 214 | } 215 | 216 | // Return all InternetExchangeLAN objects, will be nil if slice is empty 217 | return &internetExchangeLANResource.Data, nil 218 | } 219 | 220 | // GetAllInternetExchangeLANs returns a pointer to a slice of 221 | // InternetExchangeLAN structures that the PeeringDB API can provide. If an 222 | // error occurs, the returned error will be non-nil. The can be nil if no 223 | // object could be found. 224 | func (api *API) GetAllInternetExchangeLANs() (*[]InternetExchangeLAN, error) { 225 | // Return all InternetExchangeLAN objects 226 | return api.GetInternetExchangeLAN(nil) 227 | } 228 | 229 | // GetInternetExchangeLANByID returns a pointer to a InternetExchangeLAN 230 | // structure that matches the given ID. If the ID is lesser than 0, it will 231 | // return nil. The returned error will be non-nil if an issue as occurred while 232 | // trying to query the API. If for some reasons the API returns more than one 233 | // object for the given ID (but it must not) only the first will be used for 234 | // the returned value. 235 | func (api *API) GetInternetExchangeLANByID(id int) (*InternetExchangeLAN, error) { 236 | // No point of looking for the Internet exchange LAN with an ID < 0 237 | if id < 0 { 238 | return nil, nil 239 | } 240 | 241 | // Ask for the InternetExchangeLAN given it ID 242 | search := make(map[string]interface{}) 243 | search["id"] = id 244 | 245 | // Actually ask for it 246 | ixLANs, err := api.GetInternetExchangeLAN(search) 247 | 248 | // Error as occurred while querying the API 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | // No InternetExchangeLAN matching the ID 254 | if len(*ixLANs) < 1 { 255 | return nil, nil 256 | } 257 | 258 | // Only return the first match, they must be only one match (ID being 259 | // unique) 260 | return &(*ixLANs)[0], nil 261 | } 262 | 263 | // internetExchangePrefixResource is the top-level structure when parsing the 264 | // JSON output from the API. This structure is not used if the 265 | // InternetExchangePrefix JSON object is included as a field in another JSON 266 | // object. This structure is used only if the proper namespace is queried. 267 | type internetExchangePrefixResource struct { 268 | Meta struct { 269 | Generated float64 `json:"generated,omitempty"` 270 | } `json:"meta"` 271 | Data []InternetExchangePrefix `json:"data"` 272 | } 273 | 274 | // InternetExchangePrefix is a structure representing the prefix used by an 275 | // Internet exchange point. It is directly linked to an InternetExchangeLAN. 276 | type InternetExchangePrefix struct { 277 | ID int `json:"id"` 278 | InternetExchangeLANID int `json:"ixlan_id"` 279 | InternetExchangeLAN InternetExchangeLAN `json:"ixlan,omitempty"` 280 | Protocol string `json:"protocol"` 281 | Prefix string `json:"prefix"` 282 | InDFZ bool `json:"in_dfz"` 283 | Created time.Time `json:"created"` 284 | Updated time.Time `json:"updated"` 285 | Status string `json:"status"` 286 | } 287 | 288 | // getInternetExchangePrefixResource returns a pointer to an 289 | // internetExchangePrefixResource structure corresponding to the API JSON 290 | // response. An error can be returned if something went wrong. 291 | func (api *API) getInternetExchangePrefixResource(search map[string]interface{}) (*internetExchangePrefixResource, error) { 292 | // Get the InternetExchangePrefixResource from the API 293 | response, err := api.lookup(internetExchangePrefixNamespace, search) 294 | if err != nil { 295 | return nil, err 296 | } 297 | 298 | // Ask for cleanup once we are done 299 | defer response.Body.Close() 300 | 301 | // Decode what the API has given to us 302 | resource := &internetExchangePrefixResource{} 303 | err = json.NewDecoder(response.Body).Decode(&resource) 304 | if err != nil { 305 | return nil, err 306 | } 307 | 308 | return resource, nil 309 | } 310 | 311 | // GetInternetExchangePrefix returns a pointer to a slice of 312 | // InternetExchangePrefix structures that the PeeringDB API can provide 313 | // matching the given search parameters map. If an error occurs, the returned 314 | // error will be non-nil. The returned value can be nil if no object could be 315 | // found. 316 | func (api *API) GetInternetExchangePrefix(search map[string]interface{}) (*[]InternetExchangePrefix, error) { 317 | // Ask for the all InternetExchangePrefix objects 318 | internetExchangePrefixResource, err := api.getInternetExchangePrefixResource(search) 319 | 320 | // Error as occurred while querying the API 321 | if err != nil { 322 | return nil, err 323 | } 324 | 325 | // Return all InternetExchangePrefix objects, will be nil if slice is empty 326 | return &internetExchangePrefixResource.Data, nil 327 | } 328 | 329 | // GetAllInternetExchangePrefixes returns a pointer to a slice of 330 | // InternetExchangePrefix structures that the PeeringDB API can provide. If an 331 | // error occurs, the returned error will be non-nil. The can be nil if no 332 | // object could be found. 333 | func (api *API) GetAllInternetExchangePrefixes() (*[]InternetExchangePrefix, error) { 334 | // Return all InternetExchangePrefix objects 335 | return api.GetInternetExchangePrefix(nil) 336 | } 337 | 338 | // GetInternetExchangePrefixByID returns a pointer to a InternetExchangePrefix 339 | // structure that matches the given ID. If the ID is lesser than 0, it will 340 | // return nil. The returned error will be non-nil if an issue as occurred while 341 | // trying to query the API. If for some reasons the API returns more than one 342 | // object for the given ID (but it must not) only the first will be used for 343 | // the returned value. 344 | func (api *API) GetInternetExchangePrefixByID(id int) (*InternetExchangePrefix, error) { 345 | // No point of looking for the Internet exchange prefix with an ID < 0 346 | if id < 0 { 347 | return nil, nil 348 | } 349 | 350 | // Ask for the InternetExchangePrefix given it ID 351 | search := make(map[string]interface{}) 352 | search["id"] = id 353 | 354 | // Actually ask for it 355 | ixPrefixes, err := api.GetInternetExchangePrefix(search) 356 | 357 | // Error as occurred while querying the API 358 | if err != nil { 359 | return nil, err 360 | } 361 | 362 | // No InternetExchangePrefix matching the ID 363 | if len(*ixPrefixes) < 1 { 364 | return nil, nil 365 | } 366 | 367 | // Only return the first match, they must be only one match (ID being 368 | // unique) 369 | return &(*ixPrefixes)[0], nil 370 | } 371 | 372 | // internetExchangeFacilityResource is the top-level structure when parsing the 373 | // JSON output from the API. This structure is not used if the 374 | // InternetExchangeFacility JSON object is included as a field in another JSON 375 | // object. This structure is used only if the proper namespace is queried. 376 | type internetExchangeFacilityResource struct { 377 | Meta struct { 378 | Generated float64 `json:"generated,omitempty"` 379 | } `json:"meta"` 380 | Data []InternetExchangeFacility `json:"data"` 381 | } 382 | 383 | // InternetExchangeFacility is a structure used to link an InternetExchange 384 | // structure with a Facility structure. It helps to know where an Internet 385 | // exchange points can be found, or what Internet exchange points can be found 386 | // in a given facility. 387 | type InternetExchangeFacility struct { 388 | ID int `json:"id"` 389 | Name string `json:"name"` 390 | City string `json:"city"` 391 | Country string `json:"country"` 392 | InternetExchangeID int `json:"ix_id"` 393 | InternetExchange InternetExchange `json:"ix,omitempty"` 394 | FacilityID int `json:"fac_id"` 395 | Facility Facility `json:"fac,omitempty"` 396 | Created time.Time `json:"created"` 397 | Updated time.Time `json:"updated"` 398 | Status string `json:"status"` 399 | } 400 | 401 | // getInternetExchangeFacilityResource returns a pointer to an 402 | // internetExchangeFacilityResource structure corresponding to the API JSON 403 | // response. An error can be returned if something went wrong. 404 | func (api *API) getInternetExchangeFacilityResource(search map[string]interface{}) (*internetExchangeFacilityResource, error) { 405 | // Get the InternetExchangeFacilityResource from the API 406 | response, err := api.lookup(internetExchangeFacilityNamespace, search) 407 | if err != nil { 408 | return nil, err 409 | } 410 | 411 | // Ask for cleanup once we are done 412 | defer response.Body.Close() 413 | 414 | // Decode what the API has given to us 415 | resource := &internetExchangeFacilityResource{} 416 | err = json.NewDecoder(response.Body).Decode(&resource) 417 | if err != nil { 418 | return nil, err 419 | } 420 | 421 | return resource, nil 422 | } 423 | 424 | // GetInternetExchangeFacility returns a pointer to a slice of 425 | // InternetExchangeFacility structures that the PeeringDB API can provide 426 | // matching the given search parameters map. If an error occurs, the returned 427 | // error will be non-nil. The returned value can be nil if no object could be 428 | // found. 429 | func (api *API) GetInternetExchangeFacility(search map[string]interface{}) (*[]InternetExchangeFacility, error) { 430 | // Ask for the all InternetExchangeFacility objects 431 | internetExchangeFacilityResource, err := api.getInternetExchangeFacilityResource(search) 432 | 433 | // Error as occurred while querying the API 434 | if err != nil { 435 | return nil, err 436 | } 437 | 438 | // Return all InternetExchangeFacility objects, will be nil if slice is 439 | // empty 440 | return &internetExchangeFacilityResource.Data, nil 441 | } 442 | 443 | // GetAllInternetExchangeFacilities returns a pointer to a slice of 444 | // InternetExchangeFacility structures that the PeeringDB API can provide. If 445 | // an error occurs, the returned error will be non-nil. The can be nil if no 446 | // object could be found. 447 | func (api *API) GetAllInternetExchangeFacilities() (*[]InternetExchangeFacility, error) { 448 | // Return all InternetExchangeFacility objects 449 | return api.GetInternetExchangeFacility(nil) 450 | } 451 | 452 | // GetInternetExchangeFacilityByID returns a pointer to a 453 | // InternetExchangeFacility structure that matches the given ID. If the ID is 454 | // lesser than 0, it will return nil. The returned error will be non-nil if an 455 | // issue as occurred while trying to query the API. If for some reasons the API 456 | // returns more than one object for the given ID (but it must not) only the 457 | // first will be used for the returned value. 458 | func (api *API) GetInternetExchangeFacilityByID(id int) (*InternetExchangeFacility, error) { 459 | // No point of looking for the Internet exchange facility with an ID < 0 460 | if id < 0 { 461 | return nil, nil 462 | } 463 | 464 | // Ask for the InternetExchangeFacility given it ID 465 | search := make(map[string]interface{}) 466 | search["id"] = id 467 | 468 | // Actually ask for it 469 | ixFacilities, err := api.GetInternetExchangeFacility(search) 470 | 471 | // Error as occurred while querying the API 472 | if err != nil { 473 | return nil, err 474 | } 475 | 476 | // No InternetExchangeFacility matching the ID 477 | if len(*ixFacilities) < 1 { 478 | return nil, nil 479 | } 480 | 481 | // Only return the first match, they must be only one match (ID being 482 | // unique) 483 | return &(*ixFacilities)[0], nil 484 | } 485 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | --------------------------------------------------------------------------------