tcp.h 18.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
/*
 * Copyright (C) 2016 Alexander Aring <aar@pengutronix.de>
 *                    Freie Universität Berlin
 *                    HAW Hamburg
 *                    Kaspar Schleiser <kaspar@schleiser.de>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    net_sock_tcp    TCP sock API
 * @ingroup     net_sock
 * @brief       Sock submodule for TCP
 *
 * How To Use
 * ----------
 * First you need to @ref including-modules "include" a module that implements
 * this API in your application's Makefile. For example the implementation for
 * @ref net_gnrc "GNRC" is called `gnrc_sock_tcp`.
 *
 * ### A Simple TCP Echo Server
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * #include "net/sock/tcp.h"
 *
 * #define SOCK_QUEUE_LEN  (1U)
 *
 * sock_tcp_t sock_queue[SOCK_QUEUE_LEN];
 * uint8_t buf[128];
 *
 * int main(void)
 * {
 *     sock_tcp_ep_t local = SOCK_IPV6_EP_ANY;
 *     sock_tcp_queue_t queue;
 *
 *     local.port = 12345;
 *
 *     if (sock_tcp_listen(&queue, &local, sock_queue, SOCK_QUEUE_LEN, 0) < 0) {
 *         puts("Error creating listening queue");
 *         return 1;
 *     }
 *     puts("Listening on port 12345");
 *     while (1) {
 *         sock_tcp_t *sock;
 *
 *         if (sock_tcp_accept(&queue, &sock) < 0) {
 *             puts("Error accepting new sock");
 *         }
 *         else {
 *             int read_res = 0;
 *
 *             puts("Reading data");
 *             while (read_res >= 0) {
 *                 read_res = sock_tcp_read(sock, &buf, sizeof(buf),
 *                                          SOCK_NO_TIMEOUT);
 *                 if (read_res < 0) {
 *                     puts("Disconnected");
 *                     break;
 *                 }
 *                 else {
 *                     int write_res;
 *                     printf("Read: \"");
 *                     for (int i = 0; i < read_res; i++) {
 *                         printf("%c", buf[i]);
 *                     }
 *                     puts("\"");
 *                     if ((write_res = sock_tcp_write(sock, &buf,
 *                                                     read_res)) < 0) {
 *                         puts("Errored on write, finished server loop");
 *                         break;
 *                     }
 *                 }
 *             }
 *             sock_tcp_disconnect(sock);
 *         }
 *     }
 *     sock_tcp_stop_listen(queue);
 *     return 0;
 * }
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Above you see a simple TCP echo server. Don't forget to also
 * @ref including-modules "include" the IPv6 module of your networking
 * implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc GNRC) and at least
 * one network device.
 *
 *
 * After including header files for the @ref net_af "address families" and
 * the @ref net_sock_tcp "TCP `sock`s and `queue`s" themselves, we create an
 * array of @ref sock_tcp_t "sock" objects `sock_queue` as our listen queue (for
 * simplicity of length 1 in our example) and some buffer space `buf` to store
 * the data received by the server:
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * #include "net/af.h"
 * #include "net/sock/tcp.h"
 *
 * #define SOCK_QUEUE_LEN  (1U)
 *
 * sock_tcp_t sock_queue[SOCK_QUEUE_LEN];
 * uint8_t buf[128];
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * We want to listen for incoming connections on a specific port, so we set a
 * local end point with that port (`12345` in this case).
 *
 * We then proceed to creating the listen queue `queue`. Since it is bound to
 * `local` it waits for incoming connections to port `12345`. We don't need any
 * further configuration so we set the flags to 0. In case of an error we stop
 * the program:
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     sock_tcp_ep_t local = SOCK_IPV6_EP_ANY;
 *     sock_tcp_queue_t queue;
 *
 *     local.port = 12345;
 *
 *     if (sock_tcp_listen(&queue, &local, sock_queue, SOCK_QUEUE_LEN, 0) < 0) {
 *         puts("Error creating listening queue");
 *         return 1;
 *     }
 *     puts("Listening on port 12345");
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * The application then waits indefinitely for an incoming connection with
 * `sock_tcp_accept()`. If we want to timeout this wait period we could
 * alternatively set the `timeout` parameter of @ref sock_tcp_accept() to a
 * value != @ref SOCK_NO_TIMEOUT. If an error occurs during that we print an
 * error message but proceed waiting.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     while (1) {
 *         sock_tcp_t *sock;
 *
 *         if (sock_tcp_accept(&queue, &sock, SOCK_NO_TIMEOUT) < 0) {
 *             puts("Error accepting new sock");
 *         }
 *         else {
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * On successful connection establishment with a client we get a connected
 * `sock` object and we try to read the incoming stream into `buf` using
 * `sock_tcp_read()` on that `sock`. Again, we could use another timeout period
 * than @ref SOCK_NO_TIMEOUT with this function. If we error we break the read
 * loop and disconnect the `sock`.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *             int read_res = 0;
 *
 *             puts("Reading data");
 *             while (read_res >= 0) {
 *                 read_res = sock_tcp_read(sock, &buf, sizeof(buf),
 *                                          SOCK_NO_TIMEOUT);
 *                 if (read_res < 0) {
 *                     puts("Disconnected");
 *                     break;
 *                 }
 *                 else {
 *                     ...
 *                 }
 *             }
 *             sock_tcp_disconnect(sock);
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Otherwise, we print the received message and write it back to the connected
 * `sock` (an again breaking the loop on error).
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *                     int write_res;
 *                     printf("Read: \"");
 *                     for (int i = 0; i < read_res; i++) {
 *                         printf("%c", buf[i]);
 *                     }
 *                     puts("\"");
 *                     if ((write_res = sock_tcp_write(sock, &buf,
 *                                                     read_res)) < 0) {
 *                         puts("Errored on write, finished server loop");
 *                         break;
 *                     }
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * In the case of we somehow manage to break the infinite accepting loop we stop
 * the listening queue appropriately.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     sock_tcp_stop_listen(queue);
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * ### A Simple TCP Echo Client
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * #include "net/af.h"
 * #include "net/ipv6/addr.h"
 * #include "net/sock/tcp.h"
 *
 * uint8_t buf[128];
 * sock_tcp_t sock;
 *
 * int main(void)
 * {
 *     int res;
 *     sock_tcp_ep_t remote = SOCK_IPV6_EP_ANY;
 *
 *     remote.port = 12345;
 *     ipv6_addr_from_str((ipv6_addr_t *)&remote.addr,
 *                        "fe80::d8fa:55ff:fedf:4523");
 *     if (sock_tcp_connect(&sock, &remote, 0, 0) < 0) {
 *         puts("Error connecting sock");
 *         return 1;
 *     }
 *     puts("Sending \"Hello!\"");
 *     if ((res = sock_tcp_write(&sock, "Hello!", sizeof("Hello!"))) < 0) {
 *         puts("Errored on write");
 *     }
 *     else {
 *         if ((res = sock_tcp_read(&sock, &buf, sizeof(buf),
 *                                  SOCK_NO_TIMEOUT)) < 0) {
 *             puts("Disconnected");
 *         }
 *         printf("Read: \"");
 *         for (int i = 0; i < res; i++) {
 *             printf("%c", buf[i]);
 *         }
 *         puts("\"");
 *     }
 *     sock_tcp_disconnect(&sock);
 *     return res;
 * }
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Above you see a simple TCP echo client. Again: Don't forget to also
 * @ref including-modules "include" the IPv6 module of your networking
 * implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc "GNRC") and at
 * least one network device. Ad0)ditionally, for the IPv6 address parsing you need
 * the @ref net_ipv6_addr "IPv6 address module".
 *
 * This time instead of creating a listening queue we create a connected `sock`
 * object directly. To connect it to a port at a host we setup a remote
 * end-point first (with port `12345` and address `fe80::d8fa:55ff:fedf:4523` in
 * this case; your IP address may differ of course) and connect to it using
 * `sock_tcp_connect()`. We neither care about the local port nor additional
 * configuration so we set both the `local_port` and `flags` parameter of
 * `sock_tcp_connect()` to `0`:
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     sock_tcp_ep_t remote = SOCK_IPV6_EP_ANY;
 *
 *     remote.port = 12345;
 *     ipv6_addr_from_str((ipv6_addr_t *)&remote.addr,
 *                        "fe80::d8fa:55ff:fedf:4523");
 *     if (sock_tcp_connect(&sock, &remote, 0, 0) < 0) {
 *         puts("Error connecting sock");
 *         return 1;
 *     }
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * On error we just terminate the program, on success we send a message
 * (`Hello!`) and again terminate the program on error:
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     if ((res = sock_tcp_write(&sock, "Hello!", sizeof("Hello!"))) < 0) {
 *         puts("Errored on write");
 *     }
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Otherwise, we wait for the reply and print it in case of success (and
 * terminate in case of error):
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 *     else {
 *         if ((res = sock_tcp_read(&sock, &buf, sizeof(buf),
 *                                  SOCK_NO_TIMEOUT)) < 0) {
 *             puts("Disconnected");
 *         }
 *         printf("Read: \"");
 *         for (int i = 0; i < res; i++) {
 *             printf("%c", buf[i]);
 *         }
 *         puts("\"");
 *     }
 *     sock_tcp_disconnect(&sock);
 *     return res;
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * @{
 *
 * @file
 * @brief   TCP sock definitions
 *
 * @author  Alexander Aring <aar@pengutronix.de>
 * @author  Simon Brummer <simon.brummer@haw-hamburg.de>
 * @author  Cenk Gündoğan <mail@cgundogan.de>
 * @author  Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
 * @author  Martine Lenders <m.lenders@fu-berlin.de>
 * @author  Kaspar Schleiser <kaspar@schleiser.de>
 */
