// File: cybozu[P]yrmcds/cybozu/tcp.cpp
// Function: cybozu::tcp_connect(char const*, unsigned short, unsigned int)
int tcp_connect(const char* node, std::uint16_t port, unsigned int timeout) {
    std::string s_port = std::to_string(port);

    struct addrinfo hint, *res;
    std::memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_INET;  // prefer IPv4
    hint.ai_socktype = SOCK_STREAM;
    hint.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG;
    int e = getaddrinfo(node, s_port.c_str(), &hint, &res);
    if( e == EAI_FAMILY || e == EAI_ADDRFAMILY ||
        e == EAI_NODATA || e == EAI_NONAME ) {
        hint.ai_family = AF_INET6;
        // intentionally drop AI_ADDRCONFIG to support IPv6 link-local address.
        // see https://github.com/cybozu/yrmcds/issues/40
        hint.ai_flags = AI_NUMERICSERV|AI_V4MAPPED;
        e = getaddrinfo(node, s_port.c_str(), &hint, &res);
    }
    if( e == EAI_SYSTEM ) {
        throw_unix_error(errno, "getaddrinfo");
    } else if( e != 0 ) {
        throw std::runtime_error(std::string("getaddrinfo: ") +
                                 gai_strerror(e));
    }

    int s = ::socket(res->ai_family,
                     res->ai_socktype | SOCK_NONBLOCK | SOCK_CLOEXEC,
                     res->ai_protocol);
    if( s == -1 ) {
        freeaddrinfo(res);
        throw_unix_error(errno, "socket");
    }
    e = ::connect(s, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);
    if( e == 0 ) return s;
    if( errno != EINPROGRESS ) {
        ::close(s);
        throw_unix_error(errno, "connect");
    }

    struct pollfd fds;
    fds.fd = s;
    fds.events = POLLOUT;
    int poll_timeout = timeout ? timeout*1000 : -1;
    int n = ::poll(&fds, 1, poll_timeout);
    if( n == 0 ) { // timeout
        ::close(s);
        return -1;
    }
    if( n == -1 ) {
        ::close(s);
        throw_unix_error(errno, "poll");
    }

    if( fds.revents & (POLLERR|POLLHUP|POLLNVAL) ) {
        ::close(s);
        return -1;
    }
    socklen_t l = sizeof(e);
    if( getsockopt(s, SOL_SOCKET, SO_ERROR, &e, &l) == -1 ) {
        ::close(s);
        throw_unix_error(errno, "getsockopt(SO_ERROR)");
    }
    if( e != 0 ) {
        ::close(s);
        return -1;
    }
    return s;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x140, %rsp            # imm = 0x140
movw %si, %ax
movq %rdi, -0x10(%rbp)
movw %ax, -0x12(%rbp)
movl %edx, -0x18(%rbp)
movzwl -0x12(%rbp), %esi
leaq -0x38(%rbp), %rdi
movq %rdi, -0x108(%rbp)
callq 0x1baf0
movq -0x108(%rbp), %rdi
xorps %xmm0, %xmm0
movaps %xmm0, -0x50(%rbp)
movaps %xmm0, -0x60(%rbp)
movaps %xmm0, -0x70(%rbp)
movl $0x2, -0x6c(%rbp)
movl $0x1, -0x68(%rbp)
movl $0x420, -0x70(%rbp)     # imm = 0x420
movq -0x10(%rbp), %rax
movq %rax, -0x100(%rbp)
callq 0x8240
movq -0x100(%rbp), %rdi
movq %rax, %rsi
leaq -0x70(%rbp), %rdx
leaq -0x78(%rbp), %rcx
callq 0x8800
movl %eax, -0xf4(%rbp)
jmp 0x552d7
movl -0xf4(%rbp), %eax
movl %eax, -0x7c(%rbp)
cmpl $-0x6, -0x7c(%rbp)
je 0x552f8
cmpl $-0x9, -0x7c(%rbp)
je 0x552f8
cmpl $-0x5, -0x7c(%rbp)
je 0x552f8
cmpl $-0x2, -0x7c(%rbp)
jne 0x5535b
movl $0xa, -0x6c(%rbp)
movl $0x408, -0x70(%rbp)     # imm = 0x408
movq -0x10(%rbp), %rax
movq %rax, -0x118(%rbp)
leaq -0x38(%rbp), %rdi
callq 0x8240
movq -0x118(%rbp), %rdi
movq %rax, %rsi
leaq -0x70(%rbp), %rdx
leaq -0x78(%rbp), %rcx
callq 0x8800
movl %eax, -0x10c(%rbp)
jmp 0x55339
movl -0x10c(%rbp), %eax
movl %eax, -0x7c(%rbp)
jmp 0x5535b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
jmp 0x55701
cmpl $-0xb, -0x7c(%rbp)
jne 0x55376
callq 0x8050
movl (%rax), %edi
leaq 0x276b9(%rip), %rsi     # 0x7ca28
callq 0xa320
jmp 0x55376
cmpl $0x0, -0x7c(%rbp)
je 0x5549c
movb $0x1, -0xd2(%rbp)
movl $0x10, %edi
callq 0x82f0
movq %rax, -0x128(%rbp)
leaq -0xd1(%rbp), %rdi
movq %rdi, -0x120(%rbp)
callq 0x8bd0
movq -0x120(%rbp), %rdx
leaq 0x2767b(%rip), %rsi     # 0x7ca34
leaq -0xd0(%rbp), %rdi
callq 0xa160
jmp 0x553c7
movl -0x7c(%rbp), %edi
callq 0x8940
movq %rax, %rdx
leaq -0xb0(%rbp), %rdi
leaq -0xd0(%rbp), %rsi
callq 0xe500
jmp 0x553e7
movq -0x128(%rbp), %rdi
leaq -0xb0(%rbp), %rsi
callq 0x8ac0
jmp 0x553fc
movq -0x128(%rbp), %rdi
movb $0x0, -0xd2(%rbp)
movq 0x59bdf(%rip), %rsi     # 0xaeff0
movq 0x59b58(%rip), %rdx     # 0xaef70
callq 0x8b70
jmp 0x55716
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
jmp 0x55474
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
jmp 0x55468
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x88(%rbp)
movl %eax, -0x8c(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0x83f0
leaq -0xd0(%rbp), %rdi
callq 0x83f0
leaq -0xd1(%rbp), %rdi
callq 0x86a0
testb $0x1, -0xd2(%rbp)
jne 0x5548b
jmp 0x55497
movq -0x128(%rbp), %rdi
callq 0x8490
jmp 0x55701
jmp 0x5549e
movq -0x78(%rbp), %rax
movl 0x4(%rax), %edi
movq -0x78(%rbp), %rax
movl 0x8(%rax), %esi
orl $0x800, %esi            # imm = 0x800
orl $0x80000, %esi          # imm = 0x80000
movq -0x78(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x8130
movl %eax, -0xd8(%rbp)
cmpl $-0x1, -0xd8(%rbp)
jne 0x554f1
movq -0x78(%rbp), %rdi
callq 0x8850
callq 0x8050
movl (%rax), %edi
leaq 0x27558(%rip), %rsi     # 0x7ca42
callq 0xa320
jmp 0x554f1
movl -0xd8(%rbp), %edi
movq -0x78(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x10(%rax), %edx
callq 0x8550
movl %eax, -0x12c(%rbp)
jmp 0x5550f
movl -0x12c(%rbp), %eax
movl %eax, -0x7c(%rbp)
movq -0x78(%rbp), %rdi
callq 0x8850
cmpl $0x0, -0x7c(%rbp)
jne 0x5553f
movl -0xd8(%rbp), %eax
movl %eax, -0x4(%rbp)
movl $0x1, -0xdc(%rbp)
jmp 0x556ec
callq 0x8050
cmpl $0x73, (%rax)
je 0x5556b
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x55556
callq 0x8050
movl (%rax), %edi
leaq 0x274e5(%rip), %rsi     # 0x7ca49
callq 0xa320
jmp 0x5556b
movl -0xd8(%rbp), %eax
movl %eax, -0xe4(%rbp)
movw $0x4, -0xe0(%rbp)
cmpl $0x0, -0x18(%rbp)
je 0x55595
imull $0x3e8, -0x18(%rbp), %eax # imm = 0x3E8
movl %eax, -0x130(%rbp)
jmp 0x555a2
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
movl %eax, -0x130(%rbp)
jmp 0x555a2
movl -0x130(%rbp), %eax
movl %eax, -0xe8(%rbp)
movl -0xe8(%rbp), %edx
leaq -0xe4(%rbp), %rdi
movl $0x1, %esi
callq 0x8a20
movl %eax, -0x134(%rbp)
jmp 0x555cd
movl -0x134(%rbp), %eax
movl %eax, -0xec(%rbp)
cmpl $0x0, -0xec(%rbp)
jne 0x55605
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x555ef
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl $0x1, -0xdc(%rbp)
jmp 0x556ec
cmpl $-0x1, -0xec(%rbp)
jne 0x55630
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x5561b
callq 0x8050
movl (%rax), %edi
leaq 0x27428(%rip), %rsi     # 0x7ca51
callq 0xa320
jmp 0x55630
movswl -0xde(%rbp), %eax
andl $0x38, %eax
cmpl $0x0, %eax
je 0x55662
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x5564c
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl $0x1, -0xdc(%rbp)
jmp 0x556ec
movl $0x4, -0xf0(%rbp)
movl -0xd8(%rbp), %edi
movl $0x1, %esi
movl $0x4, %edx
leaq -0x7c(%rbp), %rcx
leaq -0xf0(%rbp), %r8
callq 0x84d0
cmpl $-0x1, %eax
jne 0x556b3
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x5569e
callq 0x8050
movl (%rax), %edi
leaq 0x273aa(%rip), %rsi     # 0x7ca56
callq 0xa320
jmp 0x556b3
cmpl $0x0, -0x7c(%rbp)
je 0x556d9
movl -0xd8(%rbp), %edi
callq 0x8ce0
jmp 0x556c6
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl $0x1, -0xdc(%rbp)
jmp 0x556ec
movl -0xd8(%rbp), %eax
movl %eax, -0x4(%rbp)
movl $0x1, -0xdc(%rbp)
leaq -0x38(%rbp), %rdi
callq 0x83f0
movl -0x4(%rbp), %eax
addq $0x140, %rsp            # imm = 0x140
popq %rbp
retq
leaq -0x38(%rbp), %rdi
callq 0x83f0
movq -0x88(%rbp), %rdi
callq 0x8bc0
nopw %cs:(%rax,%rax)