Author Topic: How set up socket?  (Read 15206 times)

Offline Great_cat

  • Jr. Member
  • *
  • Posts: 4
How set up socket?
« on: March 11, 2016, 11:43:58 AM »
Hello. Sorry for my English.
I have started learn assembly language recently. I would like create a messanger to send message from one computer to other computer. Command line is enough for me.
Please, suggest me how set up socket? What are there sockets? How include it in program?

p.s. I use NASM on Linux 32-bit and I would like send messages from Linux to Mac and from Mac to Linux.

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: How set up socket?
« Reply #1 on: March 11, 2016, 12:39:46 PM »
I suggest you make use of the C libraries networking functions. A really good document on network programming is Beej's Guide to Network Programming. That should make things considerably easier. There are going to be incompatibilities between the way things are done on the two different systems, you can ease this by increasing the level of abstraction from the hardware through macros and making use of libraries written in high level languages that have been ported to your target systems.

About Bryant Keller
bkeller@about.me

Offline Great_cat

  • Jr. Member
  • *
  • Posts: 4
Re: How set up socket?
« Reply #2 on: March 11, 2016, 04:43:32 PM »
Thank you for reply. I want make program in low-level language and on two platform: Linux and Mac. Is it looking like same? It have same kernel's calls and commands.
I would like that program will be in one file. Is it possible?

p.s. Sorry for my English and knowledge of programming. I learn it :)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How set up socket?
« Reply #3 on: March 11, 2016, 05:53:39 PM »
It would be even easier to bypass the HLL and simply purchase a program to do this. I understand MicroSoft sells 'em.

However, I got the impression that Great Cat wanted to do this at a low level, specifically in Nasm. Perhaps this is an "English" problem, but I don't think so.

There is only one system call to deal with sockets. HLLs - and asm macros - treat it as if there were several - "socket", "connect", "bind", "listen", "accept", "send", recv", etc. etc. etc. These are subfunctions, "commands" - small integers which go in ebx. They use the same sys_socketcall, 102. Remaining arguments - they differ with the subfunction - are pointed to by ecx. Typically, they're on the stack, but don't have to be.

The "port" we ask to connect to determines whether we get an http server, or a mail server, or an nntp server, etc. This is just a 16-bit number (word) - big endian. No relation to the "port" we get with the "in" and "out" instructions!

I have only gotten as far as a simple "echo" server and client, on "port" 2002, and on the "loopback" connection - it doesn't actually talk to a second computer - just from one console to another. For "testing" purposes, it's easier if you don't have to be on two computers at once. :)

I don't really remember just what I did here, and they're not too well commented, but let me just throw 'em out here and see if you can get any help at all from them...

Code: [Select]
; echo server
; echos lines until "quit"
; "kill" unloads server
; runs on loopback, 127.0.0.1, and port 2002,
; no options on this model!
;
; nasm -f elf echosrv.asm
; ld -o echosrv echosrv.o

global _start

;-------------------
; should probably be in an .inc file

struc sockaddr_in
    .sin_family resw 1
    .sin_port resw 1
    .sin_addr resd 1
    .sin_zero resb 8
endstruc

; Convert numbers (constants!) to network byte order
%define hton(x) ((x & 0xFF000000) >> 24) | ((x & 0x00FF0000) >>  8) | ((x & 0x0000FF00) <<  8) | ((x & 0x000000FF) << 24)
%define htons(x) ((x >> 8) & 0xFF) | ((x & 0xFF) << 8)

AF_INET        equ 2
SOCK_STREAM    equ 1
INADDR_ANY equ 0 ; /usr/include/linux/in.h

STDIN          equ 0
STDOUT         equ 1
LF equ 10
EINTR equ 4

__NR_exit equ 1
__NR_read       equ 3
__NR_write      equ 4
__NR_close equ 6
__NR_socketcall equ 102

; commands for sys_socketcall
; /usr/include/linux/in.h
SYS_SOCKET equ 1
SYS_BIND equ 2
SYS_CONNECT equ 3
SYS_LISTEN equ 4
SYS_ACCEPT equ 5
SYS_SEND equ 9
SYS_RECV equ 10
;------------------------

;_ip equ 0x7F000001 ; loopback - 127.0.0.1
_ip equ 0xC0A8012F ; loopback - 127.0.0.1
_port equ 2002

; Convert 'em to network byte order
IP equ hton(_ip)
PORT equ htons(_port)

BACKLOG equ 128 ; for "listen"
BUFLEN         equ  1000

section .data