#ifndef NET_SOCK_TCP_H
#define NET_SOCK_TCP_H

#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

#include "net/sock.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _sock_tl_ep sock_tcp_ep_t;   /**< An end point for a TCP sock object */

/**
 * @brief   Type for a TCP sock object
 *
 * @note API implementors: `struct sock_tcp` needs to be defined by
 *       implementation-specific `sock_types.h`.
 */
typedef struct sock_tcp sock_tcp_t;

/**
 * @brief   Type for a TCP listening queue
 *
 * @note API implementors: `struct sock_tcp_queue` needs to be defined by
 *                          implementation-specific `sock_types.h`.
 */
typedef struct sock_tcp_queue sock_tcp_queue_t;

/**
 * @brief   Establishes a new TCP sock connection
 *
 * @pre `sock != NULL`
 * @pre `(remote != NULL) && (remote->port != 0)`
 *
 * @param[out] sock         The resulting sock object.
 * @param[in] remote        Remote end point for the sock object.
 * @param[in] local_port    Local port for the connection. May be 0. *
 *                          If `local_port == 0` the connection is bound to a
 *                          random port.
 * @param[in] flags         Flags for the sock object. See also
 *                          @ref net_sock_flags. May be 0.
 *
 * @return  0 on success.
 * @return  -EADDRINUSE, if `(flags & SOCK_FLAGS_REUSE_EP) == 0` and
 *          @p local_port is already used elsewhere
 * @return  -EAFNOSUPPORT, if sock_tcp_ep_t::family of @p remote is not
 *          supported.
 * @return  -ECONNREFUSED, if no-one is listening on the @p remote end point.
 * @return  -EINVAL, if sock_tcp_ep_t::addr of @p remote is an invalid address.
 * @return  -EINVAL, if sock_tcp_ep_t::netif of @p remote is not a valid
 *          interface.
 * @return  -ENETUNREACH, if network defined by @p remote is not reachable.
 * @return  -ENOMEM, if system was not able to allocate sufficient memory to
 *          establish connection.
 * @return  -EPERM, if connections to @p remote are not permitted on the system
 *          (e.g. by firewall rules).
 * @return  -ETIMEDOUT, if the connection attempt to @p remote timed out.
 */
