Skip to content

Latest commit

 

History

History

socket

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

SMC Socket Programming

C

See csmc.c for a C program that runs a client or server with an AF_SMC socket.

SMC protocol definitions:

// #define AF_SMC 43
#define SMCPROTO_SMC 0
#define SMCPROTO_SMC6 1

SMC socket address (AF_INET and AF_INET6) creation code:

/* create an ipv4 socket address with address and port */
int create_sockaddr4(char *address, int port, struct sockaddr_in *sockaddr) {
        /* set ipv4 defaults */
        sockaddr->sin_family = AF_INET;
        sockaddr->sin_addr.s_addr = INADDR_ANY;
        sockaddr->sin_port = htons(PORT);

        /* parse given port if specified */
        if (port > 0) {
                sockaddr->sin_port = htons(port);
        }

        /* parse given address if specified */
        if (address && inet_pton(AF_INET, address, &sockaddr->sin_addr) != 1) {
                return -1;
        }
        return sizeof(struct sockaddr_in);
}

/* create an ipv6 socket address with address and port */
int create_sockaddr6(char *address, int port, struct sockaddr_in6 *sockaddr) {
        /* set ipv6 defaults */
        sockaddr->sin6_family = AF_INET6;
        sockaddr->sin6_addr = in6addr_any;
        sockaddr->sin6_port = htons(PORT);

        /* parse given port if specified */
        if (port > 0) {
                sockaddr->sin6_port = htons(port);
        }

        /* parse given address if specified */
        if (address &&
            inet_pton(AF_INET6, address, &sockaddr->sin6_addr) != 1) {
                return -1;
        }
        return sizeof(struct sockaddr_in6);
}

/* create an ipv4 or ipv6 sock address with address and port */
int create_sockaddr(char *address, int port, struct sockaddr_in6 *sockaddr) {
        int sockaddr_len;

        /* check if it's an ipv4 address */
        sockaddr_len = create_sockaddr4(address, port,
                                        (struct sockaddr_in *) sockaddr);
        if (sockaddr_len > 0) {
                return sockaddr_len;
        }

        /* check if it's an ipv6 address */
        sockaddr_len = create_sockaddr6(address, port, sockaddr);
        if (sockaddr_len > 0) {
                return sockaddr_len;
        }

        /* parsing error */
        return -1;
}

SMC specific server socket code:

struct sockaddr_in6 server_addr;
struct sockaddr_in6 client_addr;
int server_sock, client_sock;
int server_addr_len;
int client_addr_len;

/* create socket address from address and port */
server_addr_len = create_sockaddr(address, port, &server_addr);
if (server_addr_len < 0) {
        printf("Error parsing server address\n");
        return -1;
}

/* create socket */
if (server_addr.sin6_family == AF_INET6) {
        server_sock = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC6);
} else {
        server_sock = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC);
}
if (server_sock == -1) {
        printf("Error creating socket\n");
        return -1;
}

/* bind listening address and port */
if (bind(server_sock, (struct sockaddr *) &server_addr,
         server_addr_len)) {
        printf("Error binding socket\n");
        return -1;
}

/* wait for a new connection */
if (listen(server_sock, 1)) {
        printf("Error listening on socket\n");
        return -1;
}

/* accept new connection */
client_addr_len = server_addr_len;
client_sock = accept(server_sock, (struct sockaddr *) &client_addr,
                     &client_addr_len);
if (client_sock == -1) {
        printf("Error accepting connection\n");
        return -1;
}
printf("New client connection\n");

/* read from client socket, write to client socket */

close(client_sock);
close(server_sock);

SMC specific client socket code:

struct sockaddr_in6 server_addr;
int server_addr_len;
int client_sock;

/* create socket address from address and port */
server_addr_len = create_sockaddr(address, port, &server_addr);
if (server_addr_len < 0) {
        printf("Error parsing server address\n");
        return -1;
}

/* create socket */
if (server_addr.sin6_family == AF_INET6) {
        client_sock = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC6);
} else {
        client_sock = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC);
}
if (client_sock == -1) {
        printf("Error creating socket\n");
        return -1;
}

/* connect to server */
if (connect(client_sock, (struct sockaddr *) &server_addr,
            server_addr_len)) {
        printf("Error connecting to server\n");
        return -1;
}
printf("Connected to server\n");

/* write to socket, read from socket  */

close(client_sock);

Compiling the code:

$ gcc csmc.c -o csmc

Running the code:

$ ./csmc -s
New client connection
Read 12 bytes from client: Hello, world
Sent 12 bytes to client: Hello, world
$ ./csmc -c -a 127.0.0.1
Connected to server
Sent 12 bytes to server: Hello, world
Read 12 bytes from server: Hello, world

Go

See gosmc.go for a Go program that runs a client or server with an AF_SMC socket.

SMC protocol definitions:

const (
        SMCProtoIPv4 = 0
        SMCProtoIPv6 = 1
)

