Как получить исходный IP-адрес UDP-пакета, используя recvfrom в nasm? - PullRequest
0 голосов
/ 16 января 2020

Я пишу UDP-сокет в nasm и использую системный вызов recvfrom для получения UDP-пакета от клиента.

Я могу успешно отправить сообщение, но хочу отправить обратно клиенту ответ. Проблема в том, что я не могу извлечь исходный IP-адрес пакета, поэтому я не могу использовать системный вызов sendto.

Это код UDP-сервера:

%include "../StandardLibrary/standardlib.inc"
%include "./network.inc"

PORT_NUMBER equ 4096

%macro printError 3
    print %1
    printLineInt %2, %3
%endmacro

%macro clearBuffer 1
    getLength %1

    mov r12, 0
    %%loop:
        mov [%1 + r12], byte 0
        inc r12
        cmp r11, r12
        jne %%loop

%endmacro

section .data
    sock_addr:
    istruc sockaddr_in
        at sockaddr_in.sin_family, dw AF_INET
        at sockaddr_in.sin_port, dw htons(PORT_NUMBER)
        at sockaddr_in.in_addr, db 00h, 00h, 00h, 00h
        at sockaddr_in.sin_zero, db 0, 0, 0, 0, 0, 0, 0, 0
    iend

    client_sockaddr:
    istruc sockaddr
        at sockaddr.sa_family, dw AF_INET
        at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0
    iend

    ;Messaggi di successo
    so_creation_success db "Socket creato con successo", 0
    so_binding_success db "Binding del socket eseguito con successo", 0
    so_started_capturing db "Socket in ascolto di messaggi...", 0

    ;Messaggi di errore
    so_creation_error db "Errore nella creazione del socket: ", 0
    so_binding_error db "Errore nel binding del socket: ", 0
    so_capture_error db "Errore nella ricezione del messaggio: ", 0
    so_sending_error db "Errore nell'invio del messaggio: ", 0

section .bss
    buffer resb 100
    socket_fd resw 1
    error_code resq 1
    tmp resb 10

section .text
    global _start

_start:
    ;1) Creazione del socket
    mov rax, SYS_SOCKET
    mov rdi, AF_INET
    mov rsi, SOCK_DGRAM
    mov rdx, 0
    syscall

    cmp rax, -1
    jle _socket_error

    ;Carica nello stack il file descriptor
    mov [socket_fd], rax
    printLine so_creation_success

    ;2) Binding del socket
    mov rax, SYS_BIND
    mov rdi, [socket_fd]
    mov rsi, sock_addr
    mov rdx, sockaddr_in_size
    syscall

    cmp rax, -1
    jle  _binding_error
    printLine so_binding_success

    ;3) Ricezione dei messaggi
    _listen:
        mov rax, SYS_RECVFROM
        mov rdi, [socket_fd]
        mov rsi, buffer
        mov rdx, 20
        mov r10, 0
        mov r8, client_sockaddr
        mov r9, 16
        syscall

        cmp rax, -1          ;Controlla che non ci siano stati errori
        jle _capture_error   
        cmp rax, 1          ;Controlla che non sia stato ricevuto un pcchetto vuoto
        jle _exit           ;è definito vuoto anche un pacchetto con un solo carattere

        ;Messaggio ricevuto con successo
        print buffer
        clearBuffer buffer  ;Svuota il buffer
        jmp _listen

    ;Il programma è terminato con successo
    _exit:
        exit

    ;Errore nella creazione del socket
    _socket_error:
        neg rax
        mov [error_code], rax
        printError so_creation_error, [error_code], tmp
        exit [error_code]

    ;Errore durante il binding del socket
    _binding_error:
        neg rax
        mov [error_code], rax
        printError so_binding_error, [error_code], tmp
        exit [error_code]

    ;Errore durante la ricezione del messaggio
    _capture_error:
        neg rax
        mov [error_code], rax
        printError so_capture_error, [error_code], tmp
        exit [error_code]

И в network.in c У меня есть:

SYS_SOCKET          equ 41  ;Id system call per la creazione del socket
SYS_SENDTO          equ 44  ;Id system call per l'invio di datagram UDP
SYS_RECVFROM        equ 45  ;Id system call per la ricezione di datagram UDP
SYS_BIND            equ 49  ;Id system call per legare un socket ad un indirizzo IP (o ad una famiglia)

AF_INET             equ 2   ;Rete IPv4
SOCK_DGRAM          equ 2   ;Id del protocollo UDP
INADDR_ANY          equ 0   ;Indica che il socket accetta comunicazioni da qualsiasi indirizzo IP

;Traduce x in un valore di tipo network byte order
%define htons(x) ((x >> 8) & 0xFF) | ((x & 0xFF) << 8)

;Rappresenta un indirizzo IPv4
struc in_addr
    .s_addr:        resb 4  ;Valore dei 4 ottetti
endstruc

;Rappresenta la struttura di un socket
struc sockaddr_in
    .sin_family:    resw 1  ;Id del tipo di indirizzo
    .sin_port:      resw 1  ;Numero di porta
    .in_addr:       resb 4  ;Indirizzo IP
    .sin_zero:      resb 8  ;Byte di rimepimento
endstruc

struc sockaddr
    .sa_family      resw 1
    .sa_data        resb 14
endstruc

В качестве клиента UDP я использую команду netcat: netcat -u 127.0.0.1 4096

1 Ответ

1 голос
/ 16 января 2020

Вы можете передать указатель на вашу sockaddr структуру в SYS_RECVFROM в соответствии с документацией здесь:

mov r8, 0
mov r9, 0

И затем использовать заполненную структуру с sendto syscall.

UPD

sockaddr имеет следующую форму в C:

struct sockaddr {
    unsigned short int sa_family;
    char sa_data[14];
};

Я думаю, это выглядит примерно так в NASM:

struc sockaddr
    .sa_family resw 1
    .sa_data db 14
endstruc


client_sock_addr:
istruc sockaddr
    at sockaddr.sa_family, dw AF_INET
    at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
iend

UPD # 2

Я заметил, что вы выделяете 2 байта для сокета. На самом деле сокеты должны быть int:

socket_fd resd 1

Следующее: вам нужно перейти к r9 ссылке, а не к значению

section .data
    ; ...
    client_sockaddr_len dd 16
    ; ...

; ...
mov rax, SYS_RECVFROM
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 20
mov r10, 0
mov r8, client_sockaddr
mov r9, client_sockaddr_len
syscall

И, наконец, вы можете sendto

mov rax, SYS_SENDTO
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 50
mov r10, 0
mov r8, client_sockaddr
mov r9, [client_sockaddr_len]
syscall
...