int sock_tcp_connect(sock_tcp_t *sock, const sock_tcp_ep_t *remote,
                     uint16_t local_port, uint16_t flags);

/**
 * @brief   Listen for an incoming connection request on @p local end point
 *
 * @pre `queue != NULL`
 * @pre `(local != NULL) && (local->port != 0)`
 * @pre `(queue_array != NULL) && (queue_len != 0)`
 *
 * @param[in] queue         The resulting listening queue.
 * @param[in] local         Local end point to listen on.
 * @param[in] queue_array   Array of sock objects.
 * @param[in] queue_len     Length of @p queue_array.
 * @param[in] flags         Flags for the listening queue. See also
 *                          @ref net_sock_flags. May be 0.
 *
 * @return  0 on success.
 * @return  -EADDRINUSE, if `(flags & SOCK_FLAGS_REUSE_EP) == 0` and
 *          @p local is already used elsewhere
 * @return  -EAFNOSUPPORT, if sock_tcp_ep_t::family of @p local is not
 *          supported.
 * @return  -EINVAL, if sock_tcp_ep_t::netif of @p local is not a valid
 *          interface.
 * @return  -ENOMEM, if no memory was available to listen on @p queue.
 */
int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local,
                    sock_tcp_t *queue_array, unsigned queue_len,
                    uint16_t flags);

/**
 * @brief   Disconnects a TCP connection
 *
 * @pre `sock != NULL` If we want to timeout this wait period we could
 * alternatively set the `timeout` parameter of @ref sock_tcp_accept() to a
 * value != @ref SOCK_NO_TIMEOUT.
 *
 * @param[in] sock  A TCP sock object.
 */
void sock_tcp_disconnect(sock_tcp_t *sock);

/**
 * @brief   Stops listening on TCP listening queue
 *
 * @pre `queue != NULL`
 *
 * @param[in] queue A TCP listening queue.
 */
void sock_tcp_stop_listen(sock_tcp_queue_t *queue);