The Go package net provides high level functions Listen() and Dial() for creating a server and connecting to a server. These functions do not support SMC. As a workaround, low level socket functions in the package golang.org/x/sys/unix can be used.

Low level SMC versions of Listen() and Dial():

// parse ip address and return IPv4 and IPv6 address
func parseIP(address string) (net.IP, net.IP) {
        ip := net.ParseIP(address)
        if ip == nil {
                return nil, nil
        }
        return ip.To4(), ip.To16()
}

// construct socket address
func createSockaddr(address string, port int) (typ string, s unix.Sockaddr) {
        ipv4, ipv6 := parseIP(address)
        if ipv4 != nil {
                sockaddr4 := &unix.SockaddrInet4{}
                sockaddr4.Port = port
                copy(sockaddr4.Addr[:], ipv4[:net.IPv4len])
                return "ipv4", sockaddr4
        }
        if ipv6 != nil {
                sockaddr6 := &unix.SockaddrInet6{}
                sockaddr6.Port = port
                copy(sockaddr6.Addr[:], ipv6[:net.IPv6len])
                return "ipv6", sockaddr6
        }

        return "err", nil
}

// SMC version of Listen()
func smcListen(address string, port int) (net.Listener, error) {
        var l net.Listener
        var err error
        var fd int

        // construct socket address from address and port
        typ, sockaddr := createSockaddr(address, port)
        if typ == "err" {
                return l, fmt.Errorf("Error parsing IP")
        }

        // create socket
        if typ == "ipv4" {
                fd, err = unix.Socket(unix.AF_SMC, unix.SOCK_STREAM,
                        SMCProtoIPv4)
        } else {
                fd, err = unix.Socket(unix.AF_SMC, unix.SOCK_STREAM,
                        SMCProtoIPv6)
        }
        if err != nil {
                return l, err
        }
        defer unix.Close(fd)

        // bind socket address
        err = unix.Bind(fd, sockaddr)
        if err != nil {
                return l, err
        }

        // start listening
        err = unix.Listen(fd, 1)
        if err != nil {
                return l, err
        }

        // create a listener from listening socket
        file := os.NewFile(uintptr(fd), "")
        l, err = net.FileListener(file)
        return l, err
}

// SMC version of Dial()
func smcDial(address string, port int) (net.Conn, error) {
        var conn net.Conn
        var err error
        var fd int

        // construct socket address from address and port
        typ, sockaddr := createSockaddr(address, port)
        if typ == "err" {
                return conn, fmt.Errorf("Error parsing IP")
        }

        // create socket
        if typ == "ipv4" {
                fd, err = unix.Socket(unix.AF_SMC, unix.SOCK_STREAM,
                        SMCProtoIPv4)
        } else {
                fd, err = unix.Socket(unix.AF_SMC, unix.SOCK_STREAM,
                        SMCProtoIPv6)
        }

        if err != nil {
                return conn, err
        }
        defer unix.Close(fd)

        // connect to server
        err = unix.Connect(fd, sockaddr)
        if err != nil {
                return conn, err
        }

        // create a connection from connected socket
        file := os.NewFile(uintptr(fd), "")
        conn, err = net.FileConn(file)
        return conn, err
}

Server socket code:

// listen for smc connections
l, err := smcListen(address, port)
if err != nil {
        log.Fatal(err)
}
defer l.Close()

// accept new connections from listener and handle them
for {
        // accept new connection
        conn, err := l.Accept()
        if err != nil {
                log.Fatal(err)
        }

        // handle new connection: send data back to client
        go func(c net.Conn) {
                fmt.Printf("New client connection\n")
                written, err := io.Copy(c, c)
                if err != nil {
                        log.Fatal(err)
                }
                fmt.Printf("Echoed %d bytes to client\n", written)
                c.Close()
        }(conn)
}

Client socket code:

// connect via smc
conn, err := smcDial(address, port)
if err != nil {
        log.Fatal(err)
}
defer conn.Close()
fmt.Printf("Connected to server\n")

// sent text, read reply an
text := "Hello, world\n"
fmt.Fprintf(conn, text)
fmt.Printf("Sent %d bytes to server: %s", len(text), text)
reply, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
        log.Fatal(err)
}
fmt.Printf("Read %d bytes from server: %s", len(reply), reply)

Running the code:

$ ./gosmc -s
New client connection
Echoed 13 bytes to client
$ ./gosmc -c
Connected to server
Sent 13 bytes to server: Hello, world
Read 13 bytes from server: Hello, world

Python

See pysmc.py for a Python script that runs a client or server with an AF_SMC socket.

SMC protocol definitions:

AF_SMC = 43
SMCPROTO_SMC = 0
SMCPROTO_SMC6 = 1

The calls to the socket module's bind(), accept(), and connect() raise the error OSError: getsockaddrarg: bad family because the internal getsockaddrarg() function in Python's socket module cannot construct the socket address for an unknown address family, i.e., AF_SMC. As a workaround, the functions in Libc can be used in Python.

