// // anyRemote // a wi-fi or bluetooth remote for your PC. // // Copyright (C) 2006-2016 Mikhail Fedotov // // 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, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pr_socket.h" #include "common.h" #include "utils.h" #include "conf.h" #include "lib_wrapper.h" #include "sys_util.h" #include "security.h" #include "dispatcher.h" extern char tmp[MAXMAXLEN]; extern boolean_t stillRun; typedef struct _SocketConnection_ { int fileDescriptor; int serverFileDescriptor; char* uSocket; } _SocketConnection; // // Support server mode sockets // int socketFD(ConnectInfo* conn) { _SocketConnection* cn = (_SocketConnection*) conn->connectionData; if (!cn) { return -1; } return (conn->state == PEER_WAIT_ACCEPT || conn->state == PEER_WAIT_LISTEN ? cn->serverFileDescriptor : cn->fileDescriptor); } static int socketOpenInternal(ConnectInfo* conn) { struct sockaddr_in tcp_addr; struct sockaddr_un un_addr; struct sockaddr* socketaddr = NULL; int addFamily = 0; int proto = 0; int sz; if (conn->connectionData && ((_SocketConnection*) conn->connectionData)->serverFileDescriptor > 0) { logger(L_ERR, "[DS]: socketOpenInternal: TCP/local socket was already opened"); return 1; } if (conn->connectionData) { free(((_SocketConnection*) conn->connectionData)->uSocket); free(conn->connectionData); } conn->connectionData = (_SocketConnection*) malloc(sizeof(_SocketConnection)); _SocketConnection* cn = (_SocketConnection*) conn->connectionData; cn->serverFileDescriptor = -1; cn->fileDescriptor = -1; cn->uSocket = NULL; const char *path = (conn->portStr ? conn->portStr->str : NULL); if (conn->mode == SERVER_TCP) { addFamily = AF_INET; proto = IPPROTO_TCP; } else if (conn->mode == SERVER_UX) { addFamily = AF_UNIX; proto = 0; } else { logger(L_ERR, "socketOpenInternal: incorrect input"); return -1; } if ((cn->serverFileDescriptor = socket(addFamily, SOCK_STREAM|SOCK_CLOEXEC, proto)) < 0) { logger(L_ERR, "[DS]: socketOpenInternal: opening TCP socket"); errnoDebug("opening TCP socket ",errno); // testing debug printf("ERROR: socketOpenInternal: opening TCP socket\n"); cn->serverFileDescriptor = -1; return -1; } int optval = 1; setsockopt(cn->serverFileDescriptor, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (conn->mode == SERVER_TCP) { memset((void *) &tcp_addr, 0, sizeof(tcp_addr)); tcp_addr.sin_family = AF_INET; tcp_addr.sin_addr.s_addr = INADDR_ANY; tcp_addr.sin_port = htons(conn->port); socketaddr=(struct sockaddr *)&tcp_addr; sz = sizeof(tcp_addr); } else if (conn->mode == SERVER_UX && path != NULL) { memset(&un_addr, 0, sizeof(un_addr)); un_addr.sun_family = AF_UNIX; strncpy(un_addr.sun_path, path, sizeof un_addr.sun_path - 1); printf("ERROR: SOCKET %s\n", path); socketaddr=(struct sockaddr *)&un_addr; sz = sizeof(un_addr); } else { logger(L_ERR, "socketOpenInternal: incorrect input 2"); return -1; } if (bind(cn->serverFileDescriptor, (struct sockaddr *) socketaddr, sz) < 0) { logger(L_ERR, "[DS]: socketOpenInternal: on bind()"); printf("ERROR: on bind socket to the address %s (%d)\n", strerror(errno), errno); return -1; } if (conn->mode == SERVER_UX) { cn->uSocket = strdup(path); } return 1; } int socketOpen(ConnectInfo* conn) { if (conn->mode == SERVER_TCP) { DEBUG2("[DS]: socketOpen: TCP mode. Use port %d", conn->port); } else if (conn->mode == SERVER_UX) { DEBUG2("[DS]: socketOpen: Local socket mode. Use file %s", conn->portStr->str); } else { DEBUG2("[DS]: socketOpen: improper mode. %d", conn->mode); return EXIT_NOK; } if (socketOpenInternal(conn) < 0) { return EXIT_NOK; } conn->state = PEER_WAIT_LISTEN; return EXIT_OK; } void socketClose(ConnectInfo* conn, int final) { _SocketConnection* cn = (_SocketConnection*) conn->connectionData; if (!cn) return; if (final) { logger(L_INF, "[DS]: closeSocket"); } if (cn->fileDescriptor >= 0) { if (final) { logger(L_INF, "[DS]: closeSocket close socket"); } close(cn->fileDescriptor); cn->fileDescriptor = -1; } if (cn->serverFileDescriptor >= 0) { if (final) { logger(L_INF, "[DS]: closeSocket close server socket"); } close(cn->serverFileDescriptor); cn->serverFileDescriptor = -1; } if (final) { if (conn->mode == SERVER_UX) { unlink(cn->uSocket); free(cn->uSocket); } } free(cn); conn->connectionData = NULL; conn->state = PEER_DISCONNECTED; } void socketReset(ConnectInfo* conn) { _SocketConnection* cn = (_SocketConnection*) conn->connectionData; if (cn) { if (cn->fileDescriptor >= 0) { close(cn->fileDescriptor); cn->fileDescriptor = -1; } conn->state = PEER_WAIT_ACCEPT; } else { conn->state = PEER_DISCONNECTED; // should not happens } } // // Setup listen // int socketListen(ConnectInfo* conn) { logger(L_INF, "[DS]: socketListen"); _SocketConnection* cn = (_SocketConnection*) conn->connectionData; if (!cn) { return -1; } int ret = listen(cn->serverFileDescriptor,0); if (ret >= 0) { conn->state = PEER_WAIT_ACCEPT; } return (ret < 0 ? -1 : 1); } // // Wait for incoming connection // int socketAccept(ConnectInfo* conn) { struct sockaddr* socketaddr = NULL; struct sockaddr_in ip_addr; struct sockaddr_un un_addr; int cnt, sz; int type = conn->mode; INFO2("[DS]: socketAccept %d", conn->id); cnt = 0; if (type == SERVER_TCP) { socketaddr=(struct sockaddr *)&ip_addr; sz = sizeof(ip_addr); } else if (type == SERVER_UX) { socketaddr=(struct sockaddr *)&un_addr; sz = sizeof(un_addr); } if (!socketaddr) { return -1; } _SocketConnection* cn = (_SocketConnection*) conn->connectionData; if (!cn) return -1; while (stillRun) { INFO2("[DS]: socketAccept: accept on %d", conn->id); cn->fileDescriptor = accept(cn->serverFileDescriptor, (struct sockaddr *) socketaddr, (socklen_t *)&sz); if (cn->fileDescriptor < 0 && errno == EAGAIN) { if (cnt >= 60) { // Print to log every minute logger(L_INF, "socketAccept: waiting for connection"); //printf("."); cnt = 0; } fflush(stdout); sleep(1); cnt++; continue; } if (cn->fileDescriptor < 0) { logger(L_ERR, "[DS]: on accept"); printf("ERROR: on accept %d\n", errno); return -1; /*} else { // Set non-blocking mode if (-1 == (flags = fcntl(portfd, F_GETFL, 0))) { flags = 0; } fcntl(portfd, F_SETFL, flags | O_NONBLOCK); */ } char buf[INET6_ADDRSTRLEN]; buf[0] = '\0'; if (type == SERVER_TCP) { peerName(cn->fileDescriptor,buf,INET6_ADDRSTRLEN); if (!isAllowed(buf)) { INFO2("[DS]: socketAccept: host %s is not in the list of accepted host, close connection", buf); write(cn->fileDescriptor,CMD_STR_DISCONNECT,strlen(CMD_STR_DISCONNECT)); close(cn->fileDescriptor); cn->fileDescriptor = -1; conn->state = PEER_DISCONNECTED; return -1; } } if (getUsePassword() && !getIViewer()) { logger(L_DBG,"[DS]: socketAccept: Do password verification"); int ret = EXIT_OK; int i = 0; for ( ; i<3; i++) { ret = verifyPassword(cn->fileDescriptor); if (ret == EXIT_OK) { // got it break; } } if (ret != EXIT_OK) { if (ret == EXIT_NOK) { // if it is EXIT_STOP connection is already lost write(cn->fileDescriptor,CMD_STR_DISCONNECT,strlen(CMD_STR_DISCONNECT)); } close(cn->fileDescriptor); cn->fileDescriptor = -1; conn->state = PEER_DISCONNECTED; return -1; } logger(L_DBG,"[DS]: socketAccept: Password verification OK"); } INFO2("[DS]: socketAccept: accepted from %s", buf); conn->state = PEER_CONNECTED; // force to detect H/W and cover size. need to do that before (Connect) or syncPeer() handling getClientSize(conn->id, cn->fileDescriptor); break; } logger(L_INF, "[DS]: socketAccept exit"); return 1; } int socketWrite(ConnectInfo* connInfo, const dMessage* msg) { logger(L_DBG, "[DS]: socketWrite"); if (!msg) { return EXIT_OK; } const char* command = msg->value; int count = msg->size; if (!command || count <= 0) { return EXIT_OK; } if (strcmp("End();",command) == 0) { // used only for WEB/CMXML return EXIT_OK; } _SocketConnection* cn = (_SocketConnection*) connInfo->connectionData; if (!cn) { return EXIT_NOK; } // send command if (cn->fileDescriptor >= 0) { if (msg->type == DM_SET) { if (isDataOld(connInfo, msg->subtype, command, count)) { INFO2("[DS]: Skip to send the same data to TCP peer"); return EXIT_OK; } } memset(tmp, 0, MAXMAXLEN); strcat(tmp, "[DS]: socketWrite "); int logSz = (count > 256 ? 255 : count); // it is possible to get binary data here memcpy(tmp, command, logSz); // Do not dump long commands tmp[logSz] = '\0'; logger(L_DBG, tmp); sprintf(tmp, "[DS]: socketWrite %d bytes", count); logger(L_INF, tmp); int n = write(cn->fileDescriptor,command,count); if (n < 0) { logger(L_ERR, "[DS]: error writing to socket"); return EXIT_NOK; } return EXIT_OK; } else { logger(L_ERR, "[DS]: error writing to socket: already closed"); } return EXIT_NOK; }