summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.hw.in2
-rw-r--r--macros/neon.m45
-rw-r--r--src/ne_socket.c123
-rw-r--r--src/ne_socket.h18
-rw-r--r--test/socket.c106
5 files changed, 243 insertions, 11 deletions
diff --git a/config.hw.in b/config.hw.in
index b486918..31c80ec 100644
--- a/config.hw.in
+++ b/config.hw.in
@@ -73,6 +73,8 @@
#define in_addr_t unsigned int
#endif
+#define socklen_t int
+
#include <io.h>
#define read _read
diff --git a/macros/neon.m4 b/macros/neon.m4
index 3e11d12..47cd61e 100644
--- a/macros/neon.m4
+++ b/macros/neon.m4
@@ -679,7 +679,10 @@ else
])
fi
-AC_CHECK_TYPES(socklen_t,,,[
+AC_CHECK_TYPES(socklen_t,,
+/* Linux accept(2) says this should be size_t for SunOS 5... gah. */
+[AC_DEFINE([socklen_t], [int],
+ [Define if socklen_t is not available])],[
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
diff --git a/src/ne_socket.c b/src/ne_socket.c
index 898eba8..75f8f80 100644
--- a/src/ne_socket.c
+++ b/src/ne_socket.c
@@ -192,11 +192,15 @@ struct iofns {
int (*readable)(ne_socket *s, int n);
};
+static const ne_inet_addr dummy_laddr;
+
struct ne_socket_s {
int fd;
char error[200];
void *progress_ud;
int rdtimeout, cotimeout; /* timeouts */
+ const ne_inet_addr *laddr;
+ unsigned int lport;
const struct iofns *ops;
#ifdef NE_HAVE_SSL
ne_ssl_socket ssl;
@@ -1105,18 +1109,79 @@ ne_socket *ne_sock_create(void)
return sock;
}
+
+#ifdef USE_GETADDRINFO
+#define ia_family(a) ((a)->ai_family)
+#define ia_proto(a) ((a)->ai_protocol)
+#define ia_saddr(a) ((a)->ai_addr)
+#else
+#define ia_family(a) AF_INET
+#define ia_proto(a) 0
+#define ia_saddr(a) (*a)
+#endif
+
+void ne_sock_prebind(ne_socket *sock, const ne_inet_addr *addr,
+ unsigned int port)
+{
+ sock->lport = port;
+ sock->laddr = addr ? addr : &dummy_laddr;
+}
+
+/* Bind socket 'fd' to address/port 'addr' and 'port', for subsequent
+ * connect() to address of family 'peer_family'. */
+static int do_bind(int fd, int peer_family,
+ const ne_inet_addr *addr, unsigned int port)
+{
+#if defined(HAVE_SETSOCKOPT) && defined(SO_REUSEADDR) && defined(SOL_SOCKET)
+ {
+ int flag = 1;
+
+ (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag);
+ /* An error here is not fatal, so ignore it. */
+ }
+#endif
+
+
+#ifdef USE_GETADDRINFO
+ /* Use a sockaddr_in6 if an AF_INET6 local address is specifed, or
+ * if no address is specified and the peer address is AF_INET6: */
+ if ((addr != &dummy_laddr && addr->ai_family == AF_INET6)
+ || (addr == &dummy_laddr && peer_family == AF_INET6)) {
+ struct sockaddr_in6 in6;
+
+ if (addr == &dummy_laddr)
+ memset(&in6, 0, sizeof in6);
+ else
+ memcpy(&in6, addr->ai_addr, sizeof in6);
+ in6.sin6_port = htons(port);
+ /* fill in the _family field for AIX 4.3, which forgets to do so. */
+ in6.sin6_family = AF_INET6;
+
+ return bind(fd, (struct sockaddr *)&in6, sizeof in6);
+ } else
+#endif
+ {
+ struct sockaddr_in in;
+
+ if (addr == &dummy_laddr)
+ memset(&in, 0, sizeof in);
+ else
+ memcpy(&in, ia_saddr(addr), sizeof in);
+ in.sin_port = htons(port);
+ in.sin_family = AF_INET;
+
+ return bind(fd, (struct sockaddr *)&in, sizeof in);
+ }
+}
+
int ne_sock_connect(ne_socket *sock,
const ne_inet_addr *addr, unsigned int port)
{
int fd, ret;
-#ifdef USE_GETADDRINFO
/* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
* implementations do not set ai_socktype, e.g. RHL6.2. */
- fd = socket(addr->ai_family, SOCK_STREAM, addr->ai_protocol);
-#else
- fd = socket(AF_INET, SOCK_STREAM, 0);
-#endif
+ fd = socket(ia_family(addr), SOCK_STREAM, ia_proto(addr));
if (fd < 0) {
set_strerror(sock, ne_errno);
return -1;
@@ -1130,6 +1195,17 @@ int ne_sock_connect(ne_socket *sock,
}
#endif
+ if (sock->laddr && (sock->laddr == &dummy_laddr ||
+ ia_family(sock->laddr) == ia_family(addr))) {
+ ret = do_bind(fd, ia_family(addr), sock->laddr, sock->lport);
+ if (ret < 0) {
+ int errnum = errno;
+ ne_close(fd);
+ set_strerror(sock, errnum);
+ return NE_SOCK_ERROR;
+ }
+ }
+
#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP)
{ /* Disable the Nagle algorithm; better to add write buffering
* instead of doing this. */
@@ -1145,6 +1221,43 @@ int ne_sock_connect(ne_socket *sock,
return ret;
}
+ne_inet_addr *ne_sock_peer(ne_socket *sock, unsigned int *port)
+{
+ union saun {
+ struct sockaddr_in sin;
+#ifdef USE_GETADDRINFO
+ struct sockaddr_in6 sin6;
+#endif
+ } saun;
+ socklen_t len = sizeof saun;
+ ne_inet_addr *ia;
+ struct sockaddr *sad = (struct sockaddr *)&saun;
+
+ if (getpeername(sock->fd, sad, &len) != 0) {
+ set_strerror(sock, errno);
+ return NULL;
+ }
+
+ ia = ne_calloc(sizeof *ia);
+#ifdef USE_GETADDRINFO
+ ia->ai_addr = ne_malloc(sizeof *ia);
+ ia->ai_addrlen = len;
+ memcpy(ia->ai_addr, sad, len);
+ ia->ai_family = sad->sa_family;
+#else
+ memcpy(ia, &saun.sin.s_addr, sizeof *ia);
+#endif
+
+#ifdef USE_GETADDRINFO
+ *port = ntohs(sad->sa_family == AF_INET ?
+ saun.sin.sin_port : saun.sin6.sin6_port);
+#else
+ *port = ntohs(saun.sin.sin_port);
+#endif
+
+ return ia;
+}
+
ne_inet_addr *ne_iaddr_make(ne_iaddr_type type, const unsigned char *raw)
{
ne_inet_addr *ia;
diff --git a/src/ne_socket.h b/src/ne_socket.h
index 26a129d..6a71a35 100644
--- a/src/ne_socket.h
+++ b/src/ne_socket.h
@@ -1,6 +1,6 @@
/*
socket handling interface
- Copyright (C) 1999-2006, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2007, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -121,6 +121,18 @@ void ne_iaddr_free(ne_inet_addr *addr);
/* Create a socket object; returns NULL on error. */
ne_socket *ne_sock_create(void);
+/* Specify an address to which the local end of the socket will be
+ * bound during a subsequent ne_sock_connect() call. If the address
+ * passed to ne_sock_connect() is of a different type (family) to
+ * 'addr', 'addr' is ignored. Either 'addr' may be NULL, to use the
+ * given port with unspecified address, or 'port' may be 0, to use the
+ * given address with an unspecified port.
+ *
+ * (Note: This function is not equivalent to a BSD socket bind(), it
+ * only takes effect during the _connect() call). */
+void ne_sock_prebind(ne_socket *sock, const ne_inet_addr *addr,
+ unsigned int port);
+
/* Connect the socket to server at address 'addr' on port 'port'.
* Returns zero on success, NE_SOCK_TIMEOUT if a timeout occurs when a
* non-zero connect timeout is configured (and is supported), or
@@ -174,6 +186,10 @@ int ne_sock_accept(ne_socket *sock, int fd);
/* Returns the file descriptor used for socket 'sock'. */
int ne_sock_fd(const ne_socket *sock);
+/* Return address of peer, or NULL on error. The returned address
+ * must be destroyed by caller using ne_iaddr_free. */
+ne_inet_addr *ne_sock_peer(ne_socket *sock, unsigned int *port);
+
/* Close the socket and destroy the socket object. Returns zero on
* success, or an errno value if close() failed. */
int ne_sock_close(ne_socket *sock);
diff --git a/test/socket.c b/test/socket.c
index 941064f..b9ef3a7 100644
--- a/test/socket.c
+++ b/test/socket.c
@@ -214,9 +214,10 @@ static int resolve_ipv6(void)
#endif
static const unsigned char raw_127[4] = "\x7f\0\0\01", /* 127.0.0.1 */
-raw6_nuls[16] = /* :: */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+ raw6_nuls[16] = /* :: */ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
#ifdef TEST_IPV6
static const unsigned char
+raw6_local[16] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1",
raw6_cafe[16] = /* feed::cafe */ "\xfe\xed\0\0\0\0\0\0\0\0\0\0\0\0\xca\xfe",
raw6_babe[16] = /* cafe:babe:: */ "\xca\xfe\xba\xbe\0\0\0\0\0\0\0\0\0\0\0\0";
#endif
@@ -363,6 +364,34 @@ static int addr_connect(void)
return OK;
}
+static int addr_peer(void)
+{
+ ne_socket *sock = ne_sock_create();
+ ne_inet_addr *ia, *ia2;
+ unsigned int port = 9999;
+ int ret;
+
+ ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+ ONN("ne_iaddr_make returned NULL", ia == NULL);
+
+ CALL(spawn_server(7777, serve_close, NULL));
+ ONN("could not connect", ne_sock_connect(sock, ia, 7777));
+
+ ia2 = ne_sock_peer(sock, &port);
+ ret = ne_iaddr_cmp(ia, ia2);
+ ONV(ret != 0,
+ ("comparison of peer with server address was %d", ret));
+
+ ONV(port != 7777, ("got peer port %u", port));
+
+ ne_sock_close(sock);
+ CALL(await_server());
+
+ ne_iaddr_free(ia);
+ ne_iaddr_free(ia2);
+ return OK;
+}
+
/* Exect a read() to return EOF */
static int expect_close(ne_socket *sock)
{
@@ -561,12 +590,11 @@ static int line_expect(ne_socket *sock, const char *line)
{
ssize_t ret = ne_sock_readline(sock, buffer, BUFSIZ);
size_t len = strlen(line);
+ NE_DEBUG(NE_DBG_SOCKET, " -> expected=%s -> actual=%s", line, buffer);
ONV(ret == NE_SOCK_CLOSED, ("socket closed, expecting `%s'", line));
ONV(ret < 0, ("socket error `%s', expecting `%s'",
ne_sock_error(sock), line));
- ONV((size_t)ret != len,
- ("readline got %" NE_FMT_SSIZE_T ", expecting `%s'", ret, line));
- ONV(strcmp(line, buffer),
+ ONV((size_t)ret != len || strcmp(line, buffer),
("readline mismatch: `%s' not `%s'", buffer, line));
return OK;
}
@@ -1052,6 +1080,74 @@ static int ssl_session_id(void)
return await_server();
}
+static int serve_ppeer(ne_socket *sock, void *ud)
+{
+ unsigned int port = 99999;
+ ne_inet_addr *ia = ne_sock_peer(sock, &port);
+ char buf[128], line[256];
+
+ if (ia == NULL)
+ ne_snprintf(line, sizeof line, "error: %s", ne_sock_error(sock));
+ else
+ ne_snprintf(line, sizeof line,
+ "%s@%u\n", ne_iaddr_print(ia, buf, sizeof buf),
+ port);
+
+ CALL(full_write(sock, line, strlen(line)));
+
+ ne_iaddr_free(ia);
+
+ return OK;
+}
+
+static int try_prebind(int addr, int port)
+{
+ ne_socket *sock = ne_sock_create();
+ ne_inet_addr *ia;
+ char buf[128], line[256];
+
+ ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
+ ONN("ne_iaddr_make returned NULL", ia == NULL);
+
+ CALL(spawn_server(7777, serve_ppeer, NULL));
+
+ ne_sock_prebind(sock, addr ? ia : NULL, port ? 7778 : 0);
+
+ ONN("could not connect", ne_sock_connect(sock, ia, 7777));
+
+ ne_snprintf(line, sizeof line,
+ "%s@%d\n", ne_iaddr_print(ia, buf, sizeof buf),
+ 7778);
+
+ if (!port) {
+ /* Don't know what port will be chosen, so... */
+ ssize_t ret = ne_sock_readline(sock, buffer, BUFSIZ);
+
+ ONV(ret < 0, ("socket error `%s'", ne_sock_error(sock)));
+
+ ONV(strncmp(line, buffer, strchr(line, '@') - line) != 0,
+ ("bad address: '%s', expecting '%s'",
+ buffer, line));
+ }
+ else {
+ LINE(line);
+ }
+
+ ne_sock_close(sock);
+ CALL(await_server());
+
+ ne_iaddr_free(ia);
+ return OK;
+}
+
+static int prebind(void)
+{
+ CALL(try_prebind(1, 0));
+ CALL(try_prebind(0, 1));
+ CALL(try_prebind(1, 1));
+
+ return OK;
+}
ne_test tests[] = {
T(multi_init),
@@ -1066,6 +1162,7 @@ ne_test tests[] = {
T(addr_reverse),
T(just_connect),
T(addr_connect),
+ T(addr_peer),
T(read_close),
T(peek_close),
T(single_read),
@@ -1086,6 +1183,7 @@ ne_test tests[] = {
T(large_writes),
T(echo_lines),
T(blocking),
+ T(prebind),
#ifdef SOCKET_SSL
T(ssl_closure),
T(ssl_truncate),