my_sa istruc sockaddr_in
    at sockaddr_in.sin_family, dw AF_INET
    at sockaddr_in.sin_port, dw PORT
    at sockaddr_in.sin_addr, dd INADDR_ANY
    at sockaddr_in.sin_zero, dd 0, 0
iend

socket_args dd AF_INET, SOCK_STREAM, 0

; first of these wants to be socket descriptor
; we fill it in later...
bind_args   dd 0, my_sa, sockaddr_in_size
listen_args dd 0, BACKLOG
accept_args dd 0, 0, 0

section .bss
    my_buf resb BUFLEN
    fd_socket resd 1
    fd_conn resd 1

section .text
 _start:

    ; socket(AF_INET, SOCK_STREAM, 0)
    mov ecx, socket_args ; address of args structure
    mov ebx, SYS_SOCKET     ; subfunction or "command"
    mov eax, __NR_socketcall     ;c.f. /usr/src/linux/net/socket.c
    int 80h

    cmp eax, -4096
    ja exit

    mov [fd_socket], eax
    ; and fill in bind_args, etc.
    mov [bind_args], eax
    mov [listen_args], eax
    mov [accept_args], eax


    mov ecx, bind_args
    mov ebx, SYS_BIND ; subfunction or "command"
    mov eax, __NR_socketcall
    int 80h

    cmp eax, -4096
    ja exit

    mov ecx, listen_args
    mov ebx, SYS_LISTEN ; subfunction or "command"
    mov eax, __NR_socketcall
    int 80h

    cmp eax, -4096
    ja exit

again:
    mov ecx, accept_args
    mov ebx, SYS_ACCEPT ; subfunction or "command"
    mov eax, __NR_socketcall
    int 80h
    cmp eax, -4096
    ja  exit

    mov [fd_conn], eax


readagain:

    ; read(sock, buf, len)
    push BUFLEN ; arg 3: max count
    push my_buf ; arg 2: buffer
    push dword [fd_conn] ; arg 1: fd
    call readline
    add esp, 12
    test eax, eax
    js closeconn

    push eax ; length read is length to write
    push my_buf
    push dword [fd_conn]
    call writeNbytes
    add esp, 12

    cmp dword [my_buf], 'quit'
    jz closeconn

    cmp dword [my_buf], 'kill'
    jz killserver

    jmp readagain

closeconn:
    mov eax, __NR_close
    mov ebx, [fd_conn]
    int 80h
    cmp eax, -4096
    ja exit
    jmp again
   
killserver:
    mov eax, __NR_close
    mov ebx, [fd_conn]
    int 80h
    mov eax, __NR_close
    mov ebx, [fd_socket]
    int 80h

goodexit:
    xor eax, eax ; success
 exit:
    mov ebx, eax ; exitcode
    neg ebx
    mov eax, __NR_exit
    int 80h
;-----------------------

readline:
    push ebp
    mov ebp, esp
    sub esp, 4
    pusha
%define fd ebp + 8
%define buf ebp + 12
%define max ebp + 16
%define result ebp - 4

    mov dword [result], 0
    mov ebx, [fd]
    mov ecx, [buf]
    mov edx, [max]
.reread:
    mov eax, __NR_read
    int 80h
    cmp eax, -EINTR
    jz .reread
    cmp eax, -4096
    ja .err
    add [result], eax
    cmp byte [eax + ecx - 1], LF
    jz .done
    add ecx, eax
    sub edx, eax
    jna .done
    jmp .reread
.done:
    popa
    mov eax, [result]
    mov esp, ebp
    pop ebp
    ret
.err:
    mov [result], eax
    jmp .done
%undef fd
%undef buf
%undef max
%undef result
;--------------


writeNbytes:
    push ebp
    mov ebp, esp
    sub esp, 4
    pusha
%define fd ebp + 8
%define buf ebp + 12
%define Nbytes ebp + 16
%define bytesleft ebp - 4

    mov ebx, [fd]
    mov ecx, [buf]
    mov edx, [Nbytes]
    mov [bytesleft], edx
.rewrite:
    mov eax, __NR_write
    int 80h
    cmp eax, -EINTR
    jz .rewrite
    cmp eax, -4096
    ja exit
    sub [bytesleft], eax
    jz .done
    add ecx, eax
    sub edx, eax
    jna .done
    jmp .rewrite
.done:
    popa
    mov eax, [Nbytes]
%undef fd
%undef buf
%undef Nbytes
%undef bytesleft
    mov esp, ebp
    pop ebp
    ret

... and a client...

Code: [Select]
;-----------------------------   
;
; nasm -f elf32 echocli.asm
; ld -o echocli echocli.o

global _start

