summaryrefslogtreecommitdiffstats
path: root/lib/tsocket/tsocket_guide.txt
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tsocket/tsocket_guide.txt')
-rw-r--r--lib/tsocket/tsocket_guide.txt481
1 files changed, 481 insertions, 0 deletions
diff --git a/lib/tsocket/tsocket_guide.txt b/lib/tsocket/tsocket_guide.txt
new file mode 100644
index 0000000..1b56056
--- /dev/null
+++ b/lib/tsocket/tsocket_guide.txt
@@ -0,0 +1,481 @@
+
+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.
+