#include "network.h"
#include "tree234.h"
+#define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET)
+
struct Socket_tag {
struct socket_function_table *fn;
/* the above variable absolutely *must* be the first in this structure */
struct SockAddr_tag {
char *error;
- /* address family this belongs to, AF_INET for IPv4, AF_INET6 for IPv6. */
+ /*
+ * Which address family this address belongs to. AF_INET for
+ * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
+ * resolution has not been done and a simple host name is held
+ * in this SockAddr structure.
+ */
int family;
unsigned long address; /* Address IPv4 style. */
#ifdef IPV6
struct addrinfo *ai; /* Address IPv6 style. */
#endif
+ char hostname[512]; /* Store an unresolved host name. */
};
static tree234 *sktree;
char *error_string(int error)
{
- switch (error) {
- case EACCES:
- return "Network error: Permission denied";
- case EADDRINUSE:
- return "Network error: Address already in use";
- case EADDRNOTAVAIL:
- return "Network error: Cannot assign requested address";
- case EAFNOSUPPORT:
- return
- "Network error: Address family not supported by protocol family";
- case EALREADY:
- return "Network error: Operation already in progress";
- case ECONNABORTED:
- return "Network error: Software caused connection abort";
- case ECONNREFUSED:
- return "Network error: Connection refused";
- case ECONNRESET:
- return "Network error: Connection reset by peer";
- case EDESTADDRREQ:
- return "Network error: Destination address required";
- case EFAULT:
- return "Network error: Bad address";
- case EHOSTDOWN:
- return "Network error: Host is down";
- case EHOSTUNREACH:
- return "Network error: No route to host";
- case EINPROGRESS:
- return "Network error: Operation now in progress";
- case EINTR:
- return "Network error: Interrupted function call";
- case EINVAL:
- return "Network error: Invalid argument";
- case EISCONN:
- return "Network error: Socket is already connected";
- case EMFILE:
- return "Network error: Too many open files";
- case EMSGSIZE:
- return "Network error: Message too long";
- case ENETDOWN:
- return "Network error: Network is down";
- case ENETRESET:
- return "Network error: Network dropped connection on reset";
- case ENETUNREACH:
- return "Network error: Network is unreachable";
- case ENOBUFS:
- return "Network error: No buffer space available";
- case ENOPROTOOPT:
- return "Network error: Bad protocol option";
- case ENOTCONN:
- return "Network error: Socket is not connected";
- case ENOTSOCK:
- return "Network error: Socket operation on non-socket";
- case EOPNOTSUPP:
- return "Network error: Operation not supported";
- case EPFNOSUPPORT:
- return "Network error: Protocol family not supported";
- case EPROTONOSUPPORT:
- return "Network error: Protocol not supported";
- case EPROTOTYPE:
- return "Network error: Protocol wrong type for socket";
- case ESHUTDOWN:
- return "Network error: Cannot send after socket shutdown";
- case ESOCKTNOSUPPORT:
- return "Network error: Socket type not supported";
- case ETIMEDOUT:
- return "Network error: Connection timed out";
- case EWOULDBLOCK:
- return "Network error: Resource temporarily unavailable";
- default:
- return "Unknown network error";
- }
+ return strerror(error);
}
SockAddr sk_namelookup(char *host, char **canonicalname)
return ret;
}
+SockAddr sk_nonamelookup(char *host)
+{
+ SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
+ ret->family = AF_UNSPEC;
+ strncpy(ret->hostname, host, lenof(ret->hostname));
+ ret->hostname[lenof(ret->hostname)-1] = '\0';
+ return ret;
+}
+
void sk_getaddr(SockAddr addr, char *buf, int buflen)
{
#ifdef IPV6
- if (addr->family == AF_INET) {
+ if (addr->family == AF_INET6) {
+ FIXME; /* I don't know how to get a text form of an IPv6 address. */
+ } else
#endif
+ if (addr->family == AF_INET) {
struct in_addr a;
a.s_addr = htonl(addr->address);
strncpy(buf, inet_ntoa(a), buflen);
-#ifdef IPV6
+ buf[buflen-1] = '\0';
} else {
- FIXME; /* I don't know how to get a text form of an IPv6 address. */
+ assert(addr->family == AF_UNSPEC);
+ strncpy(buf, addr->hostname, buflen);
+ buf[buflen-1] = '\0';
}
+}
+
+int sk_hostname_is_local(char *name)
+{
+ return !strcmp(name, "localhost");
+}
+
+int sk_address_is_local(SockAddr addr)
+{
+#ifdef IPV6
+ if (addr->family == AF_INET6) {
+ FIXME; /* someone who can compile for IPV6 had better do this bit */
+ } else
#endif
+ if (addr->family == AF_INET) {
+ struct in_addr a;
+ a.s_addr = htonl(addr->address);
+ return ipv4_is_loopback(a);
+ } else {
+ assert(addr->family == AF_UNSPEC);
+ return 0; /* we don't know; assume not */
+ }
}
int sk_addrtype(SockAddr addr)
{
- return (addr->family == AF_INET ? ADDRTYPE_IPV4 : ADDRTYPE_IPV6);
+ return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
+#ifdef IPV6
+ addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
+#endif
+ ADDRTYPE_NAME);
}
void sk_addrcopy(SockAddr addr, char *buf)
{
+ assert(addr->family != AF_UNSPEC);
#ifdef IPV6
- if (addr->family == AF_INET) {
+ if (addr->family == AF_INET6) {
+ memcpy(buf, (char*) addr->ai, 16);
+ } else
#endif
+ if (addr->family == AF_INET) {
struct in_addr a;
a.s_addr = htonl(addr->address);
memcpy(buf, (char*) &a.s_addr, 4);
-#ifdef IPV6
- } else {
- memcpy(buf, (char*) addr->ai, 16);
}
-#endif
}
void sk_addr_free(SockAddr addr)
/*
* Open socket.
*/
+ assert(addr->family != AF_UNSPEC);
s = socket(addr->family, SOCK_STREAM, 0);
ret->s = s;
return (Socket) ret;
}
-Socket sk_newlistener(int port, Plug plug, int local_host_only)
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
{
static struct socket_function_table fn_table = {
sk_tcp_plug,
if (addr->family == AF_INET6) {
memset(&a6, 0, sizeof(a6));
a6.sin6_family = AF_INET6;
+ /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't
+ * know how to do it. :-) */
if (local_host_only)
a6.sin6_addr = in6addr_loopback;
else
} else
#endif
{
+ int got_addr = 0;
a.sin_family = AF_INET;
- if (local_host_only)
- a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- else
- a.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ /*
+ * Bind to source address. First try an explicitly
+ * specified one...
+ */
+ if (srcaddr) {
+ a.sin_addr.s_addr = inet_addr(srcaddr);
+ if (a.sin_addr.s_addr != INADDR_NONE) {
+ /* Override localhost_only with specified listen addr. */
+ ret->localhost_only = ipv4_is_loopback(a.sin_addr);
+ got_addr = 1;
+ }
+ }
+
+ /*
+ * ... and failing that, go with one of the standard ones.
+ */
+ if (!got_addr) {
+ if (local_host_only)
+ a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ else
+ a.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+
a.sin_port = htons((short)port);
}
#ifdef IPV6
break;
}
- if (s->localhost_only &&
- ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) {
+ if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) {
close(t); /* someone let nonlocal through?! */
} else if (plug_accepting(s->plug, (void*)t)) {
close(t); /* denied or error */