This repository has been archived on 2024-09-28. You can view files and clone it, but cannot push or open issues or pull requests.
Georg Pfuetzenreuter bad275abe2
Support adding addresses with CIDR mask
Correctly parse and add submitted networks to sets to reflect the
behavior of the `nft` command line.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
2024-09-10 22:11:03 +02:00

138 lines
3.6 KiB
Go

/*
* This file is part of nftables-http-api.
* Copyright (C) 2024 Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
*
* The nftables-http-api program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package main
import (
"encoding/json"
"errors"
"golang.org/x/crypto/bcrypt"
"log"
"net"
"net/http"
"strings"
)
type Response struct {
RError string `json:"error,omitempty"`
RResult string `json:"result,omitempty"`
}
type ResponseSet struct {
RError string `json:"error,omitempty"`
RResult []string `json:"result,omitempty"`
}
func doReturn(w http.ResponseWriter, status int, text string) {
var response any
if status == http.StatusOK || status == http.StatusCreated {
response = Response{RResult: text}
} else {
response = Response{RError: text}
}
j, err := json.Marshal(response)
if err != nil {
log.Fatalf("Failed to marshal JSON: %s", err)
}
w.WriteHeader(status)
w.Write(j)
}
func doReturnSet(w http.ResponseWriter, status int, text string, elements []string) {
var response any
if status == http.StatusOK {
response = ResponseSet{RResult: elements}
} else {
response = Response{RError: text}
}
j, err := json.Marshal(response)
if err != nil {
log.Fatalf("Failed to marshal JSON: %s", err)
}
w.WriteHeader(status)
w.Write(j)
}
func doCheckToken(token string, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(token))
if err == nil {
return true
} else {
log.Printf("Token check failed: %s", err)
return false
}
}
func parseIPAddressOrNetworkString(givenAddress string) (net.IP, *net.IPNet, string, error) {
if strings.Contains(givenAddress, "/") {
return parseIPNetworkString(givenAddress)
} else {
address, family, err := parseIPAddressString(givenAddress)
return address, nil, family, err
}
}
func parseIPNetworkString(givenAddress string) (net.IP, *net.IPNet, string, error) {
ipObject, cidrObject, err := net.ParseCIDR(givenAddress)
if err != nil {
return nil, nil, "", err
}
address, family, err := parseIPAddress(ipObject)
return address, cidrObject, family, err
}
func parseIPAddressString(givenAddress string) (net.IP, string, error) {
return parseIPAddress(net.ParseIP(givenAddress))
}
func parseIPAddress(ipObject net.IP) (net.IP, string, error) {
var address net.IP
family, err := getIPAddressFamily(ipObject)
if err == nil {
if family == "ipv4" {
address = ipObject.To4()
} else if family == "ipv6" {
address = ipObject.To16()
} else {
log.Println("unknown family, this should not happen")
return nil, family, errors.New("unknown family")
}
return address, family, nil
}
log.Println("address parsing failed:", err)
return nil, "", errors.New("invalid address")
}
func getIPAddressFamily(ipObject net.IP) (string, error) {
ipAddress := ipObject.String()
for i := 0; i < len(ipAddress); i++ {
switch ipAddress[i] {
case '.':
return "ipv4", nil
case ':':
return "ipv6", nil
}
}
return "", errors.New("address family detection failed")
}
func incrementIPAddress(ip net.IP) (net.IP, error) {
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] != 0 {
break
}
}
return ip, nil
}