1// Copyright 2019 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package net
16
17import (
18 "fmt"
19 "net"
20 "strconv"
21 "unicode/utf8"
22
23 "golang.org/x/net/idna"
24
25 "cuelang.org/go/cue"
26)
27
28var idnaProfile = idna.New(
29 idna.ValidateLabels(true),
30 idna.VerifyDNSLength(true),
31 idna.StrictDomainName(true),
32)
33
34// SplitHostPort splits a network address of the form "host:port",
35// "host%zone:port", "[host]:port" or "[host%zone]:port" into host or host%zone
36// and port.
37//
38// A literal IPv6 address in hostport must be enclosed in square brackets, as in
39// "[::1]:80", "[::1%lo0]:80".
40func SplitHostPort(s string) (hostport []string, err error) {
41 host, port, err := net.SplitHostPort(s)
42 if err != nil {
43 return nil, err
44 }
45 return []string{host, port}, nil
46}
47
48// JoinHostPort combines host and port into a network address of the
49// form "host:port". If host contains a colon, as found in literal
50// IPv6 addresses, then JoinHostPort returns "[host]:port".
51//
52// See func Dial for a description of the host and port parameters.
53func JoinHostPort(host, port cue.Value) (string, error) {
54 var err error
55 hostStr := ""
56 switch host.Kind() {
57 case cue.ListKind:
58 ipdata := netGetIP(host)
59 if !ipdata.IsValid() {
60 err = fmt.Errorf("invalid host %s", host)
61 }
62 hostStr = ipdata.String()
63 case cue.BytesKind:
64 var b []byte
65 b, err = host.Bytes()
66 hostStr = string(b)
67 default:
68 hostStr, err = host.String()
69 }
70 if err != nil {
71 return "", err
72 }
73
74 portStr := ""
75 switch port.Kind() {
76 case cue.StringKind:
77 portStr, err = port.String()
78 case cue.BytesKind:
79 var b []byte
80 b, err = port.Bytes()
81 portStr = string(b)
82 default:
83 var i int64
84 i, err = port.Int64()
85 portStr = strconv.Itoa(int(i))
86 }
87 if err != nil {
88 return "", err
89 }
90
91 return net.JoinHostPort(hostStr, portStr), nil
92}
93
94// FQDN reports whether is a valid fully qualified domain name.
95//
96// FQDN allows only ASCII characters as prescribed by RFC 1034 (A-Z, a-z, 0-9
97// and the hyphen).
98func FQDN(s string) bool {
99 for i := 0; i < len(s); i++ {
100 if s[i] >= utf8.RuneSelf {
101 return false
102 }
103 }
104 _, err := idnaProfile.ToASCII(s)
105 return err == nil
106}