News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

IPv6 structures missing?

Started by pcMike, November 15, 2017, 10:21:32 AM

Previous topic - Next topic

aw27

#15
I said, no function uses array of dwords. All that matters is the array of BYTES.

Following code connects to my IPv6 enabled website with IPv6 address 2a02:c205:2004:9655::4

You need IPv6 (Actually you can do it with a tunnel broker like https://tunnelbroker.net/ , as I am doing).


include \masm32\include\masm32rt.inc
include \masm32\include\ws2_32.inc
;includelib \masm32\lib\ws2_32.lib ; too old
includelib "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x86\ws2_32.lib"

includelib \masm32\lib\msvcrt.lib
memset proto C :ptr, :dword, :dword


inet_pton PROTO :SDWORD, :PTR, :PTR

SOCKADDR_IN6 STRUCT
sin6_family   WORD ?
sin6_port    WORD ?
sin6_flowinfo   DWORD ?
sin6_addr    BYTE 16 DUP (?)
sin6_scope_id   DWORD ?
SOCKADDR_IN6 ENDS

.Data
atelierweb db "2a02:c205:2004:9655::4",0
request db "GET / HTTP/1.1",13,10,"Host: www.atelierweb.com",13,10,"Connection: close",13,10,13,10,0;

.Code

main proc
LOCAL wsadata:WSADATA
LOCAL _addr : SOCKADDR_IN6
LOCAL sock : dword
LOCAL buffer[256] : byte
invoke memset, addr buffer,0,sizeof buffer

Invoke WSAStartup, 202h, Addr wsadata
.if eax!=0
jmp @exit2
.endif

INVOKE socket, AF_INET6, SOCK_STREAM, IPPROTO_TCP

.if eax==INVALID_SOCKET
jmp @exit1
.endif
mov sock, eax
invoke memset, addr _addr,0,sizeof _addr
mov _addr.sin6_family, AF_INET6
INVOKE htons, 80
mov _addr.sin6_port, ax
INVOKE inet_pton, AF_INET6, addr atelierweb, addr _addr.sin6_addr
INVOKE connect, sock, ADDR _addr, sizeof _addr
INVOKE send, sock, addr request, SIZEOF request, 0
INVOKE recv, sock, addr buffer, sizeof buffer,0
INVOKE crt_printf, addr buffer
INVOKE closesocket, sock

@exit1:
invoke WSACleanup
@exit2:

Invoke ExitProcess, 0
main endp

End main


HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=UTF-8
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Server: Microsoft-IIS/7.5
X-Powered-By: PHP/7.0.22
Set-Cookie: PHPSESSID=mbpa5ug5qip3in|

felipe

Quote from: jj2007 on November 15, 2017, 07:35:09 PM
the obfuscated M$ crap:

:lol:

Sorry for taking out of context, but it's too funny for me.... :greenclp:

:lol:

pcMike

Jose, Thanks for the IPv6 client example!

I see that you had to use a newer version of ws2_32.lib and defined  inet_pton to make it work.
Do you have any MASM32 example code for an IPv6 server?

Instead of using getaddrinfo (which can automatically fill in the addrinfo data with all the interface's network addresses to bind to), I decided it would be simpler to
fill in the addrinfo data manually using "in6addr_any" but MASM says this symbol is undefined. Do you know the value?

aw27

Hi Mike,

in6addr_any, is externally defined.
extern in6addr_any:oword

or
simply :: (i.e 16 zero bytes)

Quote
Do you have any MASM32 example code for an IPv6 server?

No, I am waiting for yours. :biggrin:







pcMike

After some research I found the "in6addr_any" value is 6173083.  However, I'm still not able to get the server to bind to the listening port using IPv6.

When using IPv4, I can set the address to INADDR_ANY to allow all local interface addresses to be bound to the selected TCP port.
IPv6 allows this by setting the address to in6addr_any, but I can't make it work.

Quote
If an application does not care what local address is assigned, specify the constant value INADDR_ANY for an IPv4 local address or the constant value in6addr_any for an IPv6 local address in the sa_data member of the name parameter. This allows the underlying service provider to use any appropriate network address, potentially simplifying application programming in the presence of multihomed hosts (that is, hosts that have more than one network interface and address).
Source: https://msdn.microsoft.com/en-us/library/windows/desktop/ms737550(v=vs.85).aspx

The problem appears to be related to the "sin6_addr" element of the SOCKADDR_IN6 structure, which apparently has a "sa_data" member within it's 16 bytes which is where the "in6addr_any" value belongs, according to the above MSDN quote.

Here is a minimalistic program demonstrating the issue. It will conditionally assemble for either an IPv4 or IPv6 socket, by setting the "IPv4" value TRUE or FALSE.
In IPv4 mode, it binds fine using INADDR_ANY, but in IPv6 mode it fails to bind, resulting in WSAError 10049 (WSAEADDRNOTAVAIL - Cannot assign requested address).

Quote
include \masm32\include\masm32rt.inc
include \masm32\include\ws2_32.inc
includelib \masm32\lib\ws2_32.lib
includelib \masm32\lib\msvcrt.lib

;-------------------------------------
IPv4 equ FALSE ; Set to false for IPv6!
;-------------------------------------


in6addr_any equ 61730832 ; This was not defined in Windows.inc


SOCKADDR_IN6 STRUCT
  sin6_family WORD ?
  sin6_port   WORD ?
  sin6_flowinfo DWORD ?
  sin6_addr DWORD ?,?in
  sin6_scope_id DWORD ?
SOCKADDR_IN6 ENDS

.Data
   Port dd 23           ; Inbound TCP port
   goodbye db "goodbye" ; Msg to send client

   _addr4_size dd SIZEOF _addr4
   _addr6_size dd SIZEOF _addr6

   _addr4  sockaddr_in  <?>    ; IPv4
   _addr6  sockaddr_in6 <?>    ; IPv6

.Code
main proc
       LOCAL wsadata:WSADATA
       LOCAL sock : dword
       LOCAL sock2 : dword
   
       Invoke WSAStartup, 202h, Addr wsadata

if (IPv4)
   INVOKE socket, AF_INET, SOCK_STREAM, IPPROTO_TCP ;   
   mov _addr4.sin_family, AF_INET
   mov _addr4.sin_addr, INADDR_ANY       ; IPv4: use all network interfaces available
else ;IPv6:
   invoke socket, AF_INET6,  SOCK_STREAM, IPPROTO_TCP
   mov _addr6.sin6_family, AF_INET6
   mov dword ptr _addr6.sin6_addr, in6addr_any ; IPv6: use all network interfaces available
   mov _addr6.sin6_flowinfo,0                ; flowinfo must be 0     
   mov _addr6.sin6_scope_id,0                ; scope_id must be 0
endif
   mov sock,eax                 ; save socket descriptor handle


; TCP Server functions:
; "Socket" to get socket Descriptor.
; "htons" to convert TCP port from decimal to network bytes.
; "Bind" to own a TCP port.
; "Setsockopt" to set socket options.
; "Listen" for incoming connections on port.
; "Accept" allow an incoming connection.

   invoke htons,Port                 ; Convert port values host/network bytes

if (IPv4)
       mov _addr4.sin_port, ax            ; IPv4: store sin_port to bind      
       invoke bind,sock,addr _addr4 , sizeof _addr4   ; "Bind" to port socket

else
       mov _addr6.sin6_port,ax            ; IPv6: store sin_port to bind
       invoke bind,sock,addr _addr6 , sizeof _addr6   ; "Bind" to port socket
endif

   .if eax!=NULL
      print "Bind Failed",13,10
               jmp @exit
   .endif

   invoke listen,sock,3                 ; "Listen" for incoming connection (maximum=3) SOMAXCONN
   .if eax!=NULL                        ; Check for error
      print "Listen Failed",10,13
          jmp @exit
   .endif

        print "Listening for incoming connections",13,10

Accept_loop:
if (IPv4)
     invoke accept, sock, addr _addr4, addr _addr4_size  ; IPv4: Accept Connection
else
     invoke accept, sock, addr _addr6, addr _addr6_size  ; IPv6: Accept Connection
endif

   .if eax!=INVALID_SOCKET
           mov sock2,eax               ; If socket is good, handle connection.
           print "Connection Accepted!",13,10
           invoke send,sock2,addr goodbye,sizeof goodbye,0  ; Send message to client
           invoke Sleep,500
           invoke closesocket, sock2
   .endif
        jmp Accept_loop

@exit:
       print "WSAGetLastError="
       invoke WSAGetLastError
       .if eax==WSAEADDRNOTAVAIL
       push eax
       print "WSAEADDRNOTAVAIL - Cannot assign requested address - "
       pop eax
       .endif
       print  ustr$(eax),13,10
       print  "exiting",13,10   
       Invoke ExitProcess, 0
   
main endp   
   
End main


aw27

Quote
After some research I found the "in6addr_any" value is 6173083.
So, I have been preaching to the fishes.  :dazzled:

pcMike

Jose - I just read your post after I posted mine. I changed the code to zero out all 16 bytes of the sin6_addr, and now it's binding and accepting connections! :-)

aw27

 :biggrin:

Anyway, SOCKADDR_IN6 STRUCT looks wrong and will make the program crash anytime.

pcMike

Oh yes there was a typo there with the "sin6_addr  dword ?,?in", I'm not sure how the "in" got there but I've already changed it back to "sin6_addr  db 16 dup (?)".






pcMike

I added some features such as IP and hostname output for incoming connections. Since MASM32's ws_32.lib is missing inet_ntop you will need to use a newer one from MS SDK. 

Quote
; Example IPv4 and/or IPv6 Socket Server

include \masm32\include\masm32rt.inc
include \masm32\include\ws2_32.inc
;includelib \masm32\lib\ws2_32.lib ; too old for IPv6 functions
includelib "C:\Program Files\Windows Kits\10\Lib\10.0.16299.0\um\x86\ws2_32.lib"

;--------------------------------------
IPv4 equ FALSE ; Set to FALSE for IPv6!
;--------------------------------------

;define IPV6_V6ONLY       27 // Treat IPv6 wildcard bind as AF_INET6-only - Disable this for Dual-Stack
;define in6addr_any       0  // Requires all 16 bytes of sin6_addr to be zero

inet_pton PROTO :SDWORD,:PTR,:PTR        ; Convert IPv4 or IPv6 address in text form to binary form
inet_ntop PROTO :SDWORD,:PTR,:PTR,:DWORD ; Convert IPv4 or IPv6 address in binary form to text form (replaces inet_ntoa)
KeyThread PROTO

SOCKADDR_IN6 STRUCT
  sin6_family WORD ?
  sin6_port   WORD ?
  sin6_flowinfo DWORD ?
  sin6_addr  db 16 dup (?)
  sin6_scope_id DWORD ?
SOCKADDR_IN6 ENDS

.Data
   Port dd 23           ; Inbound TCP port (23=Telnet)
   goodbye db "goodbye" ; Msg to send client

   _addr4_size dd SIZEOF _addr4 ; IPv4 size
   _addr6_size dd SIZEOF _addr6 ; IPv6 size
   zero        dd 0 ; used to set TCPv6Only option off

   _addr4  sockaddr_in  <?>    ; IPv4
   _addr6  sockaddr_in6 <?>    ; IPv6
   hostname    db 1024 dup (?)
   servicename db 32 dup (?) ; service name (or number)
   buffer      db 46 dup (?) ; buffer to print Ansi text IP address
   bufsize     dd ?
   ThreadID    dd ?

.Code
Start:

main proc
       LOCAL wsadata:WSADATA
       LOCAL sock : dword
       LOCAL sock2 : dword
       cls ; clear screen
       xor eax,eax   
       invoke CreateThread,eax,eax,addr KeyThread,eax,eax,eax ; Keyboard thread
       Invoke WSAStartup, 202h, Addr wsadata

; TCP Server functions:
; "Socket" to get socket Descriptor.
; "htons" to convert TCP port from decimal to network bytes.
; "Bind" to own a TCP port.
; "Setsockopt" to set socket options.
; "Listen" for incoming connections on port.
; "Accept" allow an incoming connection.

if (IPv4)
   invoke socket, AF_INET, SOCK_STREAM, IPPROTO_TCP   
   mov _addr4.sin_family, AF_INET
   mov _addr4.sin_addr, INADDR_ANY       ; IPv4: use all network interfaces available
else ;IPv6:
       invoke socket, AF_INET6,  SOCK_STREAM, IPPROTO_TCP
       mov _addr6.sin6_family, AF_INET6
       xor ebx,ebx
       mov dword ptr _addr6.sin6_addr, ebx   ;IPv6: Zero 16 byte .sin6_addr to use in6addr_any
       mov dword ptr _addr6.sin6_addr+4, ebx
       mov _addr6.sin6_flowinfo,ebx                ; flowinfo must be 0     
       mov _addr6.sin6_scope_id,ebx                ; scope_id must be 0
endif
   mov sock,eax                      ; save socket descriptor handle
   invoke htons,Port                 ; Convert port value to host/network bytes

if (IPv4)
       mov _addr4.sin_port, ax            ; IPv4: store sin_port to bind      
       invoke bind,sock,addr _addr4, sizeof _addr4   ; "Bind" to port socket
else ;IPv6
        push eax
        ; Disable the IPV6_V6ONLY mode, so that IPv4 connections are also accepted.
        IPV6_V6ONLY equ 27    ; value missing from windows.inc
        invoke    setsockopt, sock, IPPROTO_IPV6, IPV6_V6ONLY, addr zero, sizeof zero
   .if eax!=NULL
      print "setsockopt disable IP6_V6ONLY Failed",13,10
              jmp @exit
   .endif
        pop eax

       mov _addr6.sin6_port,ax                      ; IPv6: store sin_port to bind
       invoke bind,sock,addr _addr6, sizeof _addr6  ; "Bind" to port socket
endif
       .if eax!=NULL
            print "Bind Failed",13,10
            jmp @exit
       .endif

   invoke listen,sock,3                 ; "Listen" for incoming connection (maximum=3) SOMAXCONN
   .if eax!=NULL                        ; Check for error
      print "Listen Failed",10,13
          jmp @exit
   .endif

       print "Listening for incoming connections - Press ESC to exit.",13,10

Accept_loop:

if (IPv4)
        invoke accept, sock, addr _addr4, addr _addr4_size  ; IPv4: Accept Connection
else;IPv6
        invoke accept, sock, addr _addr6, addr _addr6_size  ; IPv6: Accept Connection
endif
   .if eax!=INVALID_SOCKET
           mov sock2,eax               ; If socket is good, handle connection.
           print "Connection Accepted!",13,10
if (IPv4)
           print "IPv4 address hex: "
           mov eax,dword ptr _addr4.sin_addr
else ;IPv6
           print "IPv6 address hex: "
           mov eax,dword ptr _addr6.sin6_addr
           bswap eax
           print uhex$(eax),32
           mov eax,dword ptr _addr6.sin6_addr+4
           bswap eax
           print uhex$(eax),32
           mov eax,dword ptr _addr6.sin6_addr+8
           bswap eax
           print uhex$(eax),32
           mov eax,dword ptr _addr6.sin6_addr+12
endif           
           bswap eax           
           print uhex$(eax),13,10
if (IPv4)
           print "IPv4 address txt: "
           invoke inet_ntop,AF_INET, addr _addr4.sin_addr, addr buffer, addr bufsize ;   Create Ansi text IP Address
           invoke StdOut,addr buffer
           print chr$(13),10

else ;IPv6
           print "IPv6 address txt: "
           invoke inet_ntop,AF_INET6, addr _addr6.sin6_addr, addr buffer, addr bufsize ;   Create Ansi text IP Address
           invoke StdOut,addr buffer
           print chr$(13),10

           ; Check if it's really an IPv4 address (00000000 00000000 0000FFFF xxxxxxxx)
           mov eax, dword ptr _addr6.sin6_addr
           or  eax, dword ptr _addr6.sin6_addr+4
           jnz notipv4
           cmp dword ptr _addr6.sin6_addr+8,0FFFF0000h
           jne notipv4
           print "IPv4 correct txt: "
           lea eax,buffer+7 ; skip first 7 characters "::ffff:"
           Invoke StdOut,eax
           print chr$(13),10
notipv4:
endif   
           ; getnameinfo - creates hostname and servicename from address
           ;
           ;      The flags parameter can be used to customize processing of the getnameinfo function.
           ;       The following flags are available:
           ;       NI_NOFQDN      - When set, local hosts only get their Relative Distinguished Name (RDN) returned.
           ;       NI_NUMERICHOST - returns the numeric form of the hostname instead of its name.
           ;       ;                The numeric form is also returned if the hostname wont resolve by DNS.
           ;       NI_NAMEREQD    - When set, a host name that cannot be resolved by DNS results in an error.
           ;       NI_NUMERICSERV - When set, The service port number is returned instead of its name.
           ;                        Also, if no hostname is found, the hostname is returned as the IP address.
           ;       NI_DGRAM       - Indicates the service is a datagram service. (providing different port numbers).

if (IPv4)
           invoke getnameinfo, addr _addr4, sizeof _addr4,\
                               addr hostname,sizeof hostname,\
                               addr servicename,sizeof servicename,0
else ; IPv6
           invoke getnameinfo, addr _addr6, sizeof _addr6,\
                               addr hostname,sizeof hostname,\
                               addr servicename,sizeof servicename,0
endif
          .if eax !=NULL
             print "getnameinfo Failed!",13,10
             jmp @exit
          .endif
   
           print "hostname: "
           invoke StdOut,addr hostname
           print chr$(13),10
           print "servicename: "
           invoke StdOut,addr servicename
           print chr$(13),10

           invoke send,sock2,addr goodbye,sizeof goodbye,0  ; Send message to client
           invoke Sleep,500
           invoke closesocket, sock2 ; Disconnect client
   .endif
        jmp Accept_loop

@exit:
       print "WSAGetLastError="
       invoke WSAGetLastError
       .if eax==WSAEADDRNOTAVAIL
       push eax
       print "WSAEADDRNOTAVAIL - Cannot assign requested address - "
       pop eax
       .endif
       print  ustr$(eax),13,10
       Invoke ExitProcess, 0   
main endp   

KeyThread proc near
       getkey
       cmp al,01Bh      ; Exit if ESC pressed
       jne KeyThread
       Invoke ExitProcess, 0
KeyThread endp

End  Start

pcMike

I tried to run my server under Windows XP, but I get the message:

Quote
Title:  Server.exe Entry Point Not Found:
Dialog: The procedure entry point inet_ntop could not be found in the dynamic link library ws_32.dll.

Is it possible to only load the inet_ntop from the ws_32.dll if GetVersionEx shows Windows Vista or later?



pcMike

Nevermind, I was able to call the function directly using  GetModuleHandle/GetProcAddress. :-)


aw27

Or you can make your own inet_ntop from WSAAddressToString. In this case, you have to watch for a "%" in the result and remove everything after it.

pcMike

I considered doing that, but I decided not so support IPv6 under XP (at least for now) because the dual-stack approach to having one socket answer both IPv6 and IPv4 connections is not available in Windows XP, and it would require a listening socket for each one.

six_L

1. the netdatas can't be crossed the CISCO switch,so the server and client must be in localnet.
2. you must set the server addr into your ipv6.
3. The server displays the information of the client that is connected
client:
include \masm32\include\masm32rt.inc
include \masm32\include\ws2_32.inc
includelib \masm32\lib\ws2_32.lib
include \masm32\include\debug.inc
includelib \masm32\lib\debug.lib
includelib \masm32\lib\msvcrt.lib

memset proto C :ptr, :dword, :dword
;inet_pton PROTO :SDWORD, :PTR, :PTR
proto_Pinet_pton typedef proto stdcall :SDWORD,:PTR,:PTR
Pinet_pton typedef ptr proto_Pinet_pton

SOCKADDR_IN6 STRUCT
sin6_family WORD ?
sin6_port WORD ?
sin6_flowinfo   DWORD ?
sin6_addr BYTE 16 DUP (?)
sin6_scope_id   DWORD ?
SOCKADDR_IN6 ENDS

.Data
svr_ipv6 db "fe80::cdff:9f34:d69:7323%11",0

.data?
_inet_pton Pinet_pton ?

.Code

ErrorMessage Proc lpCaption:dword,nFlag:BOOL
Local lpErrorMessage:DWORD

.if nFlag==TRUE
call WSAGetLastError
.else
call GetLastError
.endif
lea ecx,lpErrorMessage
invoke FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM, NULL, EAX, LANG_NEUTRAL,ecx,0,NULL

invoke MessageBox, 0, lpErrorMessage, lpCaption, MB_OK
invoke LocalFree, lpErrorMessage
ret   

ErrorMessage EndP

GetApiAdd proc dllname:DWORD,procname:DWORD
Local hdll:HWND

invoke LoadLibrary,dllname
.if eax!=0
mov hdll,eax
invoke GetProcAddress,hdll,procname
push eax
invoke FreeLibrary,hdll
pop eax
.else
invoke ErrorMessage,CTXT("LoadLibrary"),FALSE
.endif
ret
GetApiAdd endp

start proc
LOCAL wsadata:WSADATA
LOCAL _addr : SOCKADDR_IN6
LOCAL sock : dword
LOCAL buffer[128]:byte

invoke GetApiAdd,CTXT("ws2_32.dll"),CTXT("inet_pton")
.if eax==0
invoke ErrorMessage,CTXT("GetApiAdd"),FALSE
jmp @exit2
.else
mov _inet_pton,eax
.endif

invoke memset, addr buffer,0,sizeof buffer
invoke memset, addr _addr,0,sizeof _addr

Invoke WSAStartup, 202h, Addr wsadata
.if eax!=0
invoke ErrorMessage,CTXT("WSAStartup"),TRUE
jmp @exit2
.endif

INVOKE socket, AF_INET6, SOCK_STREAM, IPPROTO_TCP

.if eax==INVALID_SOCKET
invoke ErrorMessage,CTXT("socket"),TRUE
jmp @exit1
.endif
mov sock, eax

mov _addr.sin6_family, AF_INET6
INVOKE htons, 9023
mov _addr.sin6_port, ax
INVOKE _inet_pton, AF_INET6, offset svr_ipv6, addr _addr.sin6_addr
INVOKE connect, sock, ADDR _addr, sizeof _addr
.if eax==SOCKET_ERROR
invoke ErrorMessage,CTXT("connect"),TRUE
.else
INVOKE recv, sock, addr buffer, 100,0
INVOKE crt_printf, addr buffer
.endif

INVOKE closesocket, sock

@exit1:
invoke WSACleanup
@exit2:

print chr$(13),10
inkey 
Invoke ExitProcess, 0

start endp

end start


server:
include \masm32\include\masm32rt.inc

include \masm32\include\ws2_32.inc
includelib \masm32\lib\ws2_32.lib ;
include \masm32\include\debug.inc
includelib \masm32\lib\debug.lib

;--------------------------------------
IPv4 equ FALSE ; Set to FALSE for IPv6!
;--------------------------------------
proto_Pinet_ntop typedef proto stdcall :SDWORD,:PTR,:PTR,:DWORD
Pinet_ntop typedef ptr proto_Pinet_ntop

;define IPV6_V6ONLY       27 // Treat IPv6 wildcard bind as AF_INET6-only - Disable this for Dual-Stack
;define in6addr_any       0  // Requires all 16 bytes of sin6_addr to be zero

;inet_pton PROTO :SDWORD,:PTR,:PTR        ; Convert IPv4 or IPv6 address in text form to binary form
;inet_ntop PROTO :SDWORD,:PTR,:PTR,:DWORD ; Convert IPv4 or IPv6 address in binary form to text form (replaces inet_ntoa)
;KeyThread PROTO

sockaddr_in6 STRUCT
sin6_family WORD ?
sin6_port   WORD ?
sin6_flowinfo DWORD ?
sin6_addr  db 16 dup (?);sin6_addr
sin6_scope_id DWORD ?
sockaddr_in6 ENDS

.Data
Port dd 9023           ; Inbound TCP port (23=Telnet)
goodbye db "...goodbye!..." ; Msg to send client

_addr4_size dd SIZEOF _addr4 ; IPv4 size
_addr6_size dd SIZEOF _addr6 ; IPv6 size
zero dd 0 ; used to set TCPv6Only option off

_addr4 sockaddr_in  <?>    ; IPv4
_addr6 sockaddr_in6 <?>    ; IPv6
hostname db 1024 dup (?)
servicename db 32 dup (?) ; service name (or number)
buffer db 46 dup (?) ; buffer to print Ansi text IP address
bufsize dd ?
ThreadID dd ?
_inet_ntop Pinet_ntop ?

.Code

ErrorMessage Proc lpCaption:dword,nFlag:BOOL
Local lpErrorMessage:DWORD

.if nFlag==TRUE
call WSAGetLastError
.else
call GetLastError
.endif
lea ecx,lpErrorMessage
invoke FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM, NULL, EAX, LANG_NEUTRAL,ecx,0,NULL

invoke MessageBox, 0, lpErrorMessage, lpCaption, MB_OK
invoke LocalFree, lpErrorMessage
ret   

ErrorMessage EndP

GetApiAdd proc dllname:DWORD,procname:DWORD
Local hdll:HWND

invoke LoadLibrary,dllname
.if eax!=0
mov hdll,eax
invoke GetProcAddress,hdll,procname
push eax
invoke FreeLibrary,hdll
pop eax
.else
invoke ErrorMessage,CTXT("LoadLibrary"),FALSE
.endif
ret
GetApiAdd endp

KeyThread proc near
getkey
cmp al,01Bh      ; Exit if ESC pressed
jne KeyThread
invoke WSACleanup
Invoke ExitProcess, 0
KeyThread endp

start proc
LOCAL wsadata:WSADATA
LOCAL sock : dword
LOCAL sock2 : dword

invoke GetApiAdd,CTXT("ws2_32.dll"),CTXT("inet_ntop")
.if eax==0
invoke ErrorMessage,CTXT("GetApiAdd"),FALSE
jmp @exit
.else
mov _inet_ntop,eax
.endif
;PrintHex eax
cls ; clear screen
xor eax,eax   
invoke CreateThread,eax,eax,addr KeyThread,eax,eax,eax ; Keyboard thread
Invoke WSAStartup, 202h, Addr wsadata

; TCP Server functions:
; "Socket" to get socket Descriptor.
; "htons" to convert TCP port from decimal to network bytes.
; "Bind" to own a TCP port.
; "Setsockopt" to set socket options.
; "Listen" for incoming connections on port.
; "Accept" allow an incoming connection.

if (IPv4)
invoke socket, AF_INET, SOCK_STREAM, IPPROTO_TCP   
mov _addr4.sin_family, AF_INET
mov _addr4.sin_addr, INADDR_ANY       ; IPv4: use all network interfaces available
else ;IPv6:
invoke socket, AF_INET6,  SOCK_STREAM, IPPROTO_TCP
mov _addr6.sin6_family, AF_INET6
xor ebx,ebx
mov dword ptr _addr6.sin6_addr, ebx   ;IPv6: Zero 16 byte .sin6_addr to use in6addr_any
mov dword ptr _addr6.sin6_addr+4, ebx
mov _addr6.sin6_flowinfo,ebx                ; flowinfo must be 0     
mov _addr6.sin6_scope_id,ebx                ; scope_id must be 0
endif
mov sock,eax                      ; save socket descriptor handle
invoke htons,Port                 ; Convert port value to host/network bytes

if (IPv4)
mov _addr4.sin_port, ax       ; IPv4: store sin_port to bind     
invoke bind,sock,addr _addr4, sizeof _addr4   ; "Bind" to port socket
else ;IPv6
push eax
; Disable the IPV6_V6ONLY mode, so that IPv4 connections are also accepted.
IPV6_V6ONLY equ 27    ; value missing from windows.inc
invoke setsockopt, sock, IPPROTO_IPV6, IPV6_V6ONLY, addr zero, sizeof zero
.if eax!=NULL
invoke ErrorMessage,CTXT("setsockopt"),TRUE
jmp @exit
.endif
pop eax

mov _addr6.sin6_port,ax                      ; IPv6: store sin_port to bind
invoke bind,sock,addr _addr6, sizeof _addr6  ; "Bind" to port socket
endif
       .if eax!=NULL
invoke ErrorMessage,CTXT("bind"),TRUE
jmp @exit
       .endif

invoke listen,sock,3                 ; "Listen" for incoming connection (maximum=3) SOMAXCONN
.if eax!=NULL                        ; Check for error
invoke ErrorMessage,CTXT("listen"),TRUE
jmp @exit
.endif

print "Listening for incoming connections - Press ESC to exit.",13,10

Accept_loop:

if (IPv4)
        invoke accept, sock, addr _addr4, addr _addr4_size  ; IPv4: Accept Connection
else;IPv6
        invoke accept, sock, addr _addr6, addr _addr6_size  ; IPv6: Accept Connection
endif
.if eax!=INVALID_SOCKET
mov sock2,eax               ; If socket is good, handle connection.
print "Connection Accepted!",13,10
if (IPv4)
print "IPv4 address hex: "
mov eax,dword ptr _addr4.sin_addr
else ;IPv6
print "IPv6 address hex: "
mov eax,dword ptr _addr6.sin6_addr
bswap eax
print uhex$(eax),32
mov eax,dword ptr _addr6.sin6_addr+4
bswap eax
print uhex$(eax),32
mov eax,dword ptr _addr6.sin6_addr+8
bswap eax
print uhex$(eax),32
mov eax,dword ptr _addr6.sin6_addr+12
endif           
bswap eax           
print uhex$(eax),13,10
if (IPv4)
print "IPv4 address txt: "
invoke _inet_ntop,AF_INET, addr _addr4.sin_addr, addr buffer, addr bufsize ;   Create Ansi text IP Address
invoke StdOut,addr buffer
print chr$(13),10

else ;IPv6
print "IPv6 address txt: "
invoke _inet_ntop,AF_INET6, addr _addr6.sin6_addr, addr buffer, addr bufsize ;   Create Ansi text IP Address
invoke StdOut,addr buffer
print chr$(13),10

; Check if it's really an IPv4 address (00000000 00000000 0000FFFF xxxxxxxx)
mov eax, dword ptr _addr6.sin6_addr
or  eax, dword ptr _addr6.sin6_addr+4
jnz notipv4
cmp dword ptr _addr6.sin6_addr+8,0FFFF0000h
jne notipv4
print "IPv4 correct txt: "
lea eax,buffer+7 ; skip first 7 characters "::ffff:"
Invoke StdOut,eax
print chr$(13),10
notipv4:
endif   
; getnameinfo - creates hostname and servicename from address
;
;      The flags parameter can be used to customize processing of the getnameinfo function.
;       The following flags are available:
;       NI_NOFQDN      - When set, local hosts only get their Relative Distinguished Name (RDN) returned.
;       NI_NUMERICHOST - returns the numeric form of the hostname instead of its name.
;       ;                The numeric form is also returned if the hostname wont resolve by DNS.
;       NI_NAMEREQD    - When set, a host name that cannot be resolved by DNS results in an error.
;       NI_NUMERICSERV - When set, The service port number is returned instead of its name.
;                        Also, if no hostname is found, the hostname is returned as the IP address.
;       NI_DGRAM       - Indicates the service is a datagram service. (providing different port numbers).

if (IPv4)
invoke getnameinfo, addr _addr4, sizeof _addr4,addr hostname,sizeof hostname,addr servicename,sizeof servicename,0
else ; IPv6
invoke getnameinfo, addr _addr6, sizeof _addr6,addr hostname,sizeof hostname,addr servicename,sizeof servicename,0
endif
.if eax !=NULL
print "getnameinfo Failed!",13,10
jmp @exit
.endif
   
print "hostname: "
invoke StdOut,addr hostname
print chr$(13),10
print "servicename: "
invoke StdOut,addr servicename
print chr$(13),10

invoke send,sock2,addr goodbye,sizeof goodbye,0  ; Send message to client
invoke Sleep,500
invoke closesocket, sock2 ; Disconnect client
.endif
        jmp Accept_loop

@exit:
print "WSAGetLastError="
invoke WSAGetLastError
.if eax==WSAEADDRNOTAVAIL
push eax
print "WSAEADDRNOTAVAIL - Cannot assign requested address - "
pop eax
.endif
print  ustr$(eax),13,10
invoke WSACleanup
Invoke ExitProcess, 0   
start endp   

end start
Say you, Say me, Say the codes together for ever.