← index
network/mx_socket.c
Source: C2x.Examples/src/network/mx_socket.c
#include "mx_socket.h"

bool mx_socket_listen(MXSocket *sock, const char *port, int backlog) {
    if (sock == nullptr || port == nullptr)
        return false;
    struct addrinfo hints;
    struct addrinfo *rt, *rp;
    int sfd = -1, optval, s;

    mx_socket_init(sock);
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_canonname = nullptr;
    hints.ai_addr = nullptr;
    hints.ai_next = nullptr;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;
    hints.ai_flags = AI_PASSIVE;
    s = getaddrinfo(nullptr, port, &hints, &rt);
    if (s != 0)
        return false;

    optval = 1;

    for (rp = rt; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1)
            continue;

        if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
            close(sfd);
            freeaddrinfo(rt);
            return false;
        }

        if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
            break;

        if (sfd >= 0)
            close(sfd);
        sfd = -1;
    }

    if (sfd == -1) {
        freeaddrinfo(rt);
        return false;
    }

    if (rp != nullptr && sfd >= 0) {
        if (listen(sfd, backlog) == -1) {
            freeaddrinfo(rt);
            close(sfd);
            return false;
        }
        sock->sockfd = sfd;
        sock->addrlen = rp->ai_addrlen;
    } else {
        if (sfd >= 0)
            close(sfd);
        return false;
    }

    freeaddrinfo(rt);
    return true;
}

bool mx_socket_accept(const MXSocket *input, MXSocket *output) {
    if (input == nullptr || output == nullptr)
        return false;

    if (!mx_socket_valid(input))
        return false;

    int newfd = accept(input->sockfd, 0, 0);
    if (newfd == -1)
        return false;

    int flags = fcntl(newfd, F_GETFL);
    if (flags == -1) {
        close(newfd);
        return false;
    }
    if (input->blocking)
        flags &= ~O_NONBLOCK;
    else
        flags |= O_NONBLOCK;
    if (fcntl(newfd, F_SETFL, flags) == -1) {
        close(newfd);
        return false;
    }
    if (mx_socket_valid(output))
        mx_socket_close(output);
    output->sockfd = newfd;
    output->addrlen = input->addrlen;
    output->blocking = input->blocking;
    return true;
}

void mx_socket_close(MXSocket *sock) {
    if (sock == nullptr)
        return;
    if (sock->sockfd >= 0)
        close(sock->sockfd);
    sock->sockfd = -1;
}

bool mx_socket_set_blocking(MXSocket *sock, bool state) {
    if (sock == nullptr)
        return false;
    if (sock->sockfd >= 0) {
        int flags = fcntl(sock->sockfd, F_GETFL);
        if (flags == -1) {
            fprintf(stderr, "Error getting flags for: %d\n", sock->sockfd);
            return false;
        }
        if (state)
            flags &= ~O_NONBLOCK;
        else
            flags |= O_NONBLOCK;
        if (fcntl(sock->sockfd, F_SETFL, flags) == -1) {
            fprintf(stderr, "Error setting flags for: %d\n", sock->sockfd);
            return false;
        }
        sock->blocking = state;
    } else
        return false;
    return true;
}

bool mx_socket_connect(MXSocket *sock, const char *host, const char *port, int type) {
    if (sock == nullptr || host == nullptr || port == nullptr)
        return false;
    struct addrinfo hints;
    struct addrinfo *rt, *rp;
    int sfd = -1, s;
    memset(&hints, 0, sizeof(struct addrinfo));
    mx_socket_init(sock);
    hints.ai_canonname = nullptr;
    hints.ai_addr = nullptr;
    hints.ai_next = nullptr;
    hints.ai_family = AF_INET;
    hints.ai_socktype = type;
    s = getaddrinfo(host, port, &hints, &rt);
    if (s != 0) {
        errno = ENOSYS;
        return false;
    }
    for (rp = rt; rp != nullptr; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sfd == -1)
            continue;

        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;

        if (sfd >= 0) {
            close(sfd);
            sfd = -1;
        }
    }

    if (rp != nullptr) {
        sock->sockfd = sfd;
        sock->addrlen = rp->ai_addrlen;
    } else {
        freeaddrinfo(rt);
        if (sfd >= 0)
            close(sfd);
        return false;
    }

    freeaddrinfo(rt);
    return true;
}

bool mx_socket_init(MXSocket *sock) {
    if (sock == nullptr)
        return false;
    memset(sock, 0, sizeof(MXSocket));
    sock->sockfd = -1;
    sock->blocking = true;
    return true;
}

bool mx_socket_valid(const MXSocket *sock) {
    if (sock == nullptr)
        return false;
    return sock->sockfd >= 0;
}

ssize_t mx_socket_read(MXSocket *sock, void *buf, size_t len, int flags) {
    if (sock == nullptr || buf == nullptr || len == 0)
        return -1;
    if (!mx_socket_valid(sock)) {
        errno = EBADF;
        return -1;
    }
    return recv(sock->sockfd, buf, len, flags);
}

ssize_t mx_socket_send(MXSocket *sock, const void *buf, size_t len, int flags) {
    if (sock == nullptr || buf == nullptr || len == 0)
        return -1;
    if (!mx_socket_valid(sock)) {
        errno = EBADF;
        return -1;
    }
    return send(sock->sockfd, buf, len, flags);
}

bool mx_socket_is_open(const MXSocket *sock) {
    if (sock == nullptr || !mx_socket_valid(sock))
        return false;
    char c = 0;
    ssize_t r = recv(sock->sockfd, &c, 1, MSG_PEEK | MSG_DONTWAIT);
    if (r == 0)
        return false;
    if (r > 0)
        return true;
    if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
        return true;
    }
    return false;
}

bool mx_socket_readline(MXSocket *sock, char **buffer, size_t *size) {
    if (sock == nullptr || buffer == nullptr || size == nullptr)
        return false;

    if (!mx_socket_valid(sock)) {
        errno = EBADF;
        return false;
    }

    *buffer = nullptr;
    *size = 0;

    size_t init_size = 4096;
    char *temp = malloc(init_size + 1);
    if (temp == nullptr)
        return false;
    char c = 0;
    size_t index = 0;
    while (1) {
        ssize_t read_val = recv(sock->sockfd, &c, 1, 0);
        if (read_val > 0) {
            if (c == '\n')
                break;
            if (index >= init_size) {
                size_t new_init_size = init_size * 2;
                char *t = realloc(temp, new_init_size + 1);
                if (t == nullptr) {
                    free(temp);
                    return false;
                }
                temp = t;
                init_size = new_init_size;
            }
            temp[index++] = c;
            continue;
        }
        if (read_val == 0) {
            if (index == 0) {
                free(temp);
                return false;
            }
            break;
        }
        if (errno == EINTR)
            continue;
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            if (!sock->blocking)
                break;
            continue;
        }
        free(temp);
        return false;
    }
    temp[index] = 0;
    *buffer = temp;
    *size = index;
    return true;
}