/** Listen on an address list represented by the top of lua stack.
* \note kind ownership is not transferred
* \return success */
-static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind)
+static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind, bool freebind)
{
/* Case: table with 'addr' field; only follow that field directly. */
lua_getfield(L, -1, "addr");
if (str != NULL) {
struct engine *engine = engine_luaget(L);
int ret = 0;
- endpoint_flags_t flags = { .tls = tls };
+ endpoint_flags_t flags = { .tls = tls, .freebind = freebind };
if (!kind && !flags.tls) { /* normal UDP */
flags.sock_type = SOCK_DGRAM;
ret = network_listen(&engine->net, str, port, flags);
lua_error_p(L, "bad type for address");
lua_pushnil(L);
while (lua_next(L, -2)) {
- if (!net_listen_addrs(L, port, tls, kind))
+ if (!net_listen_addrs(L, port, tls, kind, freebind))
return false;
lua_pop(L, 1);
}
}
bool tls = (port == KR_DNS_TLS_PORT);
+ bool freebind = false;
const char *kind = NULL;
if (n > 2 && !lua_isnil(L, 3)) {
if (!lua_istable(L, 3))
lua_error_p(L, "wrong type of third parameter (table expected)");
tls = table_get_flag(L, 3, "tls", tls);
+ freebind = table_get_flag(L, 3, "freebind", tls);
lua_getfield(L, 3, "kind");
const char *k = lua_tostring(L, -1);
/* Now focus on the first argument. */
lua_settop(L, 1);
- const bool res = net_listen_addrs(L, port, tls, kind);
+ const bool res = net_listen_addrs(L, port, tls, kind, freebind);
lua_pushboolean(L, res);
return 1;
}
mp_flush(worker->pkt_pool.ctx);
}
-int io_bind(const struct sockaddr *addr, int type)
+int freebind_option(const struct sockaddr *addr, int *level, int *name)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ *level = IPPROTO_IP;
+#if defined(IP_FREEBIND)
+ *name = IP_FREEBIND;
+#elif defined(IP_BINDANY)
+ *name = IP_BINDANY;
+#else
+ return kr_error(ENOTSUP);
+#endif
+ break;
+ case AF_INET6:
+#if defined(IP_FREEBIND)
+ *level = IPPROTO_IP;
+ *name = IP_FREEBIND;
+#elif defined(IPV6_BINDANY)
+ *level = IPPROTO_IPV6;
+ *name = IPV6_BINDANY;
+#else
+ return kr_error(ENOTSUP);
+#endif
+ break;
+ default:
+ return kr_error(ENOTSUP);
+ }
+ return kr_ok();
+}
+
+int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags)
{
const int fd = socket(addr->sa_family, type, 0);
if (fd < 0) return kr_error(errno);
&& setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)))
return kr_error(errno);
#endif
+ if (flags != NULL && flags->freebind) {
+ int optlevel;
+ int optname;
+ int ret = freebind_option(addr, &optlevel, &optname);
+ if (ret) return kr_error(ret);
+ if (setsockopt(fd, optlevel, optname, &yes, sizeof(yes)))
+ return kr_error(errno);
+ }
if (bind(fd, addr, kr_sockaddr_len(addr)))
return kr_error(errno);
{
struct session *s = handle->data;
assert(s && session_get_handle(s) == (uv_handle_t *)handle &&
- handle->type == UV_TCP);
+ handle->type == UV_TCP);
if (session_flags(s)->closing) {
return;
struct tls_client_ctx_t;
/** Bind address into a file-descriptor (only, no libuv). type is e.g. SOCK_DGRAM */
-int io_bind(const struct sockaddr *addr, int type);
+int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags);
/** Initialize a UDP handle and start listening. */
int io_listen_udp(uv_loop_t *loop, uv_udp_t *handle, int fd);
/** Initialize a TCP handle and start listening. */
int sock_type;
_Bool tls;
const char *kind;
+ _Bool freebind;
} endpoint_flags_t;
struct endpoint {
void *handle;
flagged_fd_t ffd = { .flags = { .tls = tls } };
if (ret == 0 && !tls && family != AF_UNIX) {
/* AF_UNIX can do SOCK_DGRAM, but let's not support that *here*. */
- ffd.fd = io_bind(sa, SOCK_DGRAM);
+ ffd.fd = io_bind(sa, SOCK_DGRAM, NULL);
if (ffd.fd < 0)
ret = ffd.fd;
else if (array_push(*fds, ffd) < 0)
ret = kr_error(ENOMEM);
}
if (ret == 0) { /* common for TCP and TLS, including AF_UNIX cases */
- ffd.fd = io_bind(sa, SOCK_STREAM);
+ ffd.fd = io_bind(sa, SOCK_STREAM, NULL);
if (ffd.fd < 0)
ret = ffd.fd;
else if (array_push(*fds, ffd) < 0)
}
if (sa) {
- ep->fd = io_bind(sa, ep->flags.sock_type);
+ ep->fd = io_bind(sa, ep->flags.sock_type, &ep->flags);
if (ep->fd < 0) return ep->fd;
}
if (ep->flags.kind) {
int sock_type; /**< SOCK_DGRAM or SOCK_STREAM */
bool tls; /**< only used together with .kind == NULL and .tcp */
const char *kind; /**< tag for other types than the three usual */
+ bool freebind; /**< used for binding to non-local address **/
} endpoint_flags_t;
static inline bool endpoint_flags_eq(endpoint_flags_t f1, endpoint_flags_t f2)