Skip to content

Files

Latest commit

79767d1 · Nov 5, 2019

History

History

socket

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Nov 5, 2019
Nov 3, 2019
Nov 2, 2019
Nov 5, 2019

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'