/* * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * * This 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 2 of the License, or * (at your option) any later version. * * This software 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 software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ /* * sockets.c - functions to deal with sockets. */ #include #include #include #include #include #include #include #include #include #include void PrintInHex(char *buf, int len); rfbBool errorMessageOnReadFailure = TRUE; #define BUF_SIZE 8192 static char buf[BUF_SIZE]; static char *bufoutptr = buf; static int buffered = 0; /* * ReadFromRFBServer is called whenever we want to read some data from the RFB * server. It is non-trivial for two reasons: * * 1. For efficiency it performs some intelligent buffering, avoiding invoking * the read() system call too often. For small chunks of data, it simply * copies the data out of an internal buffer. For large amounts of data it * reads directly into the buffer provided by the caller. * * 2. Whenever read() would block, it invokes the Xt event dispatching * mechanism to process X events. In fact, this is the only place these * events are processed, as there is no XtAppMainLoop in the program. */ rfbBool ReadFromRFBServer(rfbClient* client, char *out, unsigned int n) { if (n <= buffered) { memcpy(out, bufoutptr, n); bufoutptr += n; buffered -= n; return TRUE; } memcpy(out, bufoutptr, buffered); out += buffered; n -= buffered; bufoutptr = buf; buffered = 0; if (n <= BUF_SIZE) { while (buffered < n) { int i = read(client->sock, buf + buffered, BUF_SIZE - buffered); if (i <= 0) { if (i < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) { /* TODO: ProcessXtEvents(); */ i = 0; } else { perror("read"); return FALSE; } } else { if (errorMessageOnReadFailure) { rfbClientLog("VNC server closed connection\n"); } return FALSE; } } buffered += i; } memcpy(out, bufoutptr, n); bufoutptr += n; buffered -= n; return TRUE; } else { while (n > 0) { int i = read(client->sock, out, n); if (i <= 0) { if (i < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) { /* TODO: ProcessXtEvents(); */ i = 0; } else { perror("read"); return FALSE; } } else { if (errorMessageOnReadFailure) { rfbClientLog("VNC server closed connection\n"); } return FALSE; } } out += i; n -= i; } return TRUE; } } /* * Write an exact number of bytes, and don't return until you've sent them. */ rfbBool WriteToRFBServer(rfbClient* client, char *buf, int n) { fd_set fds; int i = 0; int j; while (i < n) { j = write(client->sock, buf + i, (n - i)); if (j <= 0) { if (j < 0) { if (errno == EWOULDBLOCK || errno == EAGAIN) { FD_ZERO(&fds); FD_SET(client->sock,&fds); if (select(client->sock+1, NULL, &fds, NULL, NULL) <= 0) { perror("select"); return FALSE; } j = 0; } else { perror("write"); return FALSE; } } else { rfbClientLog("write failed\n"); return FALSE; } } i += j; } return TRUE; } /* * ConnectToTcpAddr connects to the given TCP port. */ int ConnectClientToTcpAddr(unsigned int host, int port) { int sock; struct sockaddr_in addr; int one = 1; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = host; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("ConnectToTcpAddr: socket"); return -1; } if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("ConnectToTcpAddr: connect"); close(sock); return -1; } if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { perror("ConnectToTcpAddr: setsockopt"); close(sock); return -1; } return sock; } /* * FindFreeTcpPort tries to find unused TCP port in the range * (TUNNEL_PORT_OFFSET, TUNNEL_PORT_OFFSET + 99]. Returns 0 on failure. */ int FindFreeTcpPort(void) { int sock, port; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror(": FindFreeTcpPort: socket"); return 0; } for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) { addr.sin_port = htons((unsigned short)port); if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) { close(sock); return port; } } close(sock); return 0; } /* * ListenAtTcpPort starts listening at the given TCP port. */ int ListenAtTcpPort(int port) { int sock; struct sockaddr_in addr; int one = 1; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("ListenAtTcpPort: socket"); return -1; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)) < 0) { perror("ListenAtTcpPort: setsockopt"); close(sock); return -1; } if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("ListenAtTcpPort: bind"); close(sock); return -1; } if (listen(sock, 5) < 0) { perror("ListenAtTcpPort: listen"); close(sock); return -1; } return sock; } /* * AcceptTcpConnection accepts a TCP connection. */ int AcceptTcpConnection(int listenSock) { int sock; struct sockaddr_in addr; int addrlen = sizeof(addr); int one = 1; sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen); if (sock < 0) { perror("AcceptTcpConnection: accept"); return -1; } if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { perror("AcceptTcpConnection: setsockopt"); close(sock); return -1; } return sock; } /* * SetNonBlocking sets a socket into non-blocking mode. */ rfbBool SetNonBlocking(int sock) { if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { perror("AcceptTcpConnection: fcntl"); return FALSE; } return TRUE; } /* * StringToIPAddr - convert a host string to an IP address. */ rfbBool StringToIPAddr(const char *str, unsigned int *addr) { struct hostent *hp; if (strcmp(str,"") == 0) { *addr = 0; /* local */ return TRUE; } *addr = inet_addr(str); if (*addr != -1) return TRUE; hp = gethostbyname(str); if (hp) { *addr = *(unsigned int *)hp->h_addr; return TRUE; } return FALSE; } /* * Test if the other end of a socket is on the same machine. */ rfbBool SameMachine(int sock) { struct sockaddr_in peeraddr, myaddr; int addrlen = sizeof(struct sockaddr_in); getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen); getsockname(sock, (struct sockaddr *)&myaddr, &addrlen); return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr); } /* * Print out the contents of a packet for debugging. */ void PrintInHex(char *buf, int len) { int i, j; char c, str[17]; str[16] = 0; rfbClientLog("ReadExact: "); for (i = 0; i < len; i++) { if ((i % 16 == 0) && (i != 0)) { rfbClientLog(" "); } c = buf[i]; str[i % 16] = (((c > 31) && (c < 127)) ? c : '.'); rfbClientLog("%02x ",(unsigned char)c); if ((i % 4) == 3) rfbClientLog(" "); if ((i % 16) == 15) { rfbClientLog("%s\n",str); } } if ((i % 16) != 0) { for (j = i % 16; j < 16; j++) { rfbClientLog(" "); if ((j % 4) == 3) rfbClientLog(" "); } str[i % 16] = 0; rfbClientLog("%s\n",str); } fflush(stderr); }