/**
 * @brief   Gets the local end point of a TCP sock object
 *
 * @pre `(sock != NULL) && (ep != NULL)`
 *
 * @param[in] sock  A TCP sock object.
 * @param[out] ep   The local end point.
 *
 * @return  0 on success.
 * @return  -EADDRNOTAVAIL, when @p sock has no local end point.
 */
int sock_tcp_get_local(sock_tcp_t *sock, sock_tcp_ep_t *ep);

/**
 * @brief   Gets the remote end point of a TCP sock object
 *
 * @pre `(sock != NULL) && (ep != NULL)`
 *
 * @param[in] sock  A TCP sock object.
 * @param[out] ep   The remote end point.
 *
 * @return  0 on success.
 * @return  -ENOTCONN, when @p sock is not connected to a remote end point.
 */
int sock_tcp_get_remote(sock_tcp_t *sock, sock_tcp_ep_t *ep);

/**
 * @brief   Gets the local end point of a TCP sock queue object
 *
 * @pre `(sock != NULL) && (ep != NULL)`
 *
 * @param[in] queue A TCP sock queue object.
 * @param[out] ep   The local end point.
 *
 * @return  0 on success.
 * @return  -EADDRNOTAVAIL, when @p queue has no local end point.
 */
int sock_tcp_queue_get_local(sock_tcp_queue_t *queue, sock_tcp_ep_t *ep);

/**
 * @brief   Receives and handles TCP connection requests from other peers
 *
 * @pre `(queue != NULL) && (sock != NULL)`
 *
 * @param[in] queue     A TCP listening queue.
 * @param[out] sock     A new TCP sock object for the established sock object.
 * @param[in] timeout   Timeout for accept in microseconds.
 *                      If 0 and no data is available, the function returns
 *                      immediately.
 *                      May be @ref SOCK_NO_TIMEOUT for no timeout (wait until
 *                      data is available).
 *
 * @return  0 on success.
 * @return  -EAGAIN, if @p timeout is `0` and no data is available.
 * @return  -ECONNABORTED, if the connection to @p sock has been aborted while
 *          in this function
 * @return  -EINVAL, if @p queue was not initialized using
 *          @ref sock_tcp_listen().
 * @return  -ENOMEM, if system was not able to allocate sufficient memory to
 *          establish connection.
 * @return  -EPERM, if connections on local end point of @p queue are not
 *          permitted on this system (e.g. by firewall rules).
 * @return  -ETIMEDOUT, if the operation timed out internally.
 */
int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock,
                    uint32_t timeout);

/**
 * @brief   Reads data from an established TCP stream
 *
 * @pre `(sock != NULL) && (data != NULL) && (max_len > 0)`
 *
 * @param[in] sock      A TCP sock object.
 * @param[out] data     Pointer where the read data should be stored.
 * @param[in] max_len   Maximum space available at @p data.
 *                      If read data exceeds @p max_len the data is
 *                      truncated and the remaining data can be retrieved
 *                      later on.
 * @param[in] timeout   Timeout for receive in microseconds.
 *                      If 0 and no data is available, the function returns
 *                      immediately.
 *                      May be @ref SOCK_NO_TIMEOUT for no timeout (wait until
 *                      data is available).
 *
 * @note    Function may block.
 *
 * @return  The number of bytes read on success.
 * @return  0, if no read data is available, but everything is in order.
 * @return  -EAGAIN, if @p timeout is `0` and no data is available.
 * @return  -ECONNABORTED, if the connection is aborted while waiting for the
 *          next data.
 * @return  -ECONNRESET, if the connection was forcibly closed by remote end
 *          point of @p sock.
 * @return  -ENOTCONN, when @p sock is not connected to a remote end point.
 * @return  -ETIMEDOUT, if @p timeout expired.
 */
ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,
                      uint32_t timeout);

/**
 * @brief   Writes data to an established TCP stream
 *
 * @pre `(sock != NULL)`
 * @pre `if (len != NULL): (data != NULL)`
 *
 * @param[in] sock  A TCP sock object.
 * @param[in] data  Pointer to the data to be written to the stream.
 * @param[in] len   Maximum space available at @p data.
 *
 * @note    Function may block.
 *
 * @return  The number of bytes written on success.
 * @return  -ECONNABORTED, if the connection is aborted while waiting for the
 *          next data.
 * @return  -ECONNRESET, if the connection was forcibly closed by remote end
 *          point of @p sock.
 * @return  -ENOMEM, if no memory was available to written @p data.
 * @return  -ENOTCONN, if @p sock is not connected to a remote end point.
 */
ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len);

#include "sock_types.h"

#ifdef __cplusplus
}
#endif

#endif /* NET_SOCK_TCP_H */
/** @} */