|
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <netdb.h>
- #include <stdlib.h>
- #include <string.h>
- #include <limits.h>
- #include <ctype.h>
- #include <assert.h>
- #include <unistd.h>
- #include <stdbool.h>
- #include <errno.h>
-
- #include "net.h"
- #include "config.h"
- #include "uio.h"
-
- static struct addrinfo net_server;
- static bool net_server_set = false;
- static bool net_connected = false;
- static int net_socket = -1;
-
- static bool net_parse_address(const char* srv, size_t *out_domain_len,
- char **out_port_start)
- {
- char *port_iter;
- char *port_start = strchr(srv, ':');
- if (!port_start) {
- *out_port_start = NULL;
- *out_domain_len = strlen(srv);
- return true;
- }
-
- /* Only one ':' is allowed */
- if (strchr(port_start + 1, ':'))
- return false;
-
- *out_domain_len = port_start - srv;
-
- /*port = strtol(port_start + 1, &port_end, 10);
- if (port_end == port_start || *port_end != '\0' ||
- ((port == LONG_MIN || port == LONG_MAX) && errno))
- return false;*/
-
- /*if (port <= 0 || port > 65535)
- return false;*/
-
- port_iter = port_start + 1;
- if (*port_iter == '\0')
- return false;
- while (*port_iter) {
- if (!isdigit(*port_iter))
- return false;
- port_iter++;
- }
- *out_port_start = port_start + 1;
-
- return true;
- }
-
- static const void *net_get_sockaddr_addr(const struct sockaddr *sa)
- {
- switch (sa->sa_family) {
- case AF_INET:
- return &((struct sockaddr_in*)sa)->sin_addr;
- case AF_INET6:
- return &((struct sockaddr_in6*)sa)->sin6_addr;
- default:
- uio_error("Sockaddr is not ipv4 or ipv6");
- exit(1);
- }
- }
-
- static bool net_lookup_server(const char *domain, const char *port,
- struct addrinfo *out_addr)
- {
- struct addrinfo hints = {
- .ai_family = AF_UNSPEC,
- //.ai_family = AF_INET6,
- .ai_socktype = SOCK_DGRAM,
- .ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG,
- //.ai_flags = AI_NUMERICSERV,
- }, *res = NULL, *curr_res;
-
- int ret = getaddrinfo(domain, port, &hints, &res);
- if (ret != 0) {
- uio_error("Cannot get addrinfo from address: '%s:%s' (%s)",
- domain, port, gai_strerror(ret));
- return false;
- }
-
- curr_res = res;
- while (curr_res) {
- char ip_buffer[INET6_ADDRSTRLEN] = {0};
- if (inet_ntop(curr_res->ai_family,
- net_get_sockaddr_addr(curr_res->ai_addr),
- ip_buffer, sizeof(ip_buffer)))
- uio_debug("Lookup addrinfo entry: %s", ip_buffer);
- else
- uio_debug("Cannot convert binary ip to string: %s",
- strerror(errno));
-
- /* For now, always choose the first one */
- break;
-
- curr_res = curr_res->ai_next;
- }
-
- if (!curr_res) {
- uio_error("Cannot select a usable address.");
- freeaddrinfo(res);
- return false;
- }
-
- *out_addr = *curr_res;
- out_addr->ai_addr = malloc(sizeof(struct sockaddr));
- *out_addr->ai_addr = *curr_res->ai_addr;
- out_addr->ai_next = NULL;
- out_addr->ai_canonname = NULL;
- freeaddrinfo(res);
- return true;
- }
-
- int net_socket_setup()
- {
- struct sockaddr l_addr = {0};
- socklen_t l_addr_len;
- int sock;
- uint16_t *port;
- enum error err;
-
- if ((err = config_get("port", (void**)&port)) != NOERR) {
- uio_error("Cannot get UDP binding port from config (%s)",
- error_to_string(err));
- return -1;
- }
-
- sock = socket(net_server.ai_family, net_server.ai_socktype,
- net_server.ai_protocol);
- if (sock == -1) {
- uio_error("Cannot create new socket: %s", strerror(errno));
- return -1;
- }
-
- l_addr.sa_family = net_server.ai_family;
- if (net_server.ai_family == AF_INET) {
- struct sockaddr_in *tmp = (struct sockaddr_in*)&l_addr;
- l_addr_len = sizeof(struct sockaddr_in);
-
- tmp->sin_port = htons(*port);
- tmp->sin_addr.s_addr = INADDR_ANY;
-
- } else {
- struct sockaddr_in6 *tmp = (struct sockaddr_in6*)&l_addr;
- l_addr_len = sizeof(struct sockaddr_in6);
-
- tmp->sin6_port = htons(*port);
- tmp->sin6_addr = in6addr_any;
-
- }
- if (bind(sock, &l_addr, l_addr_len) != 0) {
- uio_error("Cannot bind UDP socket to local port: %s", strerror(errno));
- close(sock);
- return -1;
- }
-
- return sock;
- }
-
- static enum error net_connect(int sock, struct addrinfo *ai)
- {
- if (net_connected)
- return ERR_NET_CONNECTED;
-
- if (connect(sock, ai->ai_addr, ai->ai_addrlen) != 0) {
- uio_error("Cannot connect to the server: %s\n", strerror(errno));
- return ERR_NET_CONNECT_FAIL;
- }
- net_connected = true;
-
- return NOERR;
- }
-
- enum error net_init()
- {
- enum error err;
- const char **srv = NULL;
- char *port_start = NULL;
- size_t domain_len;
- int sock;
-
- err = config_get("api-server", (void**)&srv);
- if (err != NOERR) {
- uio_error("Cannot get the api servers address (%s).", error_to_string(err));
- return ERR_NET_APIADDR;
- }
-
- if (!net_parse_address(*srv, &domain_len, &port_start)) {
- uio_error("Cannot parse the api server address: '%s'.", *srv);
- return ERR_NET_APIADDR;
- }
- /* Port will be set to NULL, if its not in the address */
- if (port_start == NULL)
- port_start = "9000";
-
- char api_domain[domain_len + 1];
- memcpy(api_domain, *srv, domain_len);
- api_domain[domain_len] = '\0';
-
- if (!net_lookup_server(api_domain, port_start, &net_server)) {
- //uio_error("Cannot look up the api server address");
- return ERR_NET_APIADDR;
- }
- net_server_set = true;
-
- sock = net_socket_setup();
- if (sock == -1) {
- return ERR_NET_SOCKET;
- }
-
- err = net_connect(sock, &net_server);
- if (err != NOERR) {
- net_free();
- return err;
- }
- net_socket = sock;
-
- return NOERR;
- }
-
- void net_free()
- {
- if (net_server_set) {
- free(net_server.ai_addr);
- memset(&net_server, 0, sizeof(net_server));
- net_server_set = false;
- }
- if (net_socket != -1) {
- if (net_connected)
- shutdown(net_socket, SHUT_RDWR);
- close(net_socket);
- net_socket = -1;
- }
- net_connected = false;
- }
-
- ssize_t net_send(const void *msg, size_t msg_len)
- {
- ssize_t w_len = send(net_socket, msg, msg_len, 0);
- if (w_len == -1) {
- int en = errno;
-
- uio_error("{net} Send failed: %s", strerror(en));
- if (en == EINTR)
- return -2;
- return -1;
- }
- return w_len;
- }
-
- ssize_t net_read(void* out_data, size_t read_size)
- {
- ssize_t read = recv(net_socket, out_data, read_size, 0);
- if (read == -1) {
- int en = errno;
-
- uio_error("{net} Read failed: %s", strerror(errno));
- if (en == EINTR)
- return -2;
- return -1;
- }
- if (read == read_size)
- uio_warning("{net} Data may have been discarded!");
- return read;
- }
|