#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 */
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)
#endif
}
+int sk_hostname_is_local(char *name)
+{
+ return !strcmp(name, "localhost");
+}
+
+int sk_address_is_local(SockAddr addr)
+{
+#ifdef IPV6
+ if (addr->family == AF_INET) {
+#endif
+ struct in_addr a;
+ a.s_addr = htonl(addr->address);
+ return ipv4_is_loopback(a);
+#ifdef IPV6
+ } else {
+ FIXME; /* someone who can compile for IPV6 had better do this bit */
+ }
+#endif
+}
+
int sk_addrtype(SockAddr addr)
{
return (addr->family == AF_INET ? ADDRTYPE_IPV4 : ADDRTYPE_IPV6);
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 */