Skip to content

Commit

Permalink
add mikrotik api support (#252)
Browse files Browse the repository at this point in the history
* add mikrotik api support

* ignore test TestGetMikrotikIP

* remove omitempty from Mikrotik struct

* fixed TestGetMikrotikIP struct literal uses unkeyed fields
  • Loading branch information
0987363 authored Nov 7, 2024
1 parent 6192e68 commit 479cded
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 2 deletions.
9 changes: 8 additions & 1 deletion configs/config_sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
"resolver": "8.8.8.8",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
"ip_interface": "eth0",
"mikrotik": {
"enabled": false,
"server": "http://192.168.88.1:81",
"username": "admin",
"password": "",
"interface": "pppoe-out"
},
"web_panel": {
"enabled": true,
"addr": "0.0.0.0:9000",
Expand Down Expand Up @@ -70,4 +77,4 @@
"url": "http://localhost:5000/api/v1/send",
"request_body": "{ \"domain\": \"{{.Domain}}\", \"ip\": \"{{.CurrentIP}}\", \"ip_type\": \"{{.IPType}}\" }"
}
}
}
6 changes: 6 additions & 0 deletions configs/config_sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ interval: 300
resolver: 8.8.8.8
user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36"
ip_interface: eth0
mikrotik:
enabled: false
addr: "http://192.168.20.1:81"
username": "admin"
password": ""
interface: "pppoe-out"
web_panel:
enabled: true
addr: "0.0.0.0:9000"
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (

require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/go-routeros/routeros/v3 v3.0.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
Expand Down Expand Up @@ -53,4 +54,6 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
)

go 1.20
go 1.21

toolchain go1.21.5
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-routeros/routeros/v3 v3.0.0 h1:/V4Cgr+wmn3IyyYIXUX1KYK8pA1ADPiwLSlAi912j1M=
github.com/go-routeros/routeros/v3 v3.0.0/go.mod h1:j4mq65czXfKtHsdLkgVv8w7sNzyhLZy1TKi2zQDMpiQ=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down
9 changes: 9 additions & 0 deletions internal/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ type WebPanel struct {
Password string `json:"password" yaml:"password"`
}

type Mikrotik struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Addr string `json:"addr" yaml:"addr"`
Username string `json:"username" yaml:"username"`
Password string `json:"password" yaml:"password"`
Interface string `json:"interface" yaml:"interface"`
}

// Settings struct.
type Settings struct {
Provider string `json:"provider" yaml:"provider"`
Expand All @@ -120,6 +128,7 @@ type Settings struct {
Webhook Webhook `json:"webhook,omitempty" yaml:"webhook,omitempty"`
IPInterface string `json:"ip_interface" yaml:"ip_interface"`
IPType string `json:"ip_type" yaml:"ip_type"`
Mikrotik Mikrotik `json:"mikrotik" yaml:"mikrotik"`
Resolver string `json:"resolver" yaml:"resolver"`
UseProxy bool `json:"use_proxy" yaml:"use_proxy"`
DebugInfo bool `json:"debug_info" yaml:"debug_info"`
Expand Down
73 changes: 73 additions & 0 deletions pkg/lib/ip_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package lib

import (
"context"
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"path"
"regexp"
"strings"
"sync"
Expand Down Expand Up @@ -116,6 +121,64 @@ func (helper *IPHelper) getNext() string {
return next
}

func (helper *IPHelper) getIPFromMikrotik() string {
u, err := url.Parse(helper.configuration.Mikrotik.Addr)
if err != nil {
log.Error("fail to parse mikrotik address: ", err)
return ""
}
u.Path = path.Join(u.Path, "/rest/ip/address")
q := u.Query()
q.Add("interface", helper.configuration.Mikrotik.Interface)
q.Add(".proplist", "address")
u.RawQuery = q.Encode()

req, _ := http.NewRequest("GET", u.String(), nil)

auth := fmt.Sprintf("%s:%s", helper.configuration.Mikrotik.Username, helper.configuration.Mikrotik.Password)
req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
req.Header.Add("Content-Type", "application/json")

for {
client := &http.Client{
Timeout: time.Second * utils.DefaultTimeout,
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
}

response, err := client.Do(req)
if err != nil {
log.Error("Cannot get IP:", err)
time.Sleep(time.Millisecond * 300)
continue
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
log.Error("requst code failed: ", response.StatusCode)
return ""
}

body, err := io.ReadAll(response.Body)
if err != nil {
log.Error("read body failed: ", err)
return ""
}

m := []map[string]string{}
if err := json.Unmarshal(body, &m); err != nil {
log.Error("unmarshal body failed: ", err)
return ""
}
if len(m) < 1 {
log.Error("could not get ip from: ", m)
return ""
}

res := strings.Split(m[0]["address"], "/")
return res[0]
}
}

// getIPFromInterface gets IP address from the specific interface.
func (helper *IPHelper) getIPFromInterface() (string, error) {
ifaces, err := net.InterfaceByName(helper.configuration.IPInterface)
Expand Down Expand Up @@ -173,6 +236,16 @@ func (helper *IPHelper) getCurrentIP() {
var err error
var ip string

if helper.configuration.Mikrotik.Enabled {
ip = helper.getIPFromMikrotik()
if ip == "" {
log.Error("get ip from mikrotik failed. Fallback to get ip from onlinke if possible.")
} else {
helper.setCurrentIP(ip)
return
}
}

if len(helper.reqURLs) > 0 {
ip = helper.getIPOnline()
if ip == "" {
Expand Down
22 changes: 22 additions & 0 deletions pkg/lib/ip_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,25 @@ func TestGetCurrentIP(t *testing.T) {
t.Log("IP is:" + ip)
}
}

func TestGetMikrotikIP(t *testing.T) {
t.Skip()

conf := &settings.Settings{
Mikrotik: settings.Mikrotik{
Enabled: true,
Addr: "http://192.168.20.1:81",
Username: "admin",
Password: "",
Interface: "pppoe-out",
},
}
helper := GetIPHelperInstance(conf)
ip := helper.GetCurrentIP()

if ip == "" {
t.Log("IP is empty...")
} else {
t.Log("IP is:" + ip)
}
}

0 comments on commit 479cded

Please sign in to comment.