wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+362
View File
@@ -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;
}
+143
View File
@@ -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
+59
View File
@@ -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
################################################
+216
View File
@@ -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);
}
+275
View File
@@ -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);
}
+354
View File
@@ -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));
}
+411
View File
@@ -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
+33
View File
@@ -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
+549
View File
@@ -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);
}
+200
View File
@@ -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 */
+537
View File
@@ -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;
}
+419
View File
@@ -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;
}
+421
View File
@@ -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;
}