#if defined(HAVE_CONFIG_H) #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #include #include #if defined(HAVE_UNIX_SOCKET) #if defined(HAVE_STDDEF_H) #include #endif #include #endif #if defined(HAVE_POLL) #include #else /* !HAVE_POLL */ #include #endif /* !HAVE_POLL */ #if defined(WIN32) #include #endif /* WIN32 */ #ifdef HAVE_SIGNAL #include #endif #include #include "stream.h" /* function declarations */ static IIIMF_status stream_socket_read( IIIMF_stream_private private, void *buf, size_t nbyte ); static IIIMF_status stream_socket_write( IIIMF_stream_private private, const void* buf, size_t nbyte ); /* enum and structure */ enum IIIMF_STREAM_SOCKET_FLAGS { IIIMF_STREAM_SOCKET_LISTEN, IIIMF_STREAM_SOCKET_OPEN }; typedef struct IIIMF_stream_socket_private IIIMF_stream_socket_private; struct IIIMF_stream_socket_private { int flags; int fd; int timeout; }; static IIIMF_stream_socket_private* create_sockpriv( int flags, int fd, int timeout ) { IIIMF_stream_socket_private *p; #ifdef HAVE_SO_NOSIGPIPE int optval = 1; #endif p = (IIIMF_stream_socket_private*) malloc(sizeof(*p)); if (!p) return NULL; p->timeout = timeout; p->flags = flags; p->fd = fd; #ifdef HAVE_SO_NOSIGPIPE /* need error handling ? */ setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof (int)); #endif return p; } static void delete_sockpriv( IIIMF_stream_socket_private *psockpriv ) { free(psockpriv); } #if defined(HAVE_UNIX_SOCKET) static IIIMF_status create_socket_stream_unix( const char *node, const char *service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; int r; size_t size; struct sockaddr_un sun_addr; fd = -1; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { return IIIMF_STATUS_STREAM; } sun_addr.sun_family = AF_UNIX; if ((NULL == service) || ('\0' == (*service))) { strncpy(sun_addr.sun_path, node, sizeof(sun_addr.sun_path)); } else { snprintf(sun_addr.sun_path, sizeof(sun_addr.sun_path), "%s/%s", node, service); } size = (offsetof(struct sockaddr_un, sun_path) + strlen (sun_addr.sun_path) + 1); r = connect(fd, (struct sockaddr *)(&sun_addr), size); if (r < 0) { (void)close(fd); return IIIMF_STATUS_STREAM; } fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_socket_private *sockpriv; sockpriv = create_sockpriv(IIIMF_STREAM_SOCKET_OPEN, fd, timeout); if (!sockpriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(stream_socket_read, stream_socket_write, sockpriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } #endif IIIMF_status iiimf_connect_socket_stream( const char * node, const char * service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; #if defined(HAVE_GETADDRINFO) int e; struct addrinfo * res; struct addrinfo * aip; struct addrinfo hints; #if defined(HAVE_UNIX_SOCKET) if (*node == '/') { return create_socket_stream_unix(node, service, timeout, stream_ret); } #endif fd = -1; (void)memset(&hints, 0, sizeof (hints)); hints.ai_flags = AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(node, service, &hints, &res); if (0 != e) { return -1; } for (aip = res; NULL != aip; aip = aip->ai_next) { fd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (-1 == fd) { break; } if (-1 == connect(fd, aip->ai_addr, aip->ai_addrlen)) { (void)close(fd); fd = -1; continue; } else { break; } } freeaddrinfo(res); if (-1 == fd) return IIIMF_STATUS_STREAM; #else /* !HAVE_GETADDRINFO */ #if defined(WIN32) unsigned short int port_number; #else /* !WIN32 */ int port_number; #endif /* !WIN32 */ int r; int optval; unsigned long in_addr; struct hostent * hostp; struct sockaddr_in addr; struct servent * srvp; #if defined(HAVE_UNIX_SOCKET) if (*node == '/') { return create_socket_stream_unix(node, service, timeout, stream_ret); } #endif #if defined(WIN32) start_winsock(1, 1); #endif fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0 ) return IIIMF_STATUS_STREAM; port_number = 0; (void)memset((char *)(&addr), 0, sizeof (addr)); addr.sin_family = PF_INET; if (0 < strlen(service)) { srvp = getservbyname(service, "tcp"); if (NULL != srvp) { port_number = srvp->s_port; } (void)endservent(); if (0 == port_number) { port_number = atoi(service); } } if (0 == port_number) { port_number = 9010; } addr.sin_port = htons(port_number); in_addr = inet_addr(node); if ((unsigned long)(-1) != in_addr) { (void)memcpy(&addr.sin_addr, &in_addr, sizeof (in_addr)); } else { hostp = gethostbyname(node); if (NULL == hostp) { #if defined(WIN32) (void)sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } if (0 < hostp->h_length) { (void)memcpy(&addr.sin_addr, hostp->h_addr, hostp->h_length); } else { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } } r = connect(fd, (struct sockaddr *)(&addr), sizeof (addr)); #if defined(WIN32) if (SOCKET_ERROR == r) { sock_close(fd); return IIIMF_STATUS_STREAM; } #else /* !WIN32 */ if (r < 0) { (void)close(fd); return IIIMF_STATUS_STREAM; } #endif /* !WIN32 */ #endif /* !HAVE_GETADDRINFO */ fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_socket_private *sockpriv; sockpriv = create_sockpriv(IIIMF_STREAM_SOCKET_OPEN, fd, timeout); if (!sockpriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(stream_socket_read, stream_socket_write, sockpriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_listen_socket_stream( const char * node, const char * service, int timeout, IIIMF_stream ** stream_ret ) { int fd; int fd_flag; int optval; int r = 0; #if defined(HAVE_GETADDRINFO) int e; struct addrinfo * a; struct addrinfo * aip; struct addrinfo hints; fd = -1; (void)memset(&hints, 0, sizeof (hints)); hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(NULL, service, &hints, &aip); if (0 != e) return IIIMF_STATUS_STREAM; for (a = aip; NULL != a; a = a->ai_next) { fd = socket(a->ai_family, a->ai_socktype, a->ai_protocol); if (-1 != fd) { r = bind(fd, aip->ai_addr, sizeof (struct sockaddr)); if (-1 != r) break; (void)close(fd); fd = -1; } } freeaddrinfo(aip); if ((-1 == fd) || (-1 == r)) return IIIMF_STATUS_STREAM; r = listen(fd, 5); if (-1 == r) return IIIMF_STATUS_STREAM; #else /* !HAVE_GETADDRINFO */ #if defined(WIN32) unsigned short int port_number; #else /* !WIN32 */ int port_number; #endif /* !WIN32 */ #if defined(PF_INET6) && defined(ENABLE_IPV6) struct sockaddr_in6 addr; #else /* !PF_INET6 && ENABLE_IPV6 */ struct sockaddr_in addr; #endif /* !PF_INET6 && ENABLE_IPV6 */ struct servent * srvp; #if defined(WIN32) start_winsock(1, 1); #endif #if defined(PF_INET6) && defined(ENABLE_IPV6) fd = socket(PF_INET6, SOCK_STREAM, 0); #else /* !PF_INET6 && ENABLE_IPV6 */ fd = socket(PF_INET, SOCK_STREAM, 0); #endif /* !PF_INET6 && ENABLE_IPV6 */ if (fd < 0 ) return IIIMF_STATUS_STREAM; port_number = 0; if (0 < strlen(service)) { srvp = getservbyname(service, "tcp"); if (NULL != srvp) { port_number = srvp->s_port; } (void)endservent(); if (0 == port_number) { port_number = atoi(service); } } if (0 == port_number) { port_number = 9010; } (void)memset((char *)(&addr), 0, sizeof (addr)); #if defined(PF_INET6) && defined(ENABLE_IPV6) addr.sin6_family = PF_INET6; addr.sin6_flowinfo = 0; addr.sin6_port = htons(port_number); addr.sin6_addr = in6addr_any; #else /* !PF_INET6 && ENABLE_IPV6 */ addr.sin_family = PF_INET; addr.sin_port = htons(port_number); #endif /* !PF_INET6 && ENABLE_IPV6 */ if ((bind(fd, (struct sockaddr *)(&addr), sizeof (addr)) < 0) || (listen(fd, 5) < 0) ) { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } #endif /* !HAVE_GETADDRINFO */ optval = 1; r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int)); if (-1 == r) { #if defined(WIN32) sock_close(fd); #else /* !WIN32 */ (void)close(fd); #endif /* !WIN32 */ return IIIMF_STATUS_STREAM; } fd_flag = fcntl(fd, F_GETFD); fd_flag |= FD_CLOEXEC; (void)fcntl(fd, F_SETFD, fd_flag); { IIIMF_status st; IIIMF_stream *stream; IIIMF_stream_socket_private *sockpriv; sockpriv = create_sockpriv(IIIMF_STREAM_SOCKET_LISTEN, fd, timeout); if (!sockpriv) { close(fd); return IIIMF_STATUS_MALLOC; } st = iiimf_create_stream(NULL, NULL, sockpriv, timeout, &stream); if (st != IIIMF_STATUS_SUCCESS) return st; *stream_ret = stream; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_accept_socket_stream( IIIMF_stream * stream, IIIMF_stream ** stream_ret ) { IIIMF_stream_socket_private *sockpriv = (IIIMF_stream_socket_private*) stream->private_data; IIIMF_status status; struct sockaddr address; socklen_t address_len; int fd; #if defined(HAVE_POLL) struct pollfd fds[1]; int poll_ret; #else /* !HAVE_POLL */ fd_set fdvar; struct timeval timeout; int select_ret; timeout.tv_sec = sockpriv->timeout / 1000; timeout.tv_usec = sockpriv->timeout % 1000; #endif /* !HAVE_POLL */ if (sockpriv->flags != IIIMF_STREAM_SOCKET_LISTEN) return IIIMF_STATUS_ARGUMENT; if (0 <= sockpriv->timeout) { #if defined(HAVE_POLL) fds[0].fd = sockpriv->fd; fds[0].events = POLLIN; poll_ret = poll(fds, 1, sockpriv->timeout); if (0 == poll_ret) { return IIIMF_STATUS_TIMEOUT; } else if (-1 == poll_ret) { return IIIMF_STATUS_STREAM; } #else /* !HAVE_POLL */ do { FD_ZERO(&fdvar); FD_SET(sockpriv->fd, &fdvar); select_ret = select(sockpriv->fd + 1, NULL, &fdvar, NULL, &timeout); } while ((-1 == select_ret) && (EINTR == errno)); if (-1 == select_ret) return IIIMF_STATUS_TIMEOUT; #endif /* !HAVE_POLL */ } address_len = (sizeof (address)); fd = accept(sockpriv->fd, &address, &address_len); if (-1 == fd) return IIIMF_STATUS_STREAM; { IIIMF_stream *stream_new; IIIMF_stream_socket_private *sockpriv_new; sockpriv_new = create_sockpriv(IIIMF_STREAM_SOCKET_OPEN, fd, sockpriv->timeout); if (!sockpriv_new) { close(fd); return IIIMF_STATUS_MALLOC; } status = iiimf_create_stream(stream_socket_read, stream_socket_write, sockpriv_new, sockpriv->timeout, &stream_new); if (status != IIIMF_STATUS_SUCCESS) return status; *stream_ret = stream_new; } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_delete_socket_stream( IIIMF_stream * stream ) { IIIMF_stream_socket_private *sockpriv = (IIIMF_stream_socket_private*) stream->private_data; if (sockpriv->fd > 0) close(sockpriv->fd); delete_sockpriv(sockpriv); iiimf_stream_delete(stream); return IIIMF_STATUS_SUCCESS; } static IIIMF_status stream_socket_read( IIIMF_stream_private private, void * buf, size_t nbyte) { IIIMF_stream_socket_private *sockpriv = (IIIMF_stream_socket_private*) private; char * p; ssize_t n; ssize_t r; #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) RETSIGTYPE (*old_sighandler)(int); #endif #if defined(HAVE_POLL) struct pollfd fds[1]; int poll_ret; #else /* !HAVE_POLL */ fd_set fdvar; struct timeval timeout; int select_ret; timeout.tv_sec = sockpriv->timeout / 1000; timeout.tv_usec = sockpriv->timeout % 1000; #endif /* !HAVE_POLL */ #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) /* block SIGPIPE */ old_sighandler = signal(SIGPIPE, SIG_IGN); #endif if (sockpriv->fd < 0) return IIIMF_STATUS_STREAM_RECEIVE; for (p = buf, n = nbyte; 0 < n; p += r, n -= r) { if (0 <= sockpriv->timeout) { #if defined(HAVE_POLL) fds[0].fd = sockpriv->fd; fds[0].events = POLLIN; poll_ret = poll(fds, 1, sockpriv->timeout); if (0 == poll_ret) { return IIIMF_STATUS_TIMEOUT; } else if (-1 == poll_ret) { return IIIMF_STATUS_STREAM; } #else /* !HAVE_POLL */ do { FD_ZERO(&fdvar); FD_SET(sockpriv->fd, &fdvar); select_ret = select(sockpriv->fd + 1, NULL, &fdvar, NULL, &timeout); } while ((-1 == select_ret) && (EINTR == errno)); #endif /* !HAVE_POLL */ } #ifdef HAVE_MSG_NOSIGNAL r = recv(sockpriv->fd, p, n, MSG_NOSIGNAL); #else r = recv(sockpriv->fd, p, n, 0); #endif if (r == 0) { return IIIMF_STATUS_CONNECTION_CLOSED; } if (r < 0) { if (EINTR == errno) { continue; } else { #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) /* restore old handler */ signal(SIGPIPE, old_sighandler); #endif if (EPIPE == errno) { sockpriv->fd = -1; return IIIMF_STATUS_CONNECTION_CLOSED; } return IIIMF_STATUS_STREAM_RECEIVE; } } } #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) /* restore old handler */ signal(SIGPIPE, old_sighandler); #endif return IIIMF_STATUS_SUCCESS; } static IIIMF_status stream_socket_write( IIIMF_stream_private private, const void * buf, size_t nbyte) { IIIMF_stream_socket_private *sockpriv = (IIIMF_stream_socket_private*) private; const char * p; ssize_t n; ssize_t r; #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) RETSIGTYPE (*old_sighandler)(int); /* block SIGPIPE */ old_sighandler = signal(SIGPIPE, SIG_IGN); #endif if (sockpriv->fd < 0) return IIIMF_STATUS_STREAM_SEND; for (p = buf, n = nbyte; 0 < n; p += r, n -= r) { #ifdef HAVE_MSG_NOSIGNAL r = send(sockpriv->fd, p, n, MSG_NOSIGNAL); #else r = send(sockpriv->fd, p, n, 0); #endif if (r < 0) { #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) /* restore old handler */ signal(SIGPIPE, old_sighandler); #endif if (EPIPE == errno) { sockpriv->fd = -1; return IIIMF_STATUS_CONNECTION_CLOSED; } return IIIMF_STATUS_STREAM_SEND; } } #if !defined(HAVE_SO_NOSIGPIPE) && !defined(HAVE_MSG_NOSIGNAL) && defined(HAVE_SIGNAL) /* restore old handler */ signal(SIGPIPE, old_sighandler); #endif return IIIMF_STATUS_SUCCESS; } #if defined(WIN32) static int start_winsock(int major, int minor) { WORD wVersionRequested; WSADATA wsaData; int r; wVersionRequested = MAKEWORD(major, minor); r = WSAStartup(wVersionRequested, &wsaData); if (0 != r) return -1; #ifdef CHK_WSOCK_VERSION if ((1 != LOBYTE(wnsaData.wVersion)) || (1 != HIBYTE(wsaData.wVersion))) { /* wrong version */ return 1; } #endif /* CHK_WSOCK_VERSION */ return 0; } static int end_winsock() { return WSACleanup(); } static int sock_close(int fd) { int r; if (INVALID_SOCKET != fd) { r = closesocket(fd); end_winsock(); fd = INVALID_SOCKET; return r; } return 0; } #endif /* WIN32 */ /* Local Variables: */ /* c-file-style: "iiim-project" */ /* End: */