diff options
Diffstat (limited to 'src/libosmocc/socket.c')
-rw-r--r-- | src/libosmocc/socket.c | 619 |
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; -} - |