// Copyright 2021 CNI authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ip import ( "fmt" "net" "strings" ) // IP is a CNI maintained type inherited from net.IPNet which can // represent a single IP address with or without prefix. type IP struct { net.IPNet } // newIP will create an IP with net.IP and net.IPMask func newIP(ip net.IP, mask net.IPMask) *IP { return &IP{ IPNet: net.IPNet{ IP: ip, Mask: mask, }, } } // ParseIP will parse string s as an IP, and return it. // The string s must be formed like [/]. // If s is not a valid textual representation of an IP, // will return nil. func ParseIP(s string) *IP { if strings.ContainsAny(s, "/") { ip, ipNet, err := net.ParseCIDR(s) if err != nil { return nil } return newIP(ip, ipNet.Mask) } ip := net.ParseIP(s) if ip == nil { return nil } return newIP(ip, nil) } // ToIP will return a net.IP in standard form from this IP. // If this IP can not be converted to a valid net.IP, will return nil. func (i *IP) ToIP() net.IP { switch { case i.IP.To4() != nil: return i.IP.To4() case i.IP.To16() != nil: return i.IP.To16() default: return nil } } // String returns the string form of this IP. func (i *IP) String() string { if len(i.Mask) > 0 { return i.IPNet.String() } return i.IP.String() } // MarshalText implements the encoding.TextMarshaler interface. // The encoding is the same as returned by String, // But when len(ip) is zero, will return an empty slice. func (i *IP) MarshalText() ([]byte, error) { if len(i.IP) == 0 { return []byte{}, nil } return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface. // The textual bytes are expected in a form accepted by Parse, // But when len(b) is zero, will return an empty IP. func (i *IP) UnmarshalText(b []byte) error { if len(b) == 0 { *i = IP{} return nil } ip := ParseIP(string(b)) if ip == nil { return fmt.Errorf("invalid IP address %s", string(b)) } *i = *ip return nil }