github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/net/cgo_unix.go (about)

     1  // Copyright 2011 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build !netgo
     6  // +build darwin dragonfly freebsd linux netbsd openbsd
     7  
     8  package net
     9  
    10  /*
    11  #include <sys/types.h>
    12  #include <sys/socket.h>
    13  #include <netinet/in.h>
    14  #include <netdb.h>
    15  #include <stdlib.h>
    16  #include <unistd.h>
    17  #include <string.h>
    18  */
    19  import "C"
    20  
    21  import (
    22  	"syscall"
    23  	"unsafe"
    24  )
    25  
    26  func cgoLookupHost(name string) (addrs []string, err error, completed bool) {
    27  	ip, err, completed := cgoLookupIP(name)
    28  	for _, p := range ip {
    29  		addrs = append(addrs, p.String())
    30  	}
    31  	return
    32  }
    33  
    34  func cgoLookupPort(net, service string) (port int, err error, completed bool) {
    35  	acquireThread()
    36  	defer releaseThread()
    37  
    38  	var res *C.struct_addrinfo
    39  	var hints C.struct_addrinfo
    40  
    41  	switch net {
    42  	case "":
    43  		// no hints
    44  	case "tcp", "tcp4", "tcp6":
    45  		hints.ai_socktype = C.SOCK_STREAM
    46  		hints.ai_protocol = C.IPPROTO_TCP
    47  	case "udp", "udp4", "udp6":
    48  		hints.ai_socktype = C.SOCK_DGRAM
    49  		hints.ai_protocol = C.IPPROTO_UDP
    50  	default:
    51  		return 0, UnknownNetworkError(net), true
    52  	}
    53  	if len(net) >= 4 {
    54  		switch net[3] {
    55  		case '4':
    56  			hints.ai_family = C.AF_INET
    57  		case '6':
    58  			hints.ai_family = C.AF_INET6
    59  		}
    60  	}
    61  
    62  	s := C.CString(service)
    63  	defer C.free(unsafe.Pointer(s))
    64  	if C.getaddrinfo(nil, s, &hints, &res) == 0 {
    65  		defer C.freeaddrinfo(res)
    66  		for r := res; r != nil; r = r.ai_next {
    67  			switch r.ai_family {
    68  			default:
    69  				continue
    70  			case C.AF_INET:
    71  				sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
    72  				p := (*[2]byte)(unsafe.Pointer(&sa.Port))
    73  				return int(p[0])<<8 | int(p[1]), nil, true
    74  			case C.AF_INET6:
    75  				sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
    76  				p := (*[2]byte)(unsafe.Pointer(&sa.Port))
    77  				return int(p[0])<<8 | int(p[1]), nil, true
    78  			}
    79  		}
    80  	}
    81  	return 0, &AddrError{"unknown port", net + "/" + service}, true
    82  }
    83  
    84  func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) {
    85  	acquireThread()
    86  	defer releaseThread()
    87  
    88  	var res *C.struct_addrinfo
    89  	var hints C.struct_addrinfo
    90  
    91  	hints.ai_flags = cgoAddrInfoFlags()
    92  	hints.ai_socktype = C.SOCK_STREAM
    93  
    94  	h := C.CString(name)
    95  	defer C.free(unsafe.Pointer(h))
    96  	gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
    97  	if gerrno != 0 {
    98  		var str string
    99  		if gerrno == C.EAI_NONAME {
   100  			str = noSuchHost
   101  		} else if gerrno == C.EAI_SYSTEM {
   102  			if err == nil {
   103  				// err should not be nil, but sometimes getaddrinfo returns
   104  				// gerrno == C.EAI_SYSTEM with err == nil on Linux.
   105  				// The report claims that it happens when we have too many
   106  				// open files, so use syscall.EMFILE (too many open files in system).
   107  				// Most system calls would return ENFILE (too many open files),
   108  				// so at the least EMFILE should be easy to recognize if this
   109  				// comes up again. golang.org/issue/6232.
   110  				err = syscall.EMFILE
   111  			}
   112  			str = err.Error()
   113  		} else {
   114  			str = C.GoString(C.gai_strerror(gerrno))
   115  		}
   116  		return nil, "", &DNSError{Err: str, Name: name}, true
   117  	}
   118  	defer C.freeaddrinfo(res)
   119  	if res != nil {
   120  		cname = C.GoString(res.ai_canonname)
   121  		if cname == "" {
   122  			cname = name
   123  		}
   124  		if len(cname) > 0 && cname[len(cname)-1] != '.' {
   125  			cname += "."
   126  		}
   127  	}
   128  	for r := res; r != nil; r = r.ai_next {
   129  		// We only asked for SOCK_STREAM, but check anyhow.
   130  		if r.ai_socktype != C.SOCK_STREAM {
   131  			continue
   132  		}
   133  		switch r.ai_family {
   134  		default:
   135  			continue
   136  		case C.AF_INET:
   137  			sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
   138  			addrs = append(addrs, copyIP(sa.Addr[:]))
   139  		case C.AF_INET6:
   140  			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
   141  			addrs = append(addrs, copyIP(sa.Addr[:]))
   142  		}
   143  	}
   144  	return addrs, cname, nil, true
   145  }
   146  
   147  func cgoLookupIP(name string) (addrs []IP, err error, completed bool) {
   148  	addrs, _, err, completed = cgoLookupIPCNAME(name)
   149  	return
   150  }
   151  
   152  func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
   153  	_, cname, err, completed = cgoLookupIPCNAME(name)
   154  	return
   155  }
   156  
   157  func copyIP(x IP) IP {
   158  	if len(x) < 16 {
   159  		return x.To16()
   160  	}
   161  	y := make(IP, len(x))
   162  	copy(y, x)
   163  	return y
   164  }