├── opennic-up.conf ├── opennic-up.service ├── opennic-up.timer ├── .gitignore ├── README.md ├── opennic-up └── LICENSE /opennic-up.conf: -------------------------------------------------------------------------------- 1 | # user= 2 | # auth= 3 | # maxretain=3 4 | # minreliability=90 5 | # initdns=185.121.177.177 6 | -------------------------------------------------------------------------------- /opennic-up.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=opennic-up DNS update 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | ExecStart=/usr/bin/opennic-up 8 | -------------------------------------------------------------------------------- /opennic-up.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Schedule opennic-up DNS update 3 | 4 | [Timer] 5 | OnCalendar=Wed,Sat *-*-* 11:00:00 6 | Persistent=true 7 | 8 | [Install] 9 | WantedBy=timers.target 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.deb 17 | *.gz 18 | *.tgz 19 | *.iso 20 | *.jar 21 | *.rar 22 | *.tar 23 | *.zip 24 | *.xz 25 | 26 | # Logs and databases # 27 | ###################### 28 | *.log 29 | *.sql 30 | *.sqlite 31 | 32 | # testing # 33 | ########### 34 | test/ 35 | 36 | # Arch # 37 | ######## 38 | pkg/ 39 | src/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opennic-up 2 | 3 | **OpenNIC auto DNS updater** 4 | 5 | ## Installation 6 | ### Manual installation 7 | The `opennic-up` Bash script can be downloaded to your preferred location. 8 | The systemd service and timer provided are to be copied to `/usr/lib/systemd/system/`. 9 | 10 | ### Arch Linux package 11 | If you use [Arch Linux][1], a package is available [here][2] and provides a full integration of the automated update process. 12 | 13 | ### Gentoo package 14 | If you use [Gentoo][5], a package is available [here][6] and provides full integration for the automated update process. 15 | 16 | ### Scheduled update with systemd time 17 | A systemd timer unit is provided, to enable and start the timer that will update the DNS servers twice a week, use: 18 | ``` 19 | # systemctl enable --now opennic-up.timer 20 | ``` 21 | ### Dependencies 22 | The tools *awk*, *sort*, *uniq*, *curl*, *fping*, *xargs* and *drill* are required and must be found in the environment path. 23 | Network Manager is an optional dependency and will be used if installed. 24 | 25 | #### Arch Linux 26 | For Arch Linux users this corresponds to two dependencies on top of the base distribution which will be installed if not already present: `fping` and `ldns`. 27 | 28 | #### RedHat, CentOS, Fedora 29 | You will need to install `fping` and `ldns`. On Fedora, you will need to install `ldns-utils`. 30 | 31 | #### Ubuntu 32 | For Ubuntu users, *drill* is provided by the `ldnsutils` package. 33 | 34 | #### Gentoo, Funtoo 35 | You will need to install `net-analyzer/fping`, `net-dns/ldns-utils`, and `net-misc/curl`. 36 | 37 | ## Syntax 38 | 39 | `# opennic-up [options]` 40 | ``` 41 | options: 42 | -q quiet 43 | -v version 44 | -h help 45 | -f custom resolv.conf file 46 | ``` 47 | 48 | By default, it replaces the DNS servers with the 3 most responsive [OpenNIC][0] DNS servers for your location. 49 | 50 | * If Network Manager *nmcli* is found in the path, it is used to update the DNS entries 51 | * Otherwise the `/etc/resolv.conf` file is updated directly with the new nameservers, keeping the other options untouched 52 | * When `-f` is used, Network Manager is ignored and the custom `resolv.conf` will receive the update 53 | 54 | ## Configuration 55 | 56 | `opennic-up.conf` is the configuration file for *opennic-up*. 57 | 58 | *opennic-up* looks for the file at the location `/etc/opennic-up.conf`. Alternatively it can be saved in the user location `~/.config/opennic-up/opennic-up.conf` and in this case it takes precedence over the former. 59 | 60 | * The configuration file defines the OpenNIC [member][3]'s **user** and **auth** used to register one's IP for [whitelisting][4]. For example: 61 | ``` 62 | user=myusername 63 | auth=TbuARbBxHHGznNScvVLKZDDR9ZGVKdhqxj8dkzCQ 64 | ``` 65 | * The number of DNS servers to retain, 3 by default, can be changed using the **maxretain** option: 66 | ``` 67 | maxretain=2 68 | ``` 69 | * The minimum required reliability of DNS servers as indicated in the retrieved server list, 90 by default (for 90% reliability), can be changed using the **minreliability** option: 70 | ``` 71 | minreliability=2 72 | ``` 73 | * The DNS server used for the initial api.opennicproject.org's name resolution, more than one can be provided for fallback (space separated): 74 | ``` 75 | initdns=1.1.1.1 208.67.222.123 76 | ``` 77 | 78 | [0]: https://www.opennicproject.org 79 | [1]: https://www.archlinux.org 80 | [2]: https://aur.archlinux.org/packages/opennic-up 81 | [3]: https://www.opennicproject.org/members 82 | [4]: https://wiki.opennic.org/api/whitelist 83 | [5]: https://www.gentoo.org 84 | [6]: https://github.com/gentoo-mirror/rage/tree/master/net-dns/opennic-up 85 | -------------------------------------------------------------------------------- /opennic-up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | readonly script="opennic-up" version="1.2.4" 3 | resolvconf="/etc/resolv.conf" 4 | usefile=0 5 | 6 | # pings run in parallel, send $1 pings to each target 7 | multiping() { 8 | fping -q -p 1000 -r 0 -c "$1" "${@:2}" 2>&1 9 | } 10 | 11 | # dns lookup nameserver hostname 12 | dnslookup() { 13 | drill A "$2" @"$1" | awk -e '$1 == domain && $3 == "IN" && $4 == "A" {print $5; exit}' domain="$2" 14 | } 15 | 16 | showhelp() { 17 | cat << EOF 18 | usage: $script [options] 19 | options: 20 | -q quiet, show less information 21 | -v display version 22 | -h help 23 | -f custom resolv.conf file 24 | EOF 25 | } 26 | 27 | log () { 28 | [ "$quiet" -eq 0 ] && echo "$@" >&2 29 | } 30 | 31 | logn () { 32 | [ "$quiet" -eq 0 ] && echo -n "$@" >&2 33 | } 34 | 35 | error() { 36 | printf "ERROR: %s\n" "$1" >&2 37 | exit 1 38 | } 39 | 40 | warning() { 41 | printf "WARNING: %s\n" "$1" >&2 42 | } 43 | 44 | apicurl() { 45 | curl --silent --connect-timeout 60 --resolve "$apihost:443:$apiip" "$1" 46 | } 47 | 48 | check_command() { 49 | command -v "$1" >/dev/null 2>&1 || error "$1: is required but cannot be found in the environment path" 50 | } 51 | 52 | # arguments handling 53 | quiet=0 54 | while getopts ":hvqf:" opt; do 55 | case "$opt" in 56 | h) 57 | showhelp 58 | exit 0 59 | ;; 60 | q) quiet=1 61 | ;; 62 | v) echo "$script $version" 63 | exit 0 64 | ;; 65 | f) resolvconf=$OPTARG 66 | usefile=1 67 | ;; 68 | :) echo "Missing option argument for -$OPTARG" >&2 69 | exit 1 70 | ;; 71 | ?) echo "Invalid argument -$OPTARG" >&2 72 | showhelp 73 | exit 1 74 | ;; 75 | esac 76 | done 77 | 78 | # check needed tools are present 79 | for needed in awk sort curl fping drill; do 80 | check_command "$needed" 81 | done 82 | 83 | # source opennic-up config 84 | for p in /etc ~/.config/opennic-up; do 85 | configfile=$p/opennic-up.conf 86 | [ -r "$configfile" ] && . "$configfile" 87 | done 88 | 89 | initdns=${initdns:-"192.3.165.37 147.182.243.49 137.184.12.79"} 90 | # retrieve first responding dns from initdns list 91 | log "Selecting DNS among $initdns..." 92 | respondingdns=$(multiping 2 $initdns | awk -F/ '$5 + 0.0 < 10' | awk '{print $1;exit}') 93 | echo "$respondingdns" 94 | if [ -z "$respondingdns" ]; then 95 | # none responding, network may be down, wait for first 96 | waitdns=$(echo "$initdns" | awk '{print $1}') 97 | log "Waiting for $waitdns..." 98 | fping -q -r 10 "$waitdns" && respondingdns="$waitdns" 99 | fi 100 | apihost=${apihost:-"api.opennicproject.org"} 101 | if [ -n "$respondingdns" ]; then 102 | log "Using DNS $respondingdns to retrieve $apihost's IP" 103 | apiip=$(dnslookup "$respondingdns" "$apihost") 104 | fi 105 | apiip=${apiip:-"116.203.98.109"} 106 | log "Using $apiip as API host" 107 | 108 | # record my IP in whitelist if my account login parameters have been provided 109 | if [ -n "$user" ] && [ -n "$auth" ]; then 110 | log "Updating whitelist with IP for user: $user" 111 | wlapiip=${wlapiip:-"161.97.219.82"} 112 | curl --silent --connect-timeout 60 --insecure "https://$wlapiip/ip/update/?user=$user&auth=$auth" >/dev/null 113 | fi 114 | 115 | # query the API: list format, ipv4 only, 200 sites, no server admin sorting, including servers with blocklist and IP whitelisting 116 | apiurl="https://$apihost/geoip/?list&ipv=4&res=200&adm=0&bl&wl" 117 | log "$apiurl" 118 | allhosts=$(apicurl "$apiurl") 119 | 120 | [ -z "$allhosts" ] && error 'API not available' 121 | 122 | # filter hosts with more than 90% reliability 123 | myminreliability=${minreliability:-90} 124 | reliable=$(awk -F'#' -v minrel="$myminreliability" '$3 + 0.0 > minrel {print $1}' <<< "$allhosts") 125 | reliablecount=$(wc -l <<< "$reliable") 126 | 127 | [ "$reliablecount" -lt 1 ] && error 'Not enough OpenNIC servers available' 128 | 129 | #pinging the hosts 130 | logn "Pinging $reliablecount hosts to determine the top ones..." 131 | pingresults=$(multiping 15 $reliable) 132 | 133 | # packet loss must be below 10%, then sort the servers by their average response time, eventually keep only the IP column 134 | responsive=$(awk -F/ '$5 + 0.0 < 10' <<< "$pingresults" | sort -t/ -nk8 | awk '{print $1}') 135 | responsivecount=$(wc -l <<< "$responsive") 136 | log "resulting in $responsivecount responsive hosts" 137 | 138 | mymaxretain=${maxretain:-3} 139 | [ "$responsivecount" -lt 1 ] && error 'Not enough responsive OpenNIC servers available' 140 | retain=$((mymaxretain > responsivecount ? responsivecount : mymaxretain)) 141 | 142 | # we retain the top servers for our DNS 143 | log "Selected top $retain hosts:" 144 | myhosts=$(head -n $retain <<< "$responsive") 145 | nameservers="" 146 | for dns in $myhosts; do 147 | log "$(grep -F "$dns" <<< "$allhosts")" 148 | nameservers+="nameserver $dns"$'\n' 149 | done 150 | printf "%s" "$nameservers" 151 | 152 | if [ $usefile -eq 0 ] && command -v nmcli >/dev/null 2>&1; then 153 | # nmcli: replace with our DNS all active connections 154 | for id in $(nmcli -terse -fields UUID connection show --active); do 155 | currentdnss=$(nmcli -terse -fields ipv4.dns connection show "$id" | cut -d: -f2- | tr "," "\n") 156 | if [ "$(echo "$currentdnss" | sort)" == "$(echo "$myhosts" | sort)" ]; then 157 | log 'No DNS change' 158 | else 159 | for dns in $currentdnss; do 160 | nmcli connection modify "$id" -ipv4.dns "$dns" 161 | done 162 | 163 | for dns in $myhosts; do 164 | nmcli connection modify "$id" +ipv4.dns "$dns" 165 | done 166 | log "Updating $id" 167 | nmcli connection up "$id" >/dev/null 168 | log 'Successful DNS update' 169 | fi 170 | done 171 | else 172 | # resolv.conf 173 | touch "$resolvconf" 174 | currentdnss=$(grep '^nameserver ' "$resolvconf" | cut -d' ' -f2) 175 | if [ "$(echo "$currentdnss" | sort)" == "$(echo "$myhosts" | sort)" ]; then 176 | log 'No DNS change' 177 | else 178 | if [ -w "$resolvconf" ]; then 179 | log "Updating $resolvconf" 180 | otherlines=$(grep -v '^nameserver ' "$resolvconf") 181 | echo "$otherlines"$'\n'"$nameservers" > "$resolvconf" 182 | log 'Successful DNS update' 183 | else 184 | warning "No write access to '$resolvconf', no change" 185 | fi 186 | fi 187 | fi 188 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | --------------------------------------------------------------------------------