static int tcp_client(const char *srcaddr, const char *dstaddr, void **ctxp) { struct tcp_ctx *tctx; struct sockaddr_storage sa; int ret; ret = tcp_setup_new(dstaddr, TCP_SIDE_CLIENT, ctxp); if (ret != 0) return (ret); tctx = *ctxp; if (srcaddr == NULL) return (0); ret = tcp_addr(srcaddr, 0, &sa); if (ret != 0) { tcp_close(tctx); return (ret); } if (bind(tctx->tc_fd, (struct sockaddr *)&sa, sa.ss_len) == -1) { ret = errno; tcp_close(tctx); return (ret); } return (0); }
int main() { GREENTEA_SETUP(20, "tcp_echo_client"); EthernetInterface eth; eth.connect(); printf("MBED: TCPClient IP address is '%s'\n", eth.get_ip_address()); printf("MBED: TCPClient waiting for server IP and port...\n"); greentea_send_kv("target_ip", eth.get_ip_address()); bool result = false; char recv_key[] = "host_port"; char ipbuf[60] = {0}; char portbuf[16] = {0}; unsigned int port = 0; greentea_send_kv("host_ip", " "); greentea_parse_kv(recv_key, ipbuf, sizeof(recv_key), sizeof(ipbuf)); greentea_send_kv("host_port", " "); greentea_parse_kv(recv_key, portbuf, sizeof(recv_key), sizeof(ipbuf)); sscanf(portbuf, "%u", &port); printf("MBED: Server IP address received: %s:%d \n", ipbuf, port); TCPSocket sock(ð); SocketAddress tcp_addr(ipbuf, port); if (sock.connect(tcp_addr) == 0) { printf("HTTP: Connected to %s:%d\r\n", ipbuf, port); printf("tx_buffer buffer size: %u\r\n", sizeof(tx_buffer)); printf("rx_buffer buffer size: %u\r\n", sizeof(rx_buffer)); prep_buffer(tx_buffer, sizeof(tx_buffer)); sock.send(tx_buffer, sizeof(tx_buffer)); // Server will respond with HTTP GET's success code const int ret = sock.recv(rx_buffer, sizeof(rx_buffer)); result = !memcmp(tx_buffer, rx_buffer, sizeof(tx_buffer)); TEST_ASSERT_EQUAL(ret, sizeof(rx_buffer)); TEST_ASSERT_EQUAL(true, result); } sock.close(); eth.disconnect(); GREENTEA_TESTSUITE_RESULT(result); }
static bool tcp_address_match(const void *ctx, const char *addr) { const struct tcp_ctx *tctx = ctx; struct sockaddr_storage sa1, sa2; socklen_t salen; PJDLOG_ASSERT(tctx != NULL); PJDLOG_ASSERT(tctx->tc_magic == TCP_CTX_MAGIC); if (tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &sa1) != 0) return (false); salen = sizeof(sa2); if (getpeername(tctx->tc_fd, (struct sockaddr *)&sa2, &salen) == -1) return (false); if (sa1.ss_family != sa2.ss_family || sa1.ss_len != sa2.ss_len) return (false); switch (sa1.ss_family) { case AF_INET: { struct sockaddr_in *sin1, *sin2; sin1 = (struct sockaddr_in *)&sa1; sin2 = (struct sockaddr_in *)&sa2; return (memcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)) == 0); } case AF_INET6: { struct sockaddr_in6 *sin1, *sin2; sin1 = (struct sockaddr_in6 *)&sa1; sin2 = (struct sockaddr_in6 *)&sa2; return (memcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof(sin1->sin6_addr)) == 0); } default: return (false); } }
static int tcp_setup_new(const char *addr, int side, void **ctxp) { struct tcp_ctx *tctx; int ret, nodelay; PJDLOG_ASSERT(addr != NULL); PJDLOG_ASSERT(side == TCP_SIDE_CLIENT || side == TCP_SIDE_SERVER_LISTEN); PJDLOG_ASSERT(ctxp != NULL); tctx = malloc(sizeof(*tctx)); if (tctx == NULL) return (errno); /* Parse given address. */ if ((ret = tcp_addr(addr, PROTO_TCP_DEFAULT_PORT, &tctx->tc_sa)) != 0) { free(tctx); return (ret); } PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); tctx->tc_fd = socket(tctx->tc_sa.ss_family, SOCK_STREAM, 0); if (tctx->tc_fd == -1) { ret = errno; free(tctx); return (ret); } PJDLOG_ASSERT(tctx->tc_sa.ss_family != AF_UNSPEC); /* Socket settings. */ nodelay = 1; if (setsockopt(tctx->tc_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) == -1) { pjdlog_errno(LOG_WARNING, "Unable to set TCP_NOELAY"); } tctx->tc_side = side; tctx->tc_magic = TCP_CTX_MAGIC; *ctxp = tctx; return (0); }
int tcp_setup(enum setup_action action, char * addr, char * port) { struct addrinfo * addrinfo = tcp_addr(addr, port); int fd; struct addrinfo * item; for(item = addrinfo; item != NULL; item = item->ai_next) { fd = addr_socket(item); if (fd < 0) { switch (errno) { case EAFNOSUPPORT: // Address family not supported. // Most likely disabled IPv6. // Try next. continue; case EPROTONOSUPPORT: // (address family, protocol) combination not supported. // Maybe `getaddrinfo` picked some funky protocol instead // of TCP. Maybe we should enforce TCP? // Try next. continue; case EPROTOTYPE: // (socket type, protocol) combination not supported. // `getaddrinfo` picked a non-stream protocol. // Should never happen. I hope. // Bail out. case EMFILE: // Out of FDs for process. // Bail out. case ENFILE: // Out of FDs for system. // Bail out. case ENOBUF: // Out of system resources. // Bail out. case ENOMEM: // Out of memory. // Bail out. perror("socket"); exit(1); case EACCES: // Access denied. // This process is not allowed to use create this type of // socket. // Try next. perror("socket"); continue; } } if (action == CONNECT) { int e = addr_connect(fd, item); if (e) { switch (errno) { case ECONNREFUSED: // Try next address. case EINTR: // Interrupted. Async establishment. // Must now do async poll. case ENETUNREACH: // Try next case EPROTOTYPE: // The socket on the other end is not TCP. // Bad namelookup result? // Try next case ETIMEOUT: // Try next case EADDRINUSE: // Shit, it's i bruk, try next case ECONNRESET: // How is this different from ECONNREFUSED // Try next case EHOSTUNREACH: // try next case EACCES: // Try next. case ENETDOWN: // try next case EADDRNOTAVAIL: // OS ran out of available tcp ports. // Try next. Maybe we'll get a different socket type. // Flag error. case ENOBUFS: // Ran out of buffer space. // Abort. default: // This list of errors in not exaustive. But should be // enough for blocking tcp using getaddrinfo with a fresh // socket. perror("Unexpected error in connect"); } close(fd); continue; } } if (action == BIND) { setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); { int e = addr_bind(fd, item); if (e) { perror("bind"); close(fd); continue; } } { int e = listen(fd, SOMAXCONN); if (e) { perror("listen"); close(fd); continue; } } }