From ed33f4e1c82fb2c21e0b60f457d1e3404be34980 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Tue, 25 Feb 2025 20:17:13 -0500 Subject: spec: Add initial P2P sources Signed-off-by: Ian Moffett --- lib/include/defs.h | 3 ++ lib/include/net/stpsession.h | 50 ++++++++++++++++++++++- lib/include/otconfig.h | 1 + lib/include/server.h | 1 + lib/libostp/auth.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ lib/libostp/server.c | 1 + lib/libostp/session.c | 42 +++++++++++++++++++- 7 files changed, 191 insertions(+), 2 deletions(-) diff --git a/lib/include/defs.h b/lib/include/defs.h index a15a2fd..d4a57a9 100644 --- a/lib/include/defs.h +++ b/lib/include/defs.h @@ -58,4 +58,7 @@ /* Fixed paths */ #define OSMORA_TRUST "/etc/ostp/trusted_users.osmt" +/* Maximum length of an IP */ +#define IP_LEN_MAX 46 + #endif diff --git a/lib/include/net/stpsession.h b/lib/include/net/stpsession.h index 731b8ce..9735678 100644 --- a/lib/include/net/stpsession.h +++ b/lib/include/net/stpsession.h @@ -46,6 +46,17 @@ /* Session Request option bits */ #define SESSION_REQ_USER BIT(0) +#define SESSION_REQ_P2P BIT(1) + +/* + * For ACKing/TRUNCing packets e.g., + * peer blocks + */ +#define ACK_WORD 0xAC7E +#define TRUNC_WORD 0x782C + +#define PB_N_PEERS 16 +#define PB_N_BLOCKS 16 /* * The Session Request is sent from the client to the @@ -63,11 +74,12 @@ * / * 0 1 2 3 4 5 6 * ~ ~ ~ ~ ~ ~ ~ - * U R R R R R R + * U P R R R R R * \ * Purpose * * U: User auth. + * P: Peer-to-peer. * R: Reserved, keep zero. */ struct session_request { @@ -76,6 +88,42 @@ struct session_request { uint8_t pad[8]; } PACKED; +/* + * The `peer' structure describes a peer on + * an OSTP server running in peer-to-peer mode. + * + * @pad: Must be all 1s, treat invalid otherwise + * @port: Port peer accepts connections on + * @host: Host peer is at + */ +struct peer { + uint8_t pad[1]; + uint16_t port; + char host[IP_LEN_MAX]; +}; + +/* + * If P2P is enabled and supported, the OCS + * sends one or more peer blocks in a consecutive + * manner. Each peer block can list up to 16 peers + * max. If the `seq' field is greater than zero and + * the list of peers within the current block is full, + * the client may either request the next peer block by + * sending an ACK packet or terminating the connection + * by sending a TRUNC packet. + * + * @peers: List of available peers within this block + * @seq: Descending one-based block sequence number (0=invalid) + */ +struct peer_block { + struct peer peers[PB_N_PEERS]; + uint8_t seq; +} PACKED; + +/* Peer/block counts must be powers of two */ +CTASSERT((PB_N_PEERS & 1) == 0, "PB_N_PEERS not power-of-two!"); +CTASSERT((PB_N_BLOCKS & 1) == 0, "PB_N_BLOCKS not power-of-two!"); + /* * Structure containing user information * for password protected channels. diff --git a/lib/include/otconfig.h b/lib/include/otconfig.h index 96cac38..e8400b1 100644 --- a/lib/include/otconfig.h +++ b/lib/include/otconfig.h @@ -34,6 +34,7 @@ #define DIAGNOSTIC 0 /* 1 for verbosity */ #define CONNECTION_TIMEOUT 60 /* In seconds */ #define ENABLE_MOTD 1 /* Enable message of the day */ +#define ENABLE_P2P 1 /* Enable peer-to-peer sessions */ #define ARBITRATION_MAX 5 /* Maximum number of arbitration attempts */ #define REQUIRE_USER_AUTH 1 /* 1: true, 0: false */ #define OSTP_PORT 5352 diff --git a/lib/include/server.h b/lib/include/server.h index c8eff9c..33d2b7e 100644 --- a/lib/include/server.h +++ b/lib/include/server.h @@ -40,6 +40,7 @@ struct ostp_client { struct ostp_session session; + char host[IP_LEN_MAX]; int sockfd; pthread_t td; volatile uint8_t authed : 1; diff --git a/lib/libostp/auth.c b/lib/libostp/auth.c index 5fa59e9..ddc17b8 100644 --- a/lib/libostp/auth.c +++ b/lib/libostp/auth.c @@ -30,8 +30,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -133,6 +135,78 @@ send_motd(struct ostp_client *c, const unsigned char *session_key) } } +static inline void +populate_peer(struct ostp_client *c, struct peer *pp) +{ + memset(pp, 0, sizeof(*pp)); + pp->port = htons(OSTP_PORT); + pp->pad[0] = 0xFF; + memcpy(pp->host, c->host, IP_LEN_MAX); +} + +static int +send_peerlist(struct ostp_listener *lp, struct ostp_client *c, struct ostp_session *s) +{ + struct ostp_client *c_cur, *c_tmp; + struct peer peer_cur; + struct peer_block pb_cur; + struct ostp_session *sp; + size_t peer_count; + int i, j, error; + int seq; + + memset(&pb_cur, 0, sizeof(pb_cur)); + peer_count = lp->client_count; + seq = (peer_count <= 16) ? 1 : (peer_count / PB_N_PEERS); + pb_cur.seq = seq; + printf("peer_count=%d, seq=%d\n", peer_count, pb_cur.seq); + + /* + * Grab peers and prepare blocks until seq == 0 + * + * + * lp->clients + * +-------------------+ + * i=| | | | | | | | | | | + * +-------------------+ + * 0,1,2,3,4,5,6,7,8,9 <--+ + * | + * pb_cur.peers | + * +---------------+ | lp->clients can be bigger + * j=| | | | | | | | | | + * +---------------+ + + * 0,1,2,3,4,5,6,7 <---+-/ + * + * + * lp->clients is one big contigious array + * of connected clients and is used internally + * by the server to manage connected users. + * + * During a session request, the client may request + * peer-to-peer mode by setting the 'P' bit. If accepted, + * we'll need to send one or more peer blocks back, containing + * a list of `PB_N_PEERS' peers max per block. + * + */ + for (i = 0; i < PB_N_BLOCKS && seq > 0; ++i) { + for (j = 1; j < PB_N_PEERS && j < peer_count; ++j) { + populate_peer(&lp->clients[j], &peer_cur); + pb_cur.peers[j] = peer_cur; + } + + --seq; + pb_cur.seq = seq; + sp = &c->session; + session_send(&pb_cur, sizeof(pb_cur), sp); + + if (j >= lp->client_count) { + break; + } + } + + return 0; +} + int handle_srq(struct ostp_client *c, struct ostp_listener *lp, struct session_request *srq) { @@ -140,12 +214,20 @@ handle_srq(struct ostp_client *c, struct ostp_listener *lp, struct session_reque struct ostp_session *session; int error; + /* Do we require the SRQ 'U' bit? */ if (REQUIRE_USER_AUTH && !ISSET(srq->options, SESSION_REQ_USER)) { printf("User authentication enforced but client 'U' bit not set\n"); printf("Closing connection...\n"); return -1; } + /* Don't use P2P if not enabled */ + if (!ENABLE_P2P && !ISSET(srq->options, SESSION_REQ_P2P)) { + printf("P2P not enabled but client 'P' bit set\n"); + printf("Closing connection...\n"); + return -1; + } + /* Generate a new keypair if we have no link */ if (!g_have_link) { if (gen_x25519_keypair(&keypair) < 0) { @@ -183,6 +265,19 @@ handle_srq(struct ostp_client *c, struct ostp_listener *lp, struct session_reque return -1; } + /* Handle P2P requests */ + if (ISSET(srq->options, SESSION_REQ_P2P)) { + /* + * Not an error but we want to send this down + * the callstack so everything is cleaned up + * and terminated properly. + * + * TODO: Figure something else out here maybe... + */ + send_peerlist(lp, c, session); + return -1; + } + /* Handle any requested session parameters */ if ((error = negotiate_spw(c, session->session_key)) < 0) { free_session_key(session->session_key); diff --git a/lib/libostp/server.c b/lib/libostp/server.c index 794eda0..61e89f7 100644 --- a/lib/libostp/server.c +++ b/lib/libostp/server.c @@ -199,6 +199,7 @@ listener_poll(struct ostp_listener *lp) ++lp->client_count; ip = inet_ntoa(caddr.sin_addr); + memcpy(c->host, ip, strlen(ip) + 1); printf("Incoming connection from %s\n", ip); handle_client(&caddr, c, lp); break; diff --git a/lib/libostp/session.c b/lib/libostp/session.c index 3843207..b61600f 100644 --- a/lib/libostp/session.c +++ b/lib/libostp/session.c @@ -193,6 +193,35 @@ recv_motd(int sockfd, const unsigned char *session_key) printf("%s\n", buf); } +static void +recv_peers(int sockfd, const unsigned char *session_key) +{ + struct peer_block pb; + struct peer peer; + int i, len; + + memset(&pb, 0, sizeof(pb)); + memset(&peer, 0, sizeof(peer)); + + /* Receive the peer block the server sends back */ + if ((len = recv_frame(sockfd, sizeof(pb), session_key, &pb)) < 0) { + printf("Failed to recv peers...\n"); + return; + } + + printf("Got %d bytes...\n", len); + printf("seq=%x\n", pb.seq); + + for (i = 0; i < PB_N_PEERS; ++i) { + peer = pb.peers[i]; + if (peer.pad[0] != 0xFF) { + continue; + } + + printf("Peer available @ %s:%d\n", peer.host, peer.port); + } +} + int session_new(const char *host, struct ostp_session *res) { @@ -233,7 +262,7 @@ session_new(const char *host, struct ostp_session *res) * Setup the session request and add our public * key to it. We also assume user authentication. */ - stp_sq.options = SESSION_REQ_USER; + stp_sq.options = (SESSION_REQ_USER | SESSION_REQ_P2P); memcpy(stp_sq.pubkey, keypair.pubkey, sizeof(stp_sq.pubkey)); LOG("Sending session request...\n"); @@ -266,6 +295,17 @@ session_new(const char *host, struct ostp_session *res) return error; } + /* + * If we requested P2P, the server will respond + * with peer blocks. + */ + if (ISSET(stp_sq.options, SESSION_REQ_P2P)) { + recv_peers(sockfd, session_key); + free_session_key(session_key); + close(sockfd); + return 0; + } + /* Send server SPW bits */ if ((error = client_negotiate_spw(sockfd, session_key)) < 0) { LOG("Session Parameter Negotiation failed\n"); -- cgit v1.2.3