blob: d183594d4541125130f4c63e4ed61d862a0ebe5f [file] [log] [blame]
Jiaming Wang35b4ea32020-10-06 15:23:48 -07001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15#include "pw_stream/socket_stream.h"
Jiaming Wang35b4ea32020-10-06 15:23:48 -070016
Wyatt Hepler25ce8a22021-07-15 17:45:16 -070017#include <arpa/inet.h>
18#include <unistd.h>
19
Rob Olivere2bcc032021-08-12 14:39:41 -040020#include <cstring>
21
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070022#include "pw_log/log.h"
23
Wyatt Hepler25ce8a22021-07-15 17:45:16 -070024namespace pw::stream {
25namespace {
26
27constexpr uint32_t kMaxConcurrentUser = 1;
28constexpr const char* kLocalhostAddress = "127.0.0.1";
29
30} // namespace
Jiaming Wang35b4ea32020-10-06 15:23:48 -070031
32// Listen to the port and return after a client is connected
Jason Graffius322d5942021-01-28 16:01:28 -080033Status SocketStream::Serve(uint16_t port) {
Jiaming Wang35b4ea32020-10-06 15:23:48 -070034 listen_port_ = port;
35 socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
36 if (socket_fd_ == kInvalidFd) {
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070037 PW_LOG_ERROR("Failed to create socket: %s", std::strerror(errno));
38 return Status::Unknown();
Jiaming Wang35b4ea32020-10-06 15:23:48 -070039 }
40
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070041 struct sockaddr_in addr = {};
Jiaming Wang35b4ea32020-10-06 15:23:48 -070042 addr.sin_family = AF_INET;
43 addr.sin_port = htons(listen_port_);
44 addr.sin_addr.s_addr = INADDR_ANY;
45
Wyatt Hepler25ce8a22021-07-15 17:45:16 -070046 // Configure the socket to allow reusing the address. Closing a socket does
47 // not immediately close it. Instead, the socket waits for some period of time
48 // before it is actually closed. Setting SO_REUSEADDR allows this socket to
49 // bind to an address that may still be in use by a recently closed socket.
50 // Without this option, running a program multiple times in series may fail
51 // unexpectedly.
52 constexpr int value = 1;
Wyatt Hepler25ce8a22021-07-15 17:45:16 -070053
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070054 if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)) <
55 0) {
56 PW_LOG_WARN("Failed to set SO_REUSEADDR: %s", std::strerror(errno));
Jiaming Wang35b4ea32020-10-06 15:23:48 -070057 }
58
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070059 if (bind(socket_fd_,
60 reinterpret_cast<struct sockaddr*>(&addr),
61 sizeof(addr)) < 0) {
62 PW_LOG_ERROR("Failed to bind socket to localhost:%hu: %s",
63 listen_port_,
64 std::strerror(errno));
65 return Status::Unknown();
66 }
67
68 if (listen(socket_fd_, kMaxConcurrentUser) < 0) {
69 PW_LOG_ERROR("Failed to listen to socket: %s", std::strerror(errno));
70 return Status::Unknown();
Jiaming Wang35b4ea32020-10-06 15:23:48 -070071 }
72
73 socklen_t len = sizeof(sockaddr_client_);
74
75 conn_fd_ =
76 accept(socket_fd_, reinterpret_cast<sockaddr*>(&sockaddr_client_), &len);
77 if (conn_fd_ < 0) {
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -070078 return Status::Unknown();
Jiaming Wang35b4ea32020-10-06 15:23:48 -070079 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080080 return OkStatus();
Jiaming Wang35b4ea32020-10-06 15:23:48 -070081}
82
Jason Graffius322d5942021-01-28 16:01:28 -080083Status SocketStream::SocketStream::Connect(const char* host, uint16_t port) {
84 conn_fd_ = socket(AF_INET, SOCK_STREAM, 0);
85
86 sockaddr_in addr;
87 addr.sin_family = AF_INET;
88 addr.sin_port = htons(port);
89
90 if (host == nullptr) {
91 host = kLocalhostAddress;
92 }
93
94 if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) {
95 return Status::Unknown();
96 }
97
98 int result = connect(
99 conn_fd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
100 if (result < 0) {
101 return Status::Unknown();
102 }
103
104 return OkStatus();
105}
106
Jason Graffiusef52f3a2021-01-28 15:41:51 -0800107void SocketStream::Close() {
108 if (socket_fd_ != kInvalidFd) {
109 close(socket_fd_);
110 socket_fd_ = kInvalidFd;
111 }
112
113 if (conn_fd_ != kInvalidFd) {
114 close(conn_fd_);
115 conn_fd_ = kInvalidFd;
116 }
117}
118
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700119Status SocketStream::DoWrite(std::span<const std::byte> data) {
120 ssize_t bytes_sent = send(conn_fd_, data.data(), data.size_bytes(), 0);
121
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -0700122 if (bytes_sent < 0 || static_cast<size_t>(bytes_sent) != data.size()) {
123 return Status::Unknown();
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700124 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800125 return OkStatus();
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700126}
127
128StatusWithSize SocketStream::DoRead(ByteSpan dest) {
129 ssize_t bytes_rcvd = recv(conn_fd_, dest.data(), dest.size_bytes(), 0);
130 if (bytes_rcvd < 0) {
Wyatt Hepler9b54e5e2021-08-02 11:46:01 -0700131 return StatusWithSize::Unknown();
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700132 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800133 return StatusWithSize(bytes_rcvd);
Jiaming Wang35b4ea32020-10-06 15:23:48 -0700134}
135
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800136}; // namespace pw::stream