Linux下DNS解析汇编代码偶发recv阻塞无响应问题求助
偶发recv阻塞的DNS汇编代码排查
问题描述
编写了一段基于Linux系统调用的汇编代码用于获取DNS响应,但存在偶发故障:有时能正常运行,有时会在recv函数处长时间阻塞且无响应。
编译命令:
JWasm212\JWasm.exe -elf64 -Fo=test.o test.asm gcc -o test.out test.o -nostartfiles -nostdlib -nodefaultlibs --disable-shared -no-pie
汇编代码:
public _start socket equ 41 connect equ 42 sendto equ 44 recv equ 45 exit_group equ 231 sockaddr_in struct sin_family dw ? sin_port dw ? sin_addr dd ? sin_zero db 8 dup (?) sockaddr_in ends .data queryheader db 00h, 03h ; AA AA - ID db 01h, 00h ; 01 00 - Query parameters db 00h, 01h ; 00 01 - Number of questions db 00h, 00h ; 00 00 - Number of answers db 00h, 00h ; 00 00 - Number of authority records db 00h, 00h ; 00 00 - Number of additional records db 06h db 'g' db 'o' db 'o' db 'g' db 'l' db 'e' db 03h db 'c' db 'o' db 'm' db 00h ; 00 - zero byte to end the QNAME db 00h, 01h ; 00 01 - QTYPE db 00h, 01h ; 00 01 - QCLASS .code _start: _dns proc uses r12 r13 r14 r15 rbx local sockaddr : sockaddr_in local cSocket : qword local fullpacket[512] : byte lea rdi, sockaddr mov rcx, sizeof sockaddr xor al, al rep stosb mov word ptr [sockaddr.sin_family], 02h ;AF_INET mov word ptr [sockaddr.sin_port], 3500h ;port 53 (dns) mov dword ptr [sockaddr.sin_addr], 01010101h ; 8.8.8.8 google dns mov rdi, 2 ;AF_INET mov rsi, 2 ;SOCK_DGRAM mov rdx, 0 mov rax, socket syscall mov qword ptr [cSocket], rax mov rdi, qword ptr [cSocket] lea rsi, sockaddr mov rdx, 16 mov rax, connect syscall mov rdi, qword ptr [cSocket] lea rsi, queryheader mov rdx, 28 ;len xor r10, r10 ;flags xor r8, r8 ;dest_addr xor r9, r9 ;addrlen mov rax, sendto syscall mov rdi, qword ptr [cSocket] lea rsi, fullpacket ;*buf mov rdx, 512 ;packetlen xor r10, r10 ;flags xor r8, r8 ;address xor r9, r9 ;addrlen mov rax, recv syscall mov rdi, 0 mov rax, exit_group syscall ret _dns endp end
问题排查与修复方案
1. 端口字节序错误
DNS服务端口53的网络字节序为大端序,代码中直接使用3500h(十进制13568)是小端序的写法,实际对应非DNS端口,导致数据包可能发送到错误端口,无响应触发recv阻塞。
- 修复:将
mov word ptr [sockaddr.sin_port], 3500h改为mov word ptr [sockaddr.sin_port], 0035h(大端序的53)。
2. IP地址配置错误
代码中设置的01010101h对应1.1.1.1,与注释标注的8.8.8.8不符;部分网络环境下1.1.1.1响应不稳定,偶发丢包导致recv阻塞。
- 修复:将
mov dword ptr [sockaddr.sin_addr], 01010101h改为mov dword ptr [sockaddr.sin_addr], 08080808h(对应正确的Google DNS8.8.8.8)。
3. 未处理系统调用错误
代码未检查socket、connect、sendto的返回值,若调用失败(如权限不足、网络异常),会在无效socket上执行recv导致无限阻塞。
- 修复:每个系统调用后检查返回值,若为负数(Linux系统调用错误返回-1)则直接退出,示例:
mov rax, socket syscall test rax, rax js _exit_error ; 负数则跳转到错误处理逻辑 mov qword ptr [cSocket], rax
4. UDP无超时机制
UDP为无连接协议,recv默认阻塞模式,若DNS响应丢包会一直等待。需给socket设置超时时间避免无限阻塞。
- 修复:使用
setsockopt系统调用(编号54)设置SO_RCVTIMEO选项,示例:
; 定义timeval结构存储超时时间(5秒) local timeout: timeval mov qword ptr [timeout.tv_sec], 5 mov qword ptr [timeout.tv_usec], 0 ; 设置socket接收超时 mov rdi, qword ptr [cSocket] mov rsi, 0x1005 ; SOL_SOCKET=1,SO_RCVTIMEO=0x1005 mov rdx, timeout mov r10, sizeof timeval mov rax, 54 ; setsockopt系统调用号 syscall
5. 冗余参数优化(非致命)
已调用connect绑定UDP目标地址,后续可直接使用send(编号43)替代sendto,省略冗余的dest_addr和addrlen参数,代码更规范。
内容的提问来源于stack exchange,提问作者ser
相关产品推荐
相关产品推荐

