SatCat5
coap_proxy.cc
1 // Copyright 2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/coap_proxy.h>
7 #include <satcat5/coap_reader.h>
8 #include <satcat5/coap_writer.h>
9 #include <satcat5/utils.h>
10 
15 
16 // Option to force separated-response mode for all proxy requests.
17 // * When set to 0, always use separated-response mode immediately.
18 // (i.e., All proxy requests send the separated-response message.)
19 // * When set to 1, prefer silent mode, switching to separated mode
20 // only if the forwarded request/response is taking a long time.
21 #ifndef SATCAT5_COAP_PROXY_SILENT
22 #define SATCAT5_COAP_PROXY_SILENT 1
23 #endif
24 
25 // Use even/odd tokens to identify client (LSB=0) and server (LSB=1).
26 constexpr u32 TOKEN_MASK = 0xFFFFFFFEu;
27 constexpr u32 token_client(u32 x)
28  { return (x & TOKEN_MASK) + 0; }
29 constexpr u32 token_server(u32 x)
30  { return (x & TOKEN_MASK) + 1; }
31 
32 ProxyResource::ProxyResource(
34  const char* local_uri,
35  const satcat5::ip::Addr& fwd_addr,
36  const satcat5::udp::Port& fwd_port,
37  const char* fwd_uri)
38  : Resource(server, local_uri)
39  , m_pool(server)
40  , m_fwd_addr(fwd_addr)
41  , m_fwd_port(fwd_port)
42  , m_fwd_uri(normalize_uri(fwd_uri ? fwd_uri : local_uri))
43 {
44  // No other initialization required.
45 }
46 
48  // Reuse an existing next-hop connection if practical.
49  // Otherwise, claim any idle connection from the pool.
50  ConnectionUdp* server = m_pool->find_server(client->get_proxy_token());
51  if (!server) server = (ConnectionUdp*)m_pool->get_idle_connection();
52 
53  // If there's no available connections, abort.
54  if (!server) return client->error_response(CODE_SERVER_ERROR, "Proxy busy");
55 
56  // If we're not already connected, attempt to do so now.
57  // (And set flag to allow immediate reuse once finished.)
58  bool ok = server->is_match_addr(m_fwd_addr, m_fwd_port)
59  || server->connect(m_fwd_addr, m_fwd_port, udp::PORT_NONE, true);
60 
61  // Set unique IDs for the new transaction.
62  u16 msgid = m_pool->next_msgid();
63  u32 token = m_pool->next_token();
64  client->set_proxy_token(token_client(token));
65  server->set_proxy_token(token_server(token));
66 
67  // Copy message header, including the GET/POST/PUT/DELETE code.
68  satcat5::coap::Writer fwd(server->open_request());
69  if (ok) ok = fwd.write_header(msg->type(), msg->code(), msgid, token);
70 
71  // Copy simple options in numerical order.
72  // TODO: Is there a practical way to copy *ALL* safe options? This meets
73  // our needs, but may not be compatible with third-party clients/servers.
74  if (ok) ok = fwd.write_uri(OPTION_URI_PATH, m_fwd_uri);
75  if (ok && msg->format()) ok = fwd.write_option(OPTION_FORMAT, msg->format().value());
76  if (ok && msg->block2()) ok = fwd.write_option(OPTION_BLOCK2, msg->block2().value());
77  if (ok && msg->block1()) ok = fwd.write_option(OPTION_BLOCK1, msg->block1().value());
78  if (ok && msg->size1()) ok = fwd.write_option(OPTION_SIZE1, msg->size1().value());
79 
80  // Copy message contents and send the forwarded request.
81  satcat5::io::Readable* src = msg->read_data();
83  if (ok && src && dst) ok = src->copy_and_finalize(dst, io::CopyMode::ALWAYS);
84  if (!ok) return client->error_response(CODE_SERVER_ERROR);
85 
86  // If we're in silent mode, no immediate response (see "coap_reqwait").
87  // Otherwise, immediately switch to separated-response mode.
88  return SATCAT5_COAP_PROXY_SILENT || client->open_separate(msg);
89 }
90 
92  : ResourceServer(udp, port)
93  , m_msgid(satcat5::util::prng.next())
94  , m_token(satcat5::util::prng.next())
95  , m_extra_connection(this, udp)
96 {
97  // Nothing else to initialize.
98 }
99 
101  // Check both auxiliary and local Connection objects.
102  Connection* tmp = nullptr;
103  if (m_aux_ep) tmp = m_aux_ep->find_token(token_client(token));
104  if (!tmp) tmp = find_token(token_client(token));
105  return tmp;
106 }
107 
109  // Check local ConnectionUdp objects only.
110  return (ConnectionUdp*)find_token(token_server(token));
111 }
112 
114  // Sequential numbering of outgoing messages.
115  return m_msgid++;
116 }
117 
119  // Increment counter by two for unique client and server IDs.
120  return (m_token += 2) & TOKEN_MASK;
121 }
122 
124  // Is this a valid proxy response?
125  u32 rcvd_token = token_server(msg->token());
126  Connection* client = nullptr;
127  if (rcvd_token == obj->get_proxy_token())
128  client = find_client(rcvd_token);
129 
130  // Notify the local or proxy callback accordingly.
131  if (client) {
132  proxy_response(client, msg);
133  } else {
134  local_response(obj, msg);
135  }
136 }
137 
139  // Forward the response, using whichever mode is expected.
140  // Copy message header, then known options in numerical order.
142  bool ok = fwd.write_header(msg->code(), client);
143  if (ok && msg->uri_path()) ok = fwd.write_uri(OPTION_URI_PATH, msg->uri_path().value());
144  if (ok && msg->format()) ok = fwd.write_option(OPTION_FORMAT, msg->format().value());
145  if (ok && msg->block2()) ok = fwd.write_option(OPTION_BLOCK2, msg->block2().value());
146  if (ok && msg->block1()) ok = fwd.write_option(OPTION_BLOCK1, msg->block1().value());
147  if (ok && msg->size1()) ok = fwd.write_option(OPTION_SIZE1, msg->size1().value());
148 
149  // Copy message contents and send the message.
150  satcat5::io::Readable* src = msg->read_data();
151  satcat5::io::Writeable* dst = fwd.write_data();
152  if (ok && src && dst) ok = src->copy_and_finalize(dst, io::CopyMode::ALWAYS);
153  if (!ok) client->error_response(CODE_SERVER_ERROR);
154 }
155 
157  // If we're in SATCAT5_COAP_PROXY_SILENT mode, use the second request
158  // as a clue that the forwarded request is taking a while.
159  if (!obj->is_separate()) obj->open_separate(msg);
160 }
161 
163  // If the next-hop server says the response may take a while, forward
164  // the same message to the upstream requestor.
165  if (!obj->is_separate()) obj->open_separate(msg);
166 }
167 
169  // Server timeout, attempt to find the matching client connection.
170  Connection* client = find_client(obj->get_proxy_token());
171  if (client && client != obj)
172  client->error_response(CODE_GATE_TIMEOUT, "Proxy timeout");
173 }
CoAP request/response handling for a single client-server connection.
bool is_separate() const
< Awaiting separate response?
bool open_separate(const satcat5::coap::ReadHeader *msg)
If able, send the first half of a separated response.
satcat5::io::Writeable * open_response_auto()
Automatically call open_response() or continue_separate().
bool error_response(satcat5::coap::Code code, const char *why=0)
If able, return an error in response to an incoming request from a remote client.
satcat5::io::Writeable * open_request()
If able, send a request to the current remote server.
void set_proxy_token(u32 token)
Set identifier for matching incoming proxy responses.
u32 get_proxy_token() const
Query the most recent proxy token.
Variant of coap::Connection for UDP connections.
bool is_match_addr(const satcat5::udp::Addr &dstaddr, const satcat5::udp::Port &dstport) const
Is this object connected to the designated address?
bool connect(const satcat5::udp::Addr &dstaddr, const satcat5::udp::Port &dstport=satcat5::udp::PORT_COAP, const satcat5::udp::Port &srcport=satcat5::udp::PORT_NONE, bool allow_reuse=false)
Set remote endpoint for later calls to open_request().
satcat5::coap::Connection * get_idle_connection() const
Get the first idle connection, or null if all are busy.
Connection * find_token(u32 token) const
Scan connections for a matching proxy-ID.
Define a reverse-proxy CoAP resource.
Definition: coap_proxy.h:51
bool request_any(Connection *obj, Reader *msg)
Event handler for all incoming requests.
Definition: coap_proxy.cc:47
CoAP server with a mix of local and reverse-proxy resources.
Definition: coap_proxy.h:97
void coap_response(Connection *obj, Reader *msg) override
The Child class overrides these event-handlers:
Definition: coap_proxy.cc:123
u16 m_msgid
Counter for outgoing message-IDs.
Definition: coap_proxy.h:135
void proxy_response(Connection *client, Reader *msg)
Internal handler for proxy responses.
Definition: coap_proxy.cc:138
u32 next_token()
Unique transaction tokens match client and server.
Definition: coap_proxy.cc:118
Connection * find_client(u32 token) const
Given a token, find associated client connection.
Definition: coap_proxy.cc:100
void coap_reqwait(Connection *obj, Reader *msg) override
The Child class overrides these event-handlers:
Definition: coap_proxy.cc:156
u32 m_token
Counter for client/server tokens.
Definition: coap_proxy.h:136
ProxyServer(satcat5::udp::Dispatch *udp, satcat5::udp::Port port=satcat5::udp::PORT_COAP)
Constructor. By default, bind this server to port 5683.
Definition: coap_proxy.cc:91
virtual void local_response(Connection *obj, Reader *msg)
Event handler for non-proxy responses.
Definition: coap_proxy.h:122
ConnectionUdp * find_server(u32 token) const
Given a token, find associated server connection.
Definition: coap_proxy.cc:108
void coap_error(Connection *obj) override
The Child class overrides these event-handlers:
Definition: coap_proxy.cc:168
void coap_separate(Connection *obj, Reader *msg) override
The Child class overrides these event-handlers:
Definition: coap_proxy.cc:162
u16 next_msgid()
Outgoing messages are numbered sequentially.
Definition: coap_proxy.cc:113
u8 type() const
< Type (T)
Definition: coap_reader.h:110
Code code() const
< Response code (CODE)
Definition: coap_reader.h:114
satcat5::io::Readable * read_data()
Access the message payload.
Definition: coap_reader.cc:90
u64 token() const
< Token value
Definition: coap_reader.h:118
Base-class for parsing CoAP message headers and options.
Definition: coap_reader.h:162
satcat5::util::optional< u64 > size1() const
Accessors for parsed options.
Definition: coap_reader.h:179
satcat5::util::optional< const char * > uri_path() const
Accessors for parsed options.
Definition: coap_reader.h:174
satcat5::util::optional< u64 > block2()
Accessors for parsed options.
Definition: coap_reader.h:197
satcat5::util::optional< u64 > block1() const
Accessors for parsed options.
Definition: coap_reader.h:189
satcat5::util::optional< u16 > format() const
Accessors for parsed options.
Definition: coap_reader.h:177
Define a single CoAP resource.
Definition: coap_resource.h:47
Manager for several Resource objects available on an Endpoint.
Message formatting for the Constrained Applications Protocol (CoAP).
Definition: coap_writer.h:26
bool write_uri(u16 id, const char *str)
Convert a URI to consecutive Uri-Path options.
Definition: coap_writer.cc:70
bool write_option(u16 id, unsigned len, const void *data)
Write option(s) one at a time, in various formats.
Definition: coap_writer.cc:49
satcat5::io::Writeable * write_data()
After the last option, start writing message data.
Definition: coap_writer.cc:88
bool write_header(u8 type, Code code, u16 msg_id, u64 token=0, u8 tkl=0)
Always start by writing the header, with optional token.
Definition: coap_writer.cc:25
Abstract API for reading byte-streams and packets.
Definition: io_readable.h:68
bool copy_and_finalize(satcat5::io::Writeable *dst, satcat5::io::CopyMode mode=CopyMode::PACKET)
Copy data to a Writeable object, then finalize.
Definition: io_readable.cc:234
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
Dispatcher sorts incoming UDP messages by port index.
Definition: udp_dispatch.h:20
constexpr u16 OPTION_BLOCK2
Option: Block transfer (RFC7959)
constexpr Code CODE_GATE_TIMEOUT(5, 4)
< 5.03 Service Unavailable
constexpr u16 OPTION_SIZE1
Option: Size1.
constexpr Code CODE_SERVER_ERROR(5, 0)
< 4.15 Unsupported Content-Format
constexpr u16 OPTION_BLOCK1
Option: Block transfer (RFC7959)
constexpr u16 OPTION_URI_PATH
Option: Uri-Path.
constexpr u16 OPTION_FORMAT
Option: Content-Format.
CoAP reverse-proxy for specific resources.
Message parsing for the Constrained Applications Protocol (CoAP)
IPv4 address is a 32-bit unsigned integer.
Definition: ip_core.h:15
UDP and TCP ports are both 16-bit unsigned integers.
Definition: ip_core.h:119
T value() const
Fetch the inner value.
Definition: utils.h:64
Miscellaneous mathematical utility functions.