// File: wujf[P]dnsfwd/udpfwd.cpp
// Function: UdpForwarder::run(unsigned short)
int run(u_short port) {
    // listen sock
    int listen_sock = ::socket(AF_INET, SOCK_DGRAM, 0);
    ::sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (::bind(listen_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
        ::perror("bind");
        return -1;
    }

    std::size_t buflen = 65536;
    auto buf = static_cast<char*>(std::malloc(buflen));

    for (;;) {
        ::pollfd fds[10000];
        fds[0].fd = listen_sock;
        fds[0].events = POLLIN;

        // add all sockets from gClients
        int client_count = 0;
        for (const auto& client : gClients) {
            ++client_count;
            fds[client_count].fd = client.first;
            fds[client_count].events = POLLIN;
        }

        std::cout << "Total clients: " << client_count << std::endl;

        int rc = ::poll(fds, client_count + 1, -1);
        if (rc < 0) {
            ::perror("poll");
            break;
        }

        printf("========= poll=%d =========\n", rc);
        // request
        if (fds[0].revents) {
            ::sockaddr_in from;
            ::socklen_t addrlen = sizeof(from);
            char* ptr = buf + 10;
            size_t limit = buflen - 10;
            ssize_t rsize = ::recvfrom(listen_sock, ptr, limit, 0,
                                       reinterpret_cast<sockaddr*>(&from),
                                       &addrlen);
            if (rsize < 0) {
                ::perror("recvfrom");
                break;
            }

            std::cout << "Got " << rsize << " bytes from client: "
                      << ntohs(from.sin_port) << "/" << addrlen <<  std::endl;

            char hostname[256];
            parse_dns(ptr + 12, hostname, sizeof(hostname)); 
            printf("dns: [%s]\n", hostname);

            // int sock = plain_forward(ptr, rsize);
            int sock = udp_via_socks(buf, rsize + 10);
            if (sock < 0) {
                break;
            }

            gClients[sock] = new sockaddr_in(from);
        }

        // response
        for (int i = 1; i <= client_count; i++) {
            if (fds[i].revents) {
                int sock = fds[i].fd;
                ::sockaddr_in from;
                ::socklen_t addrlen;
                ssize_t rsize = ::recvfrom(sock, buf, buflen, 0,
                                           reinterpret_cast<sockaddr*>(&from),
                                           &addrlen);
                if (rsize < 0) {
                    ::perror("recvfrom");
                    break;
                }
        
                auto client = gClients[sock];
                char ip[20];
                ::inet_ntop(AF_INET, &client->sin_addr, ip, 20);
                std::cout << "Got " << rsize << " bytes for client "
                          << ip << ":" << ntohs(client->sin_port) << std::endl;
#if 0
                for (int i = 0; i < rsize; i++)
                    printf("%02x ", (uint8_t)buf[i]);
                printf("\n");
#endif

                ssize_t sent;
                sent = ::sendto(listen_sock, buf + 10, rsize - 10, 0,
                                reinterpret_cast<sockaddr*>(client),
                                sizeof(*client));
                if (sent != rsize - 10) {
                    std::cerr << "fwd to client " << listen_sock << ": "
                              << strerror(errno) << std::endl; 
                    break;
                }

                std::cout << "Reply fwd'd: " << sent << std::endl;
                gClients.erase(sock);
                delete client;
                ::close(sock);
            }
        }
    }

    std::free(buf);
    std::cerr << "error in main loop, quitting ..." << std::endl;
    return -1;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x13a90, %rsp          # imm = 0x13A90
movw %di, %ax
movw %ax, -0x6(%rbp)
movl $0x2, %esi
xorl %edx, %edx
movl %esi, %edi
callq 0x2090
movl %eax, -0xc(%rbp)
movw $0x2, -0x1c(%rbp)
movzwl -0x6(%rbp), %edi
callq 0x2050
movw %ax, -0x1a(%rbp)
movl $0x0, -0x18(%rbp)
movl -0xc(%rbp), %edi
leaq -0x1c(%rbp), %rsi
movl $0x10, %edx
callq 0x2070
cmpl $0x0, %eax
jge 0x26db
leaq 0x2987(%rip), %rdi      # 0x5051
callq 0x2150
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x2c84
movq $0x10000, -0x28(%rbp)   # imm = 0x10000
movq -0x28(%rbp), %rdi
callq 0x2200
movq %rax, -0x30(%rbp)
movl -0xc(%rbp), %eax
movl %eax, -0x138b0(%rbp)
movw $0x1, -0x138ac(%rbp)
movl $0x0, -0x138b4(%rbp)
leaq 0x5a7d(%rip), %rax      # 0x8190
movq %rax, -0x138c0(%rbp)
leaq 0x5a6f(%rip), %rdi      # 0x8190
callq 0x2d20
movq %rax, -0x138c8(%rbp)
leaq 0x5a5c(%rip), %rdi      # 0x8190
callq 0x2d50
movq %rax, -0x138d0(%rbp)
leaq -0x138c8(%rbp), %rdi
leaq -0x138d0(%rbp), %rsi
callq 0x2d80
testb $0x1, %al
jne 0x2759
jmp 0x27b1
leaq -0x138c8(%rbp), %rdi
callq 0x2db0
movq %rax, -0x138d8(%rbp)
movl -0x138b4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x138b4(%rbp)
movq -0x138d8(%rbp), %rax
movl (%rax), %ecx
movslq -0x138b4(%rbp), %rax
movl %ecx, -0x138b0(%rbp,%rax,8)
movslq -0x138b4(%rbp), %rax
movw $0x1, -0x138ac(%rbp,%rax,8)
leaq -0x138c8(%rbp), %rdi
callq 0x2dd0
jmp 0x2740
movq 0x5818(%rip), %rdi      # 0x7fd0
leaq 0x2897(%rip), %rsi      # 0x5056
callq 0x2170
movq %rax, %rdi
movl -0x138b4(%rbp), %esi
callq 0x2280
movq %rax, %rdi
movq 0x57e4(%rip), %rsi      # 0x7fc0
callq 0x21c0
leaq -0x138b0(%rbp), %rdi
movl -0x138b4(%rbp), %eax
addl $0x1, %eax
movslq %eax, %rsi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
callq 0x2230
movl %eax, -0x138dc(%rbp)
cmpl $0x0, -0x138dc(%rbp)
jge 0x281e
leaq 0x2852(%rip), %rdi      # 0x5066
callq 0x2150
jmp 0x2c52
movl -0x138dc(%rbp), %esi
leaq 0x2840(%rip), %rdi      # 0x506b
movb $0x0, %al
callq 0x2040
cmpw $0x0, -0x138aa(%rbp)
je 0x29cb
movl $0x10, -0x138f0(%rbp)
movq -0x30(%rbp), %rax
addq $0xa, %rax
movq %rax, -0x138f8(%rbp)
movq -0x28(%rbp), %rax
subq $0xa, %rax
movq %rax, -0x13900(%rbp)
movl -0xc(%rbp), %edi
movq -0x138f8(%rbp), %rsi
movq -0x13900(%rbp), %rdx
xorl %ecx, %ecx
leaq -0x138ec(%rbp), %r8
leaq -0x138f0(%rbp), %r9
callq 0x2110
movq %rax, -0x13908(%rbp)
cmpq $0x0, -0x13908(%rbp)
jge 0x28b0
leaq 0x27e2(%rip), %rdi      # 0x5088
callq 0x2150
jmp 0x2c52
movq 0x5719(%rip), %rdi      # 0x7fd0
leaq 0x27d3(%rip), %rsi      # 0x5091
callq 0x2170
movq %rax, %rdi
movq -0x13908(%rbp), %rsi
callq 0x22b0
movq %rax, %rdi
leaq 0x27ba(%rip), %rsi      # 0x5096
callq 0x2170
movq %rax, -0x13a70(%rbp)
movzwl -0x138ea(%rbp), %edi
callq 0x22a0
movq -0x13a70(%rbp), %rdi
movzwl %ax, %esi
callq 0x2270
movq %rax, %rdi
leaq 0x279e(%rip), %rsi      # 0x50ab
callq 0x2170
movq %rax, %rdi
movl -0x138f0(%rbp), %esi
callq 0x2210
movq %rax, %rdi
movq 0x5696(%rip), %rsi      # 0x7fc0
callq 0x21c0
movq -0x138f8(%rbp), %rdi
addq $0xc, %rdi
leaq -0x13a10(%rbp), %rsi
movl $0x100, %edx            # imm = 0x100
callq 0x2440
leaq -0x13a10(%rbp), %rsi
leaq 0x2754(%rip), %rdi      # 0x50ad
movb $0x0, %al
callq 0x2040
movq -0x30(%rbp), %rdi
movq -0x13908(%rbp), %rsi
addq $0xa, %rsi
callq 0x2560
movl %eax, -0x13a14(%rbp)
cmpl $0x0, -0x13a14(%rbp)
jge 0x2988
jmp 0x2c52
movl $0x10, %edi
callq 0x2180
movq %rax, -0x13a78(%rbp)
movq -0x138ec(%rbp), %rcx
movq %rcx, (%rax)
movq -0x138e4(%rbp), %rcx
movq %rcx, 0x8(%rax)
leaq 0x57db(%rip), %rdi      # 0x8190
leaq -0x13a14(%rbp), %rsi
callq 0x2e00
movq -0x13a78(%rbp), %rcx
movq %rcx, (%rax)
movl $0x1, -0x13a18(%rbp)
movl -0x13a18(%rbp), %eax
cmpl -0x138b4(%rbp), %eax
jg 0x2c4d
movslq -0x13a18(%rbp), %rax
cmpw $0x0, -0x138aa(%rbp,%rax,8)
je 0x2c37
movslq -0x13a18(%rbp), %rax
movl -0x138b0(%rbp,%rax,8), %eax
movl %eax, -0x13a1c(%rbp)
movl -0x13a1c(%rbp), %edi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
xorl %ecx, %ecx
leaq -0x13a2c(%rbp), %r8
leaq -0x13a30(%rbp), %r9
callq 0x2110
movq %rax, -0x13a38(%rbp)
cmpq $0x0, -0x13a38(%rbp)
jge 0x2a56
leaq 0x263c(%rip), %rdi      # 0x5088
callq 0x2150
jmp 0x2c4d
leaq 0x5733(%rip), %rdi      # 0x8190
leaq -0x13a1c(%rbp), %rsi
callq 0x2e00
movq (%rax), %rax
movq %rax, -0x13a40(%rbp)
movq -0x13a40(%rbp), %rsi
addq $0x4, %rsi
leaq -0x13a60(%rbp), %rdx
movl $0x2, %edi
movl $0x14, %ecx
callq 0x21d0
movq 0x5535(%rip), %rdi      # 0x7fd0
leaq 0x25ef(%rip), %rsi      # 0x5091
callq 0x2170
movq %rax, %rdi
movq -0x13a38(%rbp), %rsi
callq 0x22b0
movq %rax, %rdi
leaq 0x25f8(%rip), %rsi      # 0x50b8
callq 0x2170
movq %rax, %rdi
leaq -0x13a60(%rbp), %rsi
callq 0x2170
movq %rax, %rdi
leaq 0x25ed(%rip), %rsi      # 0x50cb
callq 0x2170
movq %rax, -0x13a80(%rbp)
movq -0x13a40(%rbp), %rax
movzwl 0x2(%rax), %edi
callq 0x22a0
movq -0x13a80(%rbp), %rdi
movzwl %ax, %esi
callq 0x2270
movq %rax, %rdi
movq 0x54ad(%rip), %rsi      # 0x7fc0
callq 0x21c0
movl -0xc(%rbp), %edi
movq -0x30(%rbp), %rsi
addq $0xa, %rsi
movq -0x13a38(%rbp), %rdx
subq $0xa, %rdx
movq -0x13a40(%rbp), %r8
xorl %ecx, %ecx
movl $0x10, %r9d
callq 0x2060
movq %rax, -0x13a68(%rbp)
movq -0x13a68(%rbp), %rax
movq -0x13a38(%rbp), %rcx
subq $0xa, %rcx
cmpq %rcx, %rax
je 0x2bc3
movq 0x5489(%rip), %rdi      # 0x7ff0
leaq 0x255f(%rip), %rsi      # 0x50cd
callq 0x2170
movq %rax, %rdi
movl -0xc(%rbp), %esi
callq 0x2280
movq %rax, %rdi
leaq 0x255f(%rip), %rsi      # 0x50e7
callq 0x2170
movq %rax, -0x13a88(%rbp)
callq 0x2030
movl (%rax), %edi
callq 0x20b0
movq -0x13a88(%rbp), %rdi
movq %rax, %rsi
callq 0x2170
movq %rax, %rdi
movq 0x5407(%rip), %rsi      # 0x7fc0
callq 0x21c0
jmp 0x2c4d
movq 0x5406(%rip), %rdi      # 0x7fd0
leaq 0x250b(%rip), %rsi      # 0x50dc
callq 0x2170
movq %rax, %rdi
movq -0x13a68(%rbp), %rsi
callq 0x22b0
movq %rax, %rdi
movq 0x53d1(%rip), %rsi      # 0x7fc0
callq 0x21c0
leaq 0x5595(%rip), %rdi      # 0x8190
leaq -0x13a1c(%rbp), %rsi
callq 0x2ee0
movq -0x13a40(%rbp), %rax
movq %rax, -0x13a90(%rbp)
cmpq $0x0, %rax
je 0x2c2c
movq -0x13a90(%rbp), %rdi
movl $0x10, %esi
callq 0x21a0
movl -0x13a1c(%rbp), %edi
callq 0x22c0
jmp 0x2c39
movl -0x13a18(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x13a18(%rbp)
jmp 0x29d5
jmp 0x26f0
movq -0x30(%rbp), %rdi
callq 0x21f0
movq 0x538e(%rip), %rdi      # 0x7ff0
leaq 0x2481(%rip), %rsi      # 0x50ea
callq 0x2170
movq %rax, %rdi
movq 0x5348(%rip), %rsi      # 0x7fc0
callq 0x21c0
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
movl -0x4(%rbp), %eax
addq $0x13a90, %rsp          # imm = 0x13A90
popq %rbp
retq