#include "network.h"
#include "tree234.h"
+/* Solaris needs <sys/sockio.h> for SIOCATMARK. */
+#ifndef SIOCATMARK
+#include <sys/sockio.h>
+#endif
+
#ifndef X11_UNIX_PATH
# define X11_UNIX_PATH "/tmp/.X11-unix/X"
#endif
+/*
+ * We used to typedef struct Socket_tag *Socket.
+ *
+ * Since we have made the networking abstraction slightly more
+ * abstract, Socket no longer means a tcp socket (it could mean
+ * an ssl socket). So now we must use Actual_Socket when we know
+ * we are talking about a tcp socket.
+ */
+typedef struct Socket_tag *Actual_Socket;
+
struct Socket_tag {
struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
int nodelay, keepalive; /* for connect()-type sockets */
int privport, port; /* and again */
SockAddr addr;
+ /*
+ * We sometimes need pairs of Socket structures to be linked:
+ * if we are listening on the same IPv6 and v4 port, for
+ * example. So here we define `parent' and `child' pointers to
+ * track this link.
+ */
+ Actual_Socket parent, child;
};
-/*
- * We used to typedef struct Socket_tag *Socket.
- *
- * Since we have made the networking abstraction slightly more
- * abstract, Socket no longer means a tcp socket (it could mean
- * an ssl socket). So now we must use Actual_Socket when we know
- * we are talking about a tcp socket.
- */
-typedef struct Socket_tag *Actual_Socket;
-
struct SockAddr_tag {
const char *error;
/*
return -1;
if (as > bs)
return +1;
+ if (a < b)
+ return -1;
+ if (a > b)
+ return +1;
return 0;
}
static int sk_nextaddr(SockAddr addr)
{
#ifndef NO_IPV6
- if (addr->ai->ai_next) {
+ if (addr->ai && addr->ai->ai_next) {
addr->ai = addr->ai->ai_next;
addr->family = addr->ai->ai_family;
return TRUE;
ret->pending_error = 0;
ret->oobpending = FALSE;
ret->listener = 0;
+ ret->parent = ret->child = NULL;
ret->addr = NULL;
ret->connected = 1;
short localport;
int fl, salen;
+ /*
+ * Remove the socket from the tree before we overwrite its
+ * internal socket id, because that forms part of the tree's
+ * sorting criterion. We'll add it back before exiting this
+ * function, whether we changed anything or not.
+ */
+ del234(sktree, sock);
+
if (sock->s >= 0)
close(sock->s);
goto ret;
}
+ cloexec(s);
+
if (sock->oobinline) {
int b = TRUE;
setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));
default:
assert(0 && "unknown address family");
+ exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
}
fl = fcntl(s, F_GETFL);
}
uxsel_tell(sock);
- add234(sktree, sock);
ret:
+
+ /*
+ * No matter what happened, put the socket back in the tree.
+ */
+ add234(sktree, sock);
+
if (err)
plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);
return err;
ret->frozen_readable = 0;
ret->localhost_only = 0; /* unused, but best init anyway */
ret->pending_error = 0;
+ ret->parent = ret->child = NULL;
ret->oobpending = FALSE;
ret->listener = 0;
ret->addr = addr;
return (Socket) ret;
}
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int address_family)
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int orig_address_family)
{
int s;
#ifndef NO_IPV6
struct sockaddr_in a;
Actual_Socket ret;
int retcode;
+ int address_family;
int on = 1;
/*
ret->frozen_readable = 0;
ret->localhost_only = local_host_only;
ret->pending_error = 0;
+ ret->parent = ret->child = NULL;
ret->oobpending = FALSE;
ret->listener = 1;
ret->addr = NULL;
* Translate address_family from platform-independent constants
* into local reality.
*/
- address_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
- address_family == ADDRTYPE_IPV6 ? AF_INET6 : AF_UNSPEC);
+ address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :
+#ifndef NO_IPV6
+ orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :
+#endif
+ AF_UNSPEC);
#ifndef NO_IPV6
/* Let's default to IPv6.
*/
s = socket(address_family, SOCK_STREAM, 0);
+#ifndef NO_IPV6
/* If the host doesn't support IPv6 try fallback to IPv4. */
if (s < 0 && address_family == AF_INET6) {
address_family = AF_INET;
s = socket(address_family, SOCK_STREAM, 0);
}
+#endif
if (s < 0) {
ret->error = strerror(errno);
return (Socket) ret;
}
+ cloexec(s);
+
ret->oobinline = 0;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
return (Socket) ret;
}
+#ifndef NO_IPV6
+ /*
+ * If we were given ADDRTYPE_UNSPEC, we must also create an
+ * IPv4 listening socket and link it to this one.
+ */
+ if (address_family == AF_INET6 && orig_address_family == ADDRTYPE_UNSPEC) {
+ Actual_Socket other;
+
+ other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,
+ local_host_only, ADDRTYPE_IPV4);
+
+ if (other) {
+ if (!other->error) {
+ other->parent = ret;
+ ret->child = other;
+ } else {
+ /* If we couldn't create a listening socket on IPv4 as well
+ * as IPv6, we must return an error overall. */
+ close(s);
+ sfree(ret);
+ return (Socket) other;
+ }
+ }
+ }
+#endif
+
ret->s = s;
uxsel_tell(ret);
{
Actual_Socket s = (Actual_Socket) sock;
+ if (s->child)
+ sk_tcp_close((Socket)s->child);
+
uxsel_del(s->s);
del234(sktree, s);
close(s->s);
#endif
socklen_t addrlen = sizeof(ss);
int t; /* socket of connection */
+ int fl;
memset(&ss, 0, addrlen);
t = accept(s->s, (struct sockaddr *)&ss, &addrlen);
break;
}
+ fl = fcntl(t, F_GETFL);
+ if (fl != -1)
+ fcntl(t, F_SETFL, fl | O_NONBLOCK);
+
if (s->localhost_only &&
!sockaddr_is_loopback((struct sockaddr *)&ss)) {
close(t); /* someone let nonlocal through?! */
return 0;
}
-SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname)
+SockAddr platform_get_x11_unix_address(const char *display, int displaynum,
+ char **canonicalname)
{
SockAddr ret = snew(struct SockAddr_tag);
int n;
memset(ret, 0, sizeof *ret);
ret->family = AF_UNIX;
- n = snprintf(ret->hostname, sizeof ret->hostname,
- "%s%d", X11_UNIX_PATH, displaynum);
+ /*
+ * Mac OS X Leopard uses an innovative X display naming
+ * convention in which the entire display name is the path to
+ * the Unix socket, including the trailing :0 which only
+ * _looks_ like a display number. Heuristically, I think
+ * detecting this by means of a leading slash ought to be
+ * adequate.
+ */
+ if (display[0] == '/') {
+ n = snprintf(ret->hostname, sizeof ret->hostname,
+ "%s", display);
+ } else {
+ n = snprintf(ret->hostname, sizeof ret->hostname,
+ "%s%d", X11_UNIX_PATH, displaynum);
+ }
if(n < 0)
ret->error = "snprintf failed";
else if(n >= sizeof ret->hostname)
else
*canonicalname = dupstr(ret->hostname);
#ifndef NO_IPV6
- ret->ais = NULL;
+ ret->ai = ret->ais = NULL;
#else
ret->addresses = NULL;
+ ret->curraddr = ret->naddresses = 0;
#endif
return ret;
}