Files
openairinterface5g/openair3/NAS/COMMON/UTIL/socket.c
T
Robert Schmidt 0b91791f67 Use (std) bool instead of custom bool types
OAI is riddled with boolean_t, FALSE, TRUE, etc. This makes no sense, as
there is a standard bool type, and could lead to nasty bugs if a
definition of a bool is non-standard (0 == false, true == !false). This
commit removes all non-standard bools in the whole project.
2022-06-27 10:07:48 +02:00

422 lines
17 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*****************************************************************************
Source socket.c
Version 0.1
Date 2012/02/28
Product NAS stack
Subsystem Utilities
Author Frederic Maurel
Description Implements socket handlers
*****************************************************************************/
#include "socket.h"
#include "commonDef.h"
#include <stdlib.h> // malloc, free, atoi
#include <string.h> // memset
#include <unistd.h> // close
#include <errno.h> // EINTR
#include <sys/types.h>
#include <sys/socket.h> // socket, setsockopt, connect, bind, recv, send
#include <netdb.h> // getaddrinfo
/****************************************************************************/
/**************** E X T E R N A L D E F I N I T I O N S ****************/
/****************************************************************************/
/****************************************************************************/
/******************* L O C A L D E F I N I T I O N S *******************/
/****************************************************************************/
/* -------------------------------------
* Identifier of network socket endpoint
* -------------------------------------
* A network socket endpoint is defined with a type (client or server),
* a port number, and the name and address of the remote host it should
* be connected to. A socket file descriptor is created to handle the
* setup of the communication channel with the remote peer.
*/
struct socket_id_s {
int type; /* connection type (client/server) */
int port; /* port number */
#define SOCKET_HOSTNAME_SIZE 32
char rhost[SOCKET_HOSTNAME_SIZE]; /* remote hostname */
struct sockaddr_storage addr; /* remote address */
int fd; /* socket file descriptor */
};
/* Set socket option at the sockets API level (SOL_SOCKET) */
static int _socket_set_option(int sfd);
/****************************************************************************/
/****************** E X P O R T E D F U N C T I O N S ******************/
/****************************************************************************/
/****************************************************************************
** **
** Name: socket_udp_open() **
** **
** Description: Initializes a communication endpoint for any IPv4 and **
** IPv6 protocol used by a local host to send/receive UDP **
** datagrams to/from a remote host in "connected" mode of **
** operation. **
** **
** Inputs: type: Type of the connection (client/server) **
** host: For client application, the remote host- **
** name, or numerical IPv4 or IPv6 network **
** address to connect to. NULL for server **
** application. **
** port: The port number **
** Others: None **
** **
** Outputs: None **
** Return: A pointer to the local endpoint identifier **
** that has been allocated for communication **
** with the remote peer. NULL if the setup of **
** the communication endpoint failed. **
** Others: None **
** **
***************************************************************************/
void* socket_udp_open(int type, const char* host, const char* port)
{
struct addrinfo socket_info; /* endpoint information */
struct addrinfo *socket_addr, *sp; /* endpoint address */
int sfd=-1; /* socket file descriptor */
/*
* Parameters sanity check
* -----------------------
* The local connection endpoint shall be of type CLIENT or SERVER
*/
if (host == NULL) {
type = SOCKET_SERVER;
} else if (type != SOCKET_CLIENT && type != SOCKET_SERVER) {
return NULL;
}
/*
* Initialize the endpoint address information
* -------------------------------------------
* The AI_PASSIVE flag allows "wildcard address" when hostname is not
* provided (NULL). The wildcard address is used by applications (ty-
* pically servers) that intend to accept connections on any of the
* hosts's network addresses. If the hostname is not NULL, then the
* AI_PASSIVE flag is ignored.
* When the AI_V4MAPPED flag is set, and AF_INET6 is specified, and no
* matching IPv6 addresses could be found, then IPv4-mapped IPv6 addresses
* are returned by getaddrinfo in the list pointed to by result.
*/
memset (&socket_info, 0, sizeof(struct addrinfo));
socket_info.ai_socktype = SOCK_DGRAM; /* Datagram socket */
socket_info.ai_flags = AI_NUMERICSERV; /* numeric port number */
if (type != SOCKET_CLIENT) {
/* Setup socket address options at the server side */
socket_info.ai_family = AF_INET6; /* Accept either IPv4 or IPv6
* connections */
socket_info.ai_flags |= AI_PASSIVE; /* Use "wildcard address" */
socket_info.ai_flags |= AI_V4MAPPED; /* IPv4-mapped IPv6 address */
} else {
/* Setup socket address options at the client side */
socket_info.ai_family = AF_INET; /* Any address family */
// socket_info.ai_flags |= AI_V4MAPPED; /* IPv4-mapped IPv6 address */
}
/*
* getaddrinfo() returns a linked list of address structures:
* - The network host may be multi-homed, accessible over multiple
* protocols (e.g. both AF_INET and AF_INET6);
* - The same service (port number) may be available from multiple
* socket types (one SOCK_STREAM address and another SOCK_DGRAM address);
*/
int rc = getaddrinfo (host, port, &socket_info, &socket_addr);
if (rc != 0) {
if (rc != EAI_SYSTEM) {
errno = rc;
}
return NULL;
}
/*
* Try each address until we successfully connect
*/
for (sp = socket_addr; sp != NULL; sp = sp->ai_next) {
/* Create the socket endpoint for communication */
sfd = socket (sp->ai_family, sp->ai_socktype, sp->ai_protocol);
if (sfd < 0) {
continue;
}
/*
* Initiate a communication channel at the CLIENT side
*/
if (type == SOCKET_CLIENT) {
/* Connect the socket to the remote server's address */
if (connect (sfd, sp->ai_addr, sp->ai_addrlen) != -1) {
break; /* Connection succeed */
}
}
/*
* Initiate a communication channel at the SERVER side
*/
else {
if (type == SOCKET_SERVER) {
/* Set socket options */
if (_socket_set_option (sfd) != RETURNok) {
continue;
}
/* Bind the socket to the local server's address */
if (bind (sfd, sp->ai_addr, sp->ai_addrlen) != -1) {
break; /* Bind succeed */
}
}
}
close (sfd);
}
/* Free the memory that was dynamically allocated for the linked list */
freeaddrinfo (socket_addr);
if (sp == NULL) {
/* Connect or bind failed */
return NULL;
}
/* The connection endpoint has been successfully setup */
socket_id_t * sid = (socket_id_t *) malloc (sizeof(struct socket_id_s));
if (sid != NULL) {
sid->type = type;
sid->port = atoi (port);
sid->fd = sfd;
} else {
close (sfd);
}
return sid;
}
/****************************************************************************
** **
** Name: socket_close() **
** **
** Description: Cleanup the specified communication endpoint: Close the **
** socket file descriptor and frees all the memory space **
** allocated to handle the communication channel towards the **
** remote peer **
** **
** Inputs: None **
** Others: None **
** **
** Outputs: id: The identifier of the connection endpoint **
** Return: None **
** Others: None **
** **
***************************************************************************/
void socket_close(void* id)
{
if (id) {
close (((socket_id_t*) id)->fd);
free (id);
}
}
/****************************************************************************
** **
** Name: socket_recv() **
** **
** Description: Receives data from a given communication endpoint to a **
** receive buffer of specified length **
** **
** Inputs: id: The identifier of the connection endpoint **
** length: Length of the receive buffer **
** Others: None **
** **
** Outputs: id: The identifier of the connection endpoint **
** buffer: The receive buffer **
** Return: The number of bytes received when success; **
** RETURNerror otherwise. **
** Others: None **
** **
***************************************************************************/
ssize_t socket_recv(void* id, char* buffer, size_t length)
{
socket_id_t* sid = (socket_id_t*) (id);
ssize_t rbytes = -1;
if (sid->type == SOCKET_CLIENT) {
/* Receive data from the connected socket */
rbytes = recv (sid->fd, buffer, length, 0);
} else if (sid->type == SOCKET_SERVER) {
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(addr);
/* Receive data from the socket and retreive the remote host address */
rbytes = recvfrom (sid->fd, buffer, length, 0, (struct sockaddr *) &addr, &addrlen);
sid->addr = addr;
}
if (errno == EINTR) {
/* A signal was caught */
return 0;
} else if (rbytes < 0) {
/* Receive failed */
return RETURNerror;
}
return rbytes;
}
/****************************************************************************
** **
** Name: socket_send() **
** **
** Description: Sends data to a given communication endpoint from a send **
** buffer of specified length **
** **
** Inputs: id: The identifier of the connection endpoint **
** buffer: The send buffer **
** length: Length of the send buffer **
** Others: None **
** **
** Outputs: None **
** Return: The number of bytes sent when success; **
** RETURNerror otherwise. **
** Others: None **
** **
***************************************************************************/
ssize_t socket_send(const void* id, const char* buffer, size_t length)
{
const socket_id_t* sid = (socket_id_t*) (id);
ssize_t sbytes = -1;
if (sid->type == SOCKET_CLIENT) {
/* Send data to the connected socket */
sbytes = send (sid->fd, buffer, length, 0);
} else if (sid->type == SOCKET_SERVER) {
/* Send data to the socket using the remote host address */
sbytes = sendto (sid->fd, buffer, length, 0, (struct sockaddr *) &sid->addr, (socklen_t) sizeof(sid->addr));
}
if (errno == EINTR) {
/* A signal was caught */
return 0;
} else if (sbytes != length) {
/* Send failed */
return RETURNerror;
}
return sbytes;
}
/****************************************************************************
** **
** Name: socket_get_fd() **
** **
** Description: Get the value of the socket file descriptor created for **
** the given connection endpoint **
** **
** Inputs: id: The identifier of the connection endpoint **
** Others: None **
** **
** Outputs: None **
** Return: The file descriptor of the socket created **
** for this connection endpoint **
** Others: None **
** **
***************************************************************************/
int socket_get_fd(const void* id)
{
if (id) {
return ((socket_id_t*) id)->fd;
}
return RETURNerror;
}
/****************************************************************************/
/********************* L O C A L F U N C T I O N S *********************/
/****************************************************************************/
/****************************************************************************
** **
** Name: _socket_set_option() **
** **
** Description: Set socket option at the sockets API level (SOL_SOCKET) **
** **
** Inputs: sfd: Socket file descriptor **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _socket_set_option(int sfd)
{
int optval;
/* SO_REUSEADDR socket option:
* ---------------------------
* Allows the server to bind a socket to this port, unless
* there is an active listening socket already bound to the
* port. This is useful when recovering from a crash and the
* socket was not properly closed. The server can be restarted
* and it will simply open another socket on the same port and
* continue listening.
*/
optval = true;
if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
return RETURNerror;
}
/* IPV6_V6ONLY socket option
* -------------------------
* When option is set to true, the socket is restricted to sending and
* receiving IPv6 packets only.
* When option is set to false, the socket can be used to send and receive
* packets to and from an IPv6 address or an IPv4-mapped IPv6 address.
*/
optval = false;
if (setsockopt (sfd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) {
return RETURNerror;
}
return RETURNok;
}