wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
check access rules for socket connections
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
This module is an adaption of code from the tcpd-1.4 package written
|
||||
by Wietse Venema, Eindhoven University of Technology, The Netherlands.
|
||||
|
||||
The code is used here with permission.
|
||||
|
||||
The code has been considerably changed from the original. Bug reports
|
||||
should be sent to samba@samba.org
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
#define FAIL (-1)
|
||||
#define ALLONES ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
/* masked_match - match address against netnumber/netmask */
|
||||
static BOOL masked_match(TALLOC_CTX *mem_ctx, const char *tok, const char *slash, const char *s)
|
||||
{
|
||||
uint32_t net;
|
||||
uint32_t mask;
|
||||
uint32_t addr;
|
||||
char *tok_cpy;
|
||||
|
||||
if ((addr = interpret_addr(s)) == INADDR_NONE)
|
||||
return False;
|
||||
|
||||
tok_cpy = talloc_strdup(mem_ctx, tok);
|
||||
tok_cpy[PTR_DIFF(slash,tok)] = '\0';
|
||||
net = interpret_addr(tok_cpy);
|
||||
talloc_free(tok_cpy);
|
||||
|
||||
if (strlen(slash + 1) > 2) {
|
||||
mask = interpret_addr(slash + 1);
|
||||
} else {
|
||||
mask = (uint32_t)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
|
||||
/* convert to network byte order */
|
||||
mask = htonl(mask);
|
||||
}
|
||||
|
||||
if (net == INADDR_NONE || mask == INADDR_NONE) {
|
||||
DEBUG(0,("access: bad net/mask access control: %s\n", tok));
|
||||
return False;
|
||||
}
|
||||
|
||||
return (addr & mask) == (net & mask);
|
||||
}
|
||||
|
||||
/* string_match - match string against token */
|
||||
static BOOL string_match(TALLOC_CTX *mem_ctx, const char *tok,const char *s, char *invalid_char)
|
||||
{
|
||||
size_t tok_len;
|
||||
size_t str_len;
|
||||
const char *cut;
|
||||
|
||||
*invalid_char = '\0';
|
||||
|
||||
/* Return True if a token has the magic value "ALL". Return
|
||||
* FAIL if the token is "FAIL". If the token starts with a "."
|
||||
* (domain name), return True if it matches the last fields of
|
||||
* the string. If the token has the magic value "LOCAL",
|
||||
* return True if the string does not contain a "."
|
||||
* character. If the token ends on a "." (network number),
|
||||
* return True if it matches the first fields of the
|
||||
* string. If the token begins with a "@" (netgroup name),
|
||||
* return True if the string is a (host) member of the
|
||||
* netgroup. Return True if the token fully matches the
|
||||
* string. If the token is a netnumber/netmask pair, return
|
||||
* True if the address is a member of the specified subnet.
|
||||
*/
|
||||
|
||||
if (tok[0] == '.') { /* domain: match last fields */
|
||||
if ((str_len = strlen(s)) > (tok_len = strlen(tok))
|
||||
&& strcasecmp(tok, s + str_len - tok_len)==0) {
|
||||
return True;
|
||||
}
|
||||
} else if (tok[0] == '@') { /* netgroup: look it up */
|
||||
DEBUG(0,("access: netgroup support is not available\n"));
|
||||
return False;
|
||||
} else if (strcmp(tok, "ALL")==0) { /* all: match any */
|
||||
return True;
|
||||
} else if (strcmp(tok, "FAIL")==0) { /* fail: match any */
|
||||
return FAIL;
|
||||
} else if (strcmp(tok, "LOCAL")==0) { /* local: no dots */
|
||||
if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) {
|
||||
return True;
|
||||
}
|
||||
} else if (strcasecmp(tok, s)==0) { /* match host name or address */
|
||||
return True;
|
||||
} else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */
|
||||
if (strncmp(tok, s, tok_len) == 0)
|
||||
return True;
|
||||
} else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */
|
||||
if (isdigit((int)s[0]) && masked_match(mem_ctx, tok, cut, s))
|
||||
return True;
|
||||
} else if (strchr(tok, '*') != 0) {
|
||||
*invalid_char = '*';
|
||||
} else if (strchr(tok, '?') != 0) {
|
||||
*invalid_char = '?';
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
struct client_addr {
|
||||
const char *cname;
|
||||
const char *caddr;
|
||||
};
|
||||
|
||||
/* client_match - match host name and address against token */
|
||||
static BOOL client_match(TALLOC_CTX *mem_ctx, const char *tok, struct client_addr *client)
|
||||
{
|
||||
BOOL match;
|
||||
char invalid_char = '\0';
|
||||
|
||||
/*
|
||||
* Try to match the address first. If that fails, try to match the host
|
||||
* name if available.
|
||||
*/
|
||||
|
||||
if ((match = string_match(mem_ctx, tok, client->caddr, &invalid_char)) == 0) {
|
||||
if(invalid_char)
|
||||
DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
|
||||
token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
|
||||
|
||||
if (client->cname[0] != 0)
|
||||
match = string_match(mem_ctx, tok, client->cname, &invalid_char);
|
||||
|
||||
if(invalid_char)
|
||||
DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
|
||||
token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
|
||||
}
|
||||
|
||||
return (match);
|
||||
}
|
||||
|
||||
/* list_match - match an item against a list of tokens with exceptions */
|
||||
static BOOL list_match(TALLOC_CTX *mem_ctx, const char **list, struct client_addr *client)
|
||||
{
|
||||
BOOL match = False;
|
||||
|
||||
if (!list)
|
||||
return False;
|
||||
|
||||
/*
|
||||
* Process tokens one at a time. We have exhausted all possible matches
|
||||
* when we reach an "EXCEPT" token or the end of the list. If we do find
|
||||
* a match, look for an "EXCEPT" list and recurse to determine whether
|
||||
* the match is affected by any exceptions.
|
||||
*/
|
||||
|
||||
for (; *list ; list++) {
|
||||
if (strcmp(*list, "EXCEPT")==0) /* EXCEPT: give up */
|
||||
break;
|
||||
if ((match = client_match(mem_ctx, *list, client))) /* True or FAIL */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process exceptions to True or FAIL matches. */
|
||||
if (match != False) {
|
||||
while (*list && strcmp(*list, "EXCEPT")!=0)
|
||||
list++;
|
||||
|
||||
for (; *list; list++) {
|
||||
if (client_match(mem_ctx, *list, client)) /* Exception Found */
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/* return true if access should be allowed */
|
||||
static BOOL allow_access_internal(TALLOC_CTX *mem_ctx,
|
||||
const char **deny_list,const char **allow_list,
|
||||
const char *cname, const char *caddr)
|
||||
{
|
||||
struct client_addr client;
|
||||
|
||||
client.cname = cname;
|
||||
client.caddr = caddr;
|
||||
|
||||
/* if it is loopback then always allow unless specifically denied */
|
||||
if (strcmp(caddr, "127.0.0.1") == 0) {
|
||||
/*
|
||||
* If 127.0.0.1 matches both allow and deny then allow.
|
||||
* Patch from Steve Langasek vorlon@netexpress.net.
|
||||
*/
|
||||
if (deny_list &&
|
||||
list_match(mem_ctx, deny_list, &client) &&
|
||||
(!allow_list ||
|
||||
!list_match(mem_ctx, allow_list, &client))) {
|
||||
return False;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* if theres no deny list and no allow list then allow access */
|
||||
if ((!deny_list || *deny_list == 0) &&
|
||||
(!allow_list || *allow_list == 0)) {
|
||||
return True;
|
||||
}
|
||||
|
||||
/* if there is an allow list but no deny list then allow only hosts
|
||||
on the allow list */
|
||||
if (!deny_list || *deny_list == 0)
|
||||
return list_match(mem_ctx, allow_list, &client);
|
||||
|
||||
/* if theres a deny list but no allow list then allow
|
||||
all hosts not on the deny list */
|
||||
if (!allow_list || *allow_list == 0)
|
||||
return !list_match(mem_ctx, deny_list, &client);
|
||||
|
||||
/* if there are both types of list then allow all hosts on the
|
||||
allow list */
|
||||
if (list_match(mem_ctx, allow_list, &client))
|
||||
return True;
|
||||
|
||||
/* if there are both types of list and it's not on the allow then
|
||||
allow it if its not on the deny */
|
||||
if (list_match(mem_ctx, deny_list, &client))
|
||||
return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/* return true if access should be allowed */
|
||||
BOOL allow_access(TALLOC_CTX *mem_ctx,
|
||||
const char **deny_list, const char **allow_list,
|
||||
const char *cname, const char *caddr)
|
||||
{
|
||||
BOOL ret;
|
||||
char *nc_cname = talloc_strdup(mem_ctx, cname);
|
||||
char *nc_caddr = talloc_strdup(mem_ctx, caddr);
|
||||
|
||||
if (!nc_cname || !nc_caddr) {
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = allow_access_internal(mem_ctx, deny_list, allow_list, nc_cname, nc_caddr);
|
||||
|
||||
talloc_free(nc_cname);
|
||||
talloc_free(nc_caddr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return true if the char* contains ip addrs only. Used to avoid
|
||||
gethostbyaddr() calls */
|
||||
|
||||
static BOOL only_ipaddrs_in_list(const char** list)
|
||||
{
|
||||
BOOL only_ip = True;
|
||||
|
||||
if (!list)
|
||||
return True;
|
||||
|
||||
for (; *list ; list++) {
|
||||
/* factor out the special strings */
|
||||
if (strcmp(*list, "ALL")==0 ||
|
||||
strcmp(*list, "FAIL")==0 ||
|
||||
strcmp(*list, "EXCEPT")==0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_ipaddress(*list)) {
|
||||
/*
|
||||
* if we failed, make sure that it was not because the token
|
||||
* was a network/netmask pair. Only network/netmask pairs
|
||||
* have a '/' in them
|
||||
*/
|
||||
if ((strchr(*list, '/')) == NULL) {
|
||||
only_ip = False;
|
||||
DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return only_ip;
|
||||
}
|
||||
|
||||
/* return true if access should be allowed to a service for a socket */
|
||||
BOOL socket_check_access(struct socket_context *sock,
|
||||
const char *service_name,
|
||||
const char **allow_list, const char **deny_list)
|
||||
{
|
||||
BOOL ret;
|
||||
const char *name="";
|
||||
struct socket_address *addr;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if ((!deny_list || *deny_list==0) &&
|
||||
(!allow_list || *allow_list==0)) {
|
||||
return True;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_init("socket_check_access");
|
||||
if (!mem_ctx) {
|
||||
return False;
|
||||
}
|
||||
|
||||
addr = socket_get_peer_addr(sock, mem_ctx);
|
||||
if (!addr) {
|
||||
DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
/* bypass gethostbyaddr() calls if the lists only contain IP addrs */
|
||||
if (!only_ipaddrs_in_list(allow_list) ||
|
||||
!only_ipaddrs_in_list(deny_list)) {
|
||||
name = socket_get_peer_name(sock, mem_ctx);
|
||||
if (!name) {
|
||||
name = addr->addr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addr) {
|
||||
DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = allow_access(mem_ctx, deny_list, allow_list, name, addr->addr);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n",
|
||||
service_name, name, addr->addr));
|
||||
} else {
|
||||
DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n",
|
||||
service_name, name, addr->addr));
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
AC_CHECK_FUNCS(writev)
|
||||
AC_CHECK_FUNCS(readv)
|
||||
|
||||
AC_CACHE_CHECK([for sin_len in sock],samba_cv_HAVE_SOCK_SIN_LEN,[
|
||||
AC_TRY_COMPILE([#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>],
|
||||
[struct sockaddr_in sock; sock.sin_len = sizeof(sock);],
|
||||
samba_cv_HAVE_SOCK_SIN_LEN=yes,samba_cv_HAVE_SOCK_SIN_LEN=no)])
|
||||
if test x"$samba_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
|
||||
AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
|
||||
fi
|
||||
|
||||
# The following test taken from the cvs sources
|
||||
# If we can't find connect, try looking in -lsocket, -lnsl, and -linet.
|
||||
# The Irix 5 libc.so has connect and gethostbyname, but Irix 5 also has
|
||||
# libsocket.so which has a bad implementation of gethostbyname (it
|
||||
# only looks in /etc/hosts), so we only look for -lsocket if we need
|
||||
# it.
|
||||
AC_CHECK_FUNCS(connect)
|
||||
if test x"$ac_cv_func_connect" = x"no"; then
|
||||
AC_CHECK_LIB_EXT(nsl_s, SOCKET_LIBS, connect)
|
||||
AC_CHECK_LIB_EXT(nsl, SOCKET_LIBS, connect)
|
||||
AC_CHECK_LIB_EXT(socket, SOCKET_LIBS, connect)
|
||||
AC_CHECK_LIB_EXT(inet, SOCKET_LIBS, connect)
|
||||
SMB_ENABLE(EXT_SOCKET,YES)
|
||||
dnl We can't just call AC_CHECK_FUNCS(connect) here, because the value
|
||||
dnl has been cached.
|
||||
if test x"$ac_cv_lib_ext_nsl_s_connect" = x"yes" ||
|
||||
test x"$ac_cv_lib_ext_nsl_connect" = x"yes" ||
|
||||
test x"$ac_cv_lib_ext_socket_connect" = x"yes" ||
|
||||
test x"$ac_cv_lib_ext_inet_connect" = x"yes"; then
|
||||
AC_DEFINE(HAVE_CONNECT,1,[Whether the system has connect()])
|
||||
else
|
||||
AC_MSG_ERROR([no connect() function available!])
|
||||
fi
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(EXT_SOCKET,[${SOCKET_LIBS}],[${SOCKET_CFLAGS}],[${SOCKET_CPPFLAGS}],[${SOCKET_LDFLAGS}])
|
||||
|
||||
AC_CHECK_FUNCS(gethostbyname)
|
||||
if test x"$ac_cv_func_gethostbyname" = x"no"; then
|
||||
AC_CHECK_LIB_EXT(nsl_s, NSL_LIBS, gethostbyname)
|
||||
AC_CHECK_LIB_EXT(nsl, NSl_LIBS, gethostbyname)
|
||||
AC_CHECK_LIB_EXT(socket, NSL_LIBS, gethostbyname)
|
||||
SMB_ENABLE(EXT_NSL,YES)
|
||||
dnl We can't just call AC_CHECK_FUNCS(gethostbyname) here, because the value
|
||||
dnl has been cached.
|
||||
if test x"$ac_cv_lib_ext_nsl_s_gethostbyname" != x"yes" &&
|
||||
test x"$ac_cv_lib_ext_nsl_gethostbyname" != x"yes" &&
|
||||
test x"$ac_cv_lib_ext_socket_gethostbyname" != x"yes"; then
|
||||
AC_MSG_ERROR([no gethostbyname() function available!])
|
||||
fi
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(EXT_NSL,[${NSL_LIBS}],[],[],[])
|
||||
|
||||
############################################
|
||||
# check for unix domain sockets
|
||||
AC_CACHE_CHECK([for unix domain sockets],samba_cv_unixsocket, [
|
||||
AC_TRY_COMPILE([
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>],
|
||||
[
|
||||
struct sockaddr_un sunaddr;
|
||||
sunaddr.sun_family = AF_UNIX;
|
||||
],
|
||||
samba_cv_unixsocket=yes,samba_cv_unixsocket=no)])
|
||||
SMB_ENABLE(socket_unix, NO)
|
||||
if test x"$samba_cv_unixsocket" = x"yes"; then
|
||||
SMB_ENABLE(socket_unix, YES)
|
||||
AC_DEFINE(HAVE_UNIXSOCKET,1,[If we need to build with unixscoket support])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for AF_LOCAL socket support], samba_cv_HAVE_WORKING_AF_LOCAL, [
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/unixsock.c"],
|
||||
samba_cv_HAVE_WORKING_AF_LOCAL=yes,
|
||||
samba_cv_HAVE_WORKING_AF_LOCAL=no,
|
||||
samba_cv_HAVE_WORKING_AF_LOCAL=cross)])
|
||||
if test x"$samba_cv_HAVE_WORKING_AF_LOCAL" != xno
|
||||
then
|
||||
AC_DEFINE(HAVE_WORKING_AF_LOCAL, 1, [Define if you have working AF_LOCAL sockets])
|
||||
fi
|
||||
|
||||
dnl test for ipv6 using the gethostbyname2() function. That should be sufficient
|
||||
dnl for now
|
||||
AC_CHECK_FUNCS(gethostbyname2, have_ipv6=true, have_ipv6=false)
|
||||
SMB_ENABLE(socket_ipv6, NO)
|
||||
if $have_ipv6 = true; then
|
||||
SMB_ENABLE(socket_ipv6, YES)
|
||||
AC_DEFINE(HAVE_SOCKET_IPV6,1,[Whether the system has ipv6 support])
|
||||
fi
|
||||
dnl don't build ipv6 by default, unless the above test enables it, or
|
||||
dnl the configure uses --with-static-modules=socket_ipv6
|
||||
|
||||
|
||||
##################
|
||||
# look for a method of finding the list of network interfaces
|
||||
#
|
||||
# This tests need LIBS="$NSL_LIBS $SOCKET_LIBS"
|
||||
#
|
||||
old_LIBS=$LIBS
|
||||
LIBS="$NSL_LIBS $SOCKET_LIBS"
|
||||
iface=no;
|
||||
AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[
|
||||
AC_TRY_RUN([
|
||||
#define HAVE_IFACE_AIX 1
|
||||
#define AUTOCONF_TEST 1
|
||||
#undef _XOPEN_SOURCE_EXTENDED
|
||||
#include "${srcdir-.}/lib/socket/netif.c"],
|
||||
samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)])
|
||||
if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then
|
||||
iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available])
|
||||
fi
|
||||
|
||||
if test $iface = no; then
|
||||
AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[
|
||||
AC_TRY_RUN([
|
||||
#define HAVE_IFACE_IFCONF 1
|
||||
#define AUTOCONF_TEST 1
|
||||
#include "${srcdir-.}/lib/socket/netif.c"],
|
||||
samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)])
|
||||
if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then
|
||||
iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $iface = no; then
|
||||
AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[
|
||||
AC_TRY_RUN([
|
||||
#define HAVE_IFACE_IFREQ 1
|
||||
#define AUTOCONF_TEST 1
|
||||
#include "${srcdir-.}/lib/socket/netif.c"],
|
||||
samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)])
|
||||
if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then
|
||||
iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available])
|
||||
fi
|
||||
fi
|
||||
|
||||
LIBS=$old_LIBS
|
||||
@@ -0,0 +1,59 @@
|
||||
##############################
|
||||
# Start SUBSYSTEM LIBNETIF
|
||||
[SUBSYSTEM::LIBNETIF]
|
||||
PRIVATE_PROTO_HEADER = netif_proto.h
|
||||
OBJ_FILES = \
|
||||
interface.o \
|
||||
netif.o
|
||||
PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL EXT_SOCKET EXT_NSL
|
||||
# End SUBSYSTEM LIBNETIF
|
||||
##############################
|
||||
|
||||
################################################
|
||||
# Start MODULE socket_ipv4
|
||||
[MODULE::socket_ipv4]
|
||||
SUBSYSTEM = samba-socket
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
OBJ_FILES = \
|
||||
socket_ipv4.o
|
||||
PUBLIC_DEPENDENCIES = EXT_SOCKET EXT_NSL
|
||||
PRIVATE_DEPENDENCIES = LIBSAMBA-ERRORS
|
||||
# End MODULE socket_ipv4
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE socket_ipv6
|
||||
[MODULE::socket_ipv6]
|
||||
SUBSYSTEM = samba-socket
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
OBJ_FILES = \
|
||||
socket_ipv6.o
|
||||
PUBLIC_DEPENDENCIES = EXT_SOCKET EXT_NSL
|
||||
# End MODULE socket_ipv6
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE socket_unix
|
||||
[MODULE::socket_unix]
|
||||
SUBSYSTEM = samba-socket
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
OBJ_FILES = \
|
||||
socket_unix.o
|
||||
PUBLIC_DEPENDENCIES = EXT_SOCKET EXT_NSL
|
||||
# End MODULE socket_unix
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM SOCKET
|
||||
[SUBSYSTEM::samba-socket]
|
||||
OBJ_FILES = \
|
||||
socket.o \
|
||||
access.o \
|
||||
connect_multi.o \
|
||||
connect.o
|
||||
LDFLAGS = $(SUBSYSTEM_LIBCLI_RESOLVE_OUTPUT) $(SUBSYSTEM_LIBCLI_NBT_OUTPUT) $(SUBSYSTEM_NDR_NBT_OUTPUT) $(LIBRARY_NDR_SVCCTL_OUTPUT)
|
||||
PUBLIC_DEPENDENCIES = LIBTALLOC
|
||||
PRIVATE_DEPENDENCIES = SOCKET_WRAPPER LIBCLI_COMPOSITE
|
||||
#LIBCLI_RESOLVE
|
||||
# End SUBSYSTEM SOCKET
|
||||
################################################
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
implements a non-blocking connect operation that is aware of the samba4
|
||||
events system
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Volker Lendecke 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
|
||||
struct connect_state {
|
||||
struct socket_context *sock;
|
||||
const struct socket_address *my_address;
|
||||
const struct socket_address *server_address;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static void socket_connect_handler(struct event_context *ev,
|
||||
struct fd_event *fde,
|
||||
uint16_t flags, void *private);
|
||||
static void continue_resolve_name(struct composite_context *ctx);
|
||||
static void continue_socket_connect(struct composite_context *creq);
|
||||
|
||||
/*
|
||||
call the real socket_connect() call, and setup event handler
|
||||
*/
|
||||
static void socket_send_connect(struct composite_context *result)
|
||||
{
|
||||
struct composite_context *creq;
|
||||
struct fd_event *fde;
|
||||
struct connect_state *state = talloc_get_type(result->private_data,
|
||||
struct connect_state);
|
||||
|
||||
creq = talloc_zero(state, struct composite_context);
|
||||
if (composite_nomem(creq, result)) return;
|
||||
creq->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
creq->event_ctx = result->event_ctx;
|
||||
creq->async.fn = continue_socket_connect;
|
||||
creq->async.private_data = result;
|
||||
|
||||
result->status = socket_connect(state->sock,
|
||||
state->my_address,
|
||||
state->server_address,
|
||||
state->flags);
|
||||
if (NT_STATUS_IS_ERR(result->status) &&
|
||||
!NT_STATUS_EQUAL(result->status,
|
||||
NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
composite_error(result, result->status);
|
||||
return;
|
||||
}
|
||||
|
||||
fde = event_add_fd(result->event_ctx, result,
|
||||
socket_get_fd(state->sock),
|
||||
EVENT_FD_READ|EVENT_FD_WRITE,
|
||||
socket_connect_handler, result);
|
||||
composite_nomem(fde, result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send a socket connect, potentially doing some name resolution first
|
||||
*/
|
||||
struct composite_context *socket_connect_send(struct socket_context *sock,
|
||||
struct socket_address *my_address,
|
||||
struct socket_address *server_address,
|
||||
uint32_t flags,
|
||||
struct event_context *event_ctx)
|
||||
{
|
||||
struct composite_context *result;
|
||||
struct connect_state *state;
|
||||
|
||||
result = talloc_zero(sock, struct composite_context);
|
||||
if (result == NULL) return NULL;
|
||||
result->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
result->event_ctx = event_ctx;
|
||||
|
||||
state = talloc_zero(result, struct connect_state);
|
||||
if (composite_nomem(state, result)) return result;
|
||||
result->private_data = state;
|
||||
|
||||
state->sock = talloc_reference(state, sock);
|
||||
if (composite_nomem(state->sock, result)) return result;
|
||||
|
||||
if (my_address) {
|
||||
void *ref = talloc_reference(state, my_address);
|
||||
if (composite_nomem(ref, result)) {
|
||||
return result;
|
||||
}
|
||||
state->my_address = my_address;
|
||||
}
|
||||
|
||||
{
|
||||
void *ref = talloc_reference(state, server_address);
|
||||
if (composite_nomem(ref, result)) {
|
||||
return result;
|
||||
}
|
||||
state->server_address = server_address;
|
||||
}
|
||||
|
||||
state->flags = flags;
|
||||
|
||||
set_blocking(socket_get_fd(sock), False);
|
||||
|
||||
if (server_address->addr && strcmp(sock->backend_name, "ipv4") == 0) {
|
||||
struct nbt_name name;
|
||||
struct composite_context *creq;
|
||||
make_nbt_name_client(&name, server_address->addr);
|
||||
creq = resolve_name_send(&name, result->event_ctx,
|
||||
lp_name_resolve_order());
|
||||
if (composite_nomem(creq, result)) return result;
|
||||
talloc_steal(result, creq);
|
||||
composite_continue(result, creq, continue_resolve_name, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
socket_send_connect(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
handle write events on connect completion
|
||||
*/
|
||||
static void socket_connect_handler(struct event_context *ev,
|
||||
struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct composite_context *result =
|
||||
talloc_get_type(private, struct composite_context);
|
||||
struct connect_state *state = talloc_get_type(result->private_data,
|
||||
struct connect_state);
|
||||
|
||||
result->status = socket_connect_complete(state->sock, state->flags);
|
||||
if (!composite_is_ok(result)) return;
|
||||
|
||||
composite_done(result);
|
||||
}
|
||||
|
||||
/*
|
||||
recv name resolution reply then send the connect
|
||||
*/
|
||||
static void continue_resolve_name(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *result = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
struct connect_state *state = talloc_get_type(result->private_data, struct connect_state);
|
||||
const char *addr;
|
||||
|
||||
result->status = resolve_name_recv(creq, state, &addr);
|
||||
if (!composite_is_ok(result)) return;
|
||||
|
||||
state->server_address = socket_address_from_strings(state, state->sock->backend_name,
|
||||
addr, state->server_address->port);
|
||||
if (composite_nomem(state->server_address, result)) return;
|
||||
|
||||
socket_send_connect(result);
|
||||
}
|
||||
|
||||
/*
|
||||
called when a connect has finished. Complete the top level composite context
|
||||
*/
|
||||
static void continue_socket_connect(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *result = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
result->status = creq->status;
|
||||
if (!composite_is_ok(result)) return;
|
||||
composite_done(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
wait for a socket_connect_send() to finish
|
||||
*/
|
||||
NTSTATUS socket_connect_recv(struct composite_context *result)
|
||||
{
|
||||
NTSTATUS status = composite_wait(result);
|
||||
talloc_free(result);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
like socket_connect() but takes an event context, doing a semi-async connect
|
||||
*/
|
||||
NTSTATUS socket_connect_ev(struct socket_context *sock,
|
||||
struct socket_address *my_address,
|
||||
struct socket_address *server_address,
|
||||
uint32_t flags, struct event_context *ev)
|
||||
{
|
||||
struct composite_context *ctx;
|
||||
ctx = socket_connect_send(sock, my_address,
|
||||
server_address, flags, ev);
|
||||
return socket_connect_recv(ctx);
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Fire connect requests to a host and a number of ports, with a timeout
|
||||
between the connect request. Return if the first connect comes back
|
||||
successfully or return the last error.
|
||||
|
||||
Copyright (C) Volker Lendecke 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
#define MULTI_PORT_DELAY 2000 /* microseconds */
|
||||
|
||||
/*
|
||||
overall state
|
||||
*/
|
||||
struct connect_multi_state {
|
||||
const char *server_address;
|
||||
int num_ports;
|
||||
uint16_t *ports;
|
||||
|
||||
struct socket_context *sock;
|
||||
uint16_t result_port;
|
||||
|
||||
int num_connects_sent, num_connects_recv;
|
||||
};
|
||||
|
||||
/*
|
||||
state of an individual socket_connect_send() call
|
||||
*/
|
||||
struct connect_one_state {
|
||||
struct composite_context *result;
|
||||
struct socket_context *sock;
|
||||
struct socket_address *addr;
|
||||
};
|
||||
|
||||
static void continue_resolve_name(struct composite_context *creq);
|
||||
static void connect_multi_timer(struct event_context *ev,
|
||||
struct timed_event *te,
|
||||
struct timeval tv, void *p);
|
||||
static void connect_multi_next_socket(struct composite_context *result);
|
||||
static void continue_one(struct composite_context *creq);
|
||||
|
||||
/*
|
||||
setup an async socket_connect, with multiple ports
|
||||
*/
|
||||
_PUBLIC_ struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
|
||||
const char *server_address,
|
||||
int num_server_ports,
|
||||
uint16_t *server_ports,
|
||||
struct event_context *event_ctx)
|
||||
{
|
||||
struct composite_context *result;
|
||||
struct connect_multi_state *multi;
|
||||
int i;
|
||||
|
||||
result = talloc_zero(mem_ctx, struct composite_context);
|
||||
if (result == NULL) return NULL;
|
||||
result->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
result->event_ctx = event_ctx;
|
||||
|
||||
multi = talloc_zero(result, struct connect_multi_state);
|
||||
if (composite_nomem(multi, result)) goto failed;
|
||||
result->private_data = multi;
|
||||
|
||||
multi->server_address = talloc_strdup(multi, server_address);
|
||||
if (composite_nomem(multi->server_address, result)) goto failed;
|
||||
|
||||
multi->num_ports = num_server_ports;
|
||||
multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
|
||||
if (composite_nomem(multi->ports, result)) goto failed;
|
||||
|
||||
for (i=0; i<multi->num_ports; i++) {
|
||||
multi->ports[i] = server_ports[i];
|
||||
}
|
||||
|
||||
if (!is_ipaddress(server_address)) {
|
||||
/*
|
||||
we don't want to do the name resolution separately
|
||||
for each port, so start it now, then only start on
|
||||
the real sockets once we have an IP
|
||||
*/
|
||||
struct nbt_name name;
|
||||
struct composite_context *creq;
|
||||
make_nbt_name_client(&name, server_address);
|
||||
creq = resolve_name_send(&name, result->event_ctx,
|
||||
lp_name_resolve_order());
|
||||
if (composite_nomem(creq, result)) goto failed;
|
||||
composite_continue(result, creq, continue_resolve_name, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* now we've setup the state we can process the first socket */
|
||||
connect_multi_next_socket(result);
|
||||
|
||||
if (!NT_STATUS_IS_OK(result->status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
failed:
|
||||
composite_error(result, result->status);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
start connecting to the next socket/port in the list
|
||||
*/
|
||||
static void connect_multi_next_socket(struct composite_context *result)
|
||||
{
|
||||
struct connect_multi_state *multi = talloc_get_type(result->private_data,
|
||||
struct connect_multi_state);
|
||||
struct connect_one_state *state;
|
||||
struct composite_context *creq;
|
||||
int next = multi->num_connects_sent;
|
||||
|
||||
if (next == multi->num_ports) {
|
||||
/* don't do anything, just wait for the existing ones to finish */
|
||||
return;
|
||||
}
|
||||
|
||||
multi->num_connects_sent += 1;
|
||||
|
||||
state = talloc(multi, struct connect_one_state);
|
||||
if (composite_nomem(state, result)) return;
|
||||
|
||||
state->result = result;
|
||||
result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
|
||||
if (!composite_is_ok(result)) return;
|
||||
|
||||
/* Form up the particular address we are interested in */
|
||||
state->addr = socket_address_from_strings(state, state->sock->backend_name,
|
||||
multi->server_address, multi->ports[next]);
|
||||
if (composite_nomem(state->addr, result)) return;
|
||||
|
||||
talloc_steal(state, state->sock);
|
||||
|
||||
creq = socket_connect_send(state->sock, NULL,
|
||||
state->addr, 0, result->event_ctx);
|
||||
if (composite_nomem(creq, result)) return;
|
||||
talloc_steal(state, creq);
|
||||
|
||||
composite_continue(result, creq, continue_one, state);
|
||||
|
||||
/* if there are more ports to go then setup a timer to fire when we have waited
|
||||
for a couple of milli-seconds, when that goes off we try the next port regardless
|
||||
of whether this port has completed */
|
||||
if (multi->num_ports > multi->num_connects_sent) {
|
||||
/* note that this timer is a child of the single
|
||||
connect attempt state, so it will go away when this
|
||||
request completes */
|
||||
event_add_timed(result->event_ctx, state,
|
||||
timeval_current_ofs(0, MULTI_PORT_DELAY),
|
||||
connect_multi_timer, result);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
a timer has gone off telling us that we should try the next port
|
||||
*/
|
||||
static void connect_multi_timer(struct event_context *ev,
|
||||
struct timed_event *te,
|
||||
struct timeval tv, void *p)
|
||||
{
|
||||
struct composite_context *result = talloc_get_type(p, struct composite_context);
|
||||
connect_multi_next_socket(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv name resolution reply then send the next connect
|
||||
*/
|
||||
static void continue_resolve_name(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *result = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
struct connect_multi_state *multi = talloc_get_type(result->private_data,
|
||||
struct connect_multi_state);
|
||||
const char *addr;
|
||||
|
||||
result->status = resolve_name_recv(creq, multi, &addr);
|
||||
if (!composite_is_ok(result)) return;
|
||||
|
||||
multi->server_address = addr;
|
||||
|
||||
connect_multi_next_socket(result);
|
||||
}
|
||||
|
||||
/*
|
||||
one of our socket_connect_send() calls hash finished. If it got a
|
||||
connection or there are none left then we are done
|
||||
*/
|
||||
static void continue_one(struct composite_context *creq)
|
||||
{
|
||||
struct connect_one_state *state = talloc_get_type(creq->async.private_data,
|
||||
struct connect_one_state);
|
||||
struct composite_context *result = state->result;
|
||||
struct connect_multi_state *multi = talloc_get_type(result->private_data,
|
||||
struct connect_multi_state);
|
||||
NTSTATUS status;
|
||||
multi->num_connects_recv++;
|
||||
|
||||
status = socket_connect_recv(creq);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
multi->sock = talloc_steal(multi, state->sock);
|
||||
multi->result_port = state->addr->port;
|
||||
}
|
||||
|
||||
talloc_free(state);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) ||
|
||||
multi->num_connects_recv == multi->num_ports) {
|
||||
result->status = status;
|
||||
composite_done(result);
|
||||
return;
|
||||
}
|
||||
|
||||
/* try the next port */
|
||||
connect_multi_next_socket(result);
|
||||
}
|
||||
|
||||
/*
|
||||
async recv routine for socket_connect_multi()
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct socket_context **sock,
|
||||
uint16_t *port)
|
||||
{
|
||||
NTSTATUS status = composite_wait(ctx);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct connect_multi_state *multi =
|
||||
talloc_get_type(ctx->private_data,
|
||||
struct connect_multi_state);
|
||||
*sock = talloc_steal(mem_ctx, multi->sock);
|
||||
*port = multi->result_port;
|
||||
}
|
||||
talloc_free(ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
|
||||
const char *server_address,
|
||||
int num_server_ports, uint16_t *server_ports,
|
||||
struct event_context *event_ctx,
|
||||
struct socket_context **result,
|
||||
uint16_t *result_port)
|
||||
{
|
||||
struct composite_context *ctx =
|
||||
socket_connect_multi_send(mem_ctx, server_address,
|
||||
num_server_ports, server_ports,
|
||||
event_ctx);
|
||||
return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
multiple interface handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 1992-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/netif.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
/** used for network interfaces */
|
||||
struct interface {
|
||||
struct interface *next, *prev;
|
||||
struct ipv4_addr ip;
|
||||
struct ipv4_addr nmask;
|
||||
const char *ip_s;
|
||||
const char *bcast_s;
|
||||
const char *nmask_s;
|
||||
};
|
||||
|
||||
static struct interface *local_interfaces;
|
||||
|
||||
#define ALLONES ((uint32_t)0xFFFFFFFF)
|
||||
/*
|
||||
address construction based on a patch from fred@datalync.com
|
||||
*/
|
||||
#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
|
||||
#define MKNETADDR(_IP, _NM) (_IP & _NM)
|
||||
|
||||
static struct ipv4_addr tov4(struct in_addr in)
|
||||
{
|
||||
struct ipv4_addr in2;
|
||||
in2.addr = in.s_addr;
|
||||
return in2;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Try and find an interface that matches an ip. If we cannot, return NULL
|
||||
**************************************************************************/
|
||||
static struct interface *iface_find(struct in_addr ip, BOOL CheckMask)
|
||||
{
|
||||
struct interface *i;
|
||||
if (is_zero_ip(tov4(ip))) return local_interfaces;
|
||||
|
||||
for (i=local_interfaces;i;i=i->next)
|
||||
if (CheckMask) {
|
||||
if (same_net(i->ip,tov4(ip),i->nmask)) return i;
|
||||
} else if (i->ip.addr == ip.s_addr) return i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
add an interface to the linked list of interfaces
|
||||
****************************************************************************/
|
||||
static void add_interface(struct in_addr ip, struct in_addr nmask)
|
||||
{
|
||||
struct interface *iface;
|
||||
struct ipv4_addr bcast;
|
||||
if (iface_find(ip, False)) {
|
||||
DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
|
||||
return;
|
||||
}
|
||||
|
||||
iface = talloc(local_interfaces, struct interface);
|
||||
if (!iface) return;
|
||||
|
||||
ZERO_STRUCTPN(iface);
|
||||
|
||||
iface->ip = tov4(ip);
|
||||
iface->nmask = tov4(nmask);
|
||||
bcast.addr = MKBCADDR(iface->ip.addr, iface->nmask.addr);
|
||||
|
||||
/* keep string versions too, to avoid people tripping over the implied
|
||||
static in sys_inet_ntoa() */
|
||||
iface->ip_s = talloc_strdup(iface, sys_inet_ntoa(iface->ip));
|
||||
iface->nmask_s = talloc_strdup(iface, sys_inet_ntoa(iface->nmask));
|
||||
|
||||
if (nmask.s_addr != ~0) {
|
||||
iface->bcast_s = talloc_strdup(iface, sys_inet_ntoa(bcast));
|
||||
}
|
||||
|
||||
DLIST_ADD_END(local_interfaces, iface, struct interface *);
|
||||
|
||||
DEBUG(2,("added interface ip=%s nmask=%s\n", iface->ip_s, iface->nmask_s));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
interpret a single element from a interfaces= config line
|
||||
|
||||
This handles the following different forms:
|
||||
|
||||
1) wildcard interface name
|
||||
2) DNS name
|
||||
3) IP/masklen
|
||||
4) ip/mask
|
||||
5) bcast/mask
|
||||
**/
|
||||
static void interpret_interface(const char *token,
|
||||
struct iface_struct *probed_ifaces,
|
||||
int total_probed)
|
||||
{
|
||||
struct in_addr ip, nmask;
|
||||
char *p;
|
||||
int i, added=0;
|
||||
|
||||
ip.s_addr = 0;
|
||||
nmask.s_addr = 0;
|
||||
|
||||
/* first check if it is an interface name */
|
||||
for (i=0;i<total_probed;i++) {
|
||||
if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
|
||||
add_interface(probed_ifaces[i].ip,
|
||||
probed_ifaces[i].netmask);
|
||||
added = 1;
|
||||
}
|
||||
}
|
||||
if (added) return;
|
||||
|
||||
/* maybe it is a DNS name */
|
||||
p = strchr_m(token,'/');
|
||||
if (!p) {
|
||||
/* don't try to do dns lookups on wildcard names */
|
||||
if (strpbrk(token, "*?") != NULL) {
|
||||
return;
|
||||
}
|
||||
ip.s_addr = interpret_addr2(token).addr;
|
||||
for (i=0;i<total_probed;i++) {
|
||||
if (ip.s_addr == probed_ifaces[i].ip.s_addr) {
|
||||
add_interface(probed_ifaces[i].ip,
|
||||
probed_ifaces[i].netmask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG(2,("can't determine netmask for %s\n", token));
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse it into an IP address/netmasklength pair */
|
||||
*p++ = 0;
|
||||
|
||||
ip.s_addr = interpret_addr2(token).addr;
|
||||
|
||||
if (strlen(p) > 2) {
|
||||
nmask.s_addr = interpret_addr2(p).addr;
|
||||
} else {
|
||||
nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES));
|
||||
}
|
||||
|
||||
/* maybe the first component was a broadcast address */
|
||||
if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) ||
|
||||
ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) {
|
||||
for (i=0;i<total_probed;i++) {
|
||||
if (same_net(tov4(ip), tov4(probed_ifaces[i].ip), tov4(nmask))) {
|
||||
add_interface(probed_ifaces[i].ip, nmask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG(2,("Can't determine ip for broadcast address %s\n", token));
|
||||
return;
|
||||
}
|
||||
|
||||
add_interface(ip, nmask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
load the list of network interfaces
|
||||
**/
|
||||
static void load_interfaces(void)
|
||||
{
|
||||
const char **ptr;
|
||||
int i;
|
||||
struct iface_struct ifaces[MAX_INTERFACES];
|
||||
struct ipv4_addr loopback_ip;
|
||||
int total_probed;
|
||||
|
||||
if (local_interfaces != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = lp_interfaces();
|
||||
loopback_ip = interpret_addr2("127.0.0.1");
|
||||
|
||||
/* probe the kernel for interfaces */
|
||||
total_probed = get_interfaces(ifaces, MAX_INTERFACES);
|
||||
|
||||
/* if we don't have a interfaces line then use all interfaces
|
||||
except loopback */
|
||||
if (!ptr || !*ptr || !**ptr) {
|
||||
if (total_probed <= 0) {
|
||||
DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
|
||||
}
|
||||
for (i=0;i<total_probed;i++) {
|
||||
if (ifaces[i].ip.s_addr != loopback_ip.addr) {
|
||||
add_interface(ifaces[i].ip,
|
||||
ifaces[i].netmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (ptr && *ptr) {
|
||||
interpret_interface(*ptr, ifaces, total_probed);
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (!local_interfaces) {
|
||||
DEBUG(0,("WARNING: no network interfaces found\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
unload the interfaces list, so it can be reloaded when needed
|
||||
*/
|
||||
void unload_interfaces(void)
|
||||
{
|
||||
talloc_free(local_interfaces);
|
||||
local_interfaces = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
how many interfaces do we have
|
||||
**/
|
||||
int iface_count(void)
|
||||
{
|
||||
int ret = 0;
|
||||
struct interface *i;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
for (i=local_interfaces;i;i=i->next)
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
return IP of the Nth interface
|
||||
**/
|
||||
const char *iface_n_ip(int n)
|
||||
{
|
||||
struct interface *i;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
for (i=local_interfaces;i && n;i=i->next)
|
||||
n--;
|
||||
|
||||
if (i) {
|
||||
return i->ip_s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
return bcast of the Nth interface
|
||||
**/
|
||||
const char *iface_n_bcast(int n)
|
||||
{
|
||||
struct interface *i;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
for (i=local_interfaces;i && n;i=i->next)
|
||||
n--;
|
||||
|
||||
if (i) {
|
||||
return i->bcast_s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
return netmask of the Nth interface
|
||||
**/
|
||||
const char *iface_n_netmask(int n)
|
||||
{
|
||||
struct interface *i;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
for (i=local_interfaces;i && n;i=i->next)
|
||||
n--;
|
||||
|
||||
if (i) {
|
||||
return i->nmask_s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
return the local IP address that best matches a destination IP, or
|
||||
our first interface if none match
|
||||
*/
|
||||
const char *iface_best_ip(const char *dest)
|
||||
{
|
||||
struct interface *iface;
|
||||
struct in_addr ip;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
ip.s_addr = interpret_addr(dest);
|
||||
iface = iface_find(ip, True);
|
||||
if (iface) {
|
||||
return iface->ip_s;
|
||||
}
|
||||
return iface_n_ip(0);
|
||||
}
|
||||
|
||||
/**
|
||||
return True if an IP is one one of our local networks
|
||||
*/
|
||||
BOOL iface_is_local(const char *dest)
|
||||
{
|
||||
struct in_addr ip;
|
||||
|
||||
load_interfaces();
|
||||
|
||||
ip.s_addr = interpret_addr(dest);
|
||||
if (iface_find(ip, True)) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
return True if a IP matches a IP/netmask pair
|
||||
*/
|
||||
BOOL iface_same_net(const char *ip1, const char *ip2, const char *netmask)
|
||||
{
|
||||
return same_net(interpret_addr2(ip1),
|
||||
interpret_addr2(ip2),
|
||||
interpret_addr2(netmask));
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
return a list of network interfaces
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
/* working out the interfaces for a OS is an incredibly non-portable
|
||||
thing. We have several possible implementations below, and autoconf
|
||||
tries each of them to see what works
|
||||
|
||||
Note that this file does _not_ include includes.h. That is so this code
|
||||
can be called directly from the autoconf tests. That also means
|
||||
this code cannot use any of the normal Samba debug stuff or defines.
|
||||
This is standalone code.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef AUTOCONF_TEST
|
||||
#include "includes.h"
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifndef SIOCGIFCONF
|
||||
#ifdef HAVE_SYS_SOCKIO_H
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#ifdef __COMPAR_FN_T
|
||||
#define QSORT_CAST (__compar_fn_t)
|
||||
#endif
|
||||
|
||||
#ifndef QSORT_CAST
|
||||
#define QSORT_CAST (int (*)(const void *, const void *))
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NET_IF_H
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
#include "netif.h"
|
||||
|
||||
#if HAVE_IFACE_IFCONF
|
||||
|
||||
/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
|
||||
V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
|
||||
|
||||
It probably also works on any BSD style system. */
|
||||
|
||||
/****************************************************************************
|
||||
get the netmask address for a local interface
|
||||
****************************************************************************/
|
||||
static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
|
||||
{
|
||||
struct ifconf ifc;
|
||||
char buff[8192];
|
||||
int fd, i, n;
|
||||
struct ifreq *ifr=NULL;
|
||||
int total = 0;
|
||||
struct in_addr ipaddr;
|
||||
struct in_addr nmask;
|
||||
char *iname;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifc.ifc_len = sizeof(buff);
|
||||
ifc.ifc_buf = buff;
|
||||
|
||||
if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifr = ifc.ifc_req;
|
||||
|
||||
n = ifc.ifc_len / sizeof(struct ifreq);
|
||||
|
||||
/* Loop through interfaces, looking for given IP address */
|
||||
for (i=n-1;i>=0 && total < max_interfaces;i--) {
|
||||
if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iname = ifr[i].ifr_name;
|
||||
ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
|
||||
|
||||
if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ifr[i].ifr_flags & IFF_UP)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
|
||||
|
||||
strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
|
||||
ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
|
||||
ifaces[total].ip = ipaddr;
|
||||
ifaces[total].netmask = nmask;
|
||||
total++;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#define _FOUND_IFACE_ANY
|
||||
#endif /* HAVE_IFACE_IFCONF */
|
||||
#ifdef HAVE_IFACE_IFREQ
|
||||
|
||||
#ifndef I_STR
|
||||
#include <sys/stropts.h>
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
this should cover most of the streams based systems
|
||||
Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
|
||||
****************************************************************************/
|
||||
static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
|
||||
{
|
||||
struct ifreq ifreq;
|
||||
struct strioctl strioctl;
|
||||
char buff[8192];
|
||||
int fd, i, n;
|
||||
struct ifreq *ifr=NULL;
|
||||
int total = 0;
|
||||
struct in_addr ipaddr;
|
||||
struct in_addr nmask;
|
||||
char *iname;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
strioctl.ic_cmd = SIOCGIFCONF;
|
||||
strioctl.ic_dp = buff;
|
||||
strioctl.ic_len = sizeof(buff);
|
||||
if (ioctl(fd, I_STR, &strioctl) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we can ignore the possible sizeof(int) here as the resulting
|
||||
number of interface structures won't change */
|
||||
n = strioctl.ic_len / sizeof(struct ifreq);
|
||||
|
||||
/* we will assume that the kernel returns the length as an int
|
||||
at the start of the buffer if the offered size is a
|
||||
multiple of the structure size plus an int */
|
||||
if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
|
||||
ifr = (struct ifreq *)(buff + sizeof(int));
|
||||
} else {
|
||||
ifr = (struct ifreq *)buff;
|
||||
}
|
||||
|
||||
/* Loop through interfaces */
|
||||
|
||||
for (i = 0; i<n && total < max_interfaces; i++) {
|
||||
ifreq = ifr[i];
|
||||
|
||||
strioctl.ic_cmd = SIOCGIFFLAGS;
|
||||
strioctl.ic_dp = (char *)&ifreq;
|
||||
strioctl.ic_len = sizeof(struct ifreq);
|
||||
if (ioctl(fd, I_STR, &strioctl) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ifreq.ifr_flags & IFF_UP)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
strioctl.ic_cmd = SIOCGIFADDR;
|
||||
strioctl.ic_dp = (char *)&ifreq;
|
||||
strioctl.ic_len = sizeof(struct ifreq);
|
||||
if (ioctl(fd, I_STR, &strioctl) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
|
||||
iname = ifreq.ifr_name;
|
||||
|
||||
strioctl.ic_cmd = SIOCGIFNETMASK;
|
||||
strioctl.ic_dp = (char *)&ifreq;
|
||||
strioctl.ic_len = sizeof(struct ifreq);
|
||||
if (ioctl(fd, I_STR, &strioctl) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
|
||||
|
||||
strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
|
||||
ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
|
||||
ifaces[total].ip = ipaddr;
|
||||
ifaces[total].netmask = nmask;
|
||||
|
||||
total++;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#define _FOUND_IFACE_ANY
|
||||
#endif /* HAVE_IFACE_IFREQ */
|
||||
#ifdef HAVE_IFACE_AIX
|
||||
|
||||
/****************************************************************************
|
||||
this one is for AIX (tested on 4.2)
|
||||
****************************************************************************/
|
||||
static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
|
||||
{
|
||||
char buff[8192];
|
||||
int fd, i;
|
||||
struct ifconf ifc;
|
||||
struct ifreq *ifr=NULL;
|
||||
struct in_addr ipaddr;
|
||||
struct in_addr nmask;
|
||||
char *iname;
|
||||
int total = 0;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
ifc.ifc_len = sizeof(buff);
|
||||
ifc.ifc_buf = buff;
|
||||
|
||||
if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifr = ifc.ifc_req;
|
||||
|
||||
/* Loop through interfaces */
|
||||
i = ifc.ifc_len;
|
||||
|
||||
while (i > 0 && total < max_interfaces) {
|
||||
uint_t inc;
|
||||
|
||||
inc = ifr->ifr_addr.sa_len;
|
||||
|
||||
if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
|
||||
iname = ifr->ifr_name;
|
||||
|
||||
if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!(ifr->ifr_flags & IFF_UP)) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
|
||||
|
||||
strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
|
||||
ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
|
||||
ifaces[total].ip = ipaddr;
|
||||
ifaces[total].netmask = nmask;
|
||||
|
||||
total++;
|
||||
|
||||
next:
|
||||
/*
|
||||
* Patch from Archie Cobbs (archie@whistle.com). The
|
||||
* addresses in the SIOCGIFCONF interface list have a
|
||||
* minimum size. Usually this doesn't matter, but if
|
||||
* your machine has tunnel interfaces, etc. that have
|
||||
* a zero length "link address", this does matter. */
|
||||
|
||||
if (inc < sizeof(ifr->ifr_addr))
|
||||
inc = sizeof(ifr->ifr_addr);
|
||||
inc += IFNAMSIZ;
|
||||
|
||||
ifr = (struct ifreq*) (((char*) ifr) + inc);
|
||||
i -= inc;
|
||||
}
|
||||
|
||||
|
||||
close(fd);
|
||||
return total;
|
||||
}
|
||||
|
||||
#define _FOUND_IFACE_ANY
|
||||
#endif /* HAVE_IFACE_AIX */
|
||||
#ifndef _FOUND_IFACE_ANY
|
||||
static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
|
||||
{
|
||||
int r;
|
||||
r = strcmp(i1->name, i2->name);
|
||||
if (r) return r;
|
||||
r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
|
||||
if (r) return r;
|
||||
r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* this wrapper is used to remove duplicates from the interface list generated
|
||||
above */
|
||||
int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
|
||||
{
|
||||
int total, i, j;
|
||||
|
||||
total = _get_interfaces(ifaces, max_interfaces);
|
||||
if (total <= 0) return total;
|
||||
|
||||
/* now we need to remove duplicates */
|
||||
qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
|
||||
|
||||
for (i=1;i<total;) {
|
||||
if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
|
||||
for (j=i-1;j<total-1;j++) {
|
||||
ifaces[j] = ifaces[j+1];
|
||||
}
|
||||
total--;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
#ifdef AUTOCONF_TEST
|
||||
/* this is the autoconf driver to test get_interfaces() */
|
||||
|
||||
int main()
|
||||
{
|
||||
struct iface_struct ifaces[MAX_INTERFACES];
|
||||
int total = get_interfaces(ifaces, MAX_INTERFACES);
|
||||
int i;
|
||||
|
||||
printf("got %d interfaces:\n", total);
|
||||
if (total <= 0) exit(1);
|
||||
|
||||
for (i=0;i<total;i++) {
|
||||
printf("%-10s ", ifaces[i].name);
|
||||
printf("IP=%s ", inet_ntoa(ifaces[i].ip));
|
||||
printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
structures for lib/netif/
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct iface_struct {
|
||||
char name[16];
|
||||
struct in_addr ip;
|
||||
struct in_addr netmask;
|
||||
};
|
||||
|
||||
#define MAX_INTERFACES 128
|
||||
|
||||
#ifndef AUTOCONF_TEST
|
||||
#include "lib/socket/netif_proto.h"
|
||||
#endif
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Socket functions
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
Copyright (C) Tim Potter 2000-2001
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/network.h"
|
||||
|
||||
/*
|
||||
auto-close sockets on free
|
||||
*/
|
||||
static int socket_destructor(struct socket_context *sock)
|
||||
{
|
||||
if (sock->ops->fn_close) {
|
||||
sock->ops->fn_close(sock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
|
||||
struct socket_context **new_sock,
|
||||
enum socket_type type, uint32_t flags)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
(*new_sock) = talloc(mem_ctx, struct socket_context);
|
||||
if (!(*new_sock)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*new_sock)->type = type;
|
||||
(*new_sock)->state = SOCKET_STATE_UNDEFINED;
|
||||
(*new_sock)->flags = flags;
|
||||
|
||||
(*new_sock)->fd = -1;
|
||||
|
||||
(*new_sock)->private_data = NULL;
|
||||
(*new_sock)->ops = ops;
|
||||
(*new_sock)->backend_name = NULL;
|
||||
|
||||
status = (*new_sock)->ops->fn_init((*new_sock));
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(*new_sock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* by enabling "testnonblock" mode, all socket receive and
|
||||
send calls on non-blocking sockets will randomly recv/send
|
||||
less data than requested */
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK) &&
|
||||
type == SOCKET_TYPE_STREAM &&
|
||||
lp_parm_bool(-1, "socket", "testnonblock", False)) {
|
||||
(*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
|
||||
}
|
||||
|
||||
/* we don't do a connect() on dgram sockets, so need to set
|
||||
non-blocking at socket create time */
|
||||
if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
|
||||
set_blocking(socket_get_fd(*new_sock), False);
|
||||
}
|
||||
|
||||
talloc_set_destructor(*new_sock, socket_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
|
||||
struct socket_context **new_sock, uint32_t flags)
|
||||
{
|
||||
const struct socket_ops *ops;
|
||||
|
||||
ops = socket_getops_byname(name, type);
|
||||
if (!ops) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return socket_create_with_ops(NULL, ops, new_sock, type, flags);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *server_address,
|
||||
uint32_t flags)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->state != SOCKET_STATE_UNDEFINED) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_connect) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return sock->ops->fn_connect(sock, my_address, server_address, flags);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
|
||||
{
|
||||
if (!sock->ops->fn_connect_complete) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return sock->ops->fn_connect_complete(sock, flags);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->state != SOCKET_STATE_UNDEFINED) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_listen) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return sock->ops->fn_listen(sock, my_address, queue_size, flags);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->type != SOCKET_TYPE_STREAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_accept) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
status = sock->ops->fn_accept(sock, new_sock);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
talloc_set_destructor(*new_sock, socket_destructor);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
|
||||
sock->state != SOCKET_STATE_SERVER_CONNECTED &&
|
||||
sock->type != SOCKET_TYPE_DGRAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_recv) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
|
||||
&& wantlen > 1) {
|
||||
|
||||
if (random() % 10 == 0) {
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
|
||||
}
|
||||
return sock->ops->fn_recv(sock, buf, wantlen, nread);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread,
|
||||
TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->type != SOCKET_TYPE_DGRAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_recvfrom) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
|
||||
mem_ctx, src_addr);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
|
||||
sock->state != SOCKET_STATE_SERVER_CONNECTED) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_send) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
|
||||
&& blob->length > 1) {
|
||||
DATA_BLOB blob2 = *blob;
|
||||
if (random() % 10 == 0) {
|
||||
*sendlen = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
/* The random size sends are incompatible with TLS and SASL
|
||||
* sockets, which require re-sends to be consistant */
|
||||
if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
|
||||
blob2.length = 1+(random() % blob2.length);
|
||||
} else {
|
||||
/* This is particularly stressful on buggy
|
||||
* LDAP clients, that don't expect on LDAP
|
||||
* packet in many SASL packets */
|
||||
blob2.length = 1 + blob2.length/2;
|
||||
}
|
||||
return sock->ops->fn_send(sock, &blob2, sendlen);
|
||||
}
|
||||
return sock->ops->fn_send(sock, blob, sendlen);
|
||||
}
|
||||
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen,
|
||||
const struct socket_address *dest_addr)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (sock->type != SOCKET_TYPE_DGRAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
|
||||
sock->state == SOCKET_STATE_SERVER_CONNECTED) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!sock->ops->fn_sendto) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
ask for the number of bytes in a pending incoming packet
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (!sock->ops->fn_pending) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return sock->ops->fn_pending(sock, npending);
|
||||
}
|
||||
|
||||
|
||||
_PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return NT_STATUS_CONNECTION_DISCONNECTED;
|
||||
}
|
||||
if (!sock->ops->fn_set_option) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return sock->ops->fn_set_option(sock, option, val);
|
||||
}
|
||||
|
||||
_PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (!sock->ops->fn_get_peer_name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sock->ops->fn_get_peer_name(sock, mem_ctx);
|
||||
}
|
||||
|
||||
_PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (!sock->ops->fn_get_peer_addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sock->ops->fn_get_peer_addr(sock, mem_ctx);
|
||||
}
|
||||
|
||||
_PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (!sock->ops->fn_get_my_addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sock->ops->fn_get_my_addr(sock, mem_ctx);
|
||||
}
|
||||
|
||||
_PUBLIC_ int socket_get_fd(struct socket_context *sock)
|
||||
{
|
||||
if (!sock->ops->fn_get_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock->ops->fn_get_fd(sock);
|
||||
}
|
||||
|
||||
/*
|
||||
call dup() on a socket, and close the old fd. This is used to change
|
||||
the fd to the lowest available number, to make select() more
|
||||
efficient (select speed depends on the maxiumum fd number passed to
|
||||
it)
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
|
||||
{
|
||||
int fd;
|
||||
if (sock->fd == -1) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
fd = dup(sock->fd);
|
||||
if (fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
close(sock->fd);
|
||||
sock->fd = fd;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
}
|
||||
|
||||
/* Create a new socket_address. The type must match the socket type.
|
||||
* The host parameter may be an IP or a hostname
|
||||
*/
|
||||
|
||||
_PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
|
||||
const char *family,
|
||||
const char *host,
|
||||
int port)
|
||||
{
|
||||
struct socket_address *addr = talloc(mem_ctx, struct socket_address);
|
||||
if (!addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addr->family = family;
|
||||
addr->addr = talloc_strdup(addr, host);
|
||||
if (!addr->addr) {
|
||||
talloc_free(addr);
|
||||
return NULL;
|
||||
}
|
||||
addr->port = port;
|
||||
addr->sockaddr = NULL;
|
||||
addr->sockaddrlen = 0;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Create a new socket_address. Copy the struct sockaddr into the new
|
||||
* structure. Used for hooks in the kerberos libraries, where they
|
||||
* supply only a struct sockaddr */
|
||||
|
||||
_PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
|
||||
struct sockaddr *sockaddr,
|
||||
size_t sockaddrlen)
|
||||
{
|
||||
struct socket_address *addr = talloc(mem_ctx, struct socket_address);
|
||||
if (!addr) {
|
||||
return NULL;
|
||||
}
|
||||
addr->family = NULL;
|
||||
addr->addr = NULL;
|
||||
addr->port = 0;
|
||||
addr->sockaddr = talloc_memdup(addr, sockaddr, sockaddrlen);
|
||||
if (!addr->sockaddr) {
|
||||
talloc_free(addr);
|
||||
return NULL;
|
||||
}
|
||||
addr->sockaddrlen = sockaddrlen;
|
||||
return addr;
|
||||
}
|
||||
|
||||
_PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
|
||||
{
|
||||
extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
|
||||
extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
|
||||
extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
|
||||
|
||||
if (strcmp("ip", family) == 0 ||
|
||||
strcmp("ipv4", family) == 0) {
|
||||
return socket_ipv4_ops(type);
|
||||
}
|
||||
|
||||
#if HAVE_SOCKET_IPV6
|
||||
if (strcmp("ipv6", family) == 0) {
|
||||
if (lp_parm_bool(-1, "socket", "noipv6", False)) {
|
||||
DEBUG(3, ("IPv6 support was disabled in smb.conf"));
|
||||
return NULL;
|
||||
}
|
||||
return socket_ipv6_ops(type);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp("unix", family) == 0) {
|
||||
return socket_unixdom_ops(type);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int level;
|
||||
int option;
|
||||
int value;
|
||||
int opttype;
|
||||
} socket_options[] = {
|
||||
{"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
|
||||
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
|
||||
{"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
|
||||
#ifdef TCP_NODELAY
|
||||
{"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
|
||||
#endif
|
||||
#ifdef IPTOS_LOWDELAY
|
||||
{"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
|
||||
#endif
|
||||
#ifdef IPTOS_THROUGHPUT
|
||||
{"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
|
||||
#endif
|
||||
#ifdef SO_REUSEPORT
|
||||
{"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
|
||||
#endif
|
||||
#ifdef SO_SNDBUF
|
||||
{"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
|
||||
#endif
|
||||
#ifdef SO_RCVBUF
|
||||
{"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
|
||||
#endif
|
||||
#ifdef SO_SNDLOWAT
|
||||
{"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
|
||||
#endif
|
||||
#ifdef SO_RCVLOWAT
|
||||
{"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
|
||||
#endif
|
||||
#ifdef SO_SNDTIMEO
|
||||
{"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
|
||||
#endif
|
||||
#ifdef SO_RCVTIMEO
|
||||
{"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
|
||||
#endif
|
||||
{NULL,0,0,0,0}};
|
||||
|
||||
|
||||
/**
|
||||
Set user socket options.
|
||||
**/
|
||||
_PUBLIC_ void set_socket_options(int fd, const char *options)
|
||||
{
|
||||
const char **options_list = str_list_make(NULL, options, " \t,");
|
||||
int j;
|
||||
|
||||
if (!options_list)
|
||||
return;
|
||||
|
||||
for (j = 0; options_list[j]; j++) {
|
||||
const char *tok = options_list[j];
|
||||
int ret=0,i;
|
||||
int value = 1;
|
||||
char *p;
|
||||
BOOL got_value = False;
|
||||
|
||||
if ((p = strchr(tok,'='))) {
|
||||
*p = 0;
|
||||
value = atoi(p+1);
|
||||
got_value = True;
|
||||
}
|
||||
|
||||
for (i=0;socket_options[i].name;i++)
|
||||
if (strequal(socket_options[i].name,tok))
|
||||
break;
|
||||
|
||||
if (!socket_options[i].name) {
|
||||
DEBUG(0,("Unknown socket option %s\n",tok));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (socket_options[i].opttype) {
|
||||
case OPT_BOOL:
|
||||
case OPT_INT:
|
||||
ret = setsockopt(fd,socket_options[i].level,
|
||||
socket_options[i].option,(char *)&value,sizeof(int));
|
||||
break;
|
||||
|
||||
case OPT_ON:
|
||||
if (got_value)
|
||||
DEBUG(0,("syntax error - %s does not take a value\n",tok));
|
||||
|
||||
{
|
||||
int on = socket_options[i].value;
|
||||
ret = setsockopt(fd,socket_options[i].level,
|
||||
socket_options[i].option,(char *)&on,sizeof(int));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
|
||||
}
|
||||
|
||||
talloc_free(options_list);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Socket functions
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SAMBA_SOCKET_H
|
||||
#define _SAMBA_SOCKET_H
|
||||
|
||||
#include "lib/events/events.h"
|
||||
|
||||
struct socket_context;
|
||||
|
||||
enum socket_type {
|
||||
SOCKET_TYPE_STREAM,
|
||||
SOCKET_TYPE_DGRAM
|
||||
};
|
||||
|
||||
struct socket_address {
|
||||
const char *family;
|
||||
char *addr;
|
||||
int port;
|
||||
struct sockaddr *sockaddr;
|
||||
size_t sockaddrlen;
|
||||
};
|
||||
|
||||
struct socket_ops {
|
||||
const char *name;
|
||||
|
||||
NTSTATUS (*fn_init)(struct socket_context *sock);
|
||||
|
||||
/* client ops */
|
||||
NTSTATUS (*fn_connect)(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *server_address,
|
||||
uint32_t flags);
|
||||
|
||||
/* complete a non-blocking connect */
|
||||
NTSTATUS (*fn_connect_complete)(struct socket_context *sock,
|
||||
uint32_t flags);
|
||||
|
||||
/* server ops */
|
||||
NTSTATUS (*fn_listen)(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags);
|
||||
NTSTATUS (*fn_accept)(struct socket_context *sock,
|
||||
struct socket_context **new_sock);
|
||||
|
||||
/* general ops */
|
||||
NTSTATUS (*fn_recv)(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread);
|
||||
NTSTATUS (*fn_send)(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen);
|
||||
|
||||
NTSTATUS (*fn_sendto)(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen,
|
||||
const struct socket_address *dest_addr);
|
||||
NTSTATUS (*fn_recvfrom)(struct socket_context *sock,
|
||||
void *buf, size_t wantlen, size_t *nread,
|
||||
TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
|
||||
NTSTATUS (*fn_pending)(struct socket_context *sock, size_t *npending);
|
||||
|
||||
void (*fn_close)(struct socket_context *sock);
|
||||
|
||||
NTSTATUS (*fn_set_option)(struct socket_context *sock, const char *option, const char *val);
|
||||
|
||||
char *(*fn_get_peer_name)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
struct socket_address *(*fn_get_peer_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
struct socket_address *(*fn_get_my_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
|
||||
int (*fn_get_fd)(struct socket_context *sock);
|
||||
};
|
||||
|
||||
enum socket_state {
|
||||
SOCKET_STATE_UNDEFINED,
|
||||
|
||||
SOCKET_STATE_CLIENT_START,
|
||||
SOCKET_STATE_CLIENT_CONNECTED,
|
||||
SOCKET_STATE_CLIENT_STARTTLS,
|
||||
SOCKET_STATE_CLIENT_ERROR,
|
||||
|
||||
SOCKET_STATE_SERVER_LISTEN,
|
||||
SOCKET_STATE_SERVER_CONNECTED,
|
||||
SOCKET_STATE_SERVER_STARTTLS,
|
||||
SOCKET_STATE_SERVER_ERROR
|
||||
};
|
||||
|
||||
#define SOCKET_FLAG_BLOCK 0x00000001
|
||||
#define SOCKET_FLAG_PEEK 0x00000002
|
||||
#define SOCKET_FLAG_TESTNONBLOCK 0x00000004
|
||||
#define SOCKET_FLAG_ENCRYPT 0x00000008 /* This socket
|
||||
* implementation requires
|
||||
* that re-sends be
|
||||
* consistant, because it
|
||||
* is encrypting data.
|
||||
* This modifies the
|
||||
* TESTNONBLOCK case */
|
||||
|
||||
struct socket_context {
|
||||
enum socket_type type;
|
||||
enum socket_state state;
|
||||
uint32_t flags;
|
||||
|
||||
int fd;
|
||||
|
||||
void *private_data;
|
||||
const struct socket_ops *ops;
|
||||
const char *backend_name;
|
||||
};
|
||||
|
||||
|
||||
/* prototypes */
|
||||
NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
|
||||
struct socket_context **new_sock,
|
||||
enum socket_type type, uint32_t flags);
|
||||
NTSTATUS socket_create(const char *name, enum socket_type type,
|
||||
struct socket_context **new_sock, uint32_t flags);
|
||||
NTSTATUS socket_connect(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *server_address,
|
||||
uint32_t flags);
|
||||
NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags);
|
||||
NTSTATUS socket_listen(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags);
|
||||
NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock);
|
||||
NTSTATUS socket_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread);
|
||||
NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread,
|
||||
TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
|
||||
NTSTATUS socket_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen);
|
||||
NTSTATUS socket_sendto(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen,
|
||||
const struct socket_address *dest_addr);
|
||||
NTSTATUS socket_pending(struct socket_context *sock, size_t *npending);
|
||||
NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val);
|
||||
char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
|
||||
int socket_get_fd(struct socket_context *sock);
|
||||
NTSTATUS socket_dup(struct socket_context *sock);
|
||||
struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
|
||||
const char *type,
|
||||
const char *host,
|
||||
int port);
|
||||
struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
|
||||
struct sockaddr *sockaddr,
|
||||
size_t addrlen);
|
||||
const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type);
|
||||
BOOL allow_access(TALLOC_CTX *mem_ctx,
|
||||
const char **deny_list, const char **allow_list,
|
||||
const char *cname, const char *caddr);
|
||||
BOOL socket_check_access(struct socket_context *sock,
|
||||
const char *service_name,
|
||||
const char **allow_list, const char **deny_list);
|
||||
|
||||
struct composite_context *socket_connect_send(struct socket_context *sock,
|
||||
struct socket_address *my_address,
|
||||
struct socket_address *server_address,
|
||||
uint32_t flags,
|
||||
struct event_context *event_ctx);
|
||||
NTSTATUS socket_connect_recv(struct composite_context *ctx);
|
||||
NTSTATUS socket_connect_ev(struct socket_context *sock,
|
||||
struct socket_address *my_address,
|
||||
struct socket_address *server_address,
|
||||
uint32_t flags, struct event_context *ev);
|
||||
|
||||
struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
|
||||
const char *server_address,
|
||||
int num_server_ports,
|
||||
uint16_t *server_ports,
|
||||
struct event_context *event_ctx);
|
||||
NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct socket_context **result,
|
||||
uint16_t *port);
|
||||
NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, const char *server_address,
|
||||
int num_server_ports, uint16_t *server_ports,
|
||||
struct event_context *event_ctx,
|
||||
struct socket_context **result,
|
||||
uint16_t *port);
|
||||
void set_socket_options(int fd, const char *options);
|
||||
|
||||
#endif /* _SAMBA_SOCKET_H */
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Socket IPv4 functions
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Andrew Tridgell 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/network.h"
|
||||
|
||||
static NTSTATUS ipv4_init(struct socket_context *sock)
|
||||
{
|
||||
int type;
|
||||
|
||||
switch (sock->type) {
|
||||
case SOCKET_TYPE_STREAM:
|
||||
type = SOCK_STREAM;
|
||||
break;
|
||||
case SOCKET_TYPE_DGRAM:
|
||||
type = SOCK_DGRAM;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
sock->fd = socket(PF_INET, type, 0);
|
||||
if (sock->fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
sock->backend_name = "ipv4";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void ipv4_close(struct socket_context *sock)
|
||||
{
|
||||
close(sock->fd);
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_connect_complete(struct socket_context *sock, uint32_t flags)
|
||||
{
|
||||
int error=0, ret;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
/* check for any errors that may have occurred - this is needed
|
||||
for non-blocking connect */
|
||||
ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
if (error != 0) {
|
||||
return map_nt_error_from_unix(error);
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state = SOCKET_STATE_CLIENT_CONNECTED;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS ipv4_connect(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *srv_address,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct sockaddr_in srv_addr;
|
||||
struct ipv4_addr my_ip;
|
||||
struct ipv4_addr srv_ip;
|
||||
int ret;
|
||||
|
||||
if (my_address && my_address->sockaddr) {
|
||||
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
} else if (my_address) {
|
||||
my_ip = interpret_addr2(my_address->addr);
|
||||
|
||||
if (my_ip.addr != 0 || my_address->port != 0) {
|
||||
struct sockaddr_in my_addr;
|
||||
ZERO_STRUCT(my_addr);
|
||||
#ifdef HAVE_SOCK_SIN_LEN
|
||||
my_addr.sin_len = sizeof(my_addr);
|
||||
#endif
|
||||
my_addr.sin_addr.s_addr = my_ip.addr;
|
||||
my_addr.sin_port = htons(my_address->port);
|
||||
my_addr.sin_family = PF_INET;
|
||||
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (srv_address->sockaddr) {
|
||||
ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
} else {
|
||||
srv_ip = interpret_addr2(srv_address->addr);
|
||||
if (!srv_ip.addr) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(srv_addr);
|
||||
#ifdef HAVE_SOCK_SIN_LEN
|
||||
srv_addr.sin_len = sizeof(srv_addr);
|
||||
#endif
|
||||
srv_addr.sin_addr.s_addr= srv_ip.addr;
|
||||
srv_addr.sin_port = htons(srv_address->port);
|
||||
srv_addr.sin_family = PF_INET;
|
||||
|
||||
ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
return ipv4_connect_complete(sock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
note that for simplicity of the API, socket_listen() is also
|
||||
use for DGRAM sockets, but in reality only a bind() is done
|
||||
*/
|
||||
static NTSTATUS ipv4_listen(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags)
|
||||
{
|
||||
struct sockaddr_in my_addr;
|
||||
struct ipv4_addr ip_addr;
|
||||
int ret;
|
||||
|
||||
socket_set_option(sock, "SO_REUSEADDR=1", NULL);
|
||||
|
||||
if (my_address->sockaddr) {
|
||||
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
|
||||
} else {
|
||||
ip_addr = interpret_addr2(my_address->addr);
|
||||
|
||||
ZERO_STRUCT(my_addr);
|
||||
#ifdef HAVE_SOCK_SIN_LEN
|
||||
my_addr.sin_len = sizeof(my_addr);
|
||||
#endif
|
||||
my_addr.sin_addr.s_addr = ip_addr.addr;
|
||||
my_addr.sin_port = htons(my_address->port);
|
||||
my_addr.sin_family = PF_INET;
|
||||
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
if (sock->type == SOCKET_TYPE_STREAM) {
|
||||
ret = listen(sock->fd, queue_size);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state= SOCKET_STATE_SERVER_LISTEN;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
|
||||
{
|
||||
struct sockaddr_in cli_addr;
|
||||
socklen_t cli_addr_len = sizeof(cli_addr);
|
||||
int new_fd;
|
||||
|
||||
if (sock->type != SOCKET_TYPE_STREAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
|
||||
if (new_fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
|
||||
int ret = set_blocking(new_fd, False);
|
||||
if (ret == -1) {
|
||||
close(new_fd);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: we could add a 'accept_check' hook here
|
||||
* which get the black/white lists via socket_set_accept_filter()
|
||||
* or something like that
|
||||
* --metze
|
||||
*/
|
||||
|
||||
(*new_sock) = talloc(NULL, struct socket_context);
|
||||
if (!(*new_sock)) {
|
||||
close(new_fd);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* copy the socket_context */
|
||||
(*new_sock)->type = sock->type;
|
||||
(*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
|
||||
(*new_sock)->flags = sock->flags;
|
||||
|
||||
(*new_sock)->fd = new_fd;
|
||||
|
||||
(*new_sock)->private_data = NULL;
|
||||
(*new_sock)->ops = sock->ops;
|
||||
(*new_sock)->backend_name = sock->backend_name;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
ssize_t gotlen;
|
||||
|
||||
*nread = 0;
|
||||
|
||||
gotlen = recv(sock->fd, buf, wantlen, 0);
|
||||
if (gotlen == 0) {
|
||||
return NT_STATUS_END_OF_FILE;
|
||||
} else if (gotlen == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*nread = gotlen;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread,
|
||||
TALLOC_CTX *addr_ctx, struct socket_address **_src)
|
||||
{
|
||||
ssize_t gotlen;
|
||||
struct sockaddr_in *from_addr;
|
||||
socklen_t from_len = sizeof(*from_addr);
|
||||
struct socket_address *src;
|
||||
const char *addr;
|
||||
|
||||
src = talloc(addr_ctx, struct socket_address);
|
||||
if (!src) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
src->family = sock->backend_name;
|
||||
|
||||
from_addr = talloc(src, struct sockaddr_in);
|
||||
if (!from_addr) {
|
||||
talloc_free(src);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
src->sockaddr = (struct sockaddr *)from_addr;
|
||||
|
||||
*nread = 0;
|
||||
|
||||
gotlen = recvfrom(sock->fd, buf, wantlen, 0,
|
||||
src->sockaddr, &from_len);
|
||||
if (gotlen == 0) {
|
||||
talloc_free(src);
|
||||
return NT_STATUS_END_OF_FILE;
|
||||
} else if (gotlen == -1) {
|
||||
talloc_free(src);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
src->sockaddrlen = from_len;
|
||||
|
||||
addr = inet_ntoa(from_addr->sin_addr);
|
||||
if (addr == NULL) {
|
||||
talloc_free(src);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
src->addr = talloc_strdup(src, addr);
|
||||
if (src->addr == NULL) {
|
||||
talloc_free(src);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
src->port = ntohs(from_addr->sin_port);
|
||||
|
||||
*nread = gotlen;
|
||||
*_src = src;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
len = send(sock->fd, blob->data, blob->length, 0);
|
||||
if (len == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*sendlen = len;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_sendto(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen,
|
||||
const struct socket_address *dest_addr)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
if (dest_addr->sockaddr) {
|
||||
len = sendto(sock->fd, blob->data, blob->length, 0,
|
||||
dest_addr->sockaddr, dest_addr->sockaddrlen);
|
||||
} else {
|
||||
struct sockaddr_in srv_addr;
|
||||
struct ipv4_addr addr;
|
||||
|
||||
ZERO_STRUCT(srv_addr);
|
||||
#ifdef HAVE_SOCK_SIN_LEN
|
||||
srv_addr.sin_len = sizeof(srv_addr);
|
||||
#endif
|
||||
addr = interpret_addr2(dest_addr->addr);
|
||||
srv_addr.sin_addr.s_addr = addr.addr;
|
||||
srv_addr.sin_port = htons(dest_addr->port);
|
||||
srv_addr.sin_family = PF_INET;
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
len = sendto(sock->fd, blob->data, blob->length, 0,
|
||||
(struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
}
|
||||
if (len == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*sendlen = len;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
set_socket_options(sock->fd, option);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in peer_addr;
|
||||
socklen_t len = sizeof(peer_addr);
|
||||
struct hostent *he;
|
||||
int ret;
|
||||
|
||||
ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
|
||||
if (ret == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
|
||||
if (he == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return talloc_strdup(mem_ctx, he->h_name);
|
||||
}
|
||||
|
||||
static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in *peer_addr;
|
||||
socklen_t len = sizeof(*peer_addr);
|
||||
const char *addr;
|
||||
struct socket_address *peer;
|
||||
int ret;
|
||||
|
||||
peer = talloc(mem_ctx, struct socket_address);
|
||||
if (!peer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->family = sock->backend_name;
|
||||
peer_addr = talloc(peer, struct sockaddr_in);
|
||||
if (!peer_addr) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddr = (struct sockaddr *)peer_addr;
|
||||
|
||||
ret = getpeername(sock->fd, peer->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddrlen = len;
|
||||
|
||||
addr = inet_ntoa(peer_addr->sin_addr);
|
||||
if (addr == NULL) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
peer->addr = talloc_strdup(peer, addr);
|
||||
if (!peer->addr) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
peer->port = ntohs(peer_addr->sin_port);
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in *local_addr;
|
||||
socklen_t len = sizeof(*local_addr);
|
||||
const char *addr;
|
||||
struct socket_address *local;
|
||||
int ret;
|
||||
|
||||
local = talloc(mem_ctx, struct socket_address);
|
||||
if (!local) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->family = sock->backend_name;
|
||||
local_addr = talloc(local, struct sockaddr_in);
|
||||
if (!local_addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddr = (struct sockaddr *)local_addr;
|
||||
|
||||
ret = getsockname(sock->fd, local->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddrlen = len;
|
||||
|
||||
addr = inet_ntoa(local_addr->sin_addr);
|
||||
if (addr == NULL) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
local->addr = talloc_strdup(local, addr);
|
||||
if (!local->addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
local->port = ntohs(local_addr->sin_port);
|
||||
|
||||
return local;
|
||||
}
|
||||
static int ipv4_get_fd(struct socket_context *sock)
|
||||
{
|
||||
return sock->fd;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv4_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
int value = 0;
|
||||
if (ioctl(sock->fd, FIONREAD, &value) == 0) {
|
||||
*npending = value;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
static const struct socket_ops ipv4_ops = {
|
||||
.name = "ipv4",
|
||||
.fn_init = ipv4_init,
|
||||
.fn_connect = ipv4_connect,
|
||||
.fn_connect_complete = ipv4_connect_complete,
|
||||
.fn_listen = ipv4_listen,
|
||||
.fn_accept = ipv4_accept,
|
||||
.fn_recv = ipv4_recv,
|
||||
.fn_recvfrom = ipv4_recvfrom,
|
||||
.fn_send = ipv4_send,
|
||||
.fn_sendto = ipv4_sendto,
|
||||
.fn_pending = ipv4_pending,
|
||||
.fn_close = ipv4_close,
|
||||
|
||||
.fn_set_option = ipv4_set_option,
|
||||
|
||||
.fn_get_peer_name = ipv4_get_peer_name,
|
||||
.fn_get_peer_addr = ipv4_get_peer_addr,
|
||||
.fn_get_my_addr = ipv4_get_my_addr,
|
||||
.fn_get_fd = ipv4_get_fd
|
||||
};
|
||||
|
||||
const struct socket_ops *socket_ipv4_ops(enum socket_type type)
|
||||
{
|
||||
return &ipv4_ops;
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Socket IPv6 functions
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Jelmer Vernooij 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/filesys.h" /* needed for close() */
|
||||
#include "system/network.h"
|
||||
|
||||
static struct in6_addr interpret_addr6(const char *name)
|
||||
{
|
||||
struct hostent *he;
|
||||
|
||||
if (name == NULL) return in6addr_any;
|
||||
|
||||
if (strcasecmp(name, "localhost") == 0) {
|
||||
name = "::1";
|
||||
}
|
||||
|
||||
he = gethostbyname2(name, PF_INET6);
|
||||
|
||||
if (he == NULL) return in6addr_any;
|
||||
|
||||
return *((struct in6_addr *)he->h_addr);
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_init(struct socket_context *sock)
|
||||
{
|
||||
sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
|
||||
if (sock->fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
sock->backend_name = "ipv6";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void ipv6_tcp_close(struct socket_context *sock)
|
||||
{
|
||||
close(sock->fd);
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_connect_complete(struct socket_context *sock, uint32_t flags)
|
||||
{
|
||||
int error=0, ret;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
/* check for any errors that may have occurred - this is needed
|
||||
for non-blocking connect */
|
||||
ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
if (error != 0) {
|
||||
return map_nt_error_from_unix(error);
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state = SOCKET_STATE_CLIENT_CONNECTED;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *srv_address,
|
||||
uint32_t flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (my_address && my_address->sockaddr) {
|
||||
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
} else if (my_address) {
|
||||
struct in6_addr my_ip;
|
||||
my_ip = interpret_addr6(my_address->addr);
|
||||
|
||||
if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
|
||||
struct sockaddr_in6 my_addr;
|
||||
ZERO_STRUCT(my_addr);
|
||||
my_addr.sin6_addr = my_ip;
|
||||
my_addr.sin6_port = htons(my_address->port);
|
||||
my_addr.sin6_family = PF_INET6;
|
||||
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (srv_address->sockaddr) {
|
||||
ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
|
||||
} else {
|
||||
struct in6_addr srv_ip;
|
||||
struct sockaddr_in6 srv_addr;
|
||||
srv_ip = interpret_addr6(srv_address->addr);
|
||||
if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(srv_addr);
|
||||
srv_addr.sin6_addr = srv_ip;
|
||||
srv_addr.sin6_port = htons(srv_address->port);
|
||||
srv_addr.sin6_family = PF_INET6;
|
||||
|
||||
ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
}
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
return ipv6_tcp_connect_complete(sock, flags);
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_listen(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags)
|
||||
{
|
||||
struct sockaddr_in6 my_addr;
|
||||
struct in6_addr ip_addr;
|
||||
int ret;
|
||||
|
||||
socket_set_option(sock, "SO_REUSEADDR=1", NULL);
|
||||
|
||||
if (my_address->sockaddr) {
|
||||
ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
|
||||
} else {
|
||||
ip_addr = interpret_addr6(my_address->addr);
|
||||
|
||||
ZERO_STRUCT(my_addr);
|
||||
my_addr.sin6_addr = ip_addr;
|
||||
my_addr.sin6_port = htons(my_address->port);
|
||||
my_addr.sin6_family = PF_INET6;
|
||||
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
ret = listen(sock->fd, queue_size);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state= SOCKET_STATE_SERVER_LISTEN;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
|
||||
{
|
||||
struct sockaddr_in cli_addr;
|
||||
socklen_t cli_addr_len = sizeof(cli_addr);
|
||||
int new_fd;
|
||||
|
||||
new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
|
||||
if (new_fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
|
||||
int ret = set_blocking(new_fd, False);
|
||||
if (ret == -1) {
|
||||
close(new_fd);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: we could add a 'accept_check' hook here
|
||||
* which get the black/white lists via socket_set_accept_filter()
|
||||
* or something like that
|
||||
* --metze
|
||||
*/
|
||||
|
||||
(*new_sock) = talloc(NULL, struct socket_context);
|
||||
if (!(*new_sock)) {
|
||||
close(new_fd);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* copy the socket_context */
|
||||
(*new_sock)->type = sock->type;
|
||||
(*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
|
||||
(*new_sock)->flags = sock->flags;
|
||||
|
||||
(*new_sock)->fd = new_fd;
|
||||
|
||||
(*new_sock)->private_data = NULL;
|
||||
(*new_sock)->ops = sock->ops;
|
||||
(*new_sock)->backend_name = sock->backend_name;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
ssize_t gotlen;
|
||||
|
||||
*nread = 0;
|
||||
|
||||
gotlen = recv(sock->fd, buf, wantlen, 0);
|
||||
if (gotlen == 0) {
|
||||
return NT_STATUS_END_OF_FILE;
|
||||
} else if (gotlen == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*nread = gotlen;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
len = send(sock->fd, blob->data, blob->length, 0);
|
||||
if (len == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*sendlen = len;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS ipv6_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
set_socket_options(sock->fd, option);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in6 peer_addr;
|
||||
socklen_t len = sizeof(peer_addr);
|
||||
struct hostent *he;
|
||||
int ret;
|
||||
|
||||
ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
|
||||
if (ret == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
|
||||
if (he == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return talloc_strdup(mem_ctx, he->h_name);
|
||||
}
|
||||
|
||||
static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in6 *peer_addr;
|
||||
socklen_t len = sizeof(*peer_addr);
|
||||
struct socket_address *peer;
|
||||
int ret;
|
||||
char addr[128];
|
||||
const char *addr_ret;
|
||||
|
||||
peer = talloc(mem_ctx, struct socket_address);
|
||||
if (!peer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->family = sock->backend_name;
|
||||
peer_addr = talloc(peer, struct sockaddr_in6);
|
||||
if (!peer_addr) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddr = (struct sockaddr *)peer_addr;
|
||||
|
||||
ret = getpeername(sock->fd, peer->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddrlen = len;
|
||||
|
||||
addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
|
||||
if (addr_ret == NULL) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->addr = talloc_strdup(peer, addr_ret);
|
||||
if (peer->addr == NULL) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->port = ntohs(peer_addr->sin6_port);
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in6 *local_addr;
|
||||
socklen_t len = sizeof(*local_addr);
|
||||
struct socket_address *local;
|
||||
int ret;
|
||||
struct hostent *he;
|
||||
|
||||
local = talloc(mem_ctx, struct socket_address);
|
||||
if (!local) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->family = sock->backend_name;
|
||||
local_addr = talloc(local, struct sockaddr_in6);
|
||||
if (!local_addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddr = (struct sockaddr *)local_addr;
|
||||
|
||||
ret = getsockname(sock->fd, local->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddrlen = len;
|
||||
|
||||
he = gethostbyaddr((char *)&local_addr->sin6_addr, len, AF_INET6);
|
||||
|
||||
if (!he || !he->h_name) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->addr = talloc_strdup(mem_ctx, he->h_name);
|
||||
if (!local->addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
local->port = ntohs(local_addr->sin6_port);
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
static int ipv6_tcp_get_fd(struct socket_context *sock)
|
||||
{
|
||||
return sock->fd;
|
||||
}
|
||||
|
||||
static const struct socket_ops ipv6_tcp_ops = {
|
||||
.name = "ipv6",
|
||||
.fn_init = ipv6_tcp_init,
|
||||
.fn_connect = ipv6_tcp_connect,
|
||||
.fn_connect_complete = ipv6_tcp_connect_complete,
|
||||
.fn_listen = ipv6_tcp_listen,
|
||||
.fn_accept = ipv6_tcp_accept,
|
||||
.fn_recv = ipv6_tcp_recv,
|
||||
.fn_send = ipv6_tcp_send,
|
||||
.fn_close = ipv6_tcp_close,
|
||||
|
||||
.fn_set_option = ipv6_tcp_set_option,
|
||||
|
||||
.fn_get_peer_name = ipv6_tcp_get_peer_name,
|
||||
.fn_get_peer_addr = ipv6_tcp_get_peer_addr,
|
||||
.fn_get_my_addr = ipv6_tcp_get_my_addr,
|
||||
|
||||
.fn_get_fd = ipv6_tcp_get_fd
|
||||
};
|
||||
|
||||
const struct socket_ops *socket_ipv6_ops(enum socket_type type)
|
||||
{
|
||||
if (type != SOCKET_TYPE_STREAM) {
|
||||
return NULL;
|
||||
}
|
||||
return &ipv6_tcp_ops;
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
unix domain socket functions
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Andrew Tridgell 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/network.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
approximate errno mapping
|
||||
*/
|
||||
static NTSTATUS unixdom_error(int ernum)
|
||||
{
|
||||
return map_nt_error_from_unix(ernum);
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_init(struct socket_context *sock)
|
||||
{
|
||||
int type;
|
||||
|
||||
switch (sock->type) {
|
||||
case SOCKET_TYPE_STREAM:
|
||||
type = SOCK_STREAM;
|
||||
break;
|
||||
case SOCKET_TYPE_DGRAM:
|
||||
type = SOCK_DGRAM;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
sock->fd = socket(PF_UNIX, type, 0);
|
||||
if (sock->fd == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
sock->private_data = NULL;
|
||||
|
||||
sock->backend_name = "unix";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static void unixdom_close(struct socket_context *sock)
|
||||
{
|
||||
close(sock->fd);
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
|
||||
{
|
||||
int error=0, ret;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
/* check for any errors that may have occurred - this is needed
|
||||
for non-blocking connect */
|
||||
ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
if (error != 0) {
|
||||
return map_nt_error_from_unix(error);
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state = SOCKET_STATE_CLIENT_CONNECTED;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_connect(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
const struct socket_address *srv_address,
|
||||
uint32_t flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (srv_address->sockaddr) {
|
||||
ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
|
||||
} else {
|
||||
struct sockaddr_un srv_addr;
|
||||
if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) {
|
||||
return NT_STATUS_OBJECT_PATH_INVALID;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(srv_addr);
|
||||
srv_addr.sun_family = AF_UNIX;
|
||||
strncpy(srv_addr.sun_path, srv_address->addr, sizeof(srv_addr.sun_path));
|
||||
|
||||
ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
}
|
||||
if (ret == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
|
||||
return unixdom_connect_complete(sock, flags);
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_listen(struct socket_context *sock,
|
||||
const struct socket_address *my_address,
|
||||
int queue_size, uint32_t flags)
|
||||
{
|
||||
struct sockaddr_un my_addr;
|
||||
int ret;
|
||||
|
||||
/* delete if it already exists */
|
||||
if (my_address->addr) {
|
||||
unlink(my_address->addr);
|
||||
}
|
||||
|
||||
if (my_address->sockaddr) {
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
} else if (my_address->addr == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
} else {
|
||||
if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) {
|
||||
return NT_STATUS_OBJECT_PATH_INVALID;
|
||||
}
|
||||
|
||||
|
||||
ZERO_STRUCT(my_addr);
|
||||
my_addr.sun_family = AF_UNIX;
|
||||
strncpy(my_addr.sun_path, my_address->addr, sizeof(my_addr.sun_path));
|
||||
|
||||
ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
}
|
||||
if (ret == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
|
||||
if (sock->type == SOCKET_TYPE_STREAM) {
|
||||
ret = listen(sock->fd, queue_size);
|
||||
if (ret == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & SOCKET_FLAG_BLOCK)) {
|
||||
ret = set_blocking(sock->fd, False);
|
||||
if (ret == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
}
|
||||
|
||||
sock->state = SOCKET_STATE_SERVER_LISTEN;
|
||||
sock->private_data = (void *)talloc_strdup(sock, my_address->addr);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_accept(struct socket_context *sock,
|
||||
struct socket_context **new_sock)
|
||||
{
|
||||
struct sockaddr_un cli_addr;
|
||||
socklen_t cli_addr_len = sizeof(cli_addr);
|
||||
int new_fd;
|
||||
|
||||
if (sock->type != SOCKET_TYPE_STREAM) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
|
||||
if (new_fd == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
|
||||
if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
|
||||
int ret = set_blocking(new_fd, False);
|
||||
if (ret == -1) {
|
||||
close(new_fd);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
}
|
||||
|
||||
(*new_sock) = talloc(NULL, struct socket_context);
|
||||
if (!(*new_sock)) {
|
||||
close(new_fd);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* copy the socket_context */
|
||||
(*new_sock)->type = sock->type;
|
||||
(*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
|
||||
(*new_sock)->flags = sock->flags;
|
||||
|
||||
(*new_sock)->fd = new_fd;
|
||||
|
||||
(*new_sock)->private_data = NULL;
|
||||
(*new_sock)->ops = sock->ops;
|
||||
(*new_sock)->backend_name = sock->backend_name;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
ssize_t gotlen;
|
||||
|
||||
*nread = 0;
|
||||
|
||||
gotlen = recv(sock->fd, buf, wantlen, 0);
|
||||
if (gotlen == 0) {
|
||||
return NT_STATUS_END_OF_FILE;
|
||||
} else if (gotlen == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
|
||||
*nread = gotlen;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
len = send(sock->fd, blob->data, blob->length, 0);
|
||||
if (len == -1) {
|
||||
return unixdom_error(errno);
|
||||
}
|
||||
|
||||
*sendlen = len;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS unixdom_sendto(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen,
|
||||
const struct socket_address *dest)
|
||||
{
|
||||
ssize_t len;
|
||||
*sendlen = 0;
|
||||
|
||||
if (dest->sockaddr) {
|
||||
len = sendto(sock->fd, blob->data, blob->length, 0,
|
||||
dest->sockaddr, dest->sockaddrlen);
|
||||
} else {
|
||||
struct sockaddr_un srv_addr;
|
||||
|
||||
if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) {
|
||||
return NT_STATUS_OBJECT_PATH_INVALID;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(srv_addr);
|
||||
srv_addr.sun_family = AF_UNIX;
|
||||
strncpy(srv_addr.sun_path, dest->addr, sizeof(srv_addr.sun_path));
|
||||
|
||||
len = sendto(sock->fd, blob->data, blob->length, 0,
|
||||
(struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
}
|
||||
if (len == -1) {
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
*sendlen = len;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS unixdom_set_option(struct socket_context *sock,
|
||||
const char *option, const char *val)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
return talloc_strdup(mem_ctx, "LOCAL/unixdom");
|
||||
}
|
||||
|
||||
static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in *peer_addr;
|
||||
socklen_t len = sizeof(*peer_addr);
|
||||
struct socket_address *peer;
|
||||
int ret;
|
||||
|
||||
peer = talloc(mem_ctx, struct socket_address);
|
||||
if (!peer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->family = sock->backend_name;
|
||||
peer_addr = talloc(peer, struct sockaddr_in);
|
||||
if (!peer_addr) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddr = (struct sockaddr *)peer_addr;
|
||||
|
||||
ret = getpeername(sock->fd, peer->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer->sockaddrlen = len;
|
||||
|
||||
peer->port = 0;
|
||||
peer->addr = talloc_strdup(peer, "LOCAL/unixdom");
|
||||
if (!peer->addr) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct sockaddr_in *local_addr;
|
||||
socklen_t len = sizeof(*local_addr);
|
||||
struct socket_address *local;
|
||||
int ret;
|
||||
|
||||
local = talloc(mem_ctx, struct socket_address);
|
||||
if (!local) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->family = sock->backend_name;
|
||||
local_addr = talloc(local, struct sockaddr_in);
|
||||
if (!local_addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddr = (struct sockaddr *)local_addr;
|
||||
|
||||
ret = getsockname(sock->fd, local->sockaddr, &len);
|
||||
if (ret == -1) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local->sockaddrlen = len;
|
||||
|
||||
local->port = 0;
|
||||
local->addr = talloc_strdup(local, "LOCAL/unixdom");
|
||||
if (!local->addr) {
|
||||
talloc_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
static int unixdom_get_fd(struct socket_context *sock)
|
||||
{
|
||||
return sock->fd;
|
||||
}
|
||||
|
||||
static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
int value = 0;
|
||||
if (ioctl(sock->fd, FIONREAD, &value) == 0) {
|
||||
*npending = value;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
static const struct socket_ops unixdom_ops = {
|
||||
.name = "unix",
|
||||
.fn_init = unixdom_init,
|
||||
.fn_connect = unixdom_connect,
|
||||
.fn_connect_complete = unixdom_connect_complete,
|
||||
.fn_listen = unixdom_listen,
|
||||
.fn_accept = unixdom_accept,
|
||||
.fn_recv = unixdom_recv,
|
||||
.fn_send = unixdom_send,
|
||||
.fn_sendto = unixdom_sendto,
|
||||
.fn_close = unixdom_close,
|
||||
.fn_pending = unixdom_pending,
|
||||
|
||||
.fn_set_option = unixdom_set_option,
|
||||
|
||||
.fn_get_peer_name = unixdom_get_peer_name,
|
||||
.fn_get_peer_addr = unixdom_get_peer_addr,
|
||||
.fn_get_my_addr = unixdom_get_my_addr,
|
||||
|
||||
.fn_get_fd = unixdom_get_fd
|
||||
};
|
||||
|
||||
const struct socket_ops *socket_unixdom_ops(enum socket_type type)
|
||||
{
|
||||
return &unixdom_ops;
|
||||
}
|
||||
Reference in New Issue
Block a user