News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

select() failing because fd_set structure not set up properly

Started by cyrus, November 12, 2023, 10:32:12 AM

Previous topic - Next topic

cyrus

Hi. I've written a multiplexer in c++. This basically is just like socat. It does port forwarding. If you have a service listening on localhost, it cannot accept connections from other hosts. However, if you have this multiplexer, you can basically take that service that is listening on localhost and send it to a remote machine. This program thus makes 2 connections: 1 to localhost and 1 to the remote system. There are 2 sockets total involved. If you have a webserver, vncserver or anything, this will work. I wanted to write one myself because I have fun programming. In C++, it's pretty easy and straight forward although others have told me it isn't easy, I believe it was super easy in linux c and in windows c++

From my understanding, the fd_set struct looks like this:

typedef struct fd_set {
  u_int  fd_count;
  SOCKET fd_array[FD_SETSIZE];
} fd_set, FD_SET, *PFD_SET, *LPFD_SET;

on 64-bit windows systems, FD_SETSIZE is 64. I've done a sizeof(SOCKET) which returns 8. That makes sense. fd_array basically is 1 8-byte array which is 64-bits. On Linux, FD_SETSIZE is 256. I've noticed on linux, file descriptors (fd's), are single low number digits. But on Windows, I'm seeing 192, 196, 200 most of the time I am debugging. Since fd_count is an int which is 4 bytes, it makes sense that the total size of this struct is 12 bytes but since 64-bits requires 16-byte alignment, I assume this max size is 16 bytes. I've initialized this structure as so:
.data
rset_struc DB 16 DUP(0)  ; 16 byte struct initialized to 0
wset_struc DB 16 DUP(0)  ; 16 byte struct initialized to 0
ssockfd DQ 0             ;  8-byte socket to server initialized to 0
csockfd DQ 0             ;  8-byte socket to client initialized to 0

    xor r12, r12
    lea r12, rset_struc     ; 20 null-byte local stack variable

    mov DWORD PTR [r12], 00          ; 4 byte NULL rset->fd_count on subsequent loops (FD_ZERO)
    mov QWORD PTR [r12 + 4], ssockfd  ; rset->fd_array[0] and [1] are set for 2 fd's
    mov BYTE PTR [r12], 2            ; 2 for rset->fd_count;  2 sockets: client and remote server

I am doing the same thing to wset_struc as well. When I call select(), it fails because the structures are not set up properly. I know that I am not adding in the csockfd because I don't even know where to add it in since it only accepts bits. The position r12 + 4 is correct since the 8 byte SOCKET starts after the first DWORD of the fd_count. However, the ssockfd and csockfd are numbers that are 192, 196, 200 etc. How am I supposed to set the bits of fd_array with 192 and 196, respectively? I've tried reading the disassembly of my c++ code but it's pretty daunting, since I am not really an expert in assembly.

Any help would be appreciated. Thank you.

fearless

You are correct regarding the alignment, using 8 after the structure should help with that. I think this also requires that the assembler also uses the flag: -Zp8 (uasm64 uses that, i dont know if masm64 does tbh)

SOCKET I think is also is a QWORD size in 64bit, an opaque handle that windows understands what the value is actually representing

I assume the sockets need to be created or something beforehand to place them into the fd_array.

I modified the default fd_set structure found in windows.inc to reflect what it might be like for x64 - hopefully its correct.

FD_SETSIZE is 64, dont think you can change that unless modifying the winsock headers and compiling in that new value, so at runtime you will be restricted to 64 sockets at most

I commented some ways of handling the use of the stucture below in the example. I havent tested any of it though so something could be wrong somewhere, but it might give you a starting point at least.

IFNDEF SOCKET64
SOCKET64        TYPEDEF QWORD
ENDIF

IFNDEF FD_SETSIZE
FD_SETSIZE      EQU 64
ENDIF

IFNDEF fd_set64
fd_set64        STRUCT 8 ; align structure for 64bit using '8' after struct
    fd_count    DWORD 0
    fd_array    SOCKET64 FD_SETSIZE DUP (0)
fd_set64        ENDS
ENDIF

.DATA

rset fd_set64 <>
wset fd_set64 <>

.CODE

lea rbx, rset

mov eax, dword ptr [rbx].fd_set64.fd_count
; eax contains count, prob 0 at this stage

; set count to 2
lea rbx, rset
mov eax, 2
mov dword ptr [rbx].fd_set64.fd_count, eax

; load up the array of sockets
lea rbx, rset
lea rbx, [rbx].fd_set64.fd_array

; place socket1 and socket2 into the fd_set array
; socket1 and socket2 already handled elsewhere?
; contain an opaque reference to the socket
; as long as windows knows what they represent
; it wont matter what there actual value is
; rbx points to the array from 'lea rbx, [rbx].fd_set64.fd_array' above
mov rax, Socket1
mov [rbx], rax
mov rax, Socket2
mov [rbx+8], rax



cyrus

ok. I've compiled this and select() actually worked. I believe that I was setting up the structures incorrectly. The way you have recommended worked exactly like it is supposed to and is a new method to me and I am thankful for having learned that piece.

The issue I was having is testing these bits after I run select() because my program doesn't work properly without testing it. the FD_ISSET is a macro as the rest are but that is the only macro that actually calls a function! It calls __WSAFDIsSet which I couldn't find so what I did was get a verbose output of assembly from my c program from windows and analyzed what it was doing after that call. It executes 4 statements to test rax for some flags I am assuming. I wasn't able to actually get the implementation so I just included it as an externdef and it worked.

NoCforMe

Quote from: cyrus on November 12, 2023, 11:55:06 AMThe first 2 bits being set in any int will equal 3 because the first bit (0) has a value of 1 and the second is 2 and so the combination is 3. Is that correct?

I don't know nothin' about sockets and such but I can assure you that what you wrote is correct:

11B = 3D
Assembly language programming should be fun. That's why I do it.

cyrus

solved