Libc version of bind(), accept(), and connect() with the socket address structure used by these functions:

# libc
LIBC_PATH = ctypes.util.find_library("c")
LIBC = ctypes.CDLL(LIBC_PATH)


class SockaddrIn(ctypes.Structure):
    """
    Socket Address for IPv4 struct for ctypes
    """
    _fields_ = [("sin_family", ctypes.c_ushort),
                ("sin_port", ctypes.c_uint16),
                ("sin_addr", ctypes.c_uint32),
                ("sin_zero", ctypes.c_ubyte * 8)]

    def __init__(self, address=0, port=0):
        super(SockaddrIn, self).__init__()
        self.sin_family = ctypes.c_ushort(socket.AF_INET)
        self.sin_port = ctypes.c_uint16(socket.htons(int(port)))
        self.sin_addr = ctypes.c_uint32(socket.htonl(int(
            ipaddress.IPv4Address(address))))

    def __len__(self):
        return ctypes.sizeof(self)

    def __repr__(self):
        addr = ipaddress.IPv4Address(socket.ntohl(self.sin_addr))
        port = socket.ntohs(self.sin_port)
        return f"{addr}:{port}"


class SockaddrIn6(ctypes.Structure):
    """
    Socket Address for IPv6 struct for ctypes
    """
    _fields_ = [("sin6_family", ctypes.c_ushort),
                ("sin6_port", ctypes.c_uint16),
                ("sin6_flowinfo", ctypes.c_uint32),
                ("sin6_addr", ctypes.c_ubyte * 16),
                ("sin6_scope_id", ctypes.c_uint32)]

    def __init__(self, address=0, port=0):
        super(SockaddrIn6, self).__init__()
        self.sin6_family = ctypes.c_ushort(socket.AF_INET6)
        self.sin6_port = ctypes.c_uint16(socket.htons(int(port)))
        self.sin6_addr = (ctypes.c_ubyte * 16)(
            *ipaddress.IPv6Address(address).packed)

    def __len__(self):
        return ctypes.sizeof(self)

    def __repr__(self):
        addr = ipaddress.IPv6Address(bytes(self.sin6_addr))
        port = socket.ntohs(self.sin6_port)
        return f"{addr}:{port}"


def create_sockaddr(address, port):
    """
    create a sockaddr
    """

    try:
        sockaddr = SockaddrIn(address, port)
    except ipaddress.AddressValueError:
        sockaddr = None

    if not sockaddr:
        try:
            sockaddr = SockaddrIn6(address, port)
        except ipaddress.AddressValueError:
            sockaddr = None

    return sockaddr


def bind(sock, sockaddr):
    """
    bind() using libc with ctypes
    """

    # bind address and port
    LIBC.bind(sock.fileno(), ctypes.byref(sockaddr), len(sockaddr))


def accept(sock, sockaddr):
    """
    accept() using libc with ctypes
    """

    # accept connection
    sockaddr_len = ctypes.c_int(len(sockaddr))
    client_sock = LIBC.accept(sock.fileno(), ctypes.byref(sockaddr),
                              ctypes.byref(sockaddr_len))
    return socket.socket(fileno=client_sock), sockaddr


def connect(sock, sockaddr):
    """
    connect() using libc with ctypes
    """

    # connect to server
    LIBC.connect(sock.fileno(), ctypes.byref(sockaddr), len(sockaddr))

Server socket code:

# create sockaddr from host and port
server_addr = create_sockaddr(host, port)

# start server
print("Starting SMC server")
if isinstance(server_addr, SockaddrIn6):
    sock = socket.socket(AF_SMC, socket.SOCK_STREAM, SMCPROTO_SMC6)
    client_addr = SockaddrIn6()
else:
    sock = socket.socket(AF_SMC, socket.SOCK_STREAM, SMCPROTO_SMC)
    client_addr = SockaddrIn()
with sock:
    # sock.bind() does not work with SMC, use libc version
    bind(sock, server_addr)
    sock.listen(1)
    # sock.accept() does not work with SMC, use libc version
    conn, addr = accept(sock, client_addr)
    with conn:
        print("Connected:", addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

Client socket code:

# create server sockaddr from host and port
server_addr = create_sockaddr(host, port)

# start client
print("Starting SMC client")
if isinstance(server_addr, SockaddrIn6):
    sock = socket.socket(AF_SMC, socket.SOCK_STREAM, SMCPROTO_SMC6)
else:
    sock = socket.socket(AF_SMC, socket.SOCK_STREAM, SMCPROTO_SMC)
with sock:
    # sock.connect() does not work with SMC, use libc version
    connect(sock, server_addr)
    sock.sendall(b"Hello, world")
    data = sock.recv(1024)
    print('Received:', repr(data))

Running the code:

$ ./pysmc.py -s
Starting SMC server
Connected: 127.0.0.1:47090
$ ./pysmc.py -c -a 127.0.0.1
Starting SMC client
Received: b'Hello, world'