diff options
Diffstat (limited to '')
-rw-r--r-- | lib/tsocket/tsocket_guide.txt | 481 |
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..afd0bd3 --- /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 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 avilable 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 arround 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. + |