Fabrice Fontaine df27ba505f Removing implicit casts in miniserver.c
Removing implicit integer or enum casts in miniserver.c.
2012-03-29 17:10:07 +02:00

981 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**************************************************************************
*
* Copyright (c) 2000-2003 Intel Corporation
* All rights reserved.
* Copyright (C) 2012 France Telecom All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************/
#include "config.h"
#if EXCLUDE_MINISERVER == 0
/*!
* \file
*
* \brief Implements the functionality and utility functions
* used by the Miniserver module.
*
* The miniserver is a central point for processing all network requests.
* It is made of:
* - The SSDP sockets for discovery.
* - The HTTP listeners for description / control / eventing.
*
*/
#include "miniserver.h"
#include "httpreadwrite.h"
#include "ithread.h"
#include "ssdplib.h"
#include "statcodes.h"
#include "ThreadPool.h"
#include "unixutil.h" /* for socklen_t, EAFNOSUPPORT */
#include "upnpapi.h"
#include "upnputil.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
/*! . */
#define APPLICATION_LISTENING_PORT 49152
struct mserv_request_t {
/*! Connection handle. */
SOCKET connfd;
/*! . */
struct sockaddr_storage foreign_sockaddr;
};
/*! . */
typedef enum {
/*! . */
MSERV_IDLE,
/*! . */
MSERV_RUNNING,
/*! . */
MSERV_STOPPING
} MiniServerState;
/*! . */
uint16_t miniStopSockPort;
/*!
* module vars
*/
static MiniServerState gMServState = MSERV_IDLE;
#ifdef INTERNAL_WEB_SERVER
static MiniServerCallback gGetCallback = NULL;
static MiniServerCallback gSoapCallback = NULL;
static MiniServerCallback gGenaCallback = NULL;
void SetHTTPGetCallback(MiniServerCallback callback)
{
gGetCallback = callback;
}
#ifdef INCLUDE_DEVICE_APIS
void SetSoapCallback(MiniServerCallback callback)
{
gSoapCallback = callback;
}
#endif /* INCLUDE_DEVICE_APIS */
void SetGenaCallback(MiniServerCallback callback)
{
gGenaCallback = callback;
}
/*!
* \brief Based on the type pf message, appropriate callback is issued.
*
* \return 0 on Success or HTTP_INTERNAL_SERVER_ERROR if Callback is NULL.
*/
static int dispatch_request(
/*! [in] Socket Information object. */
IN SOCKINFO *info,
/*! [in] HTTP parser object. */
http_parser_t *hparser)
{
MiniServerCallback callback;
switch (hparser->msg.method) {
/* Soap Call */
case SOAPMETHOD_POST:
case HTTPMETHOD_MPOST:
callback = gSoapCallback;
break;
/* Gena Call */
case HTTPMETHOD_NOTIFY:
case HTTPMETHOD_SUBSCRIBE:
case HTTPMETHOD_UNSUBSCRIBE:
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"miniserver %d: got GENA msg\n", info->socket);
callback = gGenaCallback;
break;
/* HTTP server call */
case HTTPMETHOD_GET:
case HTTPMETHOD_POST:
case HTTPMETHOD_HEAD:
case HTTPMETHOD_SIMPLEGET:
callback = gGetCallback;
break;
default:
callback = NULL;
}
if (callback == NULL) {
return HTTP_INTERNAL_SERVER_ERROR;
}
callback(hparser, &hparser->msg, info);
return 0;
}
/*!
* \brief Send Error Message.
*/
static UPNP_INLINE void handle_error(
/*! [in] Socket Information object. */
SOCKINFO *info,
/*! [in] HTTP Error Code. */
int http_error_code,
/*! [in] Major Version Number. */
int major,
/*! [in] Minor Version Number. */
int minor)
{
http_SendStatusResponse(info, http_error_code, major, minor);
}
/*!
* \brief Free memory assigned for handling request and unitialize socket
* functionality.
*/
static void free_handle_request_arg(
/*! [in] Request Message to be freed. */
void *args)
{
struct mserv_request_t *request = (struct mserv_request_t *)args;
sock_close(request->connfd);
free(request);
}
/*!
* \brief Receive the request and dispatch it for handling.
*/
static void handle_request(
/*! [in] Request Message to be handled. */
void *args)
{
SOCKINFO info;
int http_error_code;
int ret_code;
int major = 1;
int minor = 1;
http_parser_t parser;
http_message_t *hmsg = NULL;
int timeout = HTTP_DEFAULT_TIMEOUT;
struct mserv_request_t *request = (struct mserv_request_t *)args;
SOCKET connfd = request->connfd;
UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__,
"miniserver %d: READING\n", connfd );
/* parser_request_init( &parser ); */ /* LEAK_FIX_MK */
hmsg = &parser.msg;
ret_code = sock_init_with_ip(
&info, connfd, (struct sockaddr *)&request->foreign_sockaddr);
if (ret_code != UPNP_E_SUCCESS) {
free(request);
httpmsg_destroy(hmsg);
return;
}
/* read */
ret_code = http_RecvMessage(
&info, &parser, HTTPMETHOD_UNKNOWN, &timeout, &http_error_code);
if (ret_code != 0) {
goto error_handler;
}
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"miniserver %d: PROCESSING...\n", connfd);
/* dispatch */
http_error_code = dispatch_request(&info, &parser);
if (http_error_code != 0) {
goto error_handler;
}
http_error_code = 0;
error_handler:
if (http_error_code > 0) {
if (hmsg) {
major = hmsg->major_version;
minor = hmsg->minor_version;
}
handle_error(&info, http_error_code, major, minor);
}
sock_destroy(&info, SD_BOTH);
httpmsg_destroy(hmsg);
free(request);
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"miniserver %d: COMPLETE\n", connfd);
}
/*!
* \brief Initilize the thread pool to handle a request, sets priority for the
* job and adds the job to the thread pool.
*/
static UPNP_INLINE void schedule_request_job(
/*! [in] Socket Descriptor on which connection is accepted. */
SOCKET connfd,
/*! [in] Clients Address information. */
struct sockaddr *clientAddr)
{
struct mserv_request_t *request;
ThreadPoolJob job;
memset(&job, 0, sizeof(job));
request = (struct mserv_request_t *)malloc(
sizeof (struct mserv_request_t));
if (request == NULL) {
UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__,
"mserv %d: out of memory\n", connfd);
sock_close(connfd);
return;
}
request->connfd = connfd;
memcpy(&request->foreign_sockaddr, clientAddr,
sizeof(request->foreign_sockaddr));
TPJobInit(&job, (start_routine)handle_request, (void *)request);
TPJobSetFreeFunction(&job, free_handle_request_arg);
TPJobSetPriority(&job, MED_PRIORITY);
if (ThreadPoolAdd(&gMiniServerThreadPool, &job, NULL) != 0) {
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"mserv %d: cannot schedule request\n", connfd);
free(request);
sock_close(connfd);
return;
}
}
#endif
static UPNP_INLINE void fdset_if_valid(SOCKET sock, fd_set *set)
{
if (sock != INVALID_SOCKET) {
FD_SET(sock, set);
}
}
static void web_server_accept(SOCKET lsock, fd_set *set)
{
#ifdef INTERNAL_WEB_SERVER
SOCKET asock;
socklen_t clientLen;
struct sockaddr_storage clientAddr;
char errorBuffer[ERROR_BUFFER_LEN];
if (lsock != INVALID_SOCKET && FD_ISSET(lsock, set)) {
clientLen = sizeof(clientAddr);
asock = accept(lsock, (struct sockaddr *)&clientAddr,
&clientLen);
if (asock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"miniserver: Error in accept(): %s\n",
errorBuffer);
} else {
schedule_request_job(asock,
(struct sockaddr *)&clientAddr);
}
}
#endif /* INTERNAL_WEB_SERVER */
}
static void ssdp_read(SOCKET rsock, fd_set *set)
{
if (rsock != INVALID_SOCKET && FD_ISSET(rsock, set)) {
readFromSSDPSocket(rsock);
}
}
static int receive_from_stopSock(SOCKET ssock, fd_set *set)
{
ssize_t byteReceived;
socklen_t clientLen;
struct sockaddr_storage clientAddr;
char requestBuf[256];
char buf_ntop[INET6_ADDRSTRLEN];
if (FD_ISSET(ssock, set)) {
clientLen = sizeof(clientAddr);
memset((char *)&clientAddr, 0, sizeof(clientAddr));
byteReceived = recvfrom(ssock, requestBuf,
(size_t)25, 0, (struct sockaddr *)&clientAddr, &clientLen);
if (byteReceived > 0) {
requestBuf[byteReceived] = '\0';
inet_ntop(AF_INET,
&((struct sockaddr_in*)&clientAddr)->sin_addr,
buf_ntop, sizeof(buf_ntop));
UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__,
"Received response: %s From host %s \n",
requestBuf, buf_ntop );
UpnpPrintf( UPNP_PACKET, MSERV, __FILE__, __LINE__,
"Received multicast packet: \n %s\n",
requestBuf);
if (NULL != strstr(requestBuf, "ShutDown")) {
return 1;
}
}
}
return 0;
}
/*!
* \brief Run the miniserver.
*
* The MiniServer accepts a new request and schedules a thread to handle the
* new request. Checks for socket state and invokes appropriate read and
* shutdown actions for the Miniserver and SSDP sockets.
*/
static void RunMiniServer(
/*! [in] Socket Array. */
MiniServerSockArray *miniSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
fd_set expSet;
fd_set rdSet;
SOCKET maxMiniSock;
int ret = 0;
SOCKET stopSock = 0;
maxMiniSock = 0;
maxMiniSock = max(maxMiniSock, miniSock->miniServerSock4);
maxMiniSock = max(maxMiniSock, miniSock->miniServerSock6);
maxMiniSock = max(maxMiniSock, miniSock->miniServerStopSock);
maxMiniSock = max(maxMiniSock, miniSock->ssdpSock4);
maxMiniSock = max(maxMiniSock, miniSock->ssdpSock6);
maxMiniSock = max(maxMiniSock, miniSock->ssdpSock6UlaGua);
#ifdef INCLUDE_CLIENT_APIS
maxMiniSock = max(maxMiniSock, miniSock->ssdpReqSock4);
maxMiniSock = max(maxMiniSock, miniSock->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
++maxMiniSock;
gMServState = MSERV_RUNNING;
while (!stopSock) {
FD_ZERO(&rdSet);
FD_ZERO(&expSet);
/* FD_SET()'s */
FD_SET(miniSock->miniServerStopSock, &expSet);
FD_SET(miniSock->miniServerStopSock, &rdSet);
fdset_if_valid(miniSock->miniServerSock4, &rdSet);
fdset_if_valid(miniSock->miniServerSock6, &rdSet);
fdset_if_valid(miniSock->ssdpSock4, &rdSet);
fdset_if_valid(miniSock->ssdpSock6, &rdSet);
fdset_if_valid(miniSock->ssdpSock6UlaGua, &rdSet);
#ifdef INCLUDE_CLIENT_APIS
fdset_if_valid(miniSock->ssdpReqSock4, &rdSet);
fdset_if_valid(miniSock->ssdpReqSock6, &rdSet);
#endif /* INCLUDE_CLIENT_APIS */
/* select() */
ret = select((int) maxMiniSock, &rdSet, NULL, &expSet, NULL);
if (ret == SOCKET_ERROR && errno == EINTR) {
continue;
}
if (ret == SOCKET_ERROR) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in select(): %s\n", errorBuffer);
continue;
} else {
web_server_accept(miniSock->miniServerSock4, &rdSet);
web_server_accept(miniSock->miniServerSock6, &rdSet);
#ifdef INCLUDE_CLIENT_APIS
ssdp_read(miniSock->ssdpReqSock4, &rdSet);
ssdp_read(miniSock->ssdpReqSock6, &rdSet);
#endif /* INCLUDE_CLIENT_APIS */
ssdp_read(miniSock->ssdpSock4, &rdSet);
ssdp_read(miniSock->ssdpSock6, &rdSet);
ssdp_read(miniSock->ssdpSock6UlaGua, &rdSet);
stopSock = receive_from_stopSock(
miniSock->miniServerStopSock, &rdSet);
}
}
/* Close all sockets. */
sock_close(miniSock->miniServerSock4);
sock_close(miniSock->miniServerSock6);
sock_close(miniSock->miniServerStopSock);
sock_close(miniSock->ssdpSock4);
sock_close(miniSock->ssdpSock6);
sock_close(miniSock->ssdpSock6UlaGua);
#ifdef INCLUDE_CLIENT_APIS
sock_close(miniSock->ssdpReqSock4);
sock_close(miniSock->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
/* Free minisock. */
free(miniSock);
gMServState = MSERV_IDLE;
return;
}
/*!
* \brief Returns port to which socket, sockfd, is bound.
*
* \return -1 on error; check errno. 0 if successfull.
*/
static int get_port(
/*! [in] Socket descriptor. */
SOCKET sockfd,
/*! [out] The port value if successful, otherwise, untouched. */
uint16_t *port)
{
struct sockaddr_storage sockinfo;
socklen_t len;
int code;
len = sizeof(sockinfo);
code = getsockname(sockfd, (struct sockaddr *)&sockinfo, &len);
if (code == -1) {
return -1;
}
if (sockinfo.ss_family == AF_INET) {
*port = ntohs(((struct sockaddr_in*)&sockinfo)->sin_port);
} else if(sockinfo.ss_family == AF_INET6) {
*port = ntohs(((struct sockaddr_in6*)&sockinfo)->sin6_port);
}
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"sockfd = %d, .... port = %d\n", sockfd, (int)*port);
return 0;
}
#ifdef INTERNAL_WEB_SERVER
/*!
* \brief Creates a STREAM socket, binds to INADDR_ANY and listens for
* incoming connecttions. Returns the actual port which the sockets
* sub-system returned.
*
* Also creates a DGRAM socket, binds to the loop back address and
* returns the port allocated by the socket sub-system.
*
* \return
* \li UPNP_E_OUTOF_SOCKET: Failed to create a socket.
* \li UPNP_E_SOCKET_BIND: Bind() failed.
* \li UPNP_E_LISTEN: Listen() failed.
* \li UPNP_E_INTERNAL_ERROR: Port returned by the socket layer is < 0.
* \li UPNP_E_SUCCESS: Success.
*/
static int get_miniserver_sockets(
/*! [in] Socket Array. */
MiniServerSockArray *out,
/*! [in] port on which the server is listening for incoming IPv4
* connections. */
uint16_t listen_port4,
/*! [in] port on which the server is listening for incoming IPv6
* connections. */
uint16_t listen_port6)
{
char errorBuffer[ERROR_BUFFER_LEN];
struct sockaddr_storage __ss_v4;
struct sockaddr_in* serverAddr4 = (struct sockaddr_in*)&__ss_v4;
SOCKET listenfd4;
uint16_t actual_port4 = 0u;
#ifdef UPNP_ENABLE_IPV6
struct sockaddr_storage __ss_v6;
struct sockaddr_in6* serverAddr6 = (struct sockaddr_in6*)&__ss_v6;
SOCKET listenfd6;
uint16_t actual_port6 = 0u;
int onOff;
#endif
int ret_code;
int reuseaddr_on = 0;
int sockError = UPNP_E_SUCCESS;
int errCode = 0;
/* Create listen socket for IPv4/IPv6. An error here may indicate
* that we don't have an IPv4/IPv6 stack. */
listenfd4 = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd4 == INVALID_SOCKET) {
return UPNP_E_OUTOF_SOCKET;
}
#ifdef UPNP_ENABLE_IPV6
listenfd6 = socket(AF_INET6, SOCK_STREAM, 0);
if (listenfd6 == INVALID_SOCKET) {
sock_close(listenfd4);
return UPNP_E_OUTOF_SOCKET;
}
onOff = 1;
sockError = setsockopt(listenfd6, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&onOff, sizeof(onOff));
if (sockError == SOCKET_ERROR) {
sock_close(listenfd4);
sock_close(listenfd6);
return UPNP_E_SOCKET_BIND;
}
#endif
/* As per the IANA specifications for the use of ports by applications
* override the listen port passed in with the first available. */
if (listen_port4 < APPLICATION_LISTENING_PORT) {
listen_port4 = (uint16_t)APPLICATION_LISTENING_PORT;
}
#ifdef UPNP_ENABLE_IPV6
if (listen_port6 < APPLICATION_LISTENING_PORT) {
listen_port6 = (uint16_t)APPLICATION_LISTENING_PORT;
}
#endif
memset(&__ss_v4, 0, sizeof (__ss_v4));
serverAddr4->sin_family = (sa_family_t)AF_INET;
serverAddr4->sin_addr.s_addr = htonl(INADDR_ANY);
#ifdef UPNP_ENABLE_IPV6
memset(&__ss_v6, 0, sizeof (__ss_v6));
serverAddr6->sin6_family = (sa_family_t)AF_INET6;
serverAddr6->sin6_addr = in6addr_any;
#endif
/* Getting away with implementation of re-using address:port and
* instead choosing to increment port numbers.
* Keeping the re-use address code as an optional behaviour that
* can be turned on if necessary.
* TURN ON the reuseaddr_on option to use the option. */
if (reuseaddr_on) {
/* THIS IS ALLOWS US TO BIND AGAIN IMMEDIATELY
* AFTER OUR SERVER HAS BEEN CLOSED
* THIS MAY CAUSE TCP TO BECOME LESS RELIABLE
* HOWEVER IT HAS BEEN SUGESTED FOR TCP SERVERS. */
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"get_miniserver_sockets: resuseaddr is set.\n");
if (listenfd4 != INVALID_SOCKET) {
sockError = setsockopt(listenfd4, SOL_SOCKET,
SO_REUSEADDR,
(const char *)&reuseaddr_on, sizeof (int));
if (sockError == SOCKET_ERROR) {
sock_close(listenfd4);
#ifdef UPNP_ENABLE_IPV6
sock_close(listenfd6);
#endif
return UPNP_E_SOCKET_BIND;
}
serverAddr4->sin_port = htons(listen_port4);
sockError = bind(listenfd4,
(struct sockaddr *)&__ss_v4,
sizeof (__ss_v4));
if (sockError == SOCKET_ERROR) {
strerror_r(errno, errorBuffer,
ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV,
__FILE__, __LINE__,
"get_miniserver_sockets: "
"Error in IPv4 bind(): %s\n",
errorBuffer);
sock_close(listenfd4);
#ifdef UPNP_ENABLE_IPV6
sock_close(listenfd6);
#endif
/* Bind failed */
return UPNP_E_SOCKET_BIND;
}
}
#ifdef UPNP_ENABLE_IPV6
if (listenfd6 != INVALID_SOCKET) {
sockError = setsockopt(listenfd6, SOL_SOCKET,
SO_REUSEADDR,
(const char *)&reuseaddr_on, sizeof (int));
if (sockError == SOCKET_ERROR) {
sock_close(listenfd4);
sock_close(listenfd6);
return UPNP_E_SOCKET_BIND;
}
serverAddr6->sin6_port = htons(listen_port6);
sockError = bind(listenfd6,
(struct sockaddr *)&__ss_v6,
sizeof (__ss_v6));
if (sockError == SOCKET_ERROR) {
strerror_r(errno, errorBuffer,
ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV,
__FILE__, __LINE__,
"get_miniserver_sockets: "
"Error in IPv6 bind(): %s\n",
errorBuffer);
sock_close(listenfd4);
sock_close(listenfd6);
/* Bind failed */
return UPNP_E_SOCKET_BIND;
}
}
#endif /* IPv6 */
} else {
if (listenfd4 != INVALID_SOCKET) {
uint16_t orig_listen_port4 = listen_port4;
do {
serverAddr4->sin_port = htons(listen_port4++);
sockError = bind(listenfd4,
(struct sockaddr *)serverAddr4,
sizeof(*serverAddr4));
if (sockError == SOCKET_ERROR) {
#ifdef WIN32
errCode = WSAGetLastError();
#else
errCode = errno;
#endif
if (errno == EADDRINUSE) {
errCode = 1;
}
} else {
errCode = 0;
}
} while (errCode != 0 &&
listen_port4 >= orig_listen_port4);
if (sockError == SOCKET_ERROR) {
strerror_r(errno, errorBuffer,
ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV,
__FILE__, __LINE__,
"get_miniserver_sockets: "
"Error in IPv4 bind(): %s\n",
errorBuffer);
sock_close(listenfd4);
#ifdef UPNP_ENABLE_IPV6
sock_close(listenfd6);
#endif
/* Bind failied. */
return UPNP_E_SOCKET_BIND;
}
}
#ifdef UPNP_ENABLE_IPV6
if (listenfd6 != INVALID_SOCKET) {
uint16_t orig_listen_port6 = listen_port6;
do {
serverAddr6->sin6_port = htons(listen_port6++);
sockError = bind(listenfd6,
(struct sockaddr *)serverAddr6,
sizeof(*serverAddr6));
if (sockError == SOCKET_ERROR) {
#ifdef WIN32
errCode = WSAGetLastError();
#else
errCode = errno;
#endif
if (errno == EADDRINUSE) {
errCode = 1;
}
} else {
errCode = 0;
}
} while (errCode != 0 &&
listen_port6 >= orig_listen_port6);
if (sockError == SOCKET_ERROR) {
strerror_r(errno, errorBuffer,
ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV,
__FILE__, __LINE__,
"get_miniserver_sockets: "
"Error in IPv6 bind(): %s\n",
errorBuffer);
sock_close(listenfd4);
sock_close(listenfd6);
/* Bind failied. */
return UPNP_E_SOCKET_BIND;
}
}
#endif
}
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"get_miniserver_sockets: bind successful\n");
if (listenfd4 != INVALID_SOCKET) {
ret_code = listen(listenfd4, SOMAXCONN);
if (ret_code == SOCKET_ERROR) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"mserv start: Error in IPv4 listen(): %s\n",
errorBuffer);
sock_close(listenfd4);
#ifdef UPNP_ENABLE_IPV6
sock_close(listenfd6);
#endif
return UPNP_E_LISTEN;
}
ret_code = get_port(listenfd4, &actual_port4);
if (ret_code < 0) {
sock_close(listenfd4);
#ifdef UPNP_ENABLE_IPV6
sock_close(listenfd6);
#endif
return UPNP_E_INTERNAL_ERROR;
}
out->miniServerPort4 = actual_port4;
}
#ifdef UPNP_ENABLE_IPV6
if (listenfd6 != INVALID_SOCKET) {
ret_code = listen(listenfd6, SOMAXCONN);
if (ret_code == SOCKET_ERROR) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__,
"mserv start: Error in IPv6 listen(): %s\n",
errorBuffer);
sock_close(listenfd4);
sock_close(listenfd6);
return UPNP_E_LISTEN;
}
ret_code = get_port(listenfd6, &actual_port6);
if (ret_code < 0) {
sock_close(listenfd4);
sock_close(listenfd6);
return UPNP_E_INTERNAL_ERROR;
}
out->miniServerPort6 = actual_port6;
}
#endif
out->miniServerSock4 = listenfd4;
#ifdef UPNP_ENABLE_IPV6
out->miniServerSock6 = listenfd6;
#else
/* Silence compiler warning message:
* warning: unused parameter listen_port6 */
listen_port6 = 0u;
#endif
return UPNP_E_SUCCESS;
}
#endif /* INTERNAL_WEB_SERVER */
/*!
* \brief Creates the miniserver STOP socket. This socket is created and
* listened on to know when it is time to stop the Miniserver.
*
* \return
* \li \c UPNP_E_OUTOF_SOCKET: Failed to create a socket.
* \li \c UPNP_E_SOCKET_BIND: Bind() failed.
* \li \c UPNP_E_INTERNAL_ERROR: Port returned by the socket layer is < 0.
* \li \c UPNP_E_SUCCESS: Success.
*/
static int get_miniserver_stopsock(
/*! [in] Miniserver Socket Array. */
MiniServerSockArray *out)
{
char errorBuffer[ERROR_BUFFER_LEN];
struct sockaddr_in stop_sockaddr;
SOCKET miniServerStopSock = 0;
int ret = 0;
miniServerStopSock = socket(AF_INET, SOCK_DGRAM, 0);
if (miniServerStopSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, MSERV, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
/* Bind to local socket. */
memset(&stop_sockaddr, 0, sizeof (stop_sockaddr));
stop_sockaddr.sin_family = (sa_family_t)AF_INET;
stop_sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(miniServerStopSock, (struct sockaddr *)&stop_sockaddr,
sizeof(stop_sockaddr));
if (ret == SOCKET_ERROR) {
UpnpPrintf(UPNP_CRITICAL,
MSERV, __FILE__, __LINE__,
"Error in binding localhost!!!\n");
sock_close(miniServerStopSock);
return UPNP_E_SOCKET_BIND;
}
ret = get_port(miniServerStopSock, &miniStopSockPort);
if (ret < 0) {
sock_close(miniServerStopSock);
return UPNP_E_INTERNAL_ERROR;
}
out->miniServerStopSock = miniServerStopSock;
out->stopPort = miniStopSockPort;
return UPNP_E_SUCCESS;
}
static UPNP_INLINE void InitMiniServerSockArray(MiniServerSockArray *miniSocket)
{
miniSocket->miniServerSock4 = INVALID_SOCKET;
miniSocket->miniServerSock6 = INVALID_SOCKET;
miniSocket->miniServerStopSock = INVALID_SOCKET;
miniSocket->ssdpSock4 = INVALID_SOCKET;
miniSocket->ssdpSock6 = INVALID_SOCKET;
miniSocket->ssdpSock6UlaGua = INVALID_SOCKET;
miniSocket->stopPort = 0u;
miniSocket->miniServerPort4 = 0u;
miniSocket->miniServerPort6 = 0u;
#ifdef INCLUDE_CLIENT_APIS
miniSocket->ssdpReqSock4 = INVALID_SOCKET;
miniSocket->ssdpReqSock6 = INVALID_SOCKET;
#endif /* INCLUDE_CLIENT_APIS */
}
int StartMiniServer(
/*! [in,out] Port on which the server listens for incoming IPv4
* connections. */
uint16_t *listen_port4,
/*! [in,out] Port on which the server listens for incoming IPv6
* connections. */
uint16_t *listen_port6)
{
int ret_code;
int count;
int max_count = 10000;
MiniServerSockArray *miniSocket;
ThreadPoolJob job;
memset(&job, 0, sizeof(job));
switch (gMServState) {
case MSERV_IDLE:
break;
default:
/* miniserver running. */
return UPNP_E_INTERNAL_ERROR;
}
miniSocket = (MiniServerSockArray *)malloc(
sizeof (MiniServerSockArray));
if (!miniSocket) {
return UPNP_E_OUTOF_MEMORY;
}
InitMiniServerSockArray(miniSocket);
#ifdef INTERNAL_WEB_SERVER
/* V4 and V6 http listeners. */
ret_code = get_miniserver_sockets(
miniSocket, *listen_port4, *listen_port6);
if (ret_code != UPNP_E_SUCCESS) {
free(miniSocket);
return ret_code;
}
#endif
/* Stop socket (To end miniserver processing). */
ret_code = get_miniserver_stopsock(miniSocket);
if (ret_code != UPNP_E_SUCCESS) {
sock_close(miniSocket->miniServerSock4);
sock_close(miniSocket->miniServerSock6);
free(miniSocket);
return ret_code;
}
/* SSDP socket for discovery/advertising. */
ret_code = get_ssdp_sockets(miniSocket);
if (ret_code != UPNP_E_SUCCESS) {
sock_close(miniSocket->miniServerSock4);
sock_close(miniSocket->miniServerSock6);
sock_close(miniSocket->miniServerStopSock);
free(miniSocket);
return ret_code;
}
TPJobInit(&job, (start_routine)RunMiniServer, (void *)miniSocket);
TPJobSetPriority(&job, MED_PRIORITY);
TPJobSetFreeFunction(&job, (free_routine)free);
ret_code = ThreadPoolAddPersistent(&gMiniServerThreadPool, &job, NULL);
if (ret_code < 0) {
sock_close(miniSocket->miniServerSock4);
sock_close(miniSocket->miniServerSock6);
sock_close(miniSocket->miniServerStopSock);
sock_close(miniSocket->ssdpSock4);
sock_close(miniSocket->ssdpSock6);
sock_close(miniSocket->ssdpSock6UlaGua);
#ifdef INCLUDE_CLIENT_APIS
sock_close(miniSocket->ssdpReqSock4);
sock_close(miniSocket->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
return UPNP_E_OUTOF_MEMORY;
}
/* Wait for miniserver to start. */
count = 0;
while (gMServState != (MiniServerState)MSERV_RUNNING && count < max_count) {
/* 0.05s */
usleep(50u * 1000u);
count++;
}
if (count >= max_count) {
/* Took it too long to start that thread. */
sock_close(miniSocket->miniServerSock4);
sock_close(miniSocket->miniServerSock6);
sock_close(miniSocket->miniServerStopSock);
sock_close(miniSocket->ssdpSock4);
sock_close(miniSocket->ssdpSock6);
sock_close(miniSocket->ssdpSock6UlaGua);
#ifdef INCLUDE_CLIENT_APIS
sock_close(miniSocket->ssdpReqSock4);
sock_close(miniSocket->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
return UPNP_E_INTERNAL_ERROR;
}
#ifdef INTERNAL_WEB_SERVER
*listen_port4 = miniSocket->miniServerPort4;
*listen_port6 = miniSocket->miniServerPort6;
#endif
return UPNP_E_SUCCESS;
}
int StopMiniServer()
{
char errorBuffer[ERROR_BUFFER_LEN];
socklen_t socklen = sizeof (struct sockaddr_in);
SOCKET sock;
struct sockaddr_in ssdpAddr;
char buf[256] = "ShutDown";
size_t bufLen = strlen(buf);
switch(gMServState) {
case MSERV_RUNNING:
gMServState = MSERV_STOPPING;
break;
default:
return 0;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"SSDP_SERVER: StopSSDPServer: Error in socket() %s\n",
errorBuffer);
return 0;
}
while(gMServState != (MiniServerState)MSERV_IDLE) {
ssdpAddr.sin_family = (sa_family_t)AF_INET;
ssdpAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ssdpAddr.sin_port = htons(miniStopSockPort);
sendto(sock, buf, bufLen, 0,
(struct sockaddr *)&ssdpAddr, socklen);
usleep(1000u);
if (gMServState == (MiniServerState)MSERV_IDLE) {
break;
}
isleep(1u);
}
sock_close(sock);
return 0;
}
#endif /* EXCLUDE_MINISERVER */