struc sockaddr_in
    .sin_family resw 1
    .sin_port resw 1
    .sin_addr resd 1
    .sin_zero resb 8
endstruc

_ip equ 0x7F000001 ; loopback - 127.0.0.1
;_ip equ 0x48400C6B ; loopback - 127.0.0.1

_port equ 2002

; Convert numbers to network byte order

IP equ ((_ip & 0xFF000000) >> 24) | ((_ip & 0x00FF0000) >>  8) | ((_ip & 0x0000FF00) <<  8) | ((_ip & 0x000000FF) << 24)
PORT equ ((_port >> 8) & 0xFF) | ((_port & 0xFF) << 8)

AF_INET        equ 2
SOCK_STREAM    equ 1

BUFLEN         equ  0x80


STDIN          equ 0
STDOUT         equ 1
LF equ 10
EINTR equ 4

__NR_exit equ 1
__NR_read       equ 3
__NR_write      equ 4
__NR_socketcall equ 102

SYS_SOCKET     equ 1
SYS_CONNECT    equ 3

section .data

my_sa istruc sockaddr_in
    at sockaddr_in.sin_family, dw AF_INET
    at sockaddr_in.sin_port, dw PORT
    at sockaddr_in.sin_addr, dd IP
    at sockaddr_in.sin_zero, dd 0, 0
iend

socket_args dd AF_INET, SOCK_STREAM, 0

; first of these wants to be socket descriptor
; we fill it in later...
connect_args dd 0, my_sa, sockaddr_in_size


section .bss
    my_buf resb BUFLEN
    sock_desc resd 1


section .text
 _start:

    ; socket(AF_INET, SOCK_STREAM, 0)

    mov     ecx, socket_args ; address of args structure
    mov     ebx, SYS_SOCKET     ; subfunction or "command"
    mov     eax, __NR_socketcall     ;c.f. /usr/src/linux/net/socket.c
    int     80h

    cmp     eax, -4096
    ja      exit

    mov     [sock_desc], eax
    ; and fill in connect_args
    mov     [connect_args], eax

     ; connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))

    mov     ecx, connect_args
    mov     ebx, SYS_CONNECT ; subfunction or "command"
    mov     eax, __NR_socketcall
    int     80h

    cmp     eax, -4096
    ja      exit

again:
    push BUFLEN
    push my_buf
    push STDIN
    call readline
    add esp, 12

    push eax
    push my_buf
    push dword [sock_desc]
    call writeNbytes
    add esp, 12

    cmp dword [my_buf], 'quit'
    jz goodexit

    push BUFLEN
    push my_buf
    push dword [sock_desc]
    call readline
    add esp, 12

    push eax
    push my_buf
    push STDOUT
    call writeNbytes
    add esp, 12

    jmp again

goodexit:

    xor eax, eax ; success
 exit:
    mov     ebx, eax ; exitcode
    neg ebx
    mov     eax, __NR_exit
    int     80h

readline:
    push ebp
    mov ebp, esp
    sub esp, 4
    pusha
%define fd ebp + 8
%define buf ebp + 12
%define max ebp + 16
%define result ebp - 4

    mov dword [result], 0
    mov ebx, [fd]
    mov ecx, [buf]
    mov edx, [max]
.reread:
    mov eax, __NR_read
    int 80h
    cmp eax, -EINTR
    jz .reread
    cmp eax, -4096
    ja exit
    add [result], eax
    cmp byte [eax + ecx - 1], LF
    jz .done
    add ecx, eax
    sub edx, eax
    jna .done
    jmp .reread
.done:
    popa
    mov eax, [result]
%undef fd
%undef buf
%undef max
%undef result
    mov esp, ebp
    pop ebp
    ret


writeNbytes:
    push ebp
    mov ebp, esp
    sub esp, 4
    pusha
%define fd ebp + 8
%define buf ebp + 12
%define Nbytes ebp + 16
%define bytesleft ebp - 4

    mov ebx, [fd]
    mov ecx, [buf]
    mov edx, [Nbytes]
    mov [bytesleft], edx
.rewrite:
    mov eax, __NR_write
    int 80h
    cmp eax, -EINTR
    jz .rewrite
    cmp eax, -4096
    ja exit
    sub [bytesleft], eax
    jz .done
    add ecx, eax
    sub edx, eax
    jna .done
    jmp .rewrite
.done:
    popa
    mov eax, [Nbytes]
%undef fd
%undef buf
%undef Nbytes
%undef bytesleft
    mov esp, ebp
    pop ebp
    ret

I don't know too much about this stuff, and don't remember all of the little I learned, but I'll try to answer questions if I can.

You should also read Beej's Guide, of course!

