diff --git a/CHANGELOG b/CHANGELOG
index ea554a297..29e4cce07 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] There is a new function, nmap.bind, to set the source address
+ of a socket. [David]
+
o [Nsock] Made it a fatal error instead of silent memory corruption
when an attempt is made to use a file descriptor whose number is not
less than FD_SETSIZE. This applies only on non-Windows platforms
diff --git a/nse_nsock.cc b/nse_nsock.cc
index fa4e042f0..a838c9a58 100644
--- a/nse_nsock.cc
+++ b/nse_nsock.cc
@@ -43,6 +43,8 @@ extern NmapOps o;
static int l_nsock_loop(lua_State * L);
+static int l_nsock_bind(lua_State * L);
+
static int l_nsock_connect(lua_State * L);
static int l_nsock_send(lua_State * L);
@@ -152,6 +154,9 @@ struct l_nsock_udata
int timeout;
nsock_iod nsiod;
void *ssl_session;
+ struct sockaddr_storage source_addr;
+ size_t source_addrlen;
+
struct nsock_yield yield;
/* used for buffered reading */
int bufidx; /* index inside lua's registry
@@ -429,6 +434,7 @@ int luaopen_nsock(lua_State * L)
" return connect(socket, ...);\n" "end";
static const luaL_Reg l_nsock[] = {
+ {"bind", l_nsock_bind},
{"send", l_nsock_send},
{"receive", l_nsock_receive},
{"receive_lines", l_nsock_receive_lines},
@@ -519,6 +525,8 @@ int l_nsock_new(lua_State * L)
lua_setfenv(L, -2);
udata->nsiod = NULL;
udata->ssl_session = NULL;
+ udata->source_addr.ss_family = AF_UNSPEC;
+ udata->source_addrlen = sizeof(udata->source_addr);
udata->timeout = DEFAULT_TIMEOUT;
udata->bufidx = LUA_NOREF;
udata->bufused = 0;
@@ -574,6 +582,70 @@ int l_nsock_checkstatus(lua_State * L, nsock_event nse)
return -1;
}
+/* Set the local address for socket operations. The two optional parameters
+ after the first (which is the socket object) are a string representing a
+ numeric address, and a port number. If either optional parameter is omitted
+ or nil, that part of the address will be left unspecified. */
+static int l_nsock_bind(lua_State * L)
+{
+ struct addrinfo hints = { 0 };
+ struct addrinfo *results;
+ char port_buf[16];
+ l_nsock_udata *udata;
+ const char *addr_str = NULL;
+ const char *port_str = NULL;
+ int rc;
+
+ udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock");
+ if (!lua_isnoneornil(L, 2))
+ addr_str = luaL_checkstring(L, 2);
+ if (!lua_isnoneornil(L, 3)) {
+ int port;
+ port = luaL_checkint(L, 3);
+ Snprintf(port_buf, sizeof(port_buf), "%d", port);
+ port_str = port_buf;
+ }
+
+ /* If we don't have a string to work with, set our configured address family
+ to get the proper unspecified address (0.0.0.0 or ::). Otherwise infer the
+ family from the string. */
+ if (addr_str == NULL)
+ hints.ai_family = o.af();
+ else
+ hints.ai_family = AF_UNSPEC;
+ /* AI_NUMERICHOST: don't use DNS to resolve names.
+ AI_NUMERICSERV: don't look up service names.
+ AI_PASSIVE: set an unspecified address if addr_str is NULL. */
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_PASSIVE;
+
+ rc = getaddrinfo(addr_str, port_str, &hints, &results);
+ if (rc != 0) {
+ lua_pushnil(L);
+ lua_pushstring(L, gai_strerror(rc));
+ return 2;
+ }
+ if (results == NULL) {
+ lua_pushnil(L);
+ lua_pushstring(L, "getaddrinfo: no results found");
+ return 2;
+ }
+ if (results->ai_addrlen > sizeof(udata->source_addr)) {
+ freeaddrinfo(results);
+ lua_pushnil(L);
+ lua_pushstring(L, "getaddrinfo: result is too big");
+ return 2;
+ }
+
+ /* We ignore any results after the first. */
+ /* We would just call nsi_set_localaddr here, but udata->nsiod is not created
+ until connect. So store the address in the userdatum. */
+ udata->source_addrlen = results->ai_addrlen;
+ memcpy(&udata->source_addr, results->ai_addr, udata->source_addrlen);
+
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
static int l_nsock_connect(lua_State * L)
{
enum type {TCP, UDP, SSL};
@@ -615,10 +687,10 @@ static int l_nsock_connect(lua_State * L)
}
udata->nsiod = nsi_new(nsp, NULL);
- if (o.spoofsource)
- {
+ if (udata->source_addr.ss_family != AF_UNSPEC) {
+ nsi_set_localaddr(udata->nsiod, &udata->source_addr, udata->source_addrlen);
+ } else if (o.spoofsource) {
struct sockaddr_storage ss;
-
size_t sslen;
o.SourceSockAddr(&ss, &sslen);
diff --git a/nselib/nmap.luadoc b/nselib/nmap.luadoc
index c61524a73..63567e2d6 100644
--- a/nselib/nmap.luadoc
+++ b/nselib/nmap.luadoc
@@ -220,6 +220,24 @@ function new_try(handler)
-- @usage local socket = nmap.new_socket()
function new_socket()
+--- Sets the local address of a socket.
+--
+-- This socket method sets the local address and port of a socket. It must be
+-- called before connect. The address set by bind
+-- overrides Nmap's source address and port set by the -S and
+-- -g options.
+-- @param addr Address string or nil (optional).
+-- @param port Port number or nil (optional).
+-- @return Status (true or false).
+-- @return Error string (if status is false).
+-- @usage
+-- try = nmap.new_try()
+-- try(socket:bind(nil, 53))
+-- try(socket:bind("1.2.3.4"))
+-- try(socket:bind("2001:db8::1"))
+-- try(socket:bind("1.2.3.4", 53))
+function bind(addr, port)
+
--- Establishes a connection.
--
-- This method puts a socket in a state ready for communication. It takes as