/* socklib.c * * 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #if WIN32 #include #else #include #include #include #include #include #include #include #include #include #include #endif #if __UNIX__ #include #elif __BEOS__ #include #endif #include "srtypes.h" #include "util.h" #include "socklib.h" #include "threadlib.h" #include "compat.h" #include "debug.h" #define DEFAULT_TIMEOUT 15 #if WIN32 #define FIRST_READ_TIMEOUT (30 * 1000) #elif __UNIX__ #define FIRST_READ_TIMEOUT 30 #endif /**************************************************************************** * Private Vars ****************************************************************************/ static BOOL m_done_init = FALSE; // so we don't init the mutex twice.. arg. /**************************************************************************** * Function definitions ****************************************************************************/ error_code socklib_init() { #if WIN32 WORD wVersionRequested; WSADATA wsaData; int err; #endif if (m_done_init) return SR_SUCCESS; #if WIN32 wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) return SR_ERROR_WIN32_INIT_FAILURE; #endif m_done_init = TRUE; return SR_SUCCESS; } /* Try to find the local interface to bind to */ error_code read_interface(char *if_name, uint32_t *addr) { #if defined (WIN32) return -1; #else int fd; struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); if((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { ifr.ifr_addr.sa_family = AF_INET; strcpy(ifr.ifr_name, if_name); if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) *addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; else { close(fd); return -2; } } else return -1; close(fd); return 0; #endif } /* * open's a tcp connection to host at port, host can be a dns name or IP, * socket_handle gets assigned to the handle for the connection */ error_code socklib_open(HSOCKET *socket_handle, char *host, int port, char *if_name) { int rc; struct sockaddr_in address, local; struct hostent *hp; int len; if (!socket_handle || !host) return SR_ERROR_INVALID_PARAM; socket_handle->s = socket(AF_INET, SOCK_STREAM, 0); if (if_name) { if (read_interface(if_name,&local.sin_addr.s_addr) != 0) local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(0); if (bind(socket_handle->s, (struct sockaddr *)&local, sizeof(local)) == SOCKET_ERROR) { debug_printf ("Bind failed\n"); WSACleanup(); closesocket(socket_handle->s); return SR_ERROR_CANT_BIND_ON_INTERFACE; } } if ((address.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { hp = gethostbyname(host); if (hp) { memcpy(&address.sin_addr, hp->h_addr_list[0], hp->h_length); } else { debug_printf("resolving hostname: %s failed\n", host); WSACleanup(); return SR_ERROR_CANT_RESOLVE_HOSTNAME; } } address.sin_family = AF_INET; address.sin_port = htons((unsigned short)port); len = sizeof(address); rc = connect (socket_handle->s, (struct sockaddr *)&address, len); if (rc == SOCKET_ERROR) { debug_printf("connect failed\n"); return SR_ERROR_CONNECT_FAILED; } #ifdef WIN32 { struct timeval timeout = {DEFAULT_TIMEOUT*1000, 0}; rc = setsockopt (socket_handle->s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); if (rc == SOCKET_ERROR) { debug_printf("setsockopt failed\n"); return SR_ERROR_CANT_SET_SOCKET_OPTIONS; } } #endif socket_handle->closed = FALSE; return SR_SUCCESS; } void socklib_cleanup() { WSACleanup(); m_done_init = FALSE; } void socklib_close(HSOCKET *socket_handle) { closesocket(socket_handle->s); socket_handle->closed = TRUE; } error_code socklib_read_header(HSOCKET *socket_handle, char *buffer, int size, int (*recvall)(HSOCKET *socket_handle, char* buffer, int size, int timeout)) { int i; #ifdef WIN32 struct timeval timeout = {FIRST_READ_TIMEOUT, 0}; #endif int ret; char *t; int (*myrecv)(HSOCKET *socket_handle, char* buffer, int size, int timeout); if (socket_handle->closed) return SR_ERROR_SOCKET_CLOSED; if (recvall) myrecv = recvall; else myrecv = socklib_recvall; #ifdef WIN32 if (setsockopt(socket_handle->s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) return SR_ERROR_CANT_SET_SOCKET_OPTIONS; #endif memset(buffer, 0, size); for(i = 0; i < size; i++) { if ((ret = (*myrecv)(socket_handle, &buffer[i], 1, 0)) < 0) return ret; if (ret == 0) { debug_printf("http header:\n%s\n", buffer); return SR_ERROR_NO_HTTP_HEADER; } if (socket_handle->closed) return SR_ERROR_SOCKET_CLOSED; /* GCS: This patch is too restrictive. */ #if defined (commentout) //look for the end of the icy-header if (!strstr(buffer, "icy-") && !strstr(buffer,"ice-")) continue; #endif t = buffer + (i > 3 ? i - 3: i); if (strncmp(t, "\r\n\r\n", 4) == 0) break; if (strncmp(t, "\n\0\r\n", 4) == 0) // wtf? live365 seems to do this break; } if (i == size) { debug_printf("http header:\n%s\n", buffer); return SR_ERROR_NO_HTTP_HEADER; } buffer[i] = '\0'; #ifdef WIN32 timeout.tv_sec = DEFAULT_TIMEOUT*1000; if (setsockopt(socket_handle->s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) return SR_ERROR_CANT_SET_SOCKET_OPTIONS; #endif return SR_SUCCESS; } int socklib_recvall (HSOCKET *socket_handle, char* buffer, int size, int timeout) { int ret = 0, read = 0; int sock; fd_set fds; struct timeval tv; sock = socket_handle->s; FD_ZERO(&fds); while(size) { if (socket_handle->closed) return SR_ERROR_SOCKET_CLOSED; if (timeout > 0) { /* Wait up to 'timeout' seconds for data on socket to be ready for read */ FD_SET(sock, &fds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = select(sock + 1, &fds, NULL, NULL, &tv); if (ret == SOCKET_ERROR) return SR_ERROR_SELECT_FAILED; if (ret != 1) return SR_ERROR_TIMEOUT; } ret = recv(socket_handle->s, &buffer[read], size, 0); debug_printf ("RECV req %5d bytes, got %5d bytes\n", size, ret); if (ret == SOCKET_ERROR) { debug_printf ("RECV failed, errno = %d\n", errno); debug_printf ("Err = %s\n",strerror(errno)); return SR_ERROR_RECV_FAILED; } /* Got zero bytes on blocking read. For unix this is an orderly shutdown. */ if (ret == 0) { debug_printf ("recv received zero bytes!\n"); break; } read += ret; size -= ret; } return read; } int socklib_sendall (HSOCKET *socket_handle, char* buffer, int size) { int ret = 0, sent = 0; while(size) { if (socket_handle->closed) return SR_ERROR_SOCKET_CLOSED; ret = send(socket_handle->s, &buffer[sent], size, 0); if (ret == SOCKET_ERROR) return SR_ERROR_SEND_FAILED; if (ret == 0) break; sent += ret; size -= ret; } return sent; }