From f90fb460404537da1a8ca448197e268049351f60 Mon Sep 17 00:00:00 2001 From: Phil Bayfield Date: Fri, 14 Jan 2011 06:47:54 +0800 Subject: [PATCH] improved the error handling a bit --- example.go | 8 +- libgeo.go | 264 +++++++++++++++++++++++++++-------------------------- 2 files changed, 139 insertions(+), 133 deletions(-) diff --git a/example.go b/example.go index c2179d4..766586e 100644 --- a/example.go +++ b/example.go @@ -11,7 +11,7 @@ func main() { // Check the number of arguments if flag.NArg() < 2 { - fmt.Printf("usage: main DBFILE IPADDRESS\n"); + fmt.Printf("usage: main DBFILE IPADDRESS\n") return } @@ -20,9 +20,9 @@ func main() { ipAddr := flag.Arg(1) // Load the database file, exit on failure - gi := libgeo.Load(dbFile) - if gi == nil { - fmt.Printf("GI IS NULL!\n"); + gi, err := libgeo.Load(dbFile) + if err != nil { + fmt.Printf("Error: " + err.String() + "\n") return } diff --git a/libgeo.go b/libgeo.go index cf3f019..e86aa21 100644 --- a/libgeo.go +++ b/libgeo.go @@ -37,145 +37,149 @@ import ( // Globals (const arrays that will be initialized inside init()) var ( countryCode = []string{ - "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR", - "AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ", - "BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF", - "CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ", - "DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI", - "FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL", - "GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR", - "HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP", - "KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC", - "LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK", - "ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY", - "MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM", - "PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY", - "QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ", - "SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG", - "TH","TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW","TZ","UA","UG", - "UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE", - "YT","RS","ZA","ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE","BL", + "--", "AP", "EU", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", + "AS", "AT", "AU", "AW", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", + "BM", "BN", "BO", "BR", "BS", "BT", "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CF", + "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CR", "CU", "CV", "CX", "CY", "CZ", + "DE", "DJ", "DK", "DM", "DO", "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", + "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GL", + "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", "HR", + "HT", "HU", "ID", "IE", "IL", "IN", "IO", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", + "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", + "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "MH", "MK", + "ML", "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", + "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", "NU", "NZ", "OM", + "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", "PW", "PY", + "QA", "RE", "RO", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", + "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TC", "TD", "TF", "TG", + "TH", "TJ", "TK", "TM", "TN", "TO", "TL", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", + "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU", "WF", "WS", "YE", + "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1", "AX", "GG", "IM", "JE", "BL", "MF"} countryName = []string{ - "N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates", - "Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia", - "Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa", - "Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina", - "Barbados","Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain", - "Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas", - "Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada", - "Cocos (Keeling) Islands","Congo, The Democratic Republic of the", - "Central African Republic","Congo","Switzerland","Cote D'Ivoire", - "Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba", - "Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany", - "Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador", - "Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland", - "Fiji","Falkland Islands (Malvinas)","Micronesia, Federated States of", - "Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom", - "Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia", - "Guinea","Guadeloupe","Equatorial Guinea","Greece", - "South Georgia and the South Sandwich Islands","Guatemala","Guam", - "Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands", - "Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India", - "British Indian Ocean Territory","Iraq","Iran, Islamic Republic of", - "Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia", - "Kiribati","Comoros","Saint Kitts and Nevis", - "Korea, Democratic People's Republic of","Korea, Republic of","Kuwait", - "Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon", - "Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania", - "Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco", - "Moldova, Republic of","Madagascar","Marshall Islands", - "Macedonia","Mali","Myanmar","Mongolia", - "Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat", - "Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique", - "Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua", - "Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama", - "Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan", - "Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","" + - "Palestinian Territory","Portugal","Palau","Paraguay","Qatar", - "Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia", - "Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena", - "Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino", - "Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador", - "Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad", - "French Southern Territories","Togo","Thailand","Tajikistan","Tokelau", - "Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago", - "Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda", - "United States Minor Outlying Islands","United States","Uruguay","Uzbekistan", - "Holy See (Vatican City State)","Saint Vincent and the Grenadines", - "Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam", - "Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia", - "South Africa","Zambia","Montenegro","Zimbabwe","Anonymous Proxy", - "Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", - "Saint Barthelemy","Saint Martin"} + "N/A", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates", + "Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia", + "Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa", + "Austria", "Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina", + "Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria", "Bahrain", + "Burundi", "Benin", "Bermuda", "Brunei Darussalam", "Bolivia", "Brazil", "Bahamas", + "Bhutan", "Bouvet Island", "Botswana", "Belarus", "Belize", "Canada", + "Cocos (Keeling) Islands", "Congo, The Democratic Republic of the", + "Central African Republic", "Congo", "Switzerland", "Cote D'Ivoire", + "Cook Islands", "Chile", "Cameroon", "China", "Colombia", "Costa Rica", "Cuba", + "Cape Verde", "Christmas Island", "Cyprus", "Czech Republic", "Germany", + "Djibouti", "Denmark", "Dominica", "Dominican Republic", "Algeria", "Ecuador", + "Estonia", "Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia", "Finland", + "Fiji", "Falkland Islands (Malvinas)", "Micronesia, Federated States of", + "Faroe Islands", "France", "France, Metropolitan", "Gabon", "United Kingdom", + "Grenada", "Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland", "Gambia", + "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece", + "South Georgia and the South Sandwich Islands", "Guatemala", "Guam", + "Guinea-Bissau", "Guyana", "Hong Kong", "Heard Island and McDonald Islands", + "Honduras", "Croatia", "Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India", + "British Indian Ocean Territory", "Iraq", "Iran, Islamic Republic of", + "Iceland", "Italy", "Jamaica", "Jordan", "Japan", "Kenya", "Kyrgyzstan", "Cambodia", + "Kiribati", "Comoros", "Saint Kitts and Nevis", + "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", + "Cayman Islands", "Kazakhstan", "Lao People's Democratic Republic", "Lebanon", + "Saint Lucia", "Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania", + "Luxembourg", "Latvia", "Libyan Arab Jamahiriya", "Morocco", "Monaco", + "Moldova, Republic of", "Madagascar", "Marshall Islands", + "Macedonia", "Mali", "Myanmar", "Mongolia", + "Macau", "Northern Mariana Islands", "Martinique", "Mauritania", "Montserrat", + "Malta", "Mauritius", "Maldives", "Malawi", "Mexico", "Malaysia", "Mozambique", + "Namibia", "New Caledonia", "Niger", "Norfolk Island", "Nigeria", "Nicaragua", + "Netherlands", "Norway", "Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", + "Peru", "French Polynesia", "Papua New Guinea", "Philippines", "Pakistan", + "Poland", "Saint Pierre and Miquelon", "Pitcairn Islands", "Puerto Rico", "" + + "Palestinian Territory", + "Portugal", "Palau", "Paraguay", "Qatar", + "Reunion", "Romania", "Russian Federation", "Rwanda", "Saudi Arabia", + "Solomon Islands", "Seychelles", "Sudan", "Sweden", "Singapore", "Saint Helena", + "Slovenia", "Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino", + "Senegal", "Somalia", "Suriname", "Sao Tome and Principe", "El Salvador", + "Syrian Arab Republic", "Swaziland", "Turks and Caicos Islands", "Chad", + "French Southern Territories", "Togo", "Thailand", "Tajikistan", "Tokelau", + "Turkmenistan", "Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago", + "Tuvalu", "Taiwan", "Tanzania, United Republic of", "Ukraine", "Uganda", + "United States Minor Outlying Islands", "United States", "Uruguay", "Uzbekistan", + "Holy See (Vatican City State)", "Saint Vincent and the Grenadines", + "Venezuela", "Virgin Islands, British", "Virgin Islands, U.S.", "Vietnam", + "Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte", "Serbia", + "South Africa", "Zambia", "Montenegro", "Zimbabwe", "Anonymous Proxy", + "Satellite Provider", "Other", "Aland Islands", "Guernsey", "Isle of Man", "Jersey", + "Saint Barthelemy", "Saint Martin"} ) // Constants const ( - MAX_RECORD_LENGTH = 4 - STANDARD_RECORD_LENGTH = 3 - COUNTRY_BEGIN = 16776960 + MAX_RECORD_LENGTH = 4 + STANDARD_RECORD_LENGTH = 3 + COUNTRY_BEGIN = 16776960 STRUCTURE_INFO_MAX_SIZE = 20 - FULL_RECORD_LENGTH = 60 - SEGMENT_RECORD_LENGTH = 3 - OPEN_PERM = 0666 + FULL_RECORD_LENGTH = 60 + SEGMENT_RECORD_LENGTH = 3 + OPEN_PERM = 0666 // DB Types - DB_COUNTRY_EDITION = byte(1) + DB_COUNTRY_EDITION = byte(1) DB_CITY_EDITION_REV0 = byte(6) DB_CITY_EDITION_REV1 = byte(2) ) // These are some structs type GeoIP struct { - databaseSegment int // No need to make an array of size 1 - recordLength int // Set to one of the constants above - dbType byte // Store the database type - data []byte // All of the data from the DB file + databaseSegment int // No need to make an array of size 1 + recordLength int // Set to one of the constants above + dbType byte // Store the database type + data []byte // All of the data from the DB file } type Location struct { CountryCode string // If country ed. only country info is filled CountryName string // If country ed. only country info is filled - Region string - City string - PostalCode string - Latitude float32 - Longitude float32 + Region string + City string + PostalCode string + Latitude float32 + Longitude float32 } // Load the database file in memory, detect the db format and setup the GeoIP struct -func Load(filename string) *GeoIP { +func Load(filename string) (gi *GeoIP, err os.Error) { // Try to open the requested file - dbInfo,errStat := os.Lstat(filename) - dbFile,errOpen := os.Open(filename, os.O_RDONLY, OPEN_PERM) - if errStat != nil || errOpen != nil { - return nil + dbInfo, err := os.Lstat(filename) + if err != nil { + return + } + dbFile, err := os.Open(filename, os.O_RDONLY, OPEN_PERM) + if err != nil { + return } // Copy the db into memory - gi := new(GeoIP) - gi.data = make([]byte,dbInfo.Size) + gi = new(GeoIP) + gi.data = make([]byte, dbInfo.Size) dbFile.Read(gi.data) dbFile.Close() // Check the database type - gi.dbType = DB_COUNTRY_EDITION // Default the database to country edition - gi.databaseSegment = COUNTRY_BEGIN // Default to country DB + gi.dbType = DB_COUNTRY_EDITION // Default the database to country edition + gi.databaseSegment = COUNTRY_BEGIN // Default to country DB gi.recordLength = STANDARD_RECORD_LENGTH // Default to country DB // Search for the DB type headers - delim := make([]byte,3) + delim := make([]byte, 3) for i := 0; i < STRUCTURE_INFO_MAX_SIZE; i++ { - delim = gi.data[len(gi.data)-i-3-1:len(gi.data)-i-1] + delim = gi.data[len(gi.data)-i-3-1 : len(gi.data)-i-1] if int8(delim[0]) == -1 && int8(delim[1]) == -1 && int8(delim[2]) == -1 { gi.dbType = gi.data[len(gi.data)-i-1] // If we detect city edition set the correct segment offset if gi.dbType == DB_CITY_EDITION_REV1 || gi.dbType == DB_CITY_EDITION_REV0 { - buf := make([]byte,SEGMENT_RECORD_LENGTH) - buf = gi.data[len(gi.data)-i-1+1:len(gi.data)-i-1+4] + buf := make([]byte, SEGMENT_RECORD_LENGTH) + buf = gi.data[len(gi.data)-i-1+1 : len(gi.data)-i-1+4] gi.databaseSegment = 0 for j := 0; j < SEGMENT_RECORD_LENGTH; j++ { - gi.databaseSegment += (int(buf[j]) << uint8(j*8)); + gi.databaseSegment += (int(buf[j]) << uint8(j*8)) } } break @@ -188,13 +192,12 @@ func Load(filename string) *GeoIP { } // Reject unsupported formats - if gi.dbType != DB_COUNTRY_EDITION && - gi.dbType != DB_CITY_EDITION_REV0 && - gi.dbType != DB_CITY_EDITION_REV1 { - return nil + if gi.dbType != DB_COUNTRY_EDITION && gi.dbType != DB_CITY_EDITION_REV0 && gi.dbType != DB_CITY_EDITION_REV1 { + err = os.NewError("Unsupported database format") + return } - return gi + return } // Lookup by IP address and return location @@ -209,7 +212,7 @@ func (gi *GeoIP) GetLocationByIPNum(ipNum uint32) *Location { // Check if the country was found if gi.dbType == DB_COUNTRY_EDITION && offset-COUNTRY_BEGIN == 0 || - gi.dbType != DB_COUNTRY_EDITION && gi.databaseSegment == offset { + gi.dbType != DB_COUNTRY_EDITION && gi.databaseSegment == offset { return nil } @@ -225,9 +228,9 @@ func (gi *GeoIP) GetLocationByIPNum(ipNum uint32) *Location { } // Find the max record length - recPointer := offset + (2*gi.recordLength -1) * gi.databaseSegment + recPointer := offset + (2*gi.recordLength-1)*gi.databaseSegment recordEnd := recPointer + FULL_RECORD_LENGTH - if len(gi.data) - recPointer < FULL_RECORD_LENGTH { + if len(gi.data)-recPointer < FULL_RECORD_LENGTH { recordEnd = len(gi.data) } @@ -235,38 +238,41 @@ func (gi *GeoIP) GetLocationByIPNum(ipNum uint32) *Location { location.CountryCode = countryCode[gi.data[recPointer]] location.CountryName = countryName[gi.data[recPointer]] readLen := 1 - recPointer+=1 + recPointer += 1 // Get the region for readLen = 0; gi.data[recPointer+readLen] != '\000' && - recPointer+readLen < recordEnd; readLen++ {} + recPointer+readLen < recordEnd; readLen++ { + } if readLen != 0 { - location.Region = string(gi.data[recPointer:recPointer+readLen]) + location.Region = string(gi.data[recPointer : recPointer+readLen]) } - recPointer += readLen+1 + recPointer += readLen + 1 // Get the city for readLen = 0; gi.data[recPointer+readLen] != '\000' && - recPointer+readLen < recordEnd; readLen++ {} + recPointer+readLen < recordEnd; readLen++ { + } if readLen != 0 { - location.City = string(gi.data[recPointer:recPointer+readLen]) + location.City = string(gi.data[recPointer : recPointer+readLen]) } - recPointer += readLen+1 + recPointer += readLen + 1 // Get the postal code for readLen = 0; gi.data[recPointer+readLen] != '\000' && - recPointer+readLen < recordEnd; readLen++ {} + recPointer+readLen < recordEnd; readLen++ { + } if readLen != 0 { - location.PostalCode = string(gi.data[recPointer:recPointer+readLen]) + location.PostalCode = string(gi.data[recPointer : recPointer+readLen]) } - recPointer += readLen+1 + recPointer += readLen + 1 // Get the latitude coordinate := float32(0) for j := 0; j < 3; j++ { coordinate += float32(int32(gi.data[recPointer+j]) << uint8(j*8)) } - location.Latitude = float32(coordinate/10000-180) + location.Latitude = float32(coordinate/10000 - 180) recPointer += 3 // Get the longitude @@ -274,25 +280,25 @@ func (gi *GeoIP) GetLocationByIPNum(ipNum uint32) *Location { for j := 0; j < 3; j++ { coordinate += float32(int32(gi.data[recPointer+j]) << uint8(j*8)) } - location.Longitude = float32(coordinate/10000-180) + location.Longitude = float32(coordinate/10000 - 180) return location } // Read the database and return record position func (gi *GeoIP) lookupByIPNum(ip uint32) int { - buf := make([]byte,2*MAX_RECORD_LENGTH) - x := make([]int,2) + buf := make([]byte, 2*MAX_RECORD_LENGTH) + x := make([]int, 2) offset := 0 for depth := 31; depth >= 0; depth-- { - for i :=0; i < 2*MAX_RECORD_LENGTH; i++ { - buf[i] = gi.data[(2*gi.recordLength * offset)+i] + for i := 0; i < 2*MAX_RECORD_LENGTH; i++ { + buf[i] = gi.data[(2*gi.recordLength*offset)+i] } - for i :=0; i<2; i++ { + for i := 0; i < 2; i++ { x[i] = 0 - for j := 0; j9 { + if c > 9 { return 0 } octet += uint32(c) @@ -343,6 +349,6 @@ func AddrToNum(ip string) uint32 { if (octet > 255) || (i != 0) { return 0 } - ipnum <<= 8; - return uint32(ipnum+octet) + ipnum <<= 8 + return uint32(ipnum + octet) }