diff --git a/x/smart/cname.go b/x/smart/cname.go new file mode 100644 index 00000000..78607845 --- /dev/null +++ b/x/smart/cname.go @@ -0,0 +1,71 @@ +// Copyright 2024 Jigsaw Operations LLC +// +// 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 +// +// https://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 smart + +/* +#include +#include +#include +#include +*/ +import "C" + +import ( + "context" + "fmt" + "unsafe" +) + +func lookupCNAME(ctx context.Context, domain string) (string, error) { + type result struct { + cname string + err error + } + + results := make(chan result) + go func() { + cname, err := lookupCNAMEBlocking(domain) + results <- result{cname, err} + }() + + select { + case r := <-results: + return r.cname, r.err + case <-ctx.Done(): + return "", ctx.Err() + } +} + +func lookupCNAMEBlocking(host string) (string, error) { + var hints C.struct_addrinfo + var result *C.struct_addrinfo + + chost := C.CString(host) + defer C.free(unsafe.Pointer(chost)) + + hints.ai_family = C.AF_UNSPEC + hints.ai_flags = C.AI_CANONNAME + + // Call getaddrinfo + res := C.getaddrinfo(chost, nil, &hints, &result) + if res != 0 { + return "", fmt.Errorf("getaddrinfo error: %s", C.GoString(C.gai_strerror(res))) + } + defer C.freeaddrinfo(result) + + // Extract canonical name + cname := C.GoString(result.ai_canonname) + return cname, nil +} diff --git a/x/smart/stream_dialer.go b/x/smart/stream_dialer.go index c1f35fee..ae561fc7 100644 --- a/x/smart/stream_dialer.go +++ b/x/smart/stream_dialer.go @@ -154,7 +154,7 @@ func fingerprint(pd transport.PacketDialer, sd transport.StreamDialer, testDomai func evaluateNetResolver(ctx context.Context, resolver *net.Resolver, testDomain string) ([]net.IP, error) { requestDomain := mixCase(testDomain) - _, err := resolver.LookupCNAME(ctx, requestDomain) + _, err := lookupCNAME(ctx, requestDomain) if err != nil { return nil, fmt.Errorf("could not get cname: %w", err) }