mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
89d5c0dc5c
Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
482 lines
19 KiB
Plaintext
482 lines
19 KiB
Plaintext
|
|
Basic design of the tsocket abstraction
|
|
=======================================
|
|
|
|
The tsocket abstraction is split into two
|
|
different kinds of communication interfaces.
|
|
|
|
There is the "tstream_context" interface which abstracts
|
|
the communication through a bidirectional
|
|
byte stream between two endpoints.
|
|
|
|
And there is the "tdgram_context" interface
|
|
which abstracts datagram based communication between any
|
|
number of endpoints.
|
|
|
|
Both interfaces share the "tsocket_address" abstraction
|
|
for endpoint addresses.
|
|
|
|
The whole library is based on the talloc(3) and 'tevent' libraries
|
|
and provides "tevent_req" based "foo_send()"/"foo_recv()" functions pairs
|
|
for all abstracted methods that need to be async.
|
|
|
|
The tsocket_address abstraction
|
|
===============================
|
|
|
|
A tsocket_address represents a generic socket endpoint.
|
|
It behaves like an abstract class, therefore it has no direct constructor.
|
|
Constructors are described in later sections of this document.
|
|
|
|
A function to get the string representation of an endpoint for debugging is
|
|
available but callers SHOULD NOT try to parse this string. To get more
|
|
details, callers should use getter methods of the specific tsocket_address
|
|
implementation.
|
|
|
|
char *tsocket_address_string(const struct tsocket_address *addr,
|
|
TALLOC_CTX *mem_ctx);
|
|
|
|
A function to create a copy of the tsocket_address is also available.
|
|
This is useful before doing modifications to a socket
|
|
via additional methods of the specific tsocket_address implementation.
|
|
|
|
struct tsocket_address *tsocket_address_copy(const struct tsocket_address *addr,
|
|
TALLOC_CTX *mem_ctx);
|
|
|
|
The tdgram_context abstraction
|
|
==============================
|
|
|
|
The tdgram_context is like an abstract class for datagram
|
|
based sockets. The interface provides async 'tevent_req' based
|
|
functions similar to recvfrom(2)/sendto(2)/close(2) syscalls.
|
|
|
|
The tdgram_recvfrom_send() method can be called to ask for the
|
|
next available datagram from the abstracted tdgram_context.
|
|
It returns a 'tevent_req' handle, where the caller can register
|
|
a callback with tevent_req_set_callback(). The callback is triggered
|
|
when a datagram is available or an error occurs.
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tdgram_recvfrom_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
Otherwise it returns the length of the datagram
|
|
(0 is never returned!). '*buf' will contain the buffer of the
|
|
datagram and '*src' the abstracted tsocket_address of the sender
|
|
of the received datagram.
|
|
|
|
The caller can only have one outstanding tdgram_recvfrom_send()
|
|
at a time otherwise the caller will get '*perrno = EBUSY'.
|
|
|
|
struct tevent_req *tdgram_recvfrom_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tdgram_context *dgram);
|
|
|
|
ssize_t tdgram_recvfrom_recv(struct tevent_req *req,
|
|
int *perrno,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint8_t **buf,
|
|
struct tsocket_address **src);
|
|
|
|
The tdgram_sendto_send() method can be called to send a
|
|
datagram (specified by a buf/len) to a destination endpoint
|
|
(specified by dst). It is not allowed for len to be 0.
|
|
It returns a 'tevent_req' handle, where the caller can register a
|
|
callback with tevent_req_set_callback(). The callback is triggered
|
|
when the specific implementation (thinks it)
|
|
has delivered the datagram to the "wire".
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tdgram_sendto_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
Otherwise it returns the length of the datagram
|
|
(0 is never returned!).
|
|
|
|
The caller can only have one outstanding tdgram_sendto_send()
|
|
at a time otherwise the caller will get '*perrno = EBUSY'.
|
|
|
|
struct tevent_req *tdgram_sendto_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tdgram_context *dgram,
|
|
const uint8_t *buf, size_t len,
|
|
const struct tsocket_address *dst);
|
|
|
|
ssize_t tdgram_sendto_recv(struct tevent_req *req,
|
|
int *perrno);
|
|
|
|
The tdgram_disconnect_send() method should be used to normally
|
|
shutdown/close the abstracted socket.
|
|
|
|
The caller should make sure there are no outstanding tdgram_recvfrom_send()
|
|
and tdgram_sendto_send() calls otherwise the caller will get '*perrno = EBUSY'.
|
|
|
|
Note: you can always use talloc_free(tdgram) to cleanup the resources
|
|
of the tdgram_context on a fatal error.
|
|
|
|
struct tevent_req *tdgram_disconnect_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tdgram_context *dgram);
|
|
|
|
int tdgram_disconnect_recv(struct tevent_req *req,
|
|
int *perrno);
|
|
|
|
The tstream_context abstraction
|
|
===============================
|
|
|
|
A tstream_context is like an abstract class for stream
|
|
based sockets. The interface provides async 'tevent_req' based
|
|
functions similar to the readv(2)/writev(2)/close(2) syscalls.
|
|
|
|
The tstream_pending_bytes() function is able to report how many bytes of
|
|
the incoming stream have been received but have not been consumed yet.
|
|
It returns -1 and sets 'errno' on failure.
|
|
Otherwise it returns the number of unconsumed bytes (it can return 0!).
|
|
|
|
ssize_t tstream_pending_bytes(struct tstream_context *stream);
|
|
|
|
The tstream_readv_send() method can be called to read a
|
|
specific amount of bytes from the stream into the buffers
|
|
of the given iovec vector. The caller has to preallocate the buffers
|
|
in the iovec vector. The caller might need to use
|
|
tstream_pending_bytes() if the protocol does not have a fixed pdu header
|
|
containing the pdu size. tstream_readv_send() returns a 'tevent_req' handle,
|
|
where the caller can register a callback with tevent_req_set_callback().
|
|
The callback is triggered when all iovec buffers are completely
|
|
filled with bytes from the socket or an error occurs.
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tstream_readv_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
Otherwise it returns the total number of bytes received
|
|
(0 is never returned!).
|
|
|
|
The caller can only have one outstanding tstream_readv_send()
|
|
at a time otherwise the caller will get *perrno = EBUSY.
|
|
|
|
struct tevent_req *tstream_readv_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream,
|
|
struct iovec *vector,
|
|
size_t count);
|
|
|
|
int tstream_readv_recv(struct tevent_req *req,
|
|
int *perrno);
|
|
|
|
The tstream_writev_send() method can be called to write
|
|
buffers in the given iovec vector into the stream socket.
|
|
It is invalid to pass an empty vector.
|
|
tstream_writev_send() returns a 'tevent_req' handle,
|
|
where the caller can register a callback with tevent_req_set_callback().
|
|
The callback is triggered when the specific implementation (thinks it)
|
|
has delivered the all buffers to the "wire".
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tstream_writev_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
Otherwise it returns the total amount of bytes sent
|
|
(0 is never returned!).
|
|
|
|
The caller can only have one outstanding tstream_writev_send()
|
|
at a time otherwise the caller will get '*perrno = EBUSY'.
|
|
|
|
struct tevent_req *tstream_writev_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream,
|
|
const struct iovec *vector,
|
|
size_t count);
|
|
|
|
int tstream_writev_recv(struct tevent_req *req,
|
|
int *perrno);
|
|
|
|
The tstream_disconnect_send() method should normally be used to
|
|
shutdown/close the abstracted socket.
|
|
|
|
The caller should make sure there are no outstanding tstream_readv_send()
|
|
and tstream_writev_send() calls otherwise the caller will get '*perrno = EBUSY'.
|
|
|
|
Note: you can always use talloc_free(tstream) to cleanup the resources
|
|
of the tstream_context on a fatal error.
|
|
|
|
struct tevent_req *tstream_disconnect_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream);
|
|
|
|
int tstream_disconnect_recv(struct tevent_req *req,
|
|
int *perrno);
|
|
|
|
PDU receive helper functions
|
|
============================
|
|
|
|
In order to simplify the job, for callers that want to implement
|
|
a function to receive a full PDU with a single async function pair,
|
|
some helper functions are provided.
|
|
|
|
The caller can use the tstream_readv_pdu_send() function
|
|
to ask for the next available PDU on the abstracted tstream_context.
|
|
The caller needs to provide a "next_vector" function and a private
|
|
state for this function. The tstream_readv_pdu engine will ask
|
|
the next_vector function for the next iovec vector to be used.
|
|
There is a tstream_readv_send/recv pair for each vector returned
|
|
by the next_vector function. If the next_vector function detects
|
|
it received a full pdu, it returns an empty vector. The callback
|
|
of the tevent_req (returned by tstream_readv_pdu_send()) is triggered.
|
|
Note: the buffer allocation is completely up to the next_vector function
|
|
and its private state.
|
|
|
|
See the 'dcerpc_read_ncacn_packet_send/recv' functions in Samba as an
|
|
example.
|
|
|
|
typedef int (*tstream_readv_pdu_next_vector_t)(struct tstream_context *stream,
|
|
void *private_data,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct iovec **vector,
|
|
size_t *count);
|
|
|
|
struct tevent_req *tstream_readv_pdu_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream,
|
|
tstream_readv_pdu_next_vector_t next_vector_fn,
|
|
void *next_vector_private);
|
|
|
|
int tstream_readv_pdu_recv(struct tevent_req *req, int *perrno);
|
|
|
|
Async 'tevent_queue' based helper functions
|
|
===========================================
|
|
|
|
In some cases, the caller does not care about the IO ordering on the
|
|
abstracted socket.
|
|
(Remember at the low level there is always only one IO in a specific
|
|
direction allowed, only one tdgram_sendto_send() at a time).
|
|
|
|
Some helpers that use 'tevent_queue' are available to simplify handling
|
|
multiple IO requests. The functions just get a 'queue' argument and
|
|
internally serialize all operations.
|
|
|
|
struct tevent_req *tdgram_sendto_queue_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tdgram_context *dgram,
|
|
struct tevent_queue *queue,
|
|
const uint8_t *buf,
|
|
size_t len,
|
|
struct tsocket_address *dst);
|
|
|
|
ssize_t tdgram_sendto_queue_recv(struct tevent_req *req, int *perrno);
|
|
|
|
struct tevent_req *tstream_readv_pdu_queue_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream,
|
|
struct tevent_queue *queue,
|
|
tstream_readv_pdu_next_vector_t next_vector_fn,
|
|
void *next_vector_private);
|
|
|
|
int tstream_readv_pdu_queue_recv(struct tevent_req *req, int *perrno);
|
|
|
|
struct tevent_req *tstream_writev_queue_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct tstream_context *stream,
|
|
struct tevent_queue *queue,
|
|
const struct iovec *vector,
|
|
size_t count);
|
|
|
|
int tstream_writev_queue_recv(struct tevent_req *req, int *perrno);
|
|
|
|
BSD sockets: ipv4, ipv6 and unix
|
|
================================
|
|
|
|
The main tsocket library comes with implementations
|
|
for BSD style ipv4, ipv6 and unix sockets.
|
|
|
|
You can use the tsocket_address_inet_from_strings()
|
|
function to create a tsocket_address for ipv4 and ipv6
|
|
endpoint addresses. "family" can be "ipv4", "ipv6" or "ip".
|
|
With "ip" it autodetects "ipv4" or "ipv6" based on the
|
|
"addr_string" string. "addr_string" must be a valid
|
|
ip address string based on the selected family
|
|
(dns names are not allowed!). But it is valid to pass NULL,
|
|
which gets mapped to "0.0.0.0" or "::".
|
|
It returns -1 and sets errno on error. Otherwise it returns 0.
|
|
|
|
int tsocket_address_inet_from_strings(TALLOC_CTX *mem_ctx,
|
|
const char *family,
|
|
const char *addr_string,
|
|
uint16_t port,
|
|
struct tsocket_address **addr);
|
|
|
|
To get the ip address string of an existing 'inet' tsocket_address
|
|
you can use the tsocket_address_inet_addr_string() function.
|
|
It will return NULL and set errno to EINVAL if the tsocket_address
|
|
does not represent an ipv4 or ipv6 endpoint address.
|
|
|
|
char *tsocket_address_inet_addr_string(const struct tsocket_address *addr,
|
|
TALLOC_CTX *mem_ctx);
|
|
|
|
To get the port number of an existing 'inet' tsocket_address
|
|
you can use the tsocket_address_inet_port() function.
|
|
It will return 0 and set errno to EINVAL if the tsocket_address
|
|
does not represent an ipv4 or ipv6 endpoint address.
|
|
|
|
uint16_t tsocket_address_inet_port(const struct tsocket_address *addr);
|
|
|
|
To set the port number of an existing 'inet' tsocket_address
|
|
you can use the tsocket_address_inet_set_port() function.
|
|
It will return -1 and set errno to EINVAL if the tsocket_address
|
|
does not represent an ipv4 or ipv6 endpoint address.
|
|
It returns 0 on success.
|
|
|
|
int tsocket_address_inet_set_port(struct tsocket_address *addr,
|
|
uint16_t port);
|
|
|
|
You can use the tsocket_address_unix_from_path()
|
|
function to create a tsocket_address for unix domain
|
|
endpoint addresses. "path" is the filesystem path
|
|
(NULL will map ""). If the path is longer than
|
|
the low level kernel supports the function will
|
|
return -1 and set errno to ENAMETOOLONG.
|
|
On success it returns 0.
|
|
|
|
int tsocket_address_unix_from_path(TALLOC_CTX *mem_ctx,
|
|
const char *path,
|
|
struct tsocket_address **addr);
|
|
|
|
To get the path of a 'unix' tsocket_address
|
|
you can use the tsocket_address_unix_path() function.
|
|
It will return NULL and set errno to EINVAL if the tsocket_address
|
|
does not represent a unix domain endpoint path.
|
|
|
|
char *tsocket_address_unix_path(const struct tsocket_address *addr,
|
|
TALLOC_CTX *mem_ctx);
|
|
|
|
You can use tdgram_inet_udp_socket() to create a tdgram_context
|
|
for ipv4 or ipv6 UDP communication. "local_address" has to be
|
|
an 'inet' tsocket_address and it has to represent the local
|
|
endpoint. "remote_address" can be NULL or an 'inet' tsocket_address
|
|
presenting a remote endpoint. It returns -1 ans sets errno on error
|
|
and it returns 0 on success.
|
|
|
|
int tdgram_inet_udp_socket(const struct tsocket_address *local_address,
|
|
const struct tsocket_address *remote_address,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tdgram_context **dgram);
|
|
|
|
You can use tdgram_unix_socket() to create a tdgram_context
|
|
for unix domain datagram communication. "local_address" has to be
|
|
an 'unix' tsocket_address and it has to represent the local
|
|
endpoint. "remote_address" can be NULL or an 'unix' tsocket_address
|
|
presenting a remote endpoint. It returns -1 ans sets errno on error
|
|
and it returns 0 on success.
|
|
|
|
int tdgram_unix_socket(const struct tsocket_address *local,
|
|
const struct tsocket_address *remote,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tdgram_context **dgram);
|
|
|
|
You can use tstream_inet_tcp_connect_send to asynchronously
|
|
connect to a remote ipv4 or ipv6 TCP endpoint and create a
|
|
tstream_context for the stream based communication. "local_address" has to be
|
|
an 'inet' tsocket_address and it has to represent the local
|
|
endpoint. "remote_address" has to be an 'inet' tsocket_address
|
|
presenting a remote endpoint. It returns a 'tevent_req' handle,
|
|
where the caller can register a callback with tevent_req_set_callback().
|
|
The callback is triggered when a socket is connected and ready for IO
|
|
or an error happened.
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tstream_inet_tcp_connect_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
It returns 0 on success and returns the new tstream_context
|
|
in '*stream'.
|
|
|
|
struct tevent_req *tstream_inet_tcp_connect_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const struct tsocket_address *local_address,
|
|
const struct tsocket_address *remote_address);
|
|
|
|
int tstream_inet_tcp_connect_recv(struct tevent_req *req,
|
|
int *perrno,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tstream_context **stream);
|
|
|
|
You can use tstream_unix_connect_send to asynchronously
|
|
connect to a unix domain endpoint and create a
|
|
tstream_context for the stream based communication.
|
|
"local_address" has to be an 'unix' tsocket_address and
|
|
it has to represent the local endpoint. "remote_address"
|
|
has to be an 'inet' tsocket_address presenting a remote endpoint.
|
|
It returns a 'tevent_req' handle, where the caller can register
|
|
a callback with tevent_req_set_callback(). The callback is
|
|
triggered when a socket is connected and ready for IO
|
|
or an error happened.
|
|
|
|
The callback is then supposed to get the result by calling
|
|
tstream_unix_connect_recv() on the 'tevent_req'. It returns -1
|
|
and sets '*perrno' to the actual 'errno' on failure.
|
|
It returns 0 on success and returns the new tstream_context
|
|
in '*stream'.
|
|
|
|
struct tevent_req *tstream_unix_connect_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
const struct tsocket_address *local,
|
|
const struct tsocket_address *remote);
|
|
|
|
int _tstream_unix_connect_recv(struct tevent_req *req,
|
|
int *perrno,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tstream_context **stream);
|
|
|
|
You can use tstream_unix_socketpair to create two connected
|
|
'unix' tsocket_contexts for the stream based communication.
|
|
It returns -1 and sets errno on error and it returns 0 on
|
|
success.
|
|
|
|
int tstream_unix_socketpair(TALLOC_CTX *mem_ctx1,
|
|
struct tstream_context **stream1,
|
|
TALLOC_CTX *mem_ctx2,
|
|
struct tstream_context **stream2);
|
|
|
|
In some situations, it is needed to create a tsocket_address from
|
|
a given 'struct sockaddr'. You can use tsocket_address_bsd_from_sockaddr()
|
|
for that. This should only be used if really needed, because of
|
|
already existing fixed APIs. Only AF_INET, AF_INET6 and AF_UNIX
|
|
sockets are allowed. The function returns -1 and sets errno on error.
|
|
Otherwise it returns 0.
|
|
|
|
int tsocket_address_bsd_from_sockaddr(TALLOC_CTX *mem_ctx,
|
|
struct sockaddr *sa,
|
|
socklen_t sa_socklen,
|
|
struct tsocket_address **addr);
|
|
|
|
In some situations, it is needed to get a 'struct sockaddr' from a
|
|
given tsocket_address . You can use tsocket_address_bsd_sockaddr()
|
|
for that. This should only be used if really needed. Only AF_INET,
|
|
AF_INET6 and AF_UNIX are supported. It returns the size of '*sa' on
|
|
success, otherwise it returns -1 and sets 'errno'.
|
|
|
|
ssize_t tsocket_address_bsd_sockaddr(const struct tsocket_address *addr,
|
|
struct sockaddr *sa,
|
|
socklen_t sa_socklen);
|
|
|
|
In some situations, it is needed to wrap existing file descriptors
|
|
into the tstream abstraction. You can use tstream_bsd_existing_socket()
|
|
for that. But you should read the tsocket_bsd.c code and unterstand it
|
|
in order use this function. E.g. the fd has to be non blocking already.
|
|
It will return -1 and set errno on error. Otherwise it returns 0
|
|
and sets '*stream' to point to the new tstream_context.
|
|
|
|
int tstream_bsd_existing_socket(TALLOC_CTX *mem_ctx,
|
|
int fd,
|
|
struct tstream_context **stream);
|
|
|
|
Virtual Sockets
|
|
===============
|
|
|
|
The abstracted layout of tdgram_context and tstream_context
|
|
allow implementations around virtual sockets for encrypted tunnels
|
|
(like TLS, SASL or GSSAPI) or named pipes over smb.
|
|
|
|
Named Pipe Auth (NPA) Sockets
|
|
=============================
|
|
|
|
Samba has an implementation to abstract named pipes over smb
|
|
(within the server side). See libcli/named_pipe_auth/npa_tstream.[ch]
|
|
for the core code. The current callers are located in source4/ntvfs/ipc/vfs_ipc.c
|
|
and source4/rpc_server/service_rpc.c for the users.
|
|
|