summaryrefslogtreecommitdiffstats
path: root/soxwrap/mksocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'soxwrap/mksocket.c')
-rw-r--r--soxwrap/mksocket.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/soxwrap/mksocket.c b/soxwrap/mksocket.c
new file mode 100644
index 0000000..f375917
--- /dev/null
+++ b/soxwrap/mksocket.c
@@ -0,0 +1,426 @@
+/*
+** Copyright 2004-2009 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include "mksocket.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "soxwrap.h"
+
+
+#define MKS_USEAFINET4 1
+#define MKS_ERROK 2
+
+#if HAVE_SOXWRAP_IPV6
+typedef struct in6_addr NET_ADDR;
+typedef struct sockaddr_in6 NET_SOCKADDR;
+typedef struct sockaddr_storage NET_NETADDR;
+#define NET_ADDRANY in6addr_any
+#else
+typedef struct in_addr NET_ADDR;
+typedef struct sockaddr_in NET_SOCKADDR;
+typedef struct sockaddr NET_NETADDR;
+
+extern struct in_addr rfc1035_addr_any;
+#define NET_ADDRANY rfc1035_addr_any
+#endif
+
+struct recycle_info {
+ const struct sockaddr *sin;
+ socklen_t sin_len;
+ int found_socket;
+};
+
+/*
+** If caller already has a socket listening on this address, recycle it.
+*/
+
+static int try_recycle_socket(int fd, void *voidarg)
+{
+ struct recycle_info *ri=(struct recycle_info *)voidarg;
+
+ union {
+ SOCKADDR_STORAGE ss;
+ struct sockaddr_in sin;
+#if HAVE_SOXWRAP_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+ } sa;
+
+ socklen_t salen=sizeof(sa);
+
+ if (getsockname(fd, (struct sockaddr *)&sa, &salen) < 0)
+ return 0;
+
+ if (ri->sin->sa_family != sa.ss.SS_family)
+ return 0;
+
+ switch (ri->sin->sa_family) {
+ case AF_INET:
+ {
+ struct sockaddr_in ri_sin;
+
+ memcpy(&ri_sin, ri->sin, sizeof(ri_sin));
+
+ if (ri_sin.sin_addr.s_addr != sa.sin.sin_addr.s_addr
+ || ri_sin.sin_port != sa.sin.sin_port)
+ return 0;
+ }
+ break;
+
+#if HAVE_SOXWRAP_IPV6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 ri_sin;
+
+ memcpy(&ri_sin, ri->sin, sizeof(ri_sin));
+
+ if (memcmp(&ri_sin.sin6_addr, &sa.sin6.sin6_addr,
+ sizeof(sa.sin6.sin6_addr)) ||
+ ri_sin.sin6_port != sa.sin6.sin6_port)
+ return 0;
+ }
+ break;
+#endif
+ default:
+ return 0;
+ }
+ ri->found_socket=fd;
+ return 1;
+}
+
+static int mksocket2(const char *ipaddrarg, /* Host/IP address */
+ const char *servname, /* Service/port */
+ int socktype, /* SOCKS_STREAM */
+ int flags,
+ int (*recycle_fd_func)
+ ( int(*)(int, void *), void *, void *),
+ void *voidarg)
+{
+ struct servent *servptr;
+ int port;
+ int fd;
+ struct recycle_info ri;
+ const struct sockaddr *sinaddr;
+ int sinaddrlen;
+
+#if HAVE_SOXWRAP_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+
+ struct sockaddr_in sin4;
+
+ int af;
+
+ servptr=getservbyname(servname, "tcp");
+ if (servptr)
+ port=servptr->s_port;
+ else
+ {
+ port=atoi(servname);
+ if (port <= 0 || port > 65535)
+ {
+ fprintf(stderr, "Invalid port: %s\n", servname);
+ return (-1);
+ }
+ port=htons(port);
+ }
+
+ /* Create an IPv6 or an IPv4 socket */
+
+#if HAVE_SOXWRAP_IPV6
+ if (flags & MKS_USEAFINET4)
+ {
+ fd=sox_socket(PF_INET, socktype, 0);
+ af=AF_INET;
+ }
+ else
+#endif
+ {
+#if HAVE_SOXWRAP_IPV6
+
+ if ((fd=sox_socket(PF_INET6, socktype, 0)) >= 0)
+ af=AF_INET6;
+ else
+#endif
+ {
+ af=AF_INET;
+ fd=sox_socket(PF_INET, socktype, 0);
+ }
+ }
+
+ if (fd < 0)
+ return (-1);
+
+ /* Figure out what to bind based on what socket we created */
+
+ if (ipaddrarg && strcmp(ipaddrarg, "0"))
+ {
+
+#if HAVE_SOXWRAP_IPV6
+ if (af == AF_INET6)
+ {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family=af;
+ if (inet_pton(af, ipaddrarg, &sin6.sin6_addr) <= 0)
+ {
+ errno=EINVAL;
+ close(fd);
+ return -1;
+ }
+ sin6.sin6_port=port;
+
+ sinaddr=(const struct sockaddr *)&sin6;
+ sinaddrlen=sizeof(sin6);
+ }
+ else
+#endif
+ if (af == AF_INET)
+ {
+ memset(&sin4, 0, sizeof(sin4));
+ sin4.sin_family=AF_INET;
+ if (inet_aton(ipaddrarg, &sin4.sin_addr) == 0)
+ {
+ errno=EINVAL;
+ close(fd);
+ return -1;
+ }
+ sin4.sin_port=port;
+ sinaddr=(const struct sockaddr *)&sin4;
+ sinaddrlen=sizeof(sin4);
+ }
+ else
+ {
+ errno=EAFNOSUPPORT;
+ close(fd);
+ return (-1);
+ }
+ }
+ else /* Bind default address */
+ {
+#if HAVE_SOXWRAP_IPV6
+ if (af == AF_INET6)
+ {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family=AF_INET6;
+ sin6.sin6_addr=in6addr_any;
+ sin6.sin6_port=port;
+ sinaddr=(const struct sockaddr *)&sin6;
+ sinaddrlen=sizeof(sin6);
+ }
+ else
+#endif
+ if (af == AF_INET)
+ {
+ sin4.sin_family=AF_INET;
+ sin4.sin_addr.s_addr=INADDR_ANY;
+ sin4.sin_port=port;
+ sinaddr=(const struct sockaddr *)&sin4;
+ sinaddrlen=sizeof(sin4);
+ }
+ else
+ {
+ errno=EAFNOSUPPORT;
+ close(fd);
+ return (-1);
+ }
+ }
+
+ ri.found_socket= -1;
+ ri.sin=sinaddr;
+ ri.sin_len=sinaddrlen;
+
+ if (recycle_fd_func &&
+ (*recycle_fd_func)(&try_recycle_socket, &ri, voidarg) > 0 &&
+ ri.found_socket >= 0)
+ {
+ close(fd);
+ return dup(ri.found_socket);
+ }
+
+ {
+ int dummy=1;
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&dummy, sizeof(dummy));
+ }
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC))
+ {
+ close(fd);
+ return (-1);
+ }
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK))
+ {
+ close(fd);
+ return (-1);
+ }
+ {
+ int dummy=1;
+
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *)&dummy, sizeof(dummy));
+ }
+
+ if (sox_bind(fd, sinaddr, sinaddrlen) < 0)
+ {
+ if (flags & MKS_ERROK)
+ {
+ close(fd);
+ return (-2);
+ }
+ close(fd);
+ return (-1);
+ }
+
+ if (sox_listen(fd,
+#ifdef SOMAXCONN
+ SOMAXCONN
+#else
+ 5
+#endif
+ ))
+ {
+ if (flags && MKS_ERROK)
+ {
+ close(fd);
+ return (-2);
+ }
+ close(fd);
+ return (-1);
+ }
+ return (fd);
+}
+
+
+int mksocket(const char *address,
+ const char *service,
+ int socktype,
+ int *fd1,
+ int *fd2,
+ int recycle_fd_func( int(*)(int, void *), void *, void *),
+ void *voidarg)
+{
+ int fd_flag=0;
+
+ *fd1= -1;
+ *fd2= -1;
+
+ if (address && strcmp(address, "0"))
+ {
+#if HAVE_INET_PTON
+ SOCKADDR_STORAGE ina;
+
+ if (inet_pton(AF_INET, address, &ina) > 0)
+ fd_flag=MKS_USEAFINET4;
+#else
+ struct in_addr ina;
+
+ if (inet_aton(address, &ina))
+ fd_flag=MKS_USEAFINET4;
+#endif
+
+ }
+
+
+ *fd1=mksocket2(address, service, socktype, fd_flag,
+ recycle_fd_func, voidarg);
+
+ /* BSD requires both an IPv6 and an IPv4 socket */
+
+#if HAVE_SOXWRAP_IPV6
+
+ if (address == 0 || strcmp(address, "0") == 0)
+ {
+ int fd=mksocket2(address, service, socktype,
+ (MKS_USEAFINET4|MKS_ERROK),
+ recycle_fd_func, voidarg);
+
+ if (fd < 0)
+ {
+ if (fd != -2)
+ {
+ if (*fd1 >= 0)
+ close(*fd1);
+ return -1;
+ }
+ }
+
+ *fd2=fd;
+ }
+#endif
+ if (*fd1 < 0 && *fd2 < 0)
+ return -1;
+
+ if (*fd1 < 0)
+ {
+ *fd1=*fd2;
+ *fd2=-1;
+ }
+ return 0;
+}
+
+#if HAVE_SYS_POLL_H
+
+#else
+
+int poll(struct pollfd *pfd, unsigned int n, int timeout)
+{
+ fd_set r, w, e;
+ int maxfd=-1;
+ unsigned int i;
+ struct timeval tv;
+ int cnt=0;
+
+ FD_ZERO(&r);
+ FD_ZERO(&w);
+ FD_ZERO(&e);
+
+ for (i=0; i<n; i++)
+ {
+ if (pfd[i].fd >= maxfd)
+ maxfd=pfd[i].fd;
+
+ pfd[i].revents=0;
+ if (pfd[i].events & (POLLIN|POLLPRI))
+ FD_SET(pfd[i].fd, &r);
+ if (pfd[i].events & POLLOUT)
+ FD_SET(pfd[i].fd, &w);
+ if (pfd[i].events & POLLPRI)
+ FD_SET(pfd[i].fd, &e);
+ }
+
+ tv.tv_sec=timeout/1000;
+ tv.tv_usec=(timeout % 1000) * 1000;
+
+ if (select(maxfd+1, &r, &w, &e, timeout < 0 ?NULL:&tv) < 0)
+ return -1;
+
+ for (i=0; i<n; i++)
+ {
+ if (FD_ISSET(pfd[i].fd, &r))
+ pfd[i].revents |= POLLIN;
+ if (FD_ISSET(pfd[i].fd, &w))
+ pfd[i].revents |= POLLOUT;
+ if (FD_ISSET(pfd[i].fd, &e))
+ pfd[i].revents |= POLLIN|POLLHUP;
+
+ if (pfd[i].revents)
+ ++cnt;
+ }
+
+ return cnt;
+}
+
+#endif