Best,
Frank




Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How set up socket?
« Reply #4 on: March 11, 2016, 06:31:33 PM »
Hi Great cat,

Wanting this to run on two systems makes C (or some other HLL) look like a better idea. It is possible to make the same file run on Linux and BSD, and Mac is "like" BSD, so it's probably possible. How "new" are you? Can you make any program at all run on both Linux and Mac? Take a look at http://asm.sf.net - in the "asmutils" section there are macros which will allow the same source code to assemble and run on Linux and BSD. It can probably be modified to work on Mac. Probably not a good project for a beginner. (sorry to say).

Best,
Frank


Offline Great_cat

  • Jr. Member
  • *
  • Posts: 4
Re: How set up socket?
« Reply #5 on: March 11, 2016, 06:57:01 PM »
Hello Frank Kotler.
Thank you very much for your reply.
I'm beginner in assembly. I can use kernel's calls. I can determine variables and make procedure. I bad understand syntax of the program, but I will learn it more detail. Thank you for information. I have 2 computers with Linux.
I can get access to computer with Mac in public place.
I thought that program on Linux (64-bit) with a few changes will work on Mac. Is it so?

p.s. What is HLL?

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How set up socket?
« Reply #6 on: March 11, 2016, 07:17:00 PM »
"HLL" = "High Level Language" Such as C - designed so the same source code will compile (differently!) and run on multiple platforms. Assembly Language is definitely not designed that way!

My understanding is that BSD will run a Linux program - with minor changes - parameters go on the stack instead of in registers and a "stub" needs to be called to do the int 80h. It does need the minor changes! This applies to 32-bit code. I don't know 64-bit. Linux uses different system call numbers for 64-bit code, I understand Mac uses the same numbers as 32-bit (but a different calling convention!).

Best,
Frank


Offline Great_cat

  • Jr. Member
  • *
  • Posts: 4
Re: How set up socket?
« Reply #7 on: March 11, 2016, 07:23:59 PM »
Thank you very much Frank Kotler.
I will try your program and I will learn assembly and C.

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: How set up socket?
« Reply #8 on: March 11, 2016, 09:51:47 PM »
It would be even easier to bypass the HLL and simply purchase a program to do this. I understand MicroSoft sells 'em.

However, I got the impression that Great Cat wanted to do this at a low level, specifically in Nasm. Perhaps this is an "English" problem, but I don't think so.

When I originally read his question, I started out to write a quick echo client/server by grep'ing unistd_32.h and brushing up on the manual pages for socketcall.. then I noticed the p.s. portion of his post. Unfortunately, adding the requirement of cross-platform development means the project is no longer a trivial newbie project. That is, unless, you use existing libraries that are available on both architectures and abstract subtle differences between the systems, like calling conventions, away with macros.

The funny thing is, when I posted my comment I actually thought to myself "Man, I'm suggesting HLL libraries way too much on this forum." However, there are situations in which the added abstraction is very handy and I rather prefer to suggest that as a solution rather than putting them on a path of development and exploration which would even be difficult for veteran assembler programmers.

Great_cat,

When you're not dealing with the operating system, most of your code can very easily be swapped from one system to another with very little, if any changes. These are usually in the form of procedures written to preform some specific calculations or data manipulations. A good example of this kind of problem would be random number generators or architecture extension specific math routines for optimal performance. But once you start interfacing with the operating system, things get difficult. There is no universal standard by which all operating systems handle networking, or calling procedures, or interfacing between processes. All of these, and more, problems require you to have a decent knowledge of the target system. This isn't unreasonable for a newbie, but doing this on two different operating systems and figuring out how to solve the same problem within the domain of solutions provided by the operating system isn't something I wish on any "new" programmer.

When I mentioned the C library above, there was a reason for that. The C language itself imposes certain requirements for how things should be done. This means that interfacing with the C library should be pretty uniform across the entire library. This will greatly reduce the amount of problems you're going to run into when switching between different operating systems. This doesn't mean you won't run into any problems, there are distinct differences in how C handles calling procedures on 32-bit and 64-bit systems, but this is where implementing macros to abstract away calling conventions comes into play (and is a great practice for "new" programmers to learn to use macros).

Probably the biggest down-side to working within the C library is when you run into C's single-line macros in the headers. Your first instinct will usually be to try and turn those #define lines directly into NASM %define's, but  this doesn't normally work out too well. You have to remember that the NASM %define single-line macro only supports compile-time expansion and C can expand to statements which may then be compiled into instructions. Just be aware that sometimes when you see #define single-line macros, you might be better off porting it using %macro instead of %define.

About Bryant Keller
bkeller@about.me