Codebase list geoipupdate / bb2dba1
New upstream version 4.0.6 Faidon Liambotis 4 years ago
29 changed file(s) with 2641 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 *.swp
1 /build
2 /cmd/geoipupdate/geoipupdate
0 [run]
1 deadline = "10m"
2 tests = true
3
4 [linters]
5 disable-all = true
6 enable = [
7 "deadcode",
8 "depguard",
9 "errcheck",
10 "goconst",
11 "gocyclo",
12 "gocritic",
13 "gofmt",
14 "golint",
15 "gosec",
16 "gosimple",
17 "ineffassign",
18 "maligned",
19 "misspell",
20 "nakedret",
21 "staticcheck",
22 "structcheck",
23 "typecheck",
24 "unconvert",
25 "unparam",
26 "varcheck",
27 "vet",
28 "vetshadow",
29 ]
30
31 # This goes off for MD5 usage, which we use heavily
32 [[issues.exclude-rules]]
33 text = "weak cryptographic primitive"
34 linters = ["gosec"]
0 project_name: 'geoipupdate'
1 archives:
2 - id: main
3 wrap_in_directory: true
4 files:
5 - 'CHANGELOG.md'
6 - 'LICENSE-APACHE'
7 - 'LICENSE-MIT'
8 - 'README.md'
9 - 'GeoIP.conf'
10 - 'GeoIP.conf.md'
11 - 'geoipupdate.md'
12 builds:
13 - main: './cmd/geoipupdate'
14 binary: 'geoipupdate'
15 goarch:
16 - '386'
17 - 'amd64'
18 goos:
19 - 'linux'
20 hooks:
21 post: 'make data BUILDDIR=. CONFFILE=/etc/GeoIP.conf DATADIR=/usr/share/GeoIP'
22 ldflags:
23 - '-s -w -X main.version={{.Version}} -X main.defaultConfigFile=/etc/GeoIP.conf -X main.defaultDatabaseDirectory=/usr/share/GeoIP'
24 checksum:
25 name_template: 'checksums-dpkg-rpm.txt'
26 nfpm:
27 vendor: 'MaxMind, Inc.'
28 homepage: https://www.maxmind.com/
29 maintainer: 'MaxMind, Inc. <support@maxmind.com>'
30 description: Program to perform automatic updates of GeoIP2 and GeoIP Legacy binary databases.
31 license: Apache 2.0 or MIT
32 formats:
33 - deb
34 - rpm
35 bindir: /usr/bin
36 empty_folders:
37 - /usr/share/GeoIP
38 files:
39 'CHANGELOG.md': '/usr/share/doc/geoipupdate/CHANGELOG.md'
40 'LICENSE-APACHE': '/usr/share/doc/geoipupdate/LICENSE-APACHE'
41 'LICENSE-MIT': '/usr/share/doc/geoipupdate/LICENSE-MIT'
42 'README.md': '/usr/share/doc/geoipupdate/README.md'
43 'GeoIP.conf': '/usr/share/doc/geoipupdate/GeoIP.conf'
44 'GeoIP.conf.md': '/usr/share/doc/geoipupdate/GeoIP.conf.md'
45 'geoipupdate.md': '/usr/share/doc/geoipupdate/geoipupdate.md'
46 config_files:
47 'GeoIP.conf': '/etc/GeoIP.conf'
48 release:
49 # We disable the release as there is no way to disable the creation of
50 # the archive version and we don't want to upload those. We also can
51 # only do one release.
52 disable: true
0 project_name: 'geoipupdate'
1 archives:
2 - id: main
3 wrap_in_directory: true
4 files:
5 - 'CHANGELOG.md'
6 - 'LICENSE-APACHE'
7 - 'LICENSE-MIT'
8 - 'README.md'
9 - 'GeoIP.conf'
10 - 'GeoIP.conf.md'
11 - 'geoipupdate.md'
12 format: zip
13 builds:
14 - main: './cmd/geoipupdate'
15 binary: 'geoipupdate'
16 goarch:
17 - '386'
18 - 'amd64'
19 goos:
20 - 'windows'
21 hooks:
22 post: 'make data OS=Windows_NT BUILDDIR=.'
23 checksum:
24 name_template: 'checksums-windows.txt'
25 release:
26 # We can only do one release.
27 disable: true
0 project_name: 'geoipupdate'
1 archives:
2 - id: main
3 wrap_in_directory: true
4 files:
5 - 'CHANGELOG.md'
6 - 'LICENSE-APACHE'
7 - 'LICENSE-MIT'
8 - 'README.md'
9 - 'GeoIP.conf'
10 - 'GeoIP.conf.md'
11 - 'geoipupdate.md'
12 builds:
13 - main: './cmd/geoipupdate'
14 binary: 'geoipupdate'
15 goarch:
16 - '386'
17 - 'amd64'
18 goos:
19 - 'darwin'
20 - 'linux'
21 hooks:
22 post: 'make data BUILDDIR=.'
23 checksum:
24 name_template: 'checksums-darwin-linux.txt'
0 language: go
1
2 os:
3 - linux
4 - osx
5 - windows
6
7 go:
8 - "1.10.x"
9 - "1.11.x"
10 - "1.12.x"
11 - "1.13.x"
12
13 install:
14 - go get -t ./...
15
16 before_script:
17 - |
18 if [[ $TRAVIS_GO_VERSION == '1.13.x' && $TRAVIS_OS_NAME == 'linux' ]]; then
19 curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin
20 fi
21
22 script:
23 - go test -v -race ./...
24 - |
25 if [[ $TRAVIS_GO_VERSION == '1.13.x' && $TRAVIS_OS_NAME == 'linux' ]]; then
26 golangci-lint run
27 fi
0 # CHANGELOG
1
2 ## 4.0.5 (2019-09-13)
3
4 * Ignore errors when syncing file system. These errors were primarily due
5 to the file system not supporting the sync call. Reported by devkappa.
6 GitHub #37.
7 * Use CRLF line endings on Windows for text files.
8 * Fix tests on Windows.
9 * Improve man page formatting. Reported by Faidon Liambotis. GitHub #38.
10 * Dependencies are no longer vendored. Reported by Faidon Liambotis. GitHub
11 #39.
12
13 ## 4.0.4 (2019-08-30)
14
15 * Do not try to sync the database directory when running on Windows.
16 Syncing this way is not supported there and would lead to an error. Pull
17 request by Nicholi. GitHub #32.
18
19 ## 4.0.3 (2019-06-07)
20
21 * Update flock dependency from `theckman/go-flock` to `gofrs/flock`. Pull
22 request by Paul Howarth. GitHub #22.
23 * Switch to Go modules and update dependencies.
24 * Fix version output on Ubuntu PPA and Homebrew releases.
25
26 ## 4.0.2 (2019-01-18)
27
28 * Fix dependency in `Makefile`.
29
30 ## 4.0.1 (2019-01-17)
31
32 * Improve documentation.
33 * Add script to generate man pages to `Makefile`.
34
35 ## 4.0.0 (2019-01-14)
36
37 * Expand installation instructions.
38 * First full release.
39
40 ## 0.0.2 (2018-11-28)
41
42 * Fix the output when the version output, `-V`, is passed to `geoipupdate`.
43
44 ## 0.0.1 (2018-11-27)
45
46 * Initial version
0
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
7 1. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
66 2. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
73 3. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
89 4. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
130 5. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
138 6. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
143 7. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
153 8. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
165 9. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176 END OF TERMS AND CONDITIONS
177
178 APPENDIX: How to apply the Apache License to your work.
179
180 To apply the Apache License to your work, attach the following
181 boilerplate notice, with the fields enclosed by brackets "[]"
182 replaced with your own identifying information. (Don't include
183 the brackets!) The text should be enclosed in the appropriate
184 comment syntax for the file format. We also recommend that a
185 file or class name and description of purpose be included on the
186 same "printed page" as the copyright notice for easier
187 identification within third-party archives.
188
189 Copyright [yyyy] [name of copyright owner]
190
191 Licensed under the Apache License, Version 2.0 (the "License");
192 you may not use this file except in compliance with the License.
193 You may obtain a copy of the License at
194
195 http://www.apache.org/licenses/LICENSE-2.0
196
197 Unless required by applicable law or agreed to in writing, software
198 distributed under the License is distributed on an "AS IS" BASIS,
199 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 See the License for the specific language governing permissions and
201 limitations under the License.
0 Permission is hereby granted, free of charge, to any person obtaining a copy of
1 this software and associated documentation files (the "Software"), to deal in
2 the Software without restriction, including without limitation the rights to
3 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
4 of the Software, and to permit persons to whom the Software is furnished to do
5 so, subject to the following conditions:
6
7 The above copyright notice and this permission notice shall be included in all
8 copies or substantial portions of the Software.
9
10 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
16 SOFTWARE.
0 ifndef BUILDDIR
1 BUILDDIR=build
2 endif
3
4 ifndef CONFFILE
5 ifeq ($(OS),Windows_NT)
6 CONFFILE=%SystemDrive%\ProgramData\MaxMind\GeoIPUpdate\GeoIP.conf
7 else
8 CONFFILE=/usr/local/etc/GeoIP.conf
9 endif
10 endif
11
12 ifndef DATADIR
13 ifeq ($(OS),Windows_NT)
14 DATADIR=%SystemDrive%\ProgramData\MaxMind\GeoIPUpdate\GeoIP
15 else
16 DATADIR=/usr/local/share/GeoIP
17 endif
18 endif
19
20 ifeq ($(OS),Windows_NT)
21 MAYBE_CR=\r
22 endif
23
24 ifndef VERSION
25 VERSION=unknown
26 endif
27
28 all: \
29 $(BUILDDIR)/geoipupdate \
30 data
31
32 data: \
33 $(BUILDDIR)/GeoIP.conf \
34 $(BUILDDIR)/GeoIP.conf.md \
35 $(BUILDDIR)/geoipupdate.md \
36 $(BUILDDIR)/GeoIP.conf.5 \
37 $(BUILDDIR)/geoipupdate.1
38
39 $(BUILDDIR):
40 mkdir -p $(BUILDDIR)
41
42 $(BUILDDIR)/geoipupdate: $(BUILDDIR)
43 (cd cmd/geoipupdate && go build -ldflags '-X main.defaultConfigFile=$(CONFFILE) -X main.defaultDatabaseDirectory=$(DATADIR) -X "main.version=$(VERSION)"')
44 cp cmd/geoipupdate/geoipupdate $(BUILDDIR)
45
46 $(BUILDDIR)/GeoIP.conf: $(BUILDDIR) conf/GeoIP.conf.default
47 sed -e 's|CONFFILE|$(CONFFILE)|g' -e 's|DATADIR|$(DATADIR)|g' -e 's|$$|$(MAYBE_CR)|g' conf/GeoIP.conf.default > $(BUILDDIR)/GeoIP.conf
48
49 $(BUILDDIR)/GeoIP.conf.md: $(BUILDDIR) doc/GeoIP.conf.md
50 sed -e 's|CONFFILE|$(CONFFILE)|g' -e 's|DATADIR|$(DATADIR)|g' -e 's|$$|$(MAYBE_CR)|g' doc/GeoIP.conf.md > $(BUILDDIR)/GeoIP.conf.md
51
52 $(BUILDDIR)/geoipupdate.md: $(BUILDDIR) doc/geoipupdate.md
53 sed -e 's|CONFFILE|$(CONFFILE)|g' -e 's|DATADIR|$(DATADIR)|g' -e 's|$$|$(MAYBE_CR)|g' doc/geoipupdate.md > $(BUILDDIR)/geoipupdate.md
54
55 $(BUILDDIR)/GeoIP.conf.5: $(BUILDDIR)/GeoIP.conf.md $(BUILDDIR)/geoipupdate.md
56 dev-bin/make-man-pages.pl "$(BUILDDIR)"
57
58 $(BUILDDIR)/geoipupdate.1: $(BUILDDIR)/GeoIP.conf.5
59
60 clean:
61 rm -rf $(BUILDDIR)/GeoIP.conf \
62 $(BUILDDIR)/GeoIP.conf.md \
63 $(BUILDDIR)/geoipupdate \
64 $(BUILDDIR)/geoipupdate.md \
65 $(BUILDDIR)/GeoIP.conf.5 \
66 $(BUILDDIR)/geoipupdate.1
0 # Releasing
1
2 * Make sure you have [`hub`](https://github.com/github/hub),
3 [`goreleaser`](https://goreleaser.com/), and rpmbuild installed.
4 (rpmbuild is in the Ubuntu package `rpm`).
5 * Update `CHANGELOG.md`. Set the appropriate release date.
6 * Run `GITHUB_TOKEN=<your token> ./dev-bin/release.sh`. For `goreleaser` you
7 will need a token with the `repo` scope. You may create a token
8 [here](https://github.com/settings/tokens/new).
9 * If we're not using Go modules yet, the release might fail depending on
10 your `GO111MODULE` setting. Consider setting it to `off` if necessary.
11
12 Then release to our PPA:
13
14 * Switch to the ubuntu-ppa branch. Merge the released tag into it.
15 * Set up to release to launchpad. You can see some information about
16 prerequisites for this
17 [here](https://github.com/maxmind/libmaxminddb/blob/master/README.dev.md).
18 * Delete `dist` directory.
19 * Run `dev-bin/ppa-release.sh`
20
21 Finally release to Homebrew:
22
23 * Go to https://github.com/Homebrew/homebrew-core/blob/master/Formula/geoipupdate.rb
24 * Edit the file to update the url and sha256. You can get the sha256 for the
25 tarball with the `sha256sum` command line utility.
26 * Make a commit with the summary `geoipupdate <VERSION>`
27 * Submit a PR with the changes you just made.
0 # GeoIP Update
1
2 [![Build Status](https://travis-ci.com/maxmind/geoipupdate.svg?branch=master)](https://travis-ci.com/maxmind/geoipupdate)
3
4 The GeoIP Update program performs automatic updates of GeoIP2 and GeoIP Legacy
5 binary databases. CSV databases are _not_ supported.
6
7 This is the new version of GeoIP Update. If for some reason you need the
8 legacy C version, you can find it
9 [here](https://github.com/maxmind/geoipupdate-legacy).
10
11 ## Installation
12
13 We provide releases for Linux, macOS (darwin), and Windows. Please see the
14 [Releases](https://github.com/maxmind/geoipupdate/releases) tab for the
15 latest release.
16
17 After you install geoipupdate, please refer to our
18 [documentation](https://dev.maxmind.com/geoip/geoipupdate/) for information
19 about configuration.
20
21 If you're upgrading from geoipupdate 3.x, please see our [upgrade
22 guide](https://dev.maxmind.com/geoip/geoipupdate/upgrading-to-geoip-update-4-x/).
23
24 ### Installing on Linux via the tarball
25
26 Download and extract the appropriate tarball for your system. You will end
27 up with a directory named something like `geoipupdate_4.0.0_linux_amd64`
28 depending on the version and architecture.
29
30 Copy `geoipupdate` to where you want it to live. To install it to
31 `/usr/local/bin/geoipupdate`, run the equivalent of `sudo cp
32 geoipupdate_4.0.0_linux_amd64/geoipupdate /usr/local/bin`.
33
34 `geoipupdate` looks for the config file `/usr/local/etc/GeoIP.conf` by
35 default.
36
37 ### Installing on Ubuntu via PPA
38
39 MaxMind provides a PPA for recent versions of Ubuntu. To add the PPA to
40 your sources, run:
41
42 ```
43 $ sudo add-apt-repository ppa:maxmind/ppa
44 ```
45
46 Then install `geoipupdate` by running:
47
48 ```
49 $ sudo apt update
50 $ sudo apt install geoipupdate
51 ```
52
53 ### Installing on Ubuntu or Debian via the deb
54
55 You can also use the tarball.
56
57 Download the appropriate .deb for your system.
58
59 Run `dpkg -i path/to/geoipupdate_4.0.0_linux_amd64.deb` (replacing the
60 version number and architecture as necessary). You will need to be root.
61 For Ubuntu you can prefix the command with `sudo`. This will install
62 `geoipupdate` to `/usr/bin/geoipupdate`.
63
64 `geoipupdate` looks for the config file `/etc/GeoIP.conf` by default.
65
66 ### Installing on RedHat or CentOS via the rpm
67
68 You can also use the tarball.
69
70 Download the appropriate .rpm for your system.
71
72 Run `rpm -i path/to/geoipupdate_4.0.0_linux_amd64.rpm` (replacing the
73 version number and architecture as necessary). You will need to be root.
74 This will install `geoipupdate` to `/usr/bin/geoipupdate`.
75
76 `geoipupdate` looks for the config file `/etc/GeoIP.conf` by default.
77
78 ### Installing on macOS (darwin) via the tarball
79
80 This is the same as installing on Linux via the tarball, except choose a
81 tarball with "darwin" in the name.
82
83 ### Installing on macOS via Homebrew
84
85 If you are on macOS and you have [Homebrew](http://brew.sh/) you can install
86 `geoipupdate` via `brew`
87
88 ```
89 $ brew install geoipupdate
90 ```
91
92 ### Installing on Windows
93
94 Download and extract the appropriate zip for your system. You will end up
95 with a directory named something like `geoipupdate_4.0.0_windows_amd64`
96 depending on the version and architecture.
97
98 Copy `geoipupdate.exe` to where you want it to live.
99
100 `geoipupdate` looks for the config file
101 `\ProgramData\MaxMind/GeoIPUpdate\GeoIP.conf` on your system drive by
102 default.
103
104 ### Installation from source or Git
105
106 You need the Go compiler (1.8+). You can get it at the [Go
107 website](https://golang.org).
108
109 The easiest way is via `go get`:
110
111 $ go get -u github.com/maxmind/geoipupdate/cmd/geoipupdate
112
113 This installs `geoipupdate` to `$GOPATH/bin/geoipupdate`.
114
115 # Configuring
116
117 Please see our [online guide](https://dev.maxmind.com/geoip/geoipupdate/) for
118 directions on how to configure GeoIP Update.
119
120 # Documentation
121
122 See our documentation for the [`geoipupdate` program](doc/geoipupdate.md)
123 and the [`GeoIP.conf` configuration file](doc/GeoIP.conf.md).
124
125 # Default config file and database directory paths
126
127 We define default paths for the config file and database directory. If
128 these defaults are not appropriate for you, you can change them at build
129 time using flags:
130
131 go build -ldflags "-X main.defaultConfigFile=/etc/GeoIP.conf \
132 -X main.defaultDatabaseDirectory=/usr/share/GeoIP"
133
134 # Bug Reports
135
136 Please report bugs by filing an issue with our GitHub issue tracker at
137 https://github.com/maxmind/geoipupdate/issues
138
139 # Copyright and License
140
141 This software is Copyright (c) 2018 - 2019 by MaxMind, Inc.
142
143 This is free software, licensed under the [Apache License, Version
144 2.0](LICENSE-APACHE) or the [MIT License](LICENSE-MIT), at your option.
0 exclude: ['README.dev.md', 'vendor']
1
2
0 <!DOCTYPE html>
1
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="description" content="">
6 <meta name="author" content="MaxMind, Inc.">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 <link href="//mm-staticassets.storage.googleapis.com/gh-pages-theme/maxmind.css" rel="stylesheet">
9 <link href="//dev.maxmind.com/css/dev.maxmind.com.css" rel="stylesheet">
10 <link href="//dev.maxmind.com/css/highlight-github.css" rel="stylesheet" >
11 <link rel="shortcut icon" href="//dev.maxmind.com/static/favicon.ico">
12 <script src="//dev.maxmind.com/js/highlight.pack.js"></script>
13 <script>hljs.initHighlightingOnLoad();</script>
14 <script type="text/javascript">
15
16 var _gaq = _gaq || [];
17 _gaq.push(['_setAccount', 'UA-171943-3']);
18 _gaq.push(['_trackPageview']);
19
20 (function() {
21 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
22 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
23 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
24 })();
25
26 </script>
27 <title>{{ page.title }}</title>
28 </head>
29 <body>
30
31 <div class="container">
32 <div class="row-fluid">
33 <div class="span7">
34
35 <ul class="nav nav-tabs">
36 {% for node in site.pages %}
37 {% if page.url == node.url %}
38 <li class="active"><a href="/geoipupdate2{{node.url}}" class="active">{{node.title}}</a></li>
39 {% else %}
40 <li><a href="/geoipupdate2{{node.url}}">{{node.title}}</a></li>
41 {% endif %}
42 {% endfor %}
43 </ul>
44
45 </div>
46 </div>
47
48
49 {{ content }}
50 </div>
51
52 </body>
53 </html>
0 package main
1
2 import (
3 "log"
4 "os"
5
6 flag "github.com/spf13/pflag"
7 )
8
9 // Args are command line arguments.
10 type Args struct {
11 ConfigFile string
12 DatabaseDirectory string
13 StackTrace bool
14 Verbose bool
15 }
16
17 func getArgs() *Args {
18 configFile := flag.StringP(
19 "config-file",
20 "f",
21 defaultConfigFile,
22 "Configuration file",
23 )
24 databaseDirectory := flag.StringP(
25 "database-directory",
26 "d",
27 "",
28 "Store databases in this directory (uses config if not specified)",
29 )
30 help := flag.BoolP("help", "h", false, "Display help and exit")
31 stackTrace := flag.Bool("stack-trace", false, "Show a stack trace along with any error message.")
32 verbose := flag.BoolP("verbose", "v", false, "Use verbose output")
33 displayVersion := flag.BoolP("version", "V", false, "Display the version and exit")
34
35 flag.Parse()
36
37 if *help {
38 printUsage()
39 }
40 if *displayVersion {
41 log.Printf("geoipupdate %s", version)
42 os.Exit(0)
43 }
44
45 if *configFile == "" {
46 log.Printf("You must provide a configuration file.")
47 printUsage()
48 }
49
50 return &Args{
51 ConfigFile: *configFile,
52 DatabaseDirectory: *databaseDirectory,
53 StackTrace: *stackTrace,
54 Verbose: *verbose,
55 }
56 }
57
58 func printUsage() {
59 log.Printf("Usage: %s <arguments>\n", os.Args[0])
60 flag.PrintDefaults()
61 os.Exit(1)
62 }
0 package main
1
2 import (
3 "bufio"
4 "log"
5 "net/url"
6 "os"
7 "path/filepath"
8 "regexp"
9 "strconv"
10 "strings"
11
12 "github.com/pkg/errors"
13 )
14
15 // Config is a parsed configuration file.
16 type Config struct {
17 AccountID int
18 DatabaseDirectory string
19 EditionIDs []string
20 LicenseKey string
21 LockFile string
22 PreserveFileTimes bool
23 Proxy *url.URL
24 URL string
25 }
26
27 // NewConfig parses the configuration file.
28 func NewConfig( // nolint: gocyclo
29 file,
30 databaseDirectory string,
31 ) (*Config, error) {
32 fh, err := os.Open(file)
33 if err != nil {
34 return nil, errors.Wrap(err, "error opening file")
35 }
36 defer func() {
37 if err := fh.Close(); err != nil {
38 log.Fatalf("Error closing config file: %+v", errors.Wrap(err, "closing file"))
39 }
40 }()
41
42 config := &Config{}
43 scanner := bufio.NewScanner(fh)
44 lineNumber := 0
45 keysSeen := map[string]struct{}{}
46 var host, proxy, proxyUserPassword string
47 for scanner.Scan() {
48 lineNumber++
49 line := strings.TrimSpace(scanner.Text())
50 if line == "" || line[0] == '#' {
51 continue
52 }
53
54 fields := strings.Fields(line)
55 if len(fields) < 2 {
56 return nil, errors.Errorf("invalid format on line %d", lineNumber)
57 }
58 key := fields[0]
59 value := strings.Join(fields[1:], " ")
60
61 if _, ok := keysSeen[key]; ok {
62 return nil, errors.Errorf("`%s' is in the config multiple times", key)
63 }
64 keysSeen[key] = struct{}{}
65
66 switch key {
67 case "AccountID", "UserId":
68 accountID, err := strconv.Atoi(value)
69 if err != nil {
70 return nil, errors.Wrap(err, "invalid account ID format")
71 }
72 config.AccountID = accountID
73 keysSeen["AccountID"] = struct{}{}
74 keysSeen["UserId"] = struct{}{}
75 case "DatabaseDirectory":
76 config.DatabaseDirectory = filepath.Clean(value)
77 case "EditionIDs", "ProductIds":
78 config.EditionIDs = strings.Fields(value)
79 keysSeen["EditionIDs"] = struct{}{}
80 keysSeen["ProductIds"] = struct{}{}
81 case "Host":
82 host = value
83 case "LicenseKey":
84 config.LicenseKey = value
85 case "LockFile":
86 config.LockFile = filepath.Clean(value)
87 case "PreserveFileTimes":
88 if value != "0" && value != "1" {
89 return nil, errors.New("`PreserveFileTimes' must be 0 or 1")
90 }
91 if value == "1" {
92 config.PreserveFileTimes = true
93 }
94 case "Proxy":
95 proxy = value
96 case "ProxyUserPassword":
97 proxyUserPassword = value
98 case "Protocol", "SkipHostnameVerification", "SkipPeerVerification":
99 // Deprecated.
100 default:
101 return nil, errors.Errorf("unknown option on line %d", lineNumber)
102 }
103 }
104
105 if err := scanner.Err(); err != nil {
106 return nil, errors.Wrap(err, "error reading file")
107 }
108
109 if _, ok := keysSeen["EditionIDs"]; !ok {
110 return nil, errors.Errorf("the `EditionIDs` option is required")
111 }
112
113 {
114 _, LicenseKeySeen := keysSeen["LicenseKey"]
115 _, AccountIDSeen := keysSeen["AccountID"]
116
117 if LicenseKeySeen && !AccountIDSeen {
118 return nil, errors.Errorf("the `AccountID` option is required if the `LicenseKey` option is set")
119 }
120
121 if AccountIDSeen && !LicenseKeySeen {
122 return nil, errors.Errorf("the `LicenseKey` option is required if the `AccountID` option is set")
123 }
124
125 if AccountIDSeen && config.AccountID == 0 && LicenseKeySeen && config.LicenseKey != "000000000000" {
126 return nil, errors.New("setting an `AccountID` option of 0 with a `LicenseKey` option other than 000000000000 is disallowed")
127 }
128 }
129
130 // Set defaults & post-process.
131
132 // Argument takes precedence.
133 if databaseDirectory != "" {
134 config.DatabaseDirectory = filepath.Clean(databaseDirectory)
135 }
136
137 if config.DatabaseDirectory == "" {
138 config.DatabaseDirectory = filepath.Clean(defaultDatabaseDirectory)
139 }
140
141 if host == "" {
142 host = "updates.maxmind.com"
143 }
144
145 if config.LockFile == "" {
146 config.LockFile = filepath.Join(config.DatabaseDirectory, ".geoipupdate.lock")
147 }
148
149 config.URL = "https://" + host
150
151 proxyURL, err := parseProxy(proxy, proxyUserPassword)
152 if err != nil {
153 return nil, err
154 }
155 config.Proxy = proxyURL
156
157 // We used to recommend using 999999 / 000000000000 for free downloads and
158 // many people still use this combination. We need to check for the
159 // 000000000000 license key to ensure that a real AccountID of 999999 will
160 // work in the future.
161 if (config.AccountID == 0 || config.AccountID == 999999) && config.LicenseKey == "000000000000" {
162 config.AccountID = 0
163 config.LicenseKey = ""
164 }
165
166 return config, nil
167 }
168
169 var schemeRE = regexp.MustCompile(`(?i)\A([a-z][a-z0-9+\-.]*)://`)
170
171 func parseProxy(
172 proxy,
173 proxyUserPassword string,
174 ) (*url.URL, error) {
175 if proxy == "" {
176 return nil, nil
177 }
178
179 // If no scheme is provided, use http.
180 matches := schemeRE.FindStringSubmatch(proxy)
181 if matches == nil {
182 proxy = "http://" + proxy
183 } else {
184 scheme := strings.ToLower(matches[1])
185 // The http package only supports http and socks5.
186 if scheme != "http" && scheme != "socks5" {
187 return nil, errors.Errorf("unsupported proxy type: %s", scheme)
188 }
189 }
190
191 // Now that we have a scheme, we should be able to parse.
192 u, err := url.Parse(proxy)
193 if err != nil {
194 return nil, errors.Wrap(err, "error parsing proxy URL")
195 }
196
197 if !strings.Contains(u.Host, ":") {
198 u.Host += ":1080" // The 1080 default historically came from cURL.
199 }
200
201 // Historically if the Proxy option had a username and password they would
202 // override any specified in the ProxyUserPassword option. Continue that.
203 if u.User != nil {
204 return u, nil
205 }
206
207 if proxyUserPassword == "" {
208 return u, nil
209 }
210
211 userPassword := strings.SplitN(proxyUserPassword, ":", 2)
212 if len(userPassword) != 2 {
213 return nil, errors.New("proxy user/password is malformed")
214 }
215 u.User = url.UserPassword(userPassword[0], userPassword[1])
216
217 return u, nil
218 }
0 package main
1
2 import (
3 "io/ioutil"
4 "net/url"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11 )
12
13 func TestNewConfig(t *testing.T) {
14 tests := []struct {
15 Description string
16 Input string
17 Output *Config
18 Err string
19 }{
20 {
21 Description: "Default config",
22 Input: `# Please see https://dev.maxmind.com/geoip/geoipupdate/ for instructions
23 # on setting up geoipupdate, including information on how to download a
24 # pre-filled GeoIP.conf file.
25
26 # Enter your account ID and license key below. These are available from
27 # https://www.maxmind.com/en/my_license_key. If you are only using free
28 # GeoLite databases, you may leave the 0 values.
29 AccountID 0
30 LicenseKey 000000000000
31
32 # Enter the edition IDs of the databases you would like to update.
33 # Multiple edition IDs are separated by spaces.
34 EditionIDs GeoLite2-Country GeoLite2-City
35
36 # The remaining settings are OPTIONAL.
37
38 # The directory to store the database files. Defaults to DATADIR
39 # DatabaseDirectory DATADIR
40
41 # The server to use. Defaults to "updates.maxmind.com".
42 # Host updates.maxmind.com
43
44 # The proxy host name or IP address. You may optionally specify a
45 # port number, e.g., 127.0.0.1:8888. If no port number is specified, 1080
46 # will be used.
47 # Proxy 127.0.0.1:8888
48
49 # The user name and password to use with your proxy server.
50 # ProxyUserPassword username:password
51
52 # Whether to preserve modification times of files downloaded from the server.
53 # Defaults to "0".
54 # PreserveFileTimes 0
55
56 # The lock file to use. This ensures only one geoipupdate process can run at a
57 # time.
58 # Note: Once created, this lockfile is not removed from the filesystem.
59 # Defaults to ".geoipupdate.lock" under the DatabaseDirectory.
60 # LockFile DATADIR/.geoipupdate.lock
61 `,
62 Output: &Config{
63 DatabaseDirectory: filepath.Clean("/tmp"),
64 EditionIDs: []string{"GeoLite2-Country", "GeoLite2-City"},
65 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
66 URL: "https://updates.maxmind.com",
67 },
68 },
69 {
70 Description: "Default config, old names",
71 Input: `# Please see https://dev.maxmind.com/geoip/geoipupdate/ for instructions
72 # on setting up geoipupdate, including information on how to download a
73 # pre-filled GeoIP.conf file.
74
75 # Enter your account ID and license key below. These are available from
76 # https://www.maxmind.com/en/my_license_key. If you are only using free
77 # GeoLite databases, you may leave the 0 values.
78 UserId 0
79 LicenseKey 000000000000
80
81 # Enter the edition IDs of the databases you would like to update.
82 # Multiple edition IDs are separated by spaces.
83 ProductIds GeoLite2-Country GeoLite2-City
84
85 # The remaining settings are OPTIONAL.
86
87 # The directory to store the database files. Defaults to DATADIR
88 # DatabaseDirectory DATADIR
89
90 # The server to use. Defaults to "updates.maxmind.com".
91 # Host updates.maxmind.com
92
93 # The proxy host name or IP address. You may optionally specify a
94 # port number, e.g., 127.0.0.1:8888. If no port number is specified, 1080
95 # will be used.
96 # Proxy 127.0.0.1:8888
97
98 # The user name and password to use with your proxy server.
99 # ProxyUserPassword username:password
100
101 # Whether to preserve modification times of files downloaded from the server.
102 # Defaults to "0".
103 # PreserveFileTimes 0
104
105 # The lock file to use. This ensures only one geoipupdate process can run at a
106 # time.
107 # Note: Once created, this lockfile is not removed from the filesystem.
108 # Defaults to ".geoipupdate.lock" under the DatabaseDirectory.
109 # LockFile DATADIR/.geoipupdate.lock
110 `,
111 Output: &Config{
112 DatabaseDirectory: filepath.Clean("/tmp"),
113 EditionIDs: []string{"GeoLite2-Country", "GeoLite2-City"},
114 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
115 URL: "https://updates.maxmind.com",
116 },
117 },
118 {
119 Description: "Everything populated",
120 Input: `# Please see https://dev.maxmind.com/geoip/geoipupdate/ for instructions
121 # on setting up geoipupdate, including information on how to download a
122 # pre-filled GeoIP.conf file.
123
124 # Enter your account ID and license key below. These are available from
125 # https://www.maxmind.com/en/my_license_key. If you are only using free
126 # GeoLite databases, you may leave the 0 values.
127 AccountID 1234
128 LicenseKey abcdefghi
129
130 # Enter the edition IDs of the databases you would like to update.
131 # Multiple edition IDs are separated by spaces.
132 EditionIDs GeoLite2-Country GeoLite2-City GeoIP2-City
133
134 # The remaining settings are OPTIONAL.
135
136 # The directory to store the database files. Defaults to DATADIR
137 DatabaseDirectory /home
138
139 # The server to use. Defaults to "updates.maxmind.com".
140 Host updates.example.com
141
142 # The proxy host name or IP address. You may optionally specify a
143 # port number, e.g., 127.0.0.1:8888. If no port number is specified, 1080
144 # will be used.
145 Proxy 127.0.0.1:8888
146
147 # The user name and password to use with your proxy server.
148 ProxyUserPassword username:password
149
150 # Whether to preserve modification times of files downloaded from the server.
151 # Defaults to "0".
152 PreserveFileTimes 1
153
154 # The lock file to use. This ensures only one geoipupdate process can run at a
155 # time.
156 # Note: Once created, this lockfile is not removed from the filesystem.
157 # Defaults to ".geoipupdate.lock" under the DatabaseDirectory.
158 LockFile /usr/lock
159 `,
160 Output: &Config{
161 AccountID: 1234,
162 DatabaseDirectory: filepath.Clean("/tmp"), // Argument takes precedence
163 EditionIDs: []string{"GeoLite2-Country", "GeoLite2-City", "GeoIP2-City"},
164 LicenseKey: "abcdefghi",
165 LockFile: filepath.Clean("/usr/lock"),
166 Proxy: &url.URL{
167 Scheme: "http",
168 User: url.UserPassword("username", "password"),
169 Host: "127.0.0.1:8888",
170 },
171 PreserveFileTimes: true,
172 URL: "https://updates.example.com",
173 },
174 },
175 {
176 Description: "Invalid line",
177 Input: `AccountID 123
178 LicenseKey
179 # Host updates.maxmind.com
180 `,
181 Err: "invalid format on line 2",
182 },
183 {
184 Description: "Option is there multiple times",
185 Input: `AccountID 123
186 AccountID 456
187 `,
188 Err: "`AccountID' is in the config multiple times",
189 },
190 {
191 Description: "Option is there multiple times with different names",
192 Input: `AccountID 123
193 UserId 456
194 `,
195 Err: "`UserId' is in the config multiple times",
196 },
197 {
198 Description: "Invalid account ID",
199 Input: `AccountID 1a
200 `,
201 Err: `invalid account ID format: strconv.Atoi: parsing "1a": invalid syntax`,
202 },
203 {
204 Description: "Invalid PreserveFileTimes",
205 Input: `PreserveFileTimes true
206 `,
207 Err: "`PreserveFileTimes' must be 0 or 1",
208 },
209 {
210 Description: "Unknown option",
211 Input: `AccountID 123
212 EditionID GeoIP2-City
213 `,
214 Err: "unknown option on line 2",
215 },
216 {
217 Description: "Missing required key in options",
218 Input: ``,
219 Err: "the `EditionIDs` option is required",
220 },
221 {
222 Description: "LicenseKey is found but AccountID is not",
223 Input: `LicenseKey abcd
224 EditionIDs GeoIP2-City
225 `,
226 Err: "the `AccountID` option is required if the `LicenseKey` option is set",
227 },
228 {
229 Description: "AccountID is found but LicenseKey is not",
230 Input: `AccountID 123
231 EditionIDs GeoIP2-City`,
232 Err: "the `LicenseKey` option is required if the `AccountID` option is set",
233 },
234 {
235 Description: "AccountID 0 with the LicenseKey 000000000000 is treated as no AccountID/LicenseKey",
236 Input: `AccountID 0
237 LicenseKey 000000000000
238 EditionIDs GeoIP2-City`,
239 Output: &Config{
240 DatabaseDirectory: filepath.Clean("/tmp"),
241 EditionIDs: []string{"GeoIP2-City"},
242 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
243 URL: "https://updates.maxmind.com",
244 },
245 },
246 {
247 Description: "AccountID 999999 with the LicenseKey 000000000000 is treated as no AccountID/LicenseKey",
248 Input: `AccountID 999999
249 LicenseKey 000000000000
250 EditionIDs GeoIP2-City`,
251 Output: &Config{
252 DatabaseDirectory: filepath.Clean("/tmp"),
253 EditionIDs: []string{"GeoIP2-City"},
254 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
255 URL: "https://updates.maxmind.com",
256 },
257 },
258 {
259 Description: "AccountID 999999 with a non-000000000000 LicenseKey is treated normally",
260 Input: `AccountID 999999
261 LicenseKey abcd
262 EditionIDs GeoIP2-City`,
263 Output: &Config{
264 AccountID: 999999,
265 DatabaseDirectory: filepath.Clean("/tmp"),
266 EditionIDs: []string{"GeoIP2-City"},
267 LicenseKey: "abcd",
268 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
269 URL: "https://updates.maxmind.com",
270 },
271 },
272 {
273 Description: "Deprecated options",
274 Input: `AccountID 123
275 LicenseKey abcd
276 EditionIDs GeoIP2-City
277 Protocol http
278 SkipHostnameVerification 1
279 SkipPeerVerification 1
280 `,
281 Output: &Config{
282 AccountID: 123,
283 DatabaseDirectory: filepath.Clean("/tmp"),
284 EditionIDs: []string{"GeoIP2-City"},
285 LicenseKey: "abcd",
286 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
287 URL: "https://updates.maxmind.com",
288 },
289 },
290 {
291 Description: "CRLF line ending works",
292 Input: "AccountID 123\r\nLicenseKey 123\r\nEditionIDs GeoIP2-City\r\n",
293 Output: &Config{
294 AccountID: 123,
295 DatabaseDirectory: filepath.Clean("/tmp"),
296 EditionIDs: []string{"GeoIP2-City"},
297 LicenseKey: "123",
298 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
299 URL: "https://updates.maxmind.com",
300 },
301 },
302 {
303 Description: "CR line ending does not work",
304 Input: "AccountID 0\rLicenseKey 123\rEditionIDs GeoIP2-City\r",
305 Err: `invalid account ID format: strconv.Atoi: parsing "0 LicenseKey 123 EditionIDs GeoIP2-City": invalid syntax`,
306 },
307 {
308 Description: "Multiple spaces between option and value works",
309 Input: `AccountID 123
310 LicenseKey 456
311 EditionIDs GeoLite2-City GeoLite2-Country
312 `,
313 Output: &Config{
314 AccountID: 123,
315 DatabaseDirectory: filepath.Clean("/tmp"),
316 EditionIDs: []string{"GeoLite2-City", "GeoLite2-Country"},
317 LicenseKey: "456",
318 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
319 URL: "https://updates.maxmind.com",
320 },
321 },
322 {
323 Description: "Tabs between options and values works",
324 Input: "AccountID\t123\nLicenseKey\t\t456\nEditionIDs\t\t\tGeoLite2-City\t\t\t\tGeoLite2-Country\t\t\t\t\n",
325 Output: &Config{
326 AccountID: 123,
327 DatabaseDirectory: filepath.Clean("/tmp"),
328 EditionIDs: []string{"GeoLite2-City", "GeoLite2-Country"},
329 LicenseKey: "456",
330 LockFile: filepath.Clean("/tmp/.geoipupdate.lock"),
331 URL: "https://updates.maxmind.com",
332 },
333 },
334 }
335
336 tempFh, err := ioutil.TempFile("", "conf-test")
337 require.NoError(t, err)
338 tempName := tempFh.Name()
339 require.NoError(t, tempFh.Close())
340 defer func() {
341 _ = os.Remove(tempName)
342 }()
343
344 for _, test := range tests {
345 require.NoError(t, ioutil.WriteFile(tempName, []byte(test.Input), 0600))
346 config, err := NewConfig(tempName, "/tmp")
347 if test.Err == "" {
348 assert.NoError(t, err, test.Description)
349 } else {
350 assert.EqualError(t, err, test.Err, test.Description)
351 }
352 assert.Equal(t, test.Output, config, test.Description)
353 }
354 }
355
356 func TestParseProxy(t *testing.T) {
357 tests := []struct {
358 Proxy string
359 UserPassword string
360 Output string
361 Err string
362 }{
363 {
364 Proxy: "127.0.0.1",
365 Output: "http://127.0.0.1:1080",
366 },
367 {
368 Proxy: "127.0.0.1:8888",
369 Output: "http://127.0.0.1:8888",
370 },
371 {
372 Proxy: "http://127.0.0.1:8888",
373 Output: "http://127.0.0.1:8888",
374 },
375 {
376 Proxy: "socks5://127.0.0.1",
377 Output: "socks5://127.0.0.1:1080",
378 },
379 {
380 Proxy: "socks5://127.0.0.1:8888",
381 Output: "socks5://127.0.0.1:8888",
382 },
383 {
384 Proxy: "Garbage",
385 Output: "http://Garbage:1080",
386 },
387 {
388 Proxy: "ftp://127.0.0.1",
389 Err: "unsupported proxy type: ftp",
390 },
391 {
392 Proxy: "ftp://127.0.0.1:8888",
393 Err: "unsupported proxy type: ftp",
394 },
395 {
396 Proxy: "login:password@127.0.0.1",
397 Output: "http://login:password@127.0.0.1:1080",
398 },
399 {
400 Proxy: "login:password@127.0.0.1",
401 UserPassword: "something:else",
402 Output: "http://login:password@127.0.0.1:1080",
403 },
404 {
405 Proxy: "127.0.0.1",
406 UserPassword: "something:else",
407 Output: "http://something:else@127.0.0.1:1080",
408 },
409 {
410 Proxy: "127.0.0.1:8888",
411 UserPassword: "something:else",
412 Output: "http://something:else@127.0.0.1:8888",
413 },
414 {
415 Proxy: "user:password@127.0.0.1:8888",
416 UserPassword: "user2:password2",
417 Output: "http://user:password@127.0.0.1:8888",
418 },
419 {
420 Proxy: "http://user:password@127.0.0.1:8888",
421 UserPassword: "user2:password2",
422 Output: "http://user:password@127.0.0.1:8888",
423 },
424 }
425
426 for _, test := range tests {
427 output, err := parseProxy(test.Proxy, test.UserPassword)
428 if test.Err != "" {
429 assert.EqualError(t, err, test.Err)
430 assert.Nil(t, output)
431 } else {
432 assert.NoError(t, err)
433 assert.Equal(t, test.Output, output.String())
434 }
435 }
436 }
0 // +build !windows
1
2 package main
3
4 var (
5 // These match what you'd get building the C geoipupdate from source.
6 defaultConfigFile = "/usr/local/etc/GeoIP.conf"
7 defaultDatabaseDirectory = "/usr/local/share/GeoIP"
8 )
0 package main
1
2 import (
3 "os"
4 )
5
6 var (
7 // I'm not sure these make sense. However they can be overridden at runtime
8 // and in the configuration, so we have some flexibility.
9 defaultConfigFile = os.Getenv("SYSTEMDRIVE") + `\ProgramData\MaxMind\GeoIPUpdate\GeoIP.conf`
10 defaultDatabaseDirectory = os.Getenv("SYSTEMDRIVE") + `\ProgramData\MaxMind\GeoIPUpdate\GeoIP`
11 )
0 package main
1
2 import (
3 "bytes"
4 "compress/gzip"
5 "crypto/md5"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "log"
10 "net/http"
11 "net/url"
12 "os"
13 "path/filepath"
14 "strings"
15 "time"
16
17 "github.com/gofrs/flock"
18 "github.com/pkg/errors"
19 )
20
21 // version is the program's version number.
22 var version = "unknown"
23
24 func main() {
25 log.SetFlags(0)
26
27 args := getArgs()
28
29 config, err := NewConfig(args.ConfigFile, args.DatabaseDirectory)
30 if err != nil {
31 fatal(args, "Error loading configuration file", err)
32 }
33 if args.Verbose {
34 log.Printf("Using config file %s", args.ConfigFile)
35 log.Printf("Using database directory %s", config.DatabaseDirectory)
36 }
37
38 lock, err := setup(config, args.Verbose)
39 if err != nil {
40 fatal(args, "Error preparing to update", err)
41 }
42 defer func() {
43 if err := lock.Unlock(); err != nil {
44 fatal(args, "Error unlocking lock file", errors.Wrap(err, "unlocking"))
45 }
46 }()
47
48 if err := run(config, args.Verbose); err != nil {
49 fatal(args, "Error retrieving updates", err)
50 }
51 }
52
53 func fatal(
54 args *Args,
55 msg string,
56 err error,
57 ) {
58 if args.StackTrace {
59 log.Print(msg + fmt.Sprintf(": %+v", err))
60 } else {
61 log.Print(msg + fmt.Sprintf(": %s", err))
62 }
63 os.Exit(1)
64 }
65
66 func setup(
67 config *Config,
68 verbose bool,
69 ) (*flock.Flock, error) {
70 maybeSetProxy(config, verbose)
71
72 if err := checkEnvironment(config); err != nil {
73 return nil, err
74 }
75
76 lock := flock.New(config.LockFile)
77 ok, err := lock.TryLock()
78 if err != nil {
79 return nil, errors.Wrap(err, "error acquiring a lock")
80 }
81 if !ok {
82 return nil, errors.Errorf("could not acquire lock on %s", config.LockFile)
83 }
84 if verbose {
85 log.Printf("Acquired lock file lock (%s)", config.LockFile)
86 }
87
88 return lock, nil
89 }
90
91 // Do not set a timeout to allow for very slow connections. Note the client
92 // will have TCP KeepAlive's enabled by default due to using
93 // http.DefaultTransport (which uses a net.Dialer with KeepAlive set).
94 var client = &http.Client{}
95
96 func maybeSetProxy(
97 config *Config,
98 verbose bool,
99 ) {
100 if config.Proxy == nil {
101 return
102 }
103
104 if verbose {
105 log.Printf("Using proxy: %s", config.Proxy)
106 }
107 http.DefaultTransport.(*http.Transport).Proxy = http.ProxyURL(config.Proxy)
108 }
109
110 func checkEnvironment(
111 config *Config,
112 ) error {
113 fi, err := os.Stat(config.DatabaseDirectory)
114 if err != nil {
115 return errors.Wrap(err, "database directory is not available")
116 }
117
118 if !fi.IsDir() {
119 return errors.New("database directory is not a directory")
120 }
121
122 // I don't think there is a reliable cross platform way to check the
123 // directory is writable. We'll discover that when we try to write to it
124 // anyway.
125
126 return nil
127 }
128
129 func run(
130 config *Config,
131 verbose bool,
132 ) error {
133 for _, editionID := range config.EditionIDs {
134 if err := updateEdition(config, verbose, editionID); err != nil {
135 return errors.WithMessage(err, "error updating "+editionID)
136 }
137 }
138 return nil
139 }
140
141 func updateEdition(
142 config *Config,
143 verbose bool,
144 editionID string,
145 ) error {
146 filename, err := getFilename(config, verbose, editionID)
147 if err != nil {
148 return errors.WithMessage(err, "error retrieving filename")
149 }
150
151 md5, err := getCurrentMD5(config, verbose, filename)
152 if err != nil {
153 return errors.WithMessage(err, "error retrieving current MD5 of "+filename)
154 }
155
156 if err := maybeUpdate(
157 config,
158 verbose,
159 editionID,
160 filename,
161 md5,
162 ); err != nil {
163 return errors.WithMessage(err, "error updating")
164 }
165
166 return nil
167 }
168
169 func getFilename(
170 config *Config,
171 verbose bool,
172 editionID string,
173 ) (string, error) {
174 url := fmt.Sprintf(
175 "%s/app/update_getfilename?product_id=%s",
176 config.URL,
177 url.QueryEscape(editionID),
178 )
179
180 if verbose {
181 log.Printf("Performing get filename request to %s", url)
182 }
183 res, err := client.Get(url)
184 if err != nil {
185 return "", errors.Wrap(err, "error performing HTTP request")
186 }
187 defer func() {
188 if err := res.Body.Close(); err != nil {
189 log.Fatalf("Error closing response body: %+v", errors.Wrap(err, "closing body"))
190 }
191 }()
192
193 buf, err := ioutil.ReadAll(io.LimitReader(res.Body, 256))
194 if err != nil {
195 return "", errors.Wrap(err, "error reading response body")
196 }
197
198 if res.StatusCode != http.StatusOK {
199 return "", errors.Errorf("unexpected HTTP status code: %s: %s", res.Status, buf)
200 }
201
202 if len(buf) == 0 {
203 return "", errors.New("response body is empty")
204 }
205
206 if bytes.Count(buf, []byte("\n")) > 0 ||
207 bytes.Count(buf, []byte("\x00")) > 0 {
208 return "", errors.New("invalid characters in filename")
209 }
210
211 return string(buf), nil
212 }
213
214 const zeroMD5 = "00000000000000000000000000000000"
215
216 func getCurrentMD5(
217 config *Config,
218 verbose bool,
219 filename string,
220 ) (string, error) {
221 path := filepath.Join(config.DatabaseDirectory, filename)
222
223 fh, err := os.Open(path)
224 if err != nil {
225 if os.IsNotExist(err) {
226 if verbose {
227 log.Printf("Not calculating MD5 sum as file does not exist: %s", path)
228 }
229 return zeroMD5, nil
230 }
231 return "", errors.Wrap(err, "error opening file")
232 }
233 defer func() {
234 if err := fh.Close(); err != nil {
235 log.Fatalf("Error closing file: %+v", errors.Wrap(err, "closing file"))
236 }
237 }()
238
239 fi, err := fh.Stat()
240 if err != nil {
241 return "", errors.Wrap(err, "error stat'ing file")
242 }
243 if !fi.Mode().IsRegular() {
244 return "", errors.New("not a regular file")
245 }
246
247 h := md5.New()
248 if _, err := io.Copy(h, fh); err != nil {
249 return "", errors.Wrap(err, "error reading file")
250 }
251 sum := fmt.Sprintf("%x", h.Sum(nil))
252 if verbose {
253 log.Printf("Calculated MD5 sum for %s: %s", path, sum)
254 }
255 return sum, nil
256 }
257
258 func maybeUpdate(
259 config *Config,
260 verbose bool,
261 editionID,
262 filename,
263 md5 string,
264 ) error {
265 url := fmt.Sprintf(
266 "%s/geoip/databases/%s/update?db_md5=%s",
267 config.URL,
268 url.PathEscape(editionID),
269 url.QueryEscape(md5),
270 )
271
272 req, err := http.NewRequest(http.MethodGet, url, nil)
273 if err != nil {
274 return errors.Wrap(err, "error creating request")
275 }
276 if config.AccountID != 0 {
277 req.SetBasicAuth(fmt.Sprintf("%d", config.AccountID), config.LicenseKey)
278 }
279
280 if verbose {
281 log.Printf("Performing update request to %s", url)
282 }
283 res, err := client.Do(req)
284 if err != nil {
285 return errors.Wrap(err, "error performing HTTP request")
286 }
287 defer func() {
288 if err := res.Body.Close(); err != nil {
289 log.Fatalf("Error closing response body: %+v", errors.Wrap(err, "closing body"))
290 }
291 }()
292
293 if res.StatusCode == http.StatusNotModified {
294 if verbose {
295 log.Printf("No new updates available for %s", editionID)
296 }
297 return nil
298 }
299
300 if res.StatusCode != http.StatusOK {
301 buf, err := ioutil.ReadAll(io.LimitReader(res.Body, 256))
302 if err == nil {
303 return errors.Errorf("unexpected HTTP status code: %s: %s", res.Status, buf)
304 }
305 return errors.Errorf("unexpected HTTP status code: %s", res.Status)
306 }
307
308 newMD5 := res.Header.Get("X-Database-MD5")
309 if newMD5 == "" {
310 return errors.New("no X-Database-MD5 header found")
311 }
312 lastModified, err := getLastModified(res.Header)
313 if err != nil {
314 return err
315 }
316
317 return writeAndCheck(config, verbose, filename, res.Body, newMD5, lastModified)
318 }
319
320 func getLastModified(
321 headers http.Header,
322 ) (time.Time, error) {
323 lastModifiedStr := headers.Get("Last-Modified")
324 if lastModifiedStr == "" {
325 return time.Time{}, errors.New("no Last-Modified header found")
326 }
327
328 t, err := time.ParseInLocation(time.RFC1123, lastModifiedStr, time.UTC)
329 if err != nil {
330 return time.Time{}, errors.Wrap(err, "error parsing time")
331 }
332
333 return t, nil
334 }
335
336 func writeAndCheck(
337 config *Config,
338 verbose bool,
339 filename string,
340 body io.Reader,
341 newMD5 string,
342 lastModified time.Time,
343 ) error {
344 targetTest := filepath.Join(
345 config.DatabaseDirectory,
346 fmt.Sprintf("%s.test", filename),
347 )
348
349 fh, err := os.OpenFile(targetTest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
350 if err != nil {
351 return errors.Wrap(err, "error creating file")
352 }
353
354 gzReader, err := gzip.NewReader(body)
355 if err != nil {
356 _ = fh.Close()
357 _ = os.Remove(targetTest)
358 return errors.Wrap(err, "error creating gzip reader")
359 }
360
361 md5Writer := md5.New()
362 multiWriter := io.MultiWriter(fh, md5Writer)
363
364 if _, err := io.Copy(multiWriter, gzReader); err != nil {
365 _ = fh.Close()
366 _ = os.Remove(targetTest)
367 _ = gzReader.Close()
368 return errors.Wrap(err, "error reading/writing")
369 }
370
371 if err := gzReader.Close(); err != nil {
372 _ = fh.Close()
373 _ = os.Remove(targetTest)
374 return errors.Wrap(err, "error closing gzip reader")
375 }
376
377 if err := fh.Sync(); err != nil {
378 _ = fh.Close()
379 _ = os.Remove(targetTest)
380 return errors.Wrap(err, "error syncing file")
381 }
382
383 if err := fh.Close(); err != nil {
384 _ = os.Remove(targetTest)
385 return errors.Wrap(err, "error closing file")
386 }
387
388 gotMD5 := fmt.Sprintf("%x", md5Writer.Sum(nil))
389 if !strings.EqualFold(gotMD5, newMD5) {
390 _ = os.Remove(targetTest)
391 return errors.Errorf("MD5 of new database (%s) does not match expected MD5 (%s)",
392 gotMD5, newMD5)
393 }
394
395 target := filepath.Join(config.DatabaseDirectory, filename)
396
397 if err := os.Rename(targetTest, target); err != nil {
398 _ = os.Remove(targetTest)
399 return errors.New("error moving database into place")
400 }
401
402 if config.PreserveFileTimes {
403 if err := os.Chtimes(target, lastModified, lastModified); err != nil {
404 return errors.Wrap(err, "error setting times on file")
405 }
406 }
407
408 // fsync the directory. http://austingroupbugs.net/view.php?id=672
409
410 dh, err := os.Open(config.DatabaseDirectory)
411 if err != nil {
412 return errors.Wrap(err, "error opening database directory")
413 }
414 defer func() {
415 if err := dh.Close(); err != nil {
416 log.Fatalf("Error closing directory: %+v", errors.Wrap(err, "closing directory"))
417 }
418 }()
419
420 // We ignore Sync errors as they primarily happen on file systems that do
421 // not support sync.
422 _ = dh.Sync()
423
424 if verbose {
425 log.Printf("Updated %s", target)
426 }
427 return nil
428 }
0 package main
1
2 import (
3 "bytes"
4 "compress/gzip"
5 "crypto/md5"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "net/http"
10 "net/http/httptest"
11 "os"
12 "path/filepath"
13 "regexp"
14 "testing"
15 "time"
16
17 "github.com/stretchr/testify/assert"
18 "github.com/stretchr/testify/require"
19 )
20
21 func TestUpdateEdition(t *testing.T) {
22 tests := []struct {
23 Description string
24 CreateDirectory bool
25 DatabaseBefore string
26 DatabaseAfter string
27 FilenameStatus int
28 FilenameBody string
29 DownloadStatus int
30 DownloadBody string
31 DownloadHeaders map[string]string
32 ExpectedTime time.Time
33 Err string
34 }{
35 {
36 Description: "Initial download, success",
37 CreateDirectory: true,
38 DatabaseAfter: "database goes here",
39 FilenameStatus: http.StatusOK,
40 FilenameBody: "GeoIP2-City.mmdb",
41 DownloadStatus: http.StatusOK,
42 DownloadBody: "database goes here",
43 },
44 {
45 Description: "No update, success",
46 CreateDirectory: true,
47 DatabaseBefore: "database goes here",
48 DatabaseAfter: "database goes here",
49 FilenameStatus: http.StatusOK,
50 FilenameBody: "GeoIP2-City.mmdb",
51 DownloadStatus: http.StatusNotModified,
52 DownloadBody: "database goes here",
53 },
54 {
55 Description: "Update, success",
56 CreateDirectory: true,
57 DatabaseBefore: "database goes here",
58 DatabaseAfter: "new database goes here",
59 FilenameStatus: http.StatusOK,
60 FilenameBody: "GeoIP2-City.mmdb",
61 DownloadStatus: http.StatusOK,
62 DownloadBody: "new database goes here",
63 },
64 {
65 Description: "Update, success, and modification time is set",
66 CreateDirectory: true,
67 DatabaseBefore: "new database goes here",
68 DatabaseAfter: "newer database goes here",
69 FilenameStatus: http.StatusOK,
70 FilenameBody: "GeoIP2-City.mmdb",
71 DownloadStatus: http.StatusOK,
72 DownloadBody: "newer database goes here",
73 DownloadHeaders: map[string]string{
74 "Last-Modified": time.Date(2018, 7, 24, 0, 0, 0, 0, time.UTC).Format(time.RFC1123),
75 },
76 ExpectedTime: time.Date(2018, 7, 24, 0, 0, 0, 0, time.UTC),
77 },
78 {
79 Description: "Get filename fails",
80 CreateDirectory: true,
81 DatabaseBefore: "database goes here",
82 DatabaseAfter: "database goes here",
83 FilenameStatus: http.StatusBadRequest,
84 Err: "error retrieving filename: unexpected HTTP status code: 400 Bad Request",
85 },
86 {
87 Description: "Get filename is missing body",
88 CreateDirectory: true,
89 DatabaseBefore: "database goes here",
90 DatabaseAfter: "database goes here",
91 FilenameStatus: http.StatusOK,
92 Err: "error retrieving filename: response body is empty",
93 },
94 {
95 Description: "Get filename has newlines",
96 CreateDirectory: true,
97 DatabaseBefore: "database goes here",
98 DatabaseAfter: "database goes here",
99 FilenameStatus: http.StatusOK,
100 FilenameBody: "bad\nfilename",
101 Err: "error retrieving filename: invalid characters in filename",
102 },
103 {
104 Description: "Download request fails",
105 CreateDirectory: true,
106 DatabaseBefore: "database goes here",
107 DatabaseAfter: "database goes here",
108 FilenameStatus: http.StatusOK,
109 FilenameBody: "GeoIP2-City.mmdb",
110 DownloadStatus: http.StatusBadRequest,
111 Err: "error updating: unexpected HTTP status code: 400 Bad Request",
112 },
113 {
114 Description: "Download request is missing X-Database-MD5",
115 CreateDirectory: true,
116 DatabaseBefore: "database goes here",
117 DatabaseAfter: "database goes here",
118 FilenameStatus: http.StatusOK,
119 FilenameBody: "GeoIP2-City.mmdb",
120 DownloadStatus: http.StatusOK,
121 DownloadBody: "new database goes here",
122 DownloadHeaders: map[string]string{
123 "X-Database-MD5": "",
124 },
125 Err: "error updating: no X-Database-MD5 header found",
126 },
127 {
128 Description: "Download fails because database directory does not exist",
129 FilenameStatus: http.StatusOK,
130 FilenameBody: "GeoIP2-City.mmdb",
131 DownloadStatus: http.StatusOK,
132 DownloadBody: "new database goes here",
133 Err: `error updating: error creating file: open \S+GeoIP2-City\.mmdb\.test: (?:no such file or directory|The system cannot find the path specified)`,
134 },
135 {
136 Description: "Download fails because provided checksum does not match",
137 CreateDirectory: true,
138 DatabaseBefore: "database goes here",
139 DatabaseAfter: "database goes here",
140 FilenameStatus: http.StatusOK,
141 FilenameBody: "GeoIP2-City.mmdb",
142 DownloadStatus: http.StatusOK,
143 DownloadBody: "new database goes here",
144 DownloadHeaders: map[string]string{
145 "X-Database-MD5": "5d41402abc4b2a76b9719d911017c592", // "hello"
146 },
147 Err: `error updating: MD5 of new database \(985ecf3d7959b146208b3dc0189b21a5\) does not match expected MD5 \(5d41402abc4b2a76b9719d911017c592\)`,
148 },
149 {
150 Description: "Download request redirects are followed",
151 CreateDirectory: true,
152 DatabaseBefore: "database goes here",
153 DatabaseAfter: "database goes here",
154 FilenameStatus: http.StatusOK,
155 FilenameBody: "GeoIP2-City.mmdb",
156 DownloadStatus: http.StatusMovedPermanently,
157 DownloadHeaders: map[string]string{
158 "Location": "/go-here",
159 },
160 },
161 {
162 Description: "MD5 sums are case insensitive",
163 CreateDirectory: true,
164 DatabaseBefore: "database goes here",
165 DatabaseAfter: "new database goes here",
166 FilenameStatus: http.StatusOK,
167 FilenameBody: "GeoIP2-City.mmdb",
168 DownloadStatus: http.StatusOK,
169 DownloadBody: "new database goes here",
170 DownloadHeaders: map[string]string{
171 "X-Database-MD5": "985ECF3D7959B146208B3DC0189B21A5",
172 },
173 },
174 }
175
176 updateRE := regexp.MustCompile(`\A/geoip/databases/\S+/update\z`)
177
178 tempDir, err := ioutil.TempDir("", "gutest-")
179 require.NoError(t, err)
180 err = os.RemoveAll(tempDir)
181 require.NoError(t, err)
182
183 for _, test := range tests {
184 server := httptest.NewServer(
185 http.HandlerFunc(
186 func(rw http.ResponseWriter, r *http.Request) {
187 if r.URL.Path == "/app/update_getfilename" {
188 rw.WriteHeader(test.FilenameStatus)
189 _, err := rw.Write([]byte(test.FilenameBody))
190 require.NoError(t, err)
191 return
192 }
193
194 if updateRE.MatchString(r.URL.Path) {
195 buf := &bytes.Buffer{}
196 gzWriter := gzip.NewWriter(buf)
197 md5Writer := md5.New()
198 multiWriter := io.MultiWriter(gzWriter, md5Writer)
199 _, err := multiWriter.Write([]byte(test.DownloadBody))
200 require.NoError(t, err)
201 err = gzWriter.Close()
202 require.NoError(t, err)
203
204 rw.Header().Set(
205 "X-Database-MD5",
206 fmt.Sprintf("%x", md5Writer.Sum(nil)),
207 )
208 if test.DownloadStatus == http.StatusOK {
209 rw.Header().Set(
210 "Last-Modified",
211 time.Now().Format(time.RFC1123),
212 )
213 }
214 for k, v := range test.DownloadHeaders {
215 rw.Header().Set(k, v)
216 }
217
218 rw.WriteHeader(test.DownloadStatus)
219
220 if test.DownloadStatus == http.StatusOK {
221 _, err := rw.Write(buf.Bytes())
222 require.NoError(t, err)
223 }
224
225 return
226 }
227
228 if r.URL.Path == "/go-here" {
229 rw.WriteHeader(http.StatusNotModified)
230 return
231 }
232
233 rw.WriteHeader(http.StatusBadRequest)
234 },
235 ),
236 )
237
238 config := &Config{
239 AccountID: 123,
240 DatabaseDirectory: tempDir,
241 EditionIDs: []string{"GeoIP2-City"},
242 LicenseKey: "testing",
243 LockFile: filepath.Join(tempDir, ".geoipupdate.lock"),
244 URL: server.URL,
245 }
246 verbose := false
247 if !test.ExpectedTime.IsZero() {
248 config.PreserveFileTimes = true
249 }
250
251 if test.CreateDirectory {
252 err := os.Mkdir(config.DatabaseDirectory, 0755)
253 require.NoError(t, err)
254 }
255
256 currentDatabasePath := filepath.Join(
257 config.DatabaseDirectory,
258 "GeoIP2-City.mmdb",
259 )
260 if test.DatabaseBefore != "" {
261 err := ioutil.WriteFile(
262 currentDatabasePath,
263 []byte(test.DatabaseBefore),
264 0644,
265 )
266 require.NoError(t, err)
267 }
268
269 err := updateEdition(config, verbose, config.EditionIDs[0])
270 if test.Err == "" {
271 assert.NoError(t, err, test.Description)
272 } else {
273 // regex because some errors have filenames.
274 assert.Regexp(t, test.Err, err.Error(), test.Description)
275 }
276
277 server.Close()
278
279 if test.DatabaseAfter != "" {
280 buf, err := ioutil.ReadFile(currentDatabasePath)
281 require.NoError(t, err, test.Description)
282 assert.Equal(t, test.DatabaseAfter, string(buf))
283 }
284
285 if !test.ExpectedTime.IsZero() {
286 fi, err := os.Stat(currentDatabasePath)
287 require.NoError(t, err)
288 assert.WithinDuration(t, test.ExpectedTime, fi.ModTime(), 0)
289 }
290
291 if test.CreateDirectory {
292 err := os.RemoveAll(config.DatabaseDirectory)
293 require.NoError(t, err)
294 }
295 }
296 }
297
298 func TestGetCurrentMD5(t *testing.T) {
299 tempDir, err := ioutil.TempDir("", "gutest-")
300 require.NoError(t, err)
301 defer func() {
302 err := os.RemoveAll(tempDir)
303 require.NoError(t, err)
304 }()
305
306 config := &Config{
307 DatabaseDirectory: tempDir,
308 }
309
310 dirFile := filepath.Join(tempDir, "mydir")
311 err = os.Mkdir(dirFile, 0755)
312 require.NoError(t, err)
313
314 verbose := false
315 md5, err := getCurrentMD5(config, verbose, "mydir")
316 assert.EqualError(t, err, "not a regular file")
317 assert.Equal(t, "", md5)
318 }
0 // +build go1.12
1
2 package main
3
4 import "runtime/debug"
5
6 func init() {
7 if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "(devel)" {
8 version = info.Main.Version
9 }
10 }
0 # Please see https://dev.maxmind.com/geoip/geoipupdate/ for instructions
1 # on setting up geoipupdate, including information on how to download a
2 # pre-filled GeoIP.conf file.
3
4 # Enter your account ID and license key below. These are available from
5 # https://www.maxmind.com/en/my_license_key. If you are only using free
6 # GeoLite databases, you may leave the 0 values.
7 AccountID 0
8 LicenseKey 000000000000
9
10 # Enter the edition IDs of the databases you would like to update.
11 # Multiple edition IDs are separated by spaces.
12 EditionIDs GeoLite2-Country GeoLite2-City
13
14 # The remaining settings are OPTIONAL.
15
16 # The directory to store the database files. Defaults to DATADIR
17 # DatabaseDirectory DATADIR
18
19 # The server to use. Defaults to "updates.maxmind.com".
20 # Host updates.maxmind.com
21
22 # The proxy host name or IP address. You may optionally specify a
23 # port number, e.g., 127.0.0.1:8888. If no port number is specified, 1080
24 # will be used.
25 # Proxy 127.0.0.1:8888
26
27 # The user name and password to use with your proxy server.
28 # ProxyUserPassword username:password
29
30 # Whether to preserve modification times of files downloaded from the server.
31 # Defaults to "0".
32 # PreserveFileTimes 0
33
34 # The lock file to use. This ensures only one geoipupdate process can run at a
35 # time.
36 # Note: Once created, this lockfile is not removed from the filesystem.
37 # Defaults to ".geoipupdate.lock" under the DatabaseDirectory.
38 # LockFile DATADIR/.geoipupdate.lock
0 #!/usr/bin/env perl
1 use strict;
2 use warnings;
3
4 use File::Temp qw( tempfile );
5
6 sub main {
7 my $build_dir = $ARGV[0] // 'build';
8
9 _make_man(
10 'geoipupdate',
11 1,
12 "$build_dir/geoipupdate.md",
13 "$build_dir/geoipupdate.1",
14 );
15 _make_man(
16 'GeoIP.conf',
17 5,
18 "$build_dir/GeoIP.conf.md",
19 "$build_dir/GeoIP.conf.5",
20 );
21 return 1;
22 }
23
24 sub _make_man {
25 my ( $name, $section, $input, $output ) = @_;
26
27 my ( $fh, $tmp ) = tempfile();
28 binmode $fh or die $!;
29 print {$fh} "% $name($section)\n\n" or die $!;
30 my $contents = _read($input);
31 print {$fh} $contents or die $!;
32 close $fh or die $!;
33
34 system(
35 'pandoc',
36 '-s',
37 '-f', 'markdown',
38 '-t', 'man',
39 $tmp,
40 '-o', $output,
41 ) == 0 or die 'pandoc failed';
42
43 return;
44 }
45
46 sub _read {
47 my ($file) = @_;
48 open my $fh, '<', $file or die $!;
49 binmode $fh or die $!;
50 my $contents = '';
51 while ( !eof($fh) ) {
52 my $line = <$fh>;
53 die 'error reading' unless defined $line;
54 $contents .= $line;
55 }
56 close $fh or die $!;
57 return $contents;
58 }
59
60 exit( main() ? 0 : 1 );
0 #!/bin/bash
1
2 set -eu -o pipefail
3
4 changelog=$(cat CHANGELOG.md)
5
6
7 if [[ -z ${GITHUB_TOKEN:-} ]]; then
8 echo 'GITHUB_TOKEN must be set for goreleaser!'
9 exit 1
10 fi
11
12 regex='
13 ## ([0-9]+\.[0-9]+\.[0-9]+) \(([0-9]{4}-[0-9]{2}-[0-9]{2})\)
14
15 ((.|
16 )*)'
17
18 if [[ ! $changelog =~ $regex ]]; then
19 echo "Could not find date line in change log!"
20 exit 1
21 fi
22
23 version="${BASH_REMATCH[1]}"
24 date="${BASH_REMATCH[2]}"
25 notes="$(echo "${BASH_REMATCH[3]}" | sed -n -e '/^## [0-9]\+\.[0-9]\+\.[0-9]\+/,$!p')"
26
27 if [[ "$date" != $(date +"%Y-%m-%d") ]]; then
28 echo "$date is not today!"
29 exit 1
30 fi
31
32 if [ -n "$(git status --porcelain)" ]; then
33 echo ". is not clean." >&2
34 exit 1
35 fi
36
37 tag="v$version"
38
39 echo $'\nRelease notes:'
40 echo "$notes"
41
42
43 read -p "Continue? (y/n) " ok
44
45 if [ "$ok" != "y" ]; then
46 echo "Aborting"
47 exit 1
48 fi
49
50 echo "Creating tag $tag"
51
52 message="$version
53
54 $notes"
55
56 git tag -a -m "$message" "$tag"
57
58 # goreleaser's `--rm-dist' should clear out `dist', but it didn't work for me.
59 rm -rf dist
60 goreleaser release --rm-dist -f .goreleaser.yml --release-notes <(echo "$message")
61 make clean BUILDDIR=.
62
63 rm -rf dist
64 goreleaser release --rm-dist -f .goreleaser-windows.yml --skip-publish
65 hub release edit -m "$message" \
66 -a "dist/geoipupdate_${version}_windows_386.zip" \
67 -a "dist/geoipupdate_${version}_windows_amd64.zip" \
68 -a dist/checksums-windows.txt \
69 "$tag"
70 make clean BUILDDIR=.
71
72 rm -rf dist
73 goreleaser release --rm-dist -f .goreleaser-packages.yml --skip-publish
74
75 git push
76
77 hub release edit -m "$message" \
78 -a dist/checksums-dpkg-rpm.txt \
79 -a "dist/geoipupdate_${version}_linux_386.deb" \
80 -a "dist/geoipupdate_${version}_linux_amd64.deb" \
81 -a "dist/geoipupdate_${version}_linux_386.rpm" \
82 -a "dist/geoipupdate_${version}_linux_amd64.rpm" \
83 "$tag"
84 make clean BUILDDIR=.
0 # NAME
1
2 GeoIP.conf - Configuration file for geoipupdate
3
4 # SYNOPSIS
5
6 This file allows you to configure your `geoipupdate` program to
7 download GeoIP2, GeoLite2, and GeoIP Legacy databases.
8
9 # DESCRIPTION
10
11 The file consists of one setting per line. Lines starting with `#`
12 are comments and will not be processed. All setting keywords are case
13 sensitive.
14
15 ## Required settings:
16
17 `EditionIDs`
18
19 : List of database edition IDs. Edition IDs may consist
20 of letters, digits, and dashes (e.g., "GeoIP2-City", "106"). Note: this
21 was formerly called `ProductIds`.
22
23 ## Optional settings:
24
25 `AccountID`
26
27 : Your MaxMind account ID. This was formerly known as `UserId`.
28
29 `DatabaseDirectory`
30
31 : The directory to store the database files. If not set, the default is
32 DATADIR. This can be overridden at run time by the `-d` command line
33 argument.
34
35 `Host`
36
37 : The host name of the server to use. The default is `updates.maxmind.com`.
38
39 `Proxy`
40
41 : The proxy host name or IP address. You may optionally specify
42 a port number, e.g., `127.0.0.1:8888`. If no port number is specified,
43 1080 will be used.
44
45 `ProxyUserPassword`
46
47 : The proxy user name and password, separated by a colon. For instance,
48 `username:password`.
49
50 `PreserveFileTimes`
51
52 : Whether to preserve modification times of files downloaded from the
53 server. This option is either `0` or `1`. The default is `0`.
54
55 `LicenseKey`
56
57 : Your case-sensitive MaxMind license key.
58
59 `LockFile`
60
61 : The lock file to use. This ensures only one `geoipupdate` process can run
62 at a time. Note: Once created, this lockfile is not removed from the
63 filesystem. The default is `.geoipupdate.lock` under the
64 `DatabaseDirectory`.
65
66 ## Deprecated settings:
67
68 The following are deprecated and will be ignored if present:
69
70 `Protocol`
71
72 `SkipPeerVerification`
73
74 `SkipHostnameVerification`
75
76 # FILES
77
78 `GeoIP.conf`
79
80 : Default `geoipupdate` configuration file.
81
82 # SEE ALSO
83
84 `geoipupdate`(1)
0 # NAME
1
2 geoipupdate - GeoIP2, GeoLite2, and GeoIP Legacy Update Program
3
4 # SYNOPSIS
5
6 geoipupdate [-Vvh] [-f CONFIG_FILE] [-d TARGET_DIRECTORY]
7
8 # DESCRIPTION
9
10 `geoipupdate` automatically updates GeoIP2, GeoLite2, and GeoIP Legacy
11 databases. The program connects to the MaxMind GeoIP Update server to
12 check for new databases. If a new database is available, the program will
13 download and install it.
14
15 If you are using a firewall, you must have the DNS and HTTPS ports
16 open.
17
18 # OPTIONS
19
20
21 `-d`, `--database-directory`
22
23 : Install databases to a custom directory. This is optional. If provided, it
24 overrides any `DatabaseDirectory` set in the configuration file.
25
26 `-f`, `--config-file`
27
28 : The configuration file to use. See `GeoIP.conf` and its documentation for
29 more information. This is optional. It defaults to CONFFILE.
30
31 `-h`, `--help`
32
33 : Display help and exit.
34
35 `--stack-trace`
36
37 : Show a stack trace on any error message. This is primarily useful for
38 debugging.
39
40 `-V`, `--version`
41
42 : Display version information and exit.
43
44 `-v`, `--verbose`
45
46 : Enable verbose mode. Prints out the steps that `geoipupdate` takes.
47
48 # USAGE
49
50 Typically you should run `geoipupdate` weekly. On most Unix-like systems,
51 this can be achieved by using cron. Below is a sample crontab file that
52 runs `geoipupdate` on each Wednesday at noon:
53
54 # top of crontab
55
56 MAILTO=your@email.com
57
58 0 12 * * 3 geoipupdate
59
60 # end of crontab
61
62
63 To use with a proxy server, update your `GeoIP.conf` file as specified
64 in the `GeoIP.conf` man page or set the `http_proxy` environment
65 variable.
66
67 # RETURN CODES
68
69 `geoipupdate` returns 0 on success and 1 on error.
70
71 # FILES
72
73 * `GeoIP.conf` - Configuration file for GeoIP Update. See the
74 `GeoIP.conf` documentation for more information.
75
76 # AUTHOR
77
78 Written by William Storey.
79
80 # REPORTING BUGS
81
82 Report bugs to [support@maxmind.com](mailto:support@maxmind.com).
83
84 # COPYRIGHT
85
86 This software is Copyright (c) 2018-2019 by MaxMind, Inc.
87
88 This is free software, licensed under the Apache License, Version 2.0 or
89 the MIT License, at your option.
90
91 # MORE INFORMATION
92
93 Visit [our website](https://www.maxmind.com/en/geoip2-services-and-databases)
94 to learn more about the GeoIP2 and GeoIP Legacy databases or to sign up
95 for a subscription.
96
97 # SEE ALSO
98
99 `GeoIP.conf`(5)
0 module github.com/maxmind/geoipupdate
1
2 go 1.12
3
4 require (
5 github.com/davecgh/go-spew v1.1.1 // indirect
6 github.com/gofrs/flock v0.7.1
7 github.com/kr/pretty v0.1.0 // indirect
8 github.com/pkg/errors v0.8.1
9 github.com/spf13/pflag v1.0.3
10 github.com/stretchr/testify v1.4.0
11 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
12 )
0 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
4 github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
5 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
6 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
7 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
8 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
9 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
10 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
11 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
12 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
15 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
16 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
17 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
18 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
19 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
20 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
21 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
22 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
23 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=