// socket.c // /**************************************************************************** liblscp - LinuxSampler Control Protocol API Copyright (C) 2004-2021, rncbc aka Rui Nuno Capela. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "lscp/socket.h" //------------------------------------------------------------------------- // Socket info debugging. #if defined(WIN32) static struct { int iError; const char *pszError; } _wsaErrorCodes[] = { { 0, "No error" }, { WSAEINTR, "Interrupted system call" }, { WSAEBADF, "Bad file number" }, { WSAEACCES, "Permission denied" }, { WSAEFAULT, "Bad address" }, { WSAEINVAL, "Invalid argument" }, { WSAEMFILE, "Too many open sockets" }, { WSAEWOULDBLOCK, "Operation would block" }, { WSAEINPROGRESS, "Operation now in progress" }, { WSAEALREADY, "Operation already in progress" }, { WSAENOTSOCK, "Socket operation on non-socket" }, { WSAEDESTADDRREQ, "Destination address required" }, { WSAEMSGSIZE, "Message too long" }, { WSAEPROTOTYPE, "Protocol wrong type for socket" }, { WSAENOPROTOOPT, "Bad protocol option" }, { WSAEPROTONOSUPPORT, "Protocol not supported" }, { WSAESOCKTNOSUPPORT, "Socket type not supported" }, { WSAEOPNOTSUPP, "Operation not supported on socket" }, { WSAEPFNOSUPPORT, "Protocol family not supported" }, { WSAEAFNOSUPPORT, "Address family not supported" }, { WSAEADDRINUSE, "Address already in use" }, { WSAEADDRNOTAVAIL, "Can't assign requested address" }, { WSAENETDOWN, "Network is down" }, { WSAENETUNREACH, "Network is unreachable" }, { WSAENETRESET, "Net connection reset" }, { WSAECONNABORTED, "Software caused connection abort" }, { WSAECONNRESET, "Connection reset by peer" }, { WSAENOBUFS, "No buffer space available" }, { WSAEISCONN, "Socket is already connected" }, { WSAENOTCONN, "Socket is not connected" }, { WSAESHUTDOWN, "Can't send after socket shutdown" }, { WSAETOOMANYREFS, "Too many references, can't splice" }, { WSAETIMEDOUT, "Connection timed out" }, { WSAECONNREFUSED, "Connection refused" }, { WSAELOOP, "Too many levels of symbolic links" }, { WSAENAMETOOLONG, "File name too long" }, { WSAEHOSTDOWN, "Host is down" }, { WSAEHOSTUNREACH, "No route to host" }, { WSAENOTEMPTY, "Directory not empty" }, { WSAEPROCLIM, "Too many processes" }, { WSAEUSERS, "Too many users" }, { WSAEDQUOT, "Disc quota exceeded" }, { WSAESTALE, "Stale NFS file handle" }, { WSAEREMOTE, "Too many levels of remote in path" }, { WSASYSNOTREADY, "Network system is unavailable" }, { WSAVERNOTSUPPORTED, "Winsock version out of range" }, { WSANOTINITIALISED, "WSAStartup not yet called" }, { WSAEDISCON, "Graceful shutdown in progress" }, { WSAHOST_NOT_FOUND, "Host not found" }, { WSANO_DATA, "No host data of that type was found" }, { 0, NULL } }; void lscp_socket_perror ( const char *pszPrefix ) { int iError = WSAGetLastError(); const char *pszError = "Unknown error code"; int i; for (i = 0; _wsaErrorCodes[i].pszError; i++) { if (_wsaErrorCodes[i].iError == iError) { pszError = _wsaErrorCodes[i].pszError; break; } } fprintf(stderr, "%s: %s (%d)\n", pszPrefix, pszError, iError); } void lscp_socket_herror ( const char *pszPrefix ) { lscp_socket_perror(pszPrefix); } #else void lscp_socket_perror ( const char *pszPrefix ) { perror(pszPrefix); } void lscp_socket_herror ( const char *pszPrefix ) { herror(pszPrefix); } #endif static void _lscp_socket_getopt_bool ( lscp_socket_t sock, const char *pszOptName, int iOptName ) { int iSockOpt; socklen_t cSockLen = sizeof(int); char szPrefix[33]; sprintf(szPrefix, " %s\t", pszOptName); if (getsockopt(sock, SOL_SOCKET, iOptName, (char *) &iSockOpt, &cSockLen) == SOCKET_ERROR) lscp_socket_perror(szPrefix); else fprintf(stderr, "%s: %s\n", szPrefix, (iSockOpt ? "ON" : "OFF")); } static void _lscp_socket_getopt_int ( lscp_socket_t sock, const char *pszOptName, int iOptName ) { int iSockOpt; socklen_t cSockLen = sizeof(int); char szPrefix[33]; sprintf(szPrefix, " %s\t", pszOptName); if (getsockopt(sock, SOL_SOCKET, iOptName, (char *) &iSockOpt, &cSockLen) == SOCKET_ERROR) lscp_socket_perror(szPrefix); else fprintf(stderr, "%s: %d\n", szPrefix, iSockOpt); } void lscp_socket_getopts ( const char *pszPrefix, lscp_socket_t sock ) { fprintf(stderr, "%s: sock=%d:\n", pszPrefix, sock); _lscp_socket_getopt_bool(sock, "SO_BROADCAST", SO_BROADCAST); _lscp_socket_getopt_bool(sock, "SO_DEBUG", SO_DEBUG); #if defined(WIN32) _lscp_socket_getopt_bool(sock, "SO_DONTLINGER", SO_DONTLINGER); #endif _lscp_socket_getopt_bool(sock, "SO_DONTROUTE", SO_DONTROUTE); _lscp_socket_getopt_bool(sock, "SO_KEEPALIVE", SO_KEEPALIVE); _lscp_socket_getopt_bool(sock, "SO_OOBINLINE", SO_OOBINLINE); _lscp_socket_getopt_int (sock, "SO_RCVBUF", SO_RCVBUF); _lscp_socket_getopt_bool(sock, "SO_REUSEADDR", SO_REUSEADDR); _lscp_socket_getopt_int (sock, "SO_SNDBUF", SO_SNDBUF); } void lscp_socket_trace ( const char *pszPrefix, struct sockaddr_in *pAddr, const char *pchBuffer, int cchBuffer ) { char *pszBuffer; fprintf(stderr, "%s: addr=%s port=%d:\n", pszPrefix, inet_ntoa(pAddr->sin_addr), htons(pAddr->sin_port) ); if (pchBuffer && cchBuffer > 0) { pszBuffer = (char *) malloc(cchBuffer + 1); if (pszBuffer) { memcpy(pszBuffer, pchBuffer, cchBuffer); while (cchBuffer > 0 && (pszBuffer[cchBuffer - 1] == '\n' || pszBuffer[cchBuffer- 1] == '\r')) cchBuffer--; pszBuffer[cchBuffer] = (char) 0; fprintf(stderr, "< %s\n", pszBuffer); free(pszBuffer); } } else fprintf(stderr, "< (null)\n"); } //------------------------------------------------------------------------- // Threaded socket agent struct helpers. void lscp_socket_agent_init ( lscp_socket_agent_t *pAgent, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr ) { memset(pAgent, 0, sizeof(lscp_socket_agent_t)); pAgent->sock = sock; pAgent->pThread = NULL; pAgent->iState = 0; if (pAddr) memmove((char *) &(pAgent->addr), pAddr, cAddr); } lscp_status_t lscp_socket_agent_start ( lscp_socket_agent_t *pAgent, lscp_thread_proc_t pfnProc, void *pvData, int iDetach ) { if (pAgent->iState) pAgent->iState = 0; if (pAgent->pThread) lscp_thread_destroy(pAgent->pThread); pAgent->iState = 1; pAgent->pThread = lscp_thread_create(pfnProc, pvData, iDetach); return (pAgent->pThread == NULL ? LSCP_FAILED : LSCP_OK); } lscp_status_t lscp_socket_agent_join ( lscp_socket_agent_t *pAgent ) { lscp_status_t ret = LSCP_FAILED; if (pAgent->pThread) ret = lscp_thread_join(pAgent->pThread); return ret; } lscp_status_t lscp_socket_agent_free ( lscp_socket_agent_t *pAgent ) { lscp_status_t ret = LSCP_FAILED; if (pAgent->iState) pAgent->iState = 0; if (pAgent->sock != INVALID_SOCKET) closesocket(pAgent->sock); pAgent->sock = INVALID_SOCKET; if (pAgent->pThread) ret = lscp_thread_destroy(pAgent->pThread); pAgent->pThread = NULL; return ret; } // end of socket.c