aboutsummaryrefslogtreecommitdiffstats
path: root/src/libosmocc/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libosmocc/socket.c')
-rw-r--r--src/libosmocc/socket.c619
1 files changed, 0 insertions, 619 deletions
diff --git a/src/libosmocc/socket.c b/src/libosmocc/socket.c
deleted file mode 100644
index 9d8f30a..0000000
--- a/src/libosmocc/socket.c
+++ /dev/null
@@ -1,619 +0,0 @@
-/* Osmo-CC: Socket handling
- *
- * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
- * All Rights Reserved
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include "../libdebug/debug.h"
-#include "../libtimer/timer.h"
-#include "../libselect/select.h"
-#include "message.h"
-#include "cause.h"
-#include "socket.h"
-
-static const char version_string[] = OSMO_CC_VERSION;
-
-static int _getaddrinfo(const char *host, uint16_t port, struct addrinfo **result)
-{
- char portstr[8];
- struct addrinfo hints;
- int rc;
-
- sprintf(portstr, "%d", port);
-
- /* bind socket */
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE;
- hints.ai_protocol = 0;
- hints.ai_canonname = NULL;
- hints.ai_addr = NULL;
- hints.ai_next = NULL;
-
- rc = getaddrinfo(host, portstr, &hints, result);
- if (rc < 0) {
- LOGP(DCC, LOGL_ERROR, "Failed to create socket for host '%s', port '%d': %s.\n", host, port, gai_strerror(rc));
- return rc;
- }
- return rc;
-}
-
-/* send a reject message toward CC process.
- * the CC process will change the reject message to a release message when not in INIT_IN state
- */
-static void rej_msg(osmo_cc_socket_t *os, uint32_t callref, uint8_t socket_cause, uint8_t isdn_cause, uint16_t sip_cause)
-{
- osmo_cc_msg_t *msg;
-
- /* create message */
- msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_REQ);
- if (!msg)
- abort();
-
- /* add cause */
- osmo_cc_add_ie_cause(msg, os->location, isdn_cause, sip_cause, socket_cause);
- osmo_cc_convert_cause_msg(msg);
-
- /* message down */
- os->recv_msg_cb(os->priv, callref, msg);
-}
-
-static void tx_keepalive_timeout(void *data)
-{
- osmo_cc_conn_t *conn = data;
- osmo_cc_msg_t *msg;
-
- /* send keepalive message */
- msg = osmo_cc_new_msg(OSMO_CC_MSG_DUMMY_REQ);
- osmo_cc_msg_list_enqueue(&conn->os->write_list, msg, conn->callref);
- timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
-}
-
-static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause);
-
-static void rx_keepalive_timeout(void *data)
-{
- osmo_cc_conn_t *conn = data;
-
- LOGP(DCC, LOGL_ERROR, "OsmoCC-Socket failed due to timeout.\n");
- close_conn(conn, OSMO_CC_SOCKET_CAUSE_TIMEOUT);
-}
-
-static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when);
-
-/* create socket process and bind socket */
-int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv, void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location)
-{
- int try = 0, auto_port = 0;
- struct addrinfo *result, *rp;
- int rc, sock;
-
- memset(os, 0, sizeof(*os));
-
-try_again:
- /* check for given port, if NULL, autoselect port */
- if (!port || auto_port) {
- port = OSMO_CC_DEFAULT_PORT + try;
- try++;
- auto_port = 1;
- }
-
- LOGP(DCC, LOGL_DEBUG, "Create socket for host %s port %d.\n", host, port);
-
- rc = _getaddrinfo(host, port, &result);
- if (rc < 0)
- return rc;
- for (rp = result; rp; rp = rp->ai_next) {
- int on = 1;
- sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sock < 0)
- continue;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (unsigned char *)&on, sizeof(on));
- rc = bind(sock, rp->ai_addr, rp->ai_addrlen);
- if (rc == 0)
- break;
- close(sock);
- }
- freeaddrinfo(result);
- if (rp == NULL) {
- if (auto_port && port < OSMO_CC_DEFAULT_PORT_MAX) {
- LOGP(DCC, LOGL_DEBUG, "Failed to bind host %s port %d, trying again.\n", host, port);
- goto try_again;
- }
- LOGP(DCC, LOGL_ERROR, "Failed to bind given host %s port %d.\n", host, port);
- return -EIO;
- }
-
- /* listen to socket */
- rc = listen(sock, 10);
- if (rc < 0) {
- LOGP(DCC, LOGL_ERROR, "Failed to listen on socket.\n");
- close(sock);
- return rc;
- }
-
- /* register */
- os->ofd.fd = sock;
- os->ofd.cb = socket_listen_cb;
- os->ofd.data = os;
- os->ofd.when = OSMO_FD_READ;
- osmo_fd_register(&os->ofd);
- os->recv_msg_cb = recv_msg_cb;
- os->priv = priv;
- os->location = location;
-
- return port;
-}
-
-static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when);
-
-/* create a connection */
-static osmo_cc_conn_t *open_conn(osmo_cc_socket_t *os, int sock, uint32_t callref, int read_setup)
-{
- osmo_cc_conn_t *conn, **connp;
-
- /* create connection */
- conn = calloc(1, sizeof(*conn));
- if (!conn) {
- LOGP(DCC, LOGL_ERROR, "No mem!\n");
- abort();
- }
- conn->os = os;
- conn->ofd.fd = sock;
- conn->ofd.cb = socket_conn_cb;
- conn->ofd.data = conn;
- conn->ofd.when = OSMO_FD_READ;
- osmo_fd_register(&conn->ofd);
- conn->read_version = 1;
- conn->write_version = 1;
- conn->read_setup = read_setup;
- if (callref)
- conn->callref = callref;
- else
- conn->callref = osmo_cc_new_callref();
-
- timer_init(&conn->tx_keepalive_timer, tx_keepalive_timeout, conn);
- timer_init(&conn->rx_keepalive_timer, rx_keepalive_timeout, conn);
- timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
- timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
-
- LOGP(DCC, LOGL_DEBUG, "New socket connection (callref %d).\n", conn->callref);
-
- /* attach to list */
- connp = &os->conn_list;
- while (*connp)
- connp = &((*connp)->next);
- *connp = conn;
-
- return conn;
-}
-
-/* remove a connection */
-static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause)
-{
- osmo_cc_conn_t **connp;
- osmo_cc_msg_list_t *ml;
-
- /* detach connection first, to prevent a destruction during message handling (double free) */
- connp = &conn->os->conn_list;
- while (*connp != conn)
- connp = &((*connp)->next);
- *connp = conn->next;
- /* send reject message, if socket_cause is set */
- if (socket_cause && !conn->read_setup) {
- /* receive a release or reject (depending on state), but only if we sent a setup */
- rej_msg(conn->os, conn->callref, socket_cause, 0, 0);
- }
-
- LOGP(DCC, LOGL_DEBUG, "Destroy socket connection (callref %d).\n", conn->callref);
-
- /* close socket */
- if (conn->ofd.fd) {
- osmo_fd_unregister(&conn->ofd);
- close(conn->ofd.fd);
- }
- /* free partly received message */
- if (conn->read_msg)
- osmo_cc_free_msg(conn->read_msg);
- /* free send queue */
- while ((ml = conn->write_list)) {
- osmo_cc_free_msg(ml->msg);
- conn->write_list = ml->next;
- free(ml);
- }
- /* free timers */
- timer_exit(&conn->tx_keepalive_timer);
- timer_exit(&conn->rx_keepalive_timer);
- /* free connection (already detached above) */
- free(conn);
-}
-
-/* close socket and remove */
-void osmo_cc_close_socket(osmo_cc_socket_t *os)
-{
- osmo_cc_msg_list_t *ml;
-
- LOGP(DCC, LOGL_DEBUG, "Destroy socket.\n");
-
- /* free all connections */
- while (os->conn_list)
- close_conn(os->conn_list, 0);
- /* close socket */
- if (os->ofd.fd > 0) {
- osmo_fd_unregister(&os->ofd);
- close(os->ofd.fd);
- os->ofd.fd = 0;
- }
- /* free send queue */
- while ((ml = os->write_list)) {
- osmo_cc_free_msg(ml->msg);
- os->write_list = ml->next;
- free(ml);
- }
-}
-
-/* send message to send_queue of sock instance */
-int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port)
-{
- osmo_cc_msg_list_t *ml;
-
- /* turn _IND into _REQ and _CNF into _RSP */
- msg->type &= ~1;
-
- /* create list entry */
- ml = osmo_cc_msg_list_enqueue(&os->write_list, msg, callref);
- if (host)
- strncpy(ml->host, host, sizeof(ml->host) - 1);
- ml->port = port;
-
- return 0;
-}
-
-/* receive message
- * return 1 if work was done.
- */
-static int receive_conn(osmo_cc_conn_t *conn)
-{
- uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
- int rc;
- osmo_cc_msg_t *msg;
- uint8_t msg_type;
- int len;
- int work = 0;
-
- /* get version from remote */
- if (conn->read_version) {
- rc = recv(conn->ofd.fd, conn->read_version_string + conn->read_version_pos, strlen(version_string) - conn->read_version_pos, 0);
- if (rc < 0 && errno == EAGAIN)
- return work;
- work = 1;
- if (rc <= 0) {
- goto close;
- }
- conn->read_version_pos += rc;
- if (conn->read_version_pos == strlen(version_string)) {
- conn->read_version = 0;
- if (!!memcmp(conn->read_version_string, version_string, strlen(version_string) - 1)) {
- LOGP(DCC, LOGL_NOTICE, "Remote does not seem to be an Osmo-CC socket, rejecting!\n");
- socket_cause = OSMO_CC_SOCKET_CAUSE_FAILED;
- goto close;
- }
- if (conn->read_version_string[strlen(version_string) - 1] != version_string[strlen(version_string) - 1]) {
- LOGP(DCC, LOGL_NOTICE, "Remote Osmo-CC socket has wrong version (local=%s, remote=%s), rejecting!\n", version_string, conn->read_version_string);
- socket_cause = OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH;
- goto close;
- }
- } else
- return work;
- }
-
-try_next_message:
- /* read message header from remote */
- if (!conn->read_msg) {
- rc = recv(conn->ofd.fd, ((uint8_t *)&conn->read_hdr) + conn->read_pos, sizeof(conn->read_hdr) - conn->read_pos, 0);
- if (rc < 0 && errno == EAGAIN)
- return work;
- work = 1;
- if (rc <= 0) {
- goto close;
- }
- conn->read_pos += rc;
- if (conn->read_pos == sizeof(conn->read_hdr)) {
- conn->read_msg = osmo_cc_new_msg(conn->read_hdr.type);
- if (!conn->read_msg)
- abort();
- conn->read_msg->length_networkorder = conn->read_hdr.length_networkorder;
- /* prepare for reading message */
- conn->read_pos = 0;
- } else
- return work;
- }
-
- /* read message data from remote */
- msg = conn->read_msg;
- len = ntohs(msg->length_networkorder);
- if (len == 0)
- goto empty_message;
- rc = recv(conn->ofd.fd, msg->data + conn->read_pos, len - conn->read_pos, 0);
- if (rc < 0 && errno == EAGAIN)
- return work;
- work = 1;
- if (rc <= 0) {
- goto close;
- }
- conn->read_pos += rc;
- if (conn->read_pos == len) {
-empty_message:
- /* start RX keepalive timeer, if not already */
- timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
- /* we got our setup message, so we clear the flag */
- conn->read_setup = 0;
- /* prepare for reading header */
- conn->read_pos = 0;
- /* detach message first, because the connection might be destroyed during message handling */
- msg_type = conn->read_msg->type;
- conn->read_msg = NULL;
- /* drop dummy or forward message */
- if (msg_type == OSMO_CC_MSG_DUMMY_REQ)
- osmo_cc_free_msg(msg);
- else
- conn->os->recv_msg_cb(conn->os->priv, conn->callref, msg);
- if (msg_type == OSMO_CC_MSG_REL_REQ || msg_type == OSMO_CC_MSG_REJ_REQ) {
- LOGP(DCC, LOGL_DEBUG, "closing socket because we received a release or reject message.\n");
- close_conn(conn, 0);
- return 1; /* conn removed */
- }
- goto try_next_message;
- }
- return work;
-
-close:
- LOGP(DCC, LOGL_ERROR, "OsmoCC-Socket failed, socket cause %d.\n", socket_cause);
- close_conn(conn, socket_cause);
- return work; /* conn removed */
-}
-
-/* transmit message
- * return 1 if work was done.
- */
-static int transmit_conn(osmo_cc_conn_t *conn)
-{
- uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
- int rc;
- osmo_cc_msg_t *msg;
- int len;
- osmo_cc_msg_list_t *ml;
- int work = 0;
-
- /* send socket version to remote */
- if (conn->write_version) {
- rc = write(conn->ofd.fd, version_string, strlen(version_string));
- if (rc < 0 && errno == EAGAIN)
- return work;
- work = 1;
- if (rc <= 0) {
- goto close;
- }
- if (rc != strlen(version_string)) {
- LOGP(DCC, LOGL_ERROR, "short write, please fix handling!\n");
- abort();
- }
- conn->write_version = 0;
- }
-
- /* send message to remote */
- while (conn->write_list) {
- timer_stop(&conn->tx_keepalive_timer);
- msg = conn->write_list->msg;
- len = sizeof(*msg) + ntohs(msg->length_networkorder);
- rc = write(conn->ofd.fd, msg, len);
- if (rc < 0 && errno == EAGAIN)
- return work;
- work = 1;
- if (rc <= 0) {
- goto close;
- }
- if (rc != len) {
- LOGP(DCC, LOGL_ERROR, "short write, please fix handling!\n");
- abort();
- }
- /* close socket after sending release/reject message */
- if (msg->type == OSMO_CC_MSG_REL_REQ || msg->type == OSMO_CC_MSG_REJ_REQ) {
- LOGP(DCC, LOGL_DEBUG, "closing socket because we sent a release or reject message.\n");
- close_conn(conn, 0);
- return work; /* conn removed */
- }
- /* free message after sending */
- ml = conn->write_list;
- conn->write_list = ml->next;
- osmo_cc_free_msg(msg);
- free(ml);
- }
-
- /* start TX keepalive timeer, if not already
- * because we stop at every message above, we actually restart the timer here.
- * only if there is no message for the amount of time, the timer fires.
- */
- if (!timer_running(&conn->tx_keepalive_timer))
- timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
-
- return work;
-
-close:
- LOGP(DCC, LOGL_NOTICE, "OsmoCC-Socket failed.\n");
- close_conn(conn, socket_cause);
- return work; /* conn removed */
-}
-
-/* handle all sockets of a socket interface
- * return 1 if work was done.
- */
-int osmo_cc_handle_socket(osmo_cc_socket_t *os)
-{
- int sock;
- osmo_cc_conn_t *conn;
- osmo_cc_msg_list_t *ml, **mlp;
- int flags;
- struct addrinfo *result, *rp;
- int rc;
- int work = 0;
-
- /* handle messages in send queue */
- while ((ml = os->write_list)) {
- work = 1;
- /* detach list entry */
- os->write_list = ml->next;
- ml->next = NULL;
- /* search for socket connection */
- for (conn = os->conn_list; conn; conn=conn->next) {
- if (conn->callref == ml->callref)
- break;
- }
- if (conn) {
- /* attach to list */
- mlp = &conn->write_list;
- while (*mlp)
- mlp = &((*mlp)->next);
- *mlp = ml;
- conn->ofd.when |= OSMO_FD_WRITE;
- /* done with message */
- continue;
- }
-
- /* reject and release are ignored */
- if (ml->msg->type == OSMO_CC_MSG_REJ_REQ
- || ml->msg->type == OSMO_CC_MSG_REL_REQ) {
- /* drop message */
- osmo_cc_free_msg(ml->msg);
- free(ml);
- /* done with message */
- continue;
- }
-
- /* reject, if this is not a setup message */
- if (ml->msg->type != OSMO_CC_MSG_SETUP_REQ
- && ml->msg->type != OSMO_CC_MSG_ATTACH_REQ) {
- LOGP(DCC, LOGL_ERROR, "Message with unknown callref.\n");
- rej_msg(os, ml->callref, 0, OSMO_CC_ISDN_CAUSE_INVAL_CALLREF, 0);
- /* drop message */
- osmo_cc_free_msg(ml->msg);
- free(ml);
- /* done with message */
- continue;
- }
- /* connect to remote */
- rc = _getaddrinfo(ml->host, ml->port, &result);
- if (rc < 0) {
- rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
- /* drop message */
- osmo_cc_free_msg(ml->msg);
- free(ml);
- /* done with message */
- continue;
- }
- for (rp = result; rp; rp = rp->ai_next) {
- sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sock < 0)
- continue;
- /* set nonblocking io, to prevent connect() and subsequent reads from blocking */
- flags = fcntl(sock, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(sock, F_SETFL, flags);
- /* connect */
- rc = connect(sock, rp->ai_addr, rp->ai_addrlen);
- if (rc == 0 || errno == EINPROGRESS)
- break;
- close(sock);
- }
- freeaddrinfo(result);
- if (rp == NULL) {
- LOGP(DCC, LOGL_ERROR, "Failed to connect to given host %s port %d.\n", ml->host, ml->port);
- rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
- /* drop message */
- osmo_cc_free_msg(ml->msg);
- free(ml);
- /* done with message */
- continue;
- }
- /* create connection */
- conn = open_conn(os, sock, ml->callref, 0);
- /* attach to list */
- conn->write_list = ml;
- conn->ofd.when |= OSMO_FD_WRITE;
- /* done with (setup) message */
- }
-
- return work;
-}
-
-static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when)
-{
- osmo_cc_socket_t *os = ofd->data;
- struct sockaddr_storage sa;
- socklen_t slen = sizeof(sa);
- int sock;
- int flags;
-
- if (when & OSMO_FD_READ) {
- /* handle new socket connection */
- if ((sock = accept(os->ofd.fd, (struct sockaddr *)&sa, &slen)) > 0) {
- /* set nonblocking io, to prevent subsequent reads from blocking */
- flags = fcntl(sock, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(sock, F_SETFL, flags);
- /* create connection */
- open_conn(os, sock, 0, 1);
- }
- }
-
- return 0;
-}
-
-static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when)
-{
- osmo_cc_conn_t *conn = ofd->data;
- int work;
-
- if (when & OSMO_FD_READ) {
- /* check for rx */
- work = receive_conn(conn);
- /* if "change" is set, connection list might have changed, so we restart processing the list */
- if (work)
- return 0;
- }
- if (when & OSMO_FD_WRITE) {
- /* check for tx */
- work = transmit_conn(conn);
- /* if "change" is set, connection list might have changed, so we restart processing the list */
- if (work)
- return 0;
- else
- conn->ofd.when &= ~OSMO_FD_WRITE;
- }
-
- return 0